Merge branch 'melonDS-emu:master' into master

This commit is contained in:
NPO 2024-11-21 12:11:46 -05:00 committed by GitHub
commit 36a54cb8b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
143 changed files with 18048 additions and 10982 deletions

View File

@ -13,6 +13,7 @@ env:
MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_BRANCH: ${{ github.ref }}
MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_GIT_HASH: ${{ github.sha }}
MELONDS_BUILD_PROVIDER: GitHub Actions MELONDS_BUILD_PROVIDER: GitHub Actions
MELONDS_VERSION_SUFFIX: " RC"
jobs: jobs:
build-macos: build-macos:

View File

@ -12,6 +12,7 @@ env:
MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_BRANCH: ${{ github.ref }}
MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_GIT_HASH: ${{ github.sha }}
MELONDS_BUILD_PROVIDER: GitHub Actions MELONDS_BUILD_PROVIDER: GitHub Actions
MELONDS_VERSION_SUFFIX: " RC"
jobs: jobs:
build-x86_64: build-x86_64:

View File

@ -13,6 +13,7 @@ env:
MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_BRANCH: ${{ github.ref }}
MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_GIT_HASH: ${{ github.sha }}
MELONDS_BUILD_PROVIDER: GitHub Actions MELONDS_BUILD_PROVIDER: GitHub Actions
MELONDS_VERSION_SUFFIX: " RC"
jobs: jobs:
build: build:

View File

@ -16,7 +16,7 @@ if (USE_VCPKG)
endif() endif()
project(melonDS project(melonDS
VERSION 0.9.5 VERSION 1.0
DESCRIPTION "DS emulator, sorta" DESCRIPTION "DS emulator, sorta"
HOMEPAGE_URL "https://melonds.kuribo64.net" HOMEPAGE_URL "https://melonds.kuribo64.net"
LANGUAGES C CXX) LANGUAGES C CXX)

View File

@ -21,7 +21,7 @@
melonDS = pkgs.stdenv.mkDerivation { melonDS = pkgs.stdenv.mkDerivation {
pname = "melonDS"; pname = "melonDS";
version = "0.9.5-${shortRevision}"; version = "1.0-${shortRevision}";
src = ./.; src = ./.;
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [

BIN
res/icon/melon_192x192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@ -2,6 +2,6 @@
<RCC version="1.0"> <RCC version="1.0">
<qresource> <qresource>
<file alias="melon-icon">icon/melon_256x256.png</file> <file alias="melon-icon">icon/melon_256x256.png</file>
<file alias="melon-logo">melon.svg</file> <file alias="melon-logo">melon384.png</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -6,8 +6,8 @@
//include version information in .exe, modify these values to match your needs //include version information in .exe, modify these values to match your needs
1 VERSIONINFO 1 VERSIONINFO
FILEVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0 FILEVERSION ${MELON_RC_VERSION}
PRODUCTVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0 PRODUCTVERSION ${MELON_RC_VERSION}
FILETYPE VFT_APP FILETYPE VFT_APP
{ {
BLOCK "StringFileInfo" BLOCK "StringFileInfo"

BIN
res/melon384.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
res/melon512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -109,21 +109,13 @@ const u32 ARM::ConditionTable[16] =
ARM::ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, melonDS::NDS& nds) : ARM::ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, melonDS::NDS& nds) :
#ifdef GDBSTUB_ENABLED #ifdef GDBSTUB_ENABLED
GdbStub(this, gdb ? (num ? gdb->PortARM7 : gdb->PortARM9) : 0), GdbStub(this),
BreakOnStartup(gdb ? (num ? gdb->ARM7BreakOnStartup : gdb->ARM9BreakOnStartup) : false), BreakOnStartup(false),
#endif #endif
Num(num), // well uh Num(num), // well uh
NDS(nds) NDS(nds)
{ {
#ifdef GDBSTUB_ENABLED SetGdbArgs(jit ? gdb : std::nullopt);
if (gdb
#ifdef JIT_ENABLED
&& !jit // TODO: Should we support toggling the GdbStub without destroying the ARM?
#endif
)
GdbStub.Init();
IsSingleStep = false;
#endif
} }
ARM::~ARM() ARM::~ARM()
@ -148,6 +140,20 @@ ARMv5::~ARMv5()
// DTCM is owned by Memory, not going to delete it // DTCM is owned by Memory, not going to delete it
} }
void ARM::SetGdbArgs(std::optional<GDBArgs> gdb)
{
#ifdef GDBSTUB_ENABLED
GdbStub.Close();
if (gdb)
{
int port = Num ? gdb->PortARM7 : gdb->PortARM9;
GdbStub.Init(port);
BreakOnStartup = Num ? gdb->ARM7BreakOnStartup : gdb->ARM9BreakOnStartup;
}
IsSingleStep = false;
#endif
}
void ARM::Reset() void ARM::Reset()
{ {
Cycles = 0; Cycles = 0;

View File

@ -68,6 +68,8 @@ public:
ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, NDS& nds); ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, NDS& nds);
virtual ~ARM(); // destroy shit virtual ~ARM(); // destroy shit
void SetGdbArgs(std::optional<GDBArgs> gdb);
virtual void Reset(); virtual void Reset();
virtual void DoSavestate(Savestate* file); virtual void DoSavestate(Savestate* file);
@ -323,6 +325,7 @@ public:
u32 CP15Control; u32 CP15Control;
u32 RNGSeed; u32 RNGSeed;
u32 TraceProcessID;
u32 DTCMSetting, ITCMSetting; u32 DTCMSetting, ITCMSetting;

View File

@ -30,6 +30,7 @@
#include "ARMJIT_Internal.h" #include "ARMJIT_Internal.h"
#include "ARMJIT_Memory.h" #include "ARMJIT_Memory.h"
#include "ARMJIT_Compiler.h" #include "ARMJIT_Compiler.h"
#include "ARMJIT_Global.h"
#include "ARMInterpreter_ALU.h" #include "ARMInterpreter_ALU.h"
#include "ARMInterpreter_LoadStore.h" #include "ARMInterpreter_LoadStore.h"
@ -467,6 +468,16 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] =
}; };
#undef F #undef F
ARMJIT::ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept :
NDS(nds),
Memory(nds),
JITCompiler(nds),
MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32),
LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false),
BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false),
FastMemory((jit.has_value() ? jit->FastMemory : false) && ARMJIT_Memory::IsFastMemSupported())
{}
void ARMJIT::RetireJitBlock(JitBlock* block) noexcept void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
{ {
auto it = RestoreCandidates.find(block->InstrHash); auto it = RestoreCandidates.find(block->InstrHash);
@ -483,6 +494,7 @@ void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
void ARMJIT::SetJITArgs(JITArgs args) noexcept void ARMJIT::SetJITArgs(JITArgs args) noexcept
{ {
args.FastMemory = args.FastMemory && ARMJIT_Memory::IsFastMemSupported();
args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u); args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u);
if (MaxBlockSize != args.MaxBlockSize if (MaxBlockSize != args.MaxBlockSize
@ -499,36 +511,22 @@ void ARMJIT::SetJITArgs(JITArgs args) noexcept
void ARMJIT::SetMaxBlockSize(int size) noexcept void ARMJIT::SetMaxBlockSize(int size) noexcept
{ {
size = std::clamp(size, 1, 32); SetJITArgs(JITArgs{static_cast<unsigned>(size), LiteralOptimizations, LiteralOptimizations, FastMemory});
if (size != MaxBlockSize)
ResetBlockCache();
MaxBlockSize = size;
} }
void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept
{ {
if (LiteralOptimizations != enabled) SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), enabled, BranchOptimizations, FastMemory});
ResetBlockCache();
LiteralOptimizations = enabled;
} }
void ARMJIT::SetBranchOptimizations(bool enabled) noexcept void ARMJIT::SetBranchOptimizations(bool enabled) noexcept
{ {
if (BranchOptimizations != enabled) SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), LiteralOptimizations, enabled, FastMemory});
ResetBlockCache();
BranchOptimizations = enabled;
} }
void ARMJIT::SetFastMemory(bool enabled) noexcept void ARMJIT::SetFastMemory(bool enabled) noexcept
{ {
if (FastMemory != enabled) SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), LiteralOptimizations, BranchOptimizations, enabled});
ResetBlockCache();
FastMemory = enabled;
} }
void ARMJIT::CompileBlock(ARM* cpu) noexcept void ARMJIT::CompileBlock(ARM* cpu) noexcept
@ -918,7 +916,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
AddressRange* region = CodeMemRegions[addressRanges[j] >> 27]; AddressRange* region = CodeMemRegions[addressRanges[j] >> 27];
if (!PageContainsCode(&region[(addressRanges[j] & 0x7FFF000) / 512])) if (!PageContainsCode(&region[(addressRanges[j] & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true); Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
AddressRange* range = &region[(addressRanges[j] & 0x7FFFFFF) / 512]; AddressRange* range = &region[(addressRanges[j] & 0x7FFFFFF) / 512];
@ -971,7 +969,7 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
range->Blocks.Remove(i); range->Blocks.Remove(i);
if (range->Blocks.Length == 0 if (range->Blocks.Length == 0
&& !PageContainsCode(&region[(localAddr & 0x7FFF000) / 512])) && !PageContainsCode(&region[(localAddr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
{ {
Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false); Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
} }
@ -1005,7 +1003,7 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
if (otherRange->Blocks.Length == 0) if (otherRange->Blocks.Length == 0)
{ {
if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000) / 512])) if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
Memory.SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false); Memory.SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
otherRange->Code = 0; otherRange->Code = 0;

View File

@ -44,15 +44,7 @@ class JitBlock;
class ARMJIT class ARMJIT
{ {
public: public:
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept : ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept;
NDS(nds),
Memory(nds),
JITCompiler(nds),
MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32),
LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false),
BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false),
FastMemory(jit.has_value() ? jit->FastMemory : false)
{}
~ARMJIT() noexcept; ~ARMJIT() noexcept;
void InvalidateByAddr(u32) noexcept; void InvalidateByAddr(u32) noexcept;
void CheckAndInvalidateWVRAM(int) noexcept; void CheckAndInvalidateWVRAM(int) noexcept;
@ -80,6 +72,7 @@ private:
bool LiteralOptimizations = false; bool LiteralOptimizations = false;
bool BranchOptimizations = false; bool BranchOptimizations = false;
bool FastMemory = false; bool FastMemory = false;
public: public:
melonDS::NDS& NDS; melonDS::NDS& NDS;
TinyVector<u32> InvalidLiterals {}; TinyVector<u32> InvalidLiterals {};

View File

@ -22,17 +22,7 @@
#include "../ARMInterpreter.h" #include "../ARMInterpreter.h"
#include "../ARMJIT.h" #include "../ARMJIT.h"
#include "../NDS.h" #include "../NDS.h"
#include "../ARMJIT_Global.h"
#if defined(__SWITCH__)
#include <switch.h>
extern char __start__;
#elif defined(_WIN32)
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
#include <stdlib.h> #include <stdlib.h>
@ -66,11 +56,6 @@ const int RegisterCache<Compiler, ARM64Reg>::NativeRegsAvailable = 15;
const BitSet32 CallerSavedPushRegs({W8, W9, W10, W11, W12, W13, W14, W15}); const BitSet32 CallerSavedPushRegs({W8, W9, W10, W11, W12, W13, W14, W15});
const int JitMemSize = 16 * 1024 * 1024;
#ifndef __SWITCH__
u8 JitMem[JitMemSize];
#endif
void Compiler::MovePC() void Compiler::MovePC()
{ {
ADD(MapReg(15), MapReg(15), Thumb ? 2 : 4); ADD(MapReg(15), MapReg(15), Thumb ? 2 : 4);
@ -260,29 +245,13 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
SetCodeBase((u8*)JitRWStart, (u8*)JitRXStart); SetCodeBase((u8*)JitRWStart, (u8*)JitRXStart);
JitMemMainSize = JitMemSize; JitMemMainSize = JitMemSize;
#else #else
#ifdef _WIN32 ARMJIT_Global::Init();
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
u64 pageSize = (u64)sysInfo.dwPageSize; CodeMemBase = ARMJIT_Global::AllocateCodeMem();
#else nds.JIT.JitEnableWrite();
u64 pageSize = sysconf(_SC_PAGE_SIZE);
#endif
u8* pageAligned = (u8*)(((u64)JitMem & ~(pageSize - 1)) + pageSize);
u64 alignedSize = (((u64)JitMem + sizeof(JitMem)) & ~(pageSize - 1)) - (u64)pageAligned;
#if defined(_WIN32) SetCodeBase(reinterpret_cast<u8*>(CodeMemBase), reinterpret_cast<u8*>(CodeMemBase));
DWORD dummy; JitMemMainSize = ARMJIT_Global::CodeMemorySliceSize;
VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
#elif defined(__APPLE__)
pageAligned = (u8*)mmap(NULL, 1024*1024*16, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
nds.JIT.JitEnableWrite();
#else
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
#endif
SetCodeBase(pageAligned, pageAligned);
JitMemMainSize = alignedSize;
#endif #endif
SetCodePtr(0); SetCodePtr(0);
@ -493,6 +462,9 @@ Compiler::~Compiler()
free(JitRWBase); free(JitRWBase);
} }
#endif #endif
ARMJIT_Global::FreeCodeMem(CodeMemBase);
ARMJIT_Global::DeInit();
} }
void Compiler::LoadCycles() void Compiler::LoadCycles()

View File

@ -275,6 +275,7 @@ public:
void* JitRWStart; void* JitRWStart;
void* JitRXStart; void* JitRXStart;
#endif #endif
void* CodeMemBase;
void* ReadBanked, *WriteBanked; void* ReadBanked, *WriteBanked;

126
src/ARMJIT_Global.cpp Normal file
View File

@ -0,0 +1,126 @@
#include "ARMJIT_Global.h"
#include "ARMJIT_Memory.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdint.h>
#include <mutex>
namespace melonDS
{
namespace ARMJIT_Global
{
std::mutex globalMutex;
#if defined(__APPLE__) && defined(__aarch64__)
#define APPLE_AARCH64
#endif
#ifndef APPLE_AARCH64
static constexpr size_t NumCodeMemSlices = 4;
static constexpr size_t CodeMemoryAlignedSize = NumCodeMemSlices * CodeMemorySliceSize;
// I haven't heard of pages larger than 16 KB
u8 CodeMemory[CodeMemoryAlignedSize + 16*1024];
u32 AvailableCodeMemSlices = (1 << NumCodeMemSlices) - 1;
u8* GetAlignedCodeMemoryStart()
{
return reinterpret_cast<u8*>((reinterpret_cast<intptr_t>(CodeMemory) + (16*1024-1)) & ~static_cast<intptr_t>(16*1024-1));
}
#endif
int RefCounter = 0;
void* AllocateCodeMem()
{
std::lock_guard guard(globalMutex);
#ifndef APPLE_AARCH64
if (AvailableCodeMemSlices)
{
int slice = __builtin_ctz(AvailableCodeMemSlices);
AvailableCodeMemSlices &= ~(1 << slice);
//printf("allocating slice %d\n", slice);
return &GetAlignedCodeMemoryStart()[slice * CodeMemorySliceSize];
}
#endif
// allocate
#ifdef _WIN32
return VirtualAlloc(nullptr, CodeMemorySliceSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#elif defined(APPLE_AARCH64)
return mmap(NULL, CodeMemorySliceSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
#else
//printf("mmaping...\n");
return mmap(nullptr, CodeMemorySliceSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
#endif
}
void FreeCodeMem(void* codeMem)
{
std::lock_guard guard(globalMutex);
#ifndef APPLE_AARCH64
for (int i = 0; i < NumCodeMemSlices; i++)
{
if (codeMem == &GetAlignedCodeMemoryStart()[CodeMemorySliceSize * i])
{
//printf("freeing slice\n");
AvailableCodeMemSlices |= 1 << i;
return;
}
}
#endif
#ifdef _WIN32
VirtualFree(codeMem, CodeMemorySliceSize, MEM_RELEASE|MEM_DECOMMIT);
#else
munmap(codeMem, CodeMemorySliceSize);
#endif
}
void Init()
{
std::lock_guard guard(globalMutex);
RefCounter++;
if (RefCounter == 1)
{
#ifdef _WIN32
DWORD dummy;
VirtualProtect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PAGE_EXECUTE_READWRITE, &dummy);
#elif defined(APPLE_AARCH64)
// Apple aarch64 always uses dynamic allocation
#else
mprotect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
#endif
ARMJIT_Memory::RegisterFaultHandler();
}
}
void DeInit()
{
std::lock_guard guard(globalMutex);
RefCounter--;
if (RefCounter == 0)
{
ARMJIT_Memory::UnregisterFaultHandler();
}
}
}
}

44
src/ARMJIT_Global.h Normal file
View File

@ -0,0 +1,44 @@
/*
Copyright 2016-2024 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef ARMJIT_GLOBAL_H
#define ARMJIT_GLOBAL_H
#include "types.h"
#include <stdlib.h>
namespace melonDS
{
namespace ARMJIT_Global
{
static constexpr size_t CodeMemorySliceSize = 1024*1024*32;
void Init();
void DeInit();
void* AllocateCodeMem();
void FreeCodeMem(void* codeMem);
}
}
#endif

View File

@ -85,9 +85,9 @@ typedef void (*InterpreterFunc)(ARM* cpu);
extern InterpreterFunc InterpretARM[]; extern InterpreterFunc InterpretARM[];
extern InterpreterFunc InterpretTHUMB[]; extern InterpreterFunc InterpretTHUMB[];
inline bool PageContainsCode(const AddressRange* range) inline bool PageContainsCode(const AddressRange* range, u32 pageSize)
{ {
for (int i = 0; i < 8; i++) for (int i = 0; i < pageSize / 512; i++)
{ {
if (range[i].Blocks.Length > 0) if (range[i].Blocks.Length > 0)
return true; return true;

View File

@ -39,6 +39,7 @@
#include "ARMJIT_Internal.h" #include "ARMJIT_Internal.h"
#include "ARMJIT_Compiler.h" #include "ARMJIT_Compiler.h"
#include "ARMJIT_Global.h"
#include "DSi.h" #include "DSi.h"
#include "GPU.h" #include "GPU.h"
@ -100,6 +101,9 @@
namespace melonDS namespace melonDS
{ {
static constexpr u64 AddrSpaceSize = 0x100000000;
static constexpr u64 VirtmemAreaSize = AddrSpaceSize * 2 + MemoryTotalSize;
using Platform::Log; using Platform::Log;
using Platform::LogLevel; using Platform::LogLevel;
@ -152,6 +156,15 @@ void __libnx_exception_handler(ThreadExceptionDump* ctx)
#elif defined(_WIN32) #elif defined(_WIN32)
static LPVOID ExceptionHandlerHandle = nullptr;
static HMODULE KernelBaseDll = nullptr;
using VirtualAlloc2Type = PVOID WINAPI (*)(HANDLE Process, PVOID BaseAddress, SIZE_T Size, ULONG AllocationType, ULONG PageProtection, MEM_EXTENDED_PARAMETER* ExtendedParameters, ULONG ParameterCount);
using MapViewOfFile3Type = PVOID WINAPI (*)(HANDLE FileMapping, HANDLE Process, PVOID BaseAddress, ULONG64 Offset, SIZE_T ViewSize, ULONG AllocationType, ULONG PageProtection, MEM_EXTENDED_PARAMETER* ExtendedParameters, ULONG ParameterCount);
static VirtualAlloc2Type virtualAlloc2Ptr;
static MapViewOfFile3Type mapViewOfFile3Ptr;
LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo)
{ {
if (exceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) if (exceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
@ -170,6 +183,7 @@ LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo)
return EXCEPTION_CONTINUE_EXECUTION; return EXCEPTION_CONTINUE_EXECUTION;
} }
Log(LogLevel::Debug, "it all returns to nothing\n");
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
@ -261,18 +275,61 @@ enum
memstate_MappedProtected, memstate_MappedProtected,
}; };
#define CHECK_ALIGNED(value) assert(((value) & (PageSize-1)) == 0)
bool ARMJIT_Memory::MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept bool ARMJIT_Memory::MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept
{ {
CHECK_ALIGNED(addr);
CHECK_ALIGNED(offset);
CHECK_ALIGNED(size);
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr; u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
#ifdef __SWITCH__ #ifdef __SWITCH__
Result r = (svcMapProcessMemory(dst, envGetOwnProcessHandle(), Result r = (svcMapProcessMemory(dst, envGetOwnProcessHandle(),
(u64)(MemoryBaseCodeMem + offset), size)); (u64)(MemoryBaseCodeMem + offset), size));
return R_SUCCEEDED(r); return R_SUCCEEDED(r);
#elif defined(_WIN32) #elif defined(_WIN32)
bool r = MapViewOfFileEx(MemoryFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, offset, size, dst) == dst; uintptr_t uintptrDst = reinterpret_cast<uintptr_t>(dst);
return r; for (auto it = VirtmemPlaceholders.begin(); it != VirtmemPlaceholders.end(); it++)
{
if (uintptrDst >= it->Start && uintptrDst+size <= it->Start+it->Size)
{
//Log(LogLevel::Debug, "found mapping %llx %llx %llx %llx\n", uintptrDst, size, it->Start, it->Size);
// we split this place holder so that we have a fitting place holder for the mapping
if (uintptrDst != it->Start || size != it->Size)
{
if (!VirtualFree(dst, size, MEM_RELEASE|MEM_PRESERVE_PLACEHOLDER))
{
Log(LogLevel::Debug, "VirtualFree failed with %x\n", GetLastError());
return false;
}
}
VirtmemPlaceholder splitPlaceholder = *it;
VirtmemPlaceholders.erase(it);
if (uintptrDst > splitPlaceholder.Start)
{
//Log(LogLevel::Debug, "splitting on the left %llx\n", uintptrDst - splitPlaceholder.Start);
VirtmemPlaceholders.push_back({splitPlaceholder.Start, uintptrDst - splitPlaceholder.Start});
}
if (uintptrDst+size < splitPlaceholder.Start+splitPlaceholder.Size)
{
//Log(LogLevel::Debug, "splitting on the right %llx\n", (splitPlaceholder.Start+splitPlaceholder.Size)-(uintptrDst+size));
VirtmemPlaceholders.push_back({uintptrDst+size, (splitPlaceholder.Start+splitPlaceholder.Size)-(uintptrDst+size)});
}
if (!mapViewOfFile3Ptr(MemoryFile, nullptr, dst, offset, size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0))
{
Log(LogLevel::Debug, "MapViewOfFile3 failed with %x\n", GetLastError());
return false;
}
return true;
}
}
Log(LogLevel::Debug, "no mapping at all found??? %p %x %p\n", dst, size, MemoryBase);
return false;
#else #else
return mmap(dst, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, offset) != MAP_FAILED; return mmap(dst, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, offset) != MAP_FAILED;
#endif #endif
@ -280,21 +337,68 @@ bool ARMJIT_Memory::MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexce
bool ARMJIT_Memory::UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept bool ARMJIT_Memory::UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept
{ {
CHECK_ALIGNED(addr);
CHECK_ALIGNED(offset);
CHECK_ALIGNED(size);
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr; u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
#ifdef __SWITCH__ #ifdef __SWITCH__
Result r = svcUnmapProcessMemory(dst, envGetOwnProcessHandle(), Result r = svcUnmapProcessMemory(dst, envGetOwnProcessHandle(),
(u64)(MemoryBaseCodeMem + offset), size); (u64)(MemoryBaseCodeMem + offset), size);
return R_SUCCEEDED(r); return R_SUCCEEDED(r);
#elif defined(_WIN32) #elif defined(_WIN32)
return UnmapViewOfFile(dst); if (!UnmapViewOfFileEx(dst, MEM_PRESERVE_PLACEHOLDER))
{
Log(LogLevel::Debug, "UnmapViewOfFileEx failed %x\n", GetLastError());
return false;
}
uintptr_t uintptrDst = reinterpret_cast<uintptr_t>(dst);
uintptr_t coalesceStart = uintptrDst;
size_t coalesceSize = size;
for (auto it = VirtmemPlaceholders.begin(); it != VirtmemPlaceholders.end();)
{
if (it->Start+it->Size == uintptrDst)
{
//Log(LogLevel::Debug, "Coalescing to the left\n");
coalesceStart = it->Start;
coalesceSize += it->Size;
it = VirtmemPlaceholders.erase(it);
}
else if (it->Start == uintptrDst+size)
{
//Log(LogLevel::Debug, "Coalescing to the right\n");
coalesceSize += it->Size;
it = VirtmemPlaceholders.erase(it);
}
else
{
it++;
}
}
if (coalesceStart != uintptrDst || coalesceSize != size)
{
if (!VirtualFree(reinterpret_cast<void*>(coalesceStart), coalesceSize, MEM_RELEASE|MEM_COALESCE_PLACEHOLDERS))
return false;
}
VirtmemPlaceholders.push_back({coalesceStart, coalesceSize});
//Log(LogLevel::Debug, "Adding coalesced region %llx %llx", coalesceStart, coalesceSize);
return true;
#else #else
return munmap(dst, size) == 0; return mmap(dst, size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0) != MAP_FAILED;
#endif #endif
} }
#ifndef __SWITCH__ #ifndef __SWITCH__
void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept
{ {
CHECK_ALIGNED(addr);
CHECK_ALIGNED(size);
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr; u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
#if defined(_WIN32) #if defined(_WIN32)
DWORD winProtection, oldProtection; DWORD winProtection, oldProtection;
@ -305,6 +409,10 @@ void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int prot
else else
winProtection = PAGE_READWRITE; winProtection = PAGE_READWRITE;
bool success = VirtualProtect(dst, size, winProtection, &oldProtection); bool success = VirtualProtect(dst, size, winProtection, &oldProtection);
if (!success)
{
Log(LogLevel::Debug, "VirtualProtect failed with %x\n", GetLastError());
}
assert(success); assert(success);
#else #else
int posixProt; int posixProt;
@ -335,14 +443,14 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
else else
{ {
u32 segmentOffset = offset; u32 segmentOffset = offset;
u8 status = statuses[(Addr + offset) >> 12]; u8 status = statuses[(Addr + offset) >> PageShift];
while (statuses[(Addr + offset) >> 12] == status while (statuses[(Addr + offset) >> PageShift] == status
&& offset < Size && offset < Size
&& (!skipDTCM || Addr + offset != dtcmStart)) && (!skipDTCM || Addr + offset != dtcmStart))
{ {
assert(statuses[(Addr + offset) >> 12] != memstate_Unmapped); assert(statuses[(Addr + offset) >> PageShift] != memstate_Unmapped);
statuses[(Addr + offset) >> 12] = memstate_Unmapped; statuses[(Addr + offset) >> PageShift] = memstate_Unmapped;
offset += 0x1000; offset += PageSize;
} }
#ifdef __SWITCH__ #ifdef __SWITCH__
@ -358,7 +466,6 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
} }
#ifndef __SWITCH__ #ifndef __SWITCH__
#ifndef _WIN32
u32 dtcmEnd = dtcmStart + dtcmSize; u32 dtcmEnd = dtcmStart + dtcmSize;
if (Num == 0 if (Num == 0
&& dtcmEnd >= Addr && dtcmEnd >= Addr
@ -378,7 +485,6 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
} }
} }
else else
#endif
{ {
bool succeded = nds.JIT.Memory.UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size); bool succeded = nds.JIT.Memory.UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size);
assert(succeded); assert(succeded);
@ -388,7 +494,7 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noexcept void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noexcept
{ {
offset &= ~0xFFF; offset &= ~(PageSize - 1);
//printf("set code protection %d %x %d\n", region, offset, protect); //printf("set code protection %d %x %d\n", region, offset, protect);
for (int i = 0; i < Mappings[region].Length; i++) for (int i = 0; i < Mappings[region].Length; i++)
@ -406,9 +512,9 @@ void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noex
u8* states = (u8*)(mapping.Num == 0 ? MappingStatus9 : MappingStatus7); u8* states = (u8*)(mapping.Num == 0 ? MappingStatus9 : MappingStatus7);
//printf("%x %d %x %x %x %d\n", effectiveAddr, mapping.Num, mapping.Addr, mapping.LocalOffset, mapping.Size, states[effectiveAddr >> 12]); //printf("%x %d %x %x %x %d\n", effectiveAddr, mapping.Num, mapping.Addr, mapping.LocalOffset, mapping.Size, states[effectiveAddr >> PageShift]);
assert(states[effectiveAddr >> 12] == (protect ? memstate_MappedRW : memstate_MappedProtected)); assert(states[effectiveAddr >> PageShift] == (protect ? memstate_MappedRW : memstate_MappedProtected));
states[effectiveAddr >> 12] = protect ? memstate_MappedProtected : memstate_MappedRW; states[effectiveAddr >> PageShift] = protect ? memstate_MappedProtected : memstate_MappedRW;
#if defined(__SWITCH__) #if defined(__SWITCH__)
bool success; bool success;
@ -418,7 +524,7 @@ void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noex
success = MapIntoRange(effectiveAddr, mapping.Num, OffsetsPerRegion[region] + offset, 0x1000); success = MapIntoRange(effectiveAddr, mapping.Num, OffsetsPerRegion[region] + offset, 0x1000);
assert(success); assert(success);
#else #else
SetCodeProtectionRange(effectiveAddr, 0x1000, mapping.Num, protect ? 1 : 2); SetCodeProtectionRange(effectiveAddr, PageSize, mapping.Num, protect ? 1 : 2);
#endif #endif
} }
} }
@ -543,11 +649,19 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
u32 dtcmSize = ~NDS.ARM9.DTCMMask + 1; u32 dtcmSize = ~NDS.ARM9.DTCMMask + 1;
u32 dtcmEnd = dtcmStart + dtcmSize; u32 dtcmEnd = dtcmStart + dtcmSize;
#ifndef __SWITCH__ #ifndef __SWITCH__
#ifndef _WIN32
if (num == 0 if (num == 0
&& dtcmEnd >= mirrorStart && dtcmEnd >= mirrorStart
&& dtcmStart < mirrorStart + mirrorSize) && dtcmStart < mirrorStart + mirrorSize)
{ {
if (dtcmSize < PageSize)
{
// we could technically mask out the DTCM by setting a hole to access permissions
// but realistically there isn't much of a point in mapping less than 16kb of DTCM
// so it isn't worth more complex support
Log(LogLevel::Info, "DTCM size smaller than 16kb skipping mapping entirely");
return false;
}
bool success; bool success;
if (dtcmStart > mirrorStart) if (dtcmStart > mirrorStart)
{ {
@ -562,7 +676,6 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
} }
} }
else else
#endif
{ {
bool succeded = MapIntoRange(mirrorStart, num, OffsetsPerRegion[region] + memoryOffset, mirrorSize); bool succeded = MapIntoRange(mirrorStart, num, OffsetsPerRegion[region] + memoryOffset, mirrorSize);
assert(succeded); assert(succeded);
@ -579,22 +692,19 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
{ {
if (skipDTCM && mirrorStart + offset == dtcmStart) if (skipDTCM && mirrorStart + offset == dtcmStart)
{ {
#ifdef _WIN32
SetCodeProtectionRange(dtcmStart, dtcmSize, 0, 0);
#endif
offset += dtcmSize; offset += dtcmSize;
} }
else else
{ {
u32 sectionOffset = offset; u32 sectionOffset = offset;
bool hasCode = isExecutable && PageContainsCode(&range[offset / 512]); bool hasCode = isExecutable && PageContainsCode(&range[offset / 512], PageSize);
while (offset < mirrorSize while (offset < mirrorSize
&& (!isExecutable || PageContainsCode(&range[offset / 512]) == hasCode) && (!isExecutable || PageContainsCode(&range[offset / 512], PageSize) == hasCode)
&& (!skipDTCM || mirrorStart + offset != NDS.ARM9.DTCMBase)) && (!skipDTCM || mirrorStart + offset != NDS.ARM9.DTCMBase))
{ {
assert(states[(mirrorStart + offset) >> 12] == memstate_Unmapped); assert(states[(mirrorStart + offset) >> PageShift] == memstate_Unmapped);
states[(mirrorStart + offset) >> 12] = hasCode ? memstate_MappedProtected : memstate_MappedRW; states[(mirrorStart + offset) >> PageShift] = hasCode ? memstate_MappedProtected : memstate_MappedRW;
offset += 0x1000; offset += PageSize;
} }
u32 sectionSize = offset - sectionOffset; u32 sectionSize = offset - sectionOffset;
@ -624,6 +734,86 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
return true; return true;
} }
u32 ARMJIT_Memory::PageSize = 0;
u32 ARMJIT_Memory::PageShift = 0;
bool ARMJIT_Memory::IsFastMemSupported()
{
#ifdef __APPLE__
return false;
#else
static bool initialised = false;
static bool isSupported = false;
if (!initialised)
{
#ifdef _WIN32
ARMJIT_Global::Init();
isSupported = virtualAlloc2Ptr != nullptr;
ARMJIT_Global::DeInit();
PageSize = RegularPageSize;
#else
PageSize = __sysconf(_SC_PAGESIZE);
isSupported = PageSize == RegularPageSize || PageSize == LargePageSize;
#endif
PageShift = __builtin_ctz(PageSize);
initialised = true;
}
return isSupported;
#endif
}
void ARMJIT_Memory::RegisterFaultHandler()
{
#ifdef _WIN32
ExceptionHandlerHandle = AddVectoredExceptionHandler(1, ExceptionHandler);
KernelBaseDll = LoadLibrary("KernelBase.dll");
if (KernelBaseDll)
{
virtualAlloc2Ptr = reinterpret_cast<VirtualAlloc2Type>(GetProcAddress(KernelBaseDll, "VirtualAlloc2"));
mapViewOfFile3Ptr = reinterpret_cast<MapViewOfFile3Type>(GetProcAddress(KernelBaseDll, "MapViewOfFile3"));
}
if (!virtualAlloc2Ptr)
{
Log(LogLevel::Error, "Could not load new Windows virtual memory functions, fast memory is disabled.\n");
}
#else
struct sigaction sa;
sa.sa_handler = nullptr;
sa.sa_sigaction = &SigsegvHandler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, &OldSaSegv);
#ifdef __APPLE__
sigaction(SIGBUS, &sa, &OldSaBus);
#endif
#endif
}
void ARMJIT_Memory::UnregisterFaultHandler()
{
#ifdef _WIN32
if (ExceptionHandlerHandle)
{
RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
ExceptionHandlerHandle = nullptr;
}
if (KernelBaseDll)
{
FreeLibrary(KernelBaseDll);
KernelBaseDll = nullptr;
}
#else
sigaction(SIGSEGV, &OldSaSegv, nullptr);
#ifdef __APPLE__
sigaction(SIGBUS, &OldSaBus, nullptr);
#endif
#endif
}
bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds) bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
{ {
if (nds.JIT.JITCompiler.IsJITFault(faultDesc.FaultPC)) if (nds.JIT.JITCompiler.IsJITFault(faultDesc.FaultPC))
@ -632,7 +822,7 @@ bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
u8* memStatus = nds.CurCPU == 0 ? nds.JIT.Memory.MappingStatus9 : nds.JIT.Memory.MappingStatus7; u8* memStatus = nds.CurCPU == 0 ? nds.JIT.Memory.MappingStatus9 : nds.JIT.Memory.MappingStatus7;
if (memStatus[faultDesc.EmulatedFaultAddr >> 12] == memstate_Unmapped) if (memStatus[faultDesc.EmulatedFaultAddr >> PageShift] == memstate_Unmapped)
rewriteToSlowPath = !nds.JIT.Memory.MapAtAddress(faultDesc.EmulatedFaultAddr); rewriteToSlowPath = !nds.JIT.Memory.MapAtAddress(faultDesc.EmulatedFaultAddr);
if (rewriteToSlowPath) if (rewriteToSlowPath)
@ -643,10 +833,9 @@ bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
return false; return false;
} }
const u64 AddrSpaceSize = 0x100000000;
ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds) ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
{ {
ARMJIT_Global::Init();
#if defined(__SWITCH__) #if defined(__SWITCH__)
MemoryBase = (u8*)aligned_alloc(0x1000, MemoryTotalSize); MemoryBase = (u8*)aligned_alloc(0x1000, MemoryTotalSize);
virtmemLock(); virtmemLock();
@ -671,33 +860,27 @@ ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
u8* basePtr = MemoryBaseCodeMem; u8* basePtr = MemoryBaseCodeMem;
#elif defined(_WIN32) #elif defined(_WIN32)
ExceptionHandlerHandle = AddVectoredExceptionHandler(1, ExceptionHandler); if (virtualAlloc2Ptr)
{
MemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, MemoryTotalSize, nullptr);
MemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MemoryTotalSize, NULL); MemoryBase = reinterpret_cast<u8*>(virtualAlloc2Ptr(nullptr, nullptr, VirtmemAreaSize,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS,
nullptr, 0));
// split off placeholder and map base mapping
VirtualFree(MemoryBase, MemoryTotalSize, MEM_RELEASE|MEM_PRESERVE_PLACEHOLDER);
mapViewOfFile3Ptr(MemoryFile, nullptr, MemoryBase, 0, MemoryTotalSize, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
MemoryBase = (u8*)VirtualAlloc(NULL, AddrSpaceSize*4, MEM_RESERVE, PAGE_READWRITE); VirtmemPlaceholders.push_back({reinterpret_cast<uintptr_t>(MemoryBase)+MemoryTotalSize, AddrSpaceSize*2});
VirtualFree(MemoryBase, 0, MEM_RELEASE); }
// this is incredible hacky else
// but someone else is trying to go into our address space! {
// Windows will very likely give them virtual memory starting at the same address // old Windows version
// as it is giving us now. MemoryBase = new u8[MemoryTotalSize];
// That's why we don't use this address, but instead 4gb inwards }
// I know this is terrible
FastMem9Start = MemoryBase + AddrSpaceSize;
FastMem7Start = MemoryBase + AddrSpaceSize*2;
MemoryBase = MemoryBase + AddrSpaceSize*3;
MapViewOfFileEx(MemoryFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, MemoryTotalSize, MemoryBase);
#else #else
// this used to be allocated with three different mmaps MemoryBase = (u8*)mmap(nullptr, VirtmemAreaSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
// The idea was to give the OS more freedom where to position the buffers,
// but something was bad about this so instead we take this vmem eating monster
// which seems to work better.
MemoryBase = (u8*)mmap(NULL, AddrSpaceSize*4, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
munmap(MemoryBase, AddrSpaceSize*4);
FastMem9Start = MemoryBase;
FastMem7Start = MemoryBase + AddrSpaceSize;
MemoryBase = MemoryBase + AddrSpaceSize*2;
#if defined(__ANDROID__) #if defined(__ANDROID__)
Libandroid = Platform::DynamicLibrary_Load("libandroid.so"); Libandroid = Platform::DynamicLibrary_Load("libandroid.so");
@ -730,20 +913,10 @@ ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
Log(LogLevel::Error, "Failed to allocate memory using ftruncate! (%s)", strerror(errno)); Log(LogLevel::Error, "Failed to allocate memory using ftruncate! (%s)", strerror(errno));
} }
struct sigaction sa;
sa.sa_handler = nullptr;
sa.sa_sigaction = &SigsegvHandler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, &OldSaSegv);
#ifdef __APPLE__
sigaction(SIGBUS, &sa, &OldSaBus);
#endif
mmap(MemoryBase, MemoryTotalSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, 0); mmap(MemoryBase, MemoryTotalSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, 0);
u8* basePtr = MemoryBase;
#endif #endif
FastMem9Start = MemoryBase+MemoryTotalSize;
FastMem7Start = static_cast<u8*>(FastMem9Start)+AddrSpaceSize;
} }
ARMJIT_Memory::~ARMJIT_Memory() noexcept ARMJIT_Memory::~ARMJIT_Memory() noexcept
@ -764,34 +937,37 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept
free(MemoryBase); free(MemoryBase);
MemoryBase = nullptr; MemoryBase = nullptr;
#elif defined(_WIN32) #elif defined(_WIN32)
if (MemoryBase) if (virtualAlloc2Ptr)
{ {
bool viewUnmapped = UnmapViewOfFile(MemoryBase); if (MemoryBase)
assert(viewUnmapped); {
MemoryBase = nullptr; bool viewUnmapped = UnmapViewOfFileEx(MemoryBase, MEM_PRESERVE_PLACEHOLDER);
FastMem9Start = nullptr; assert(viewUnmapped);
FastMem7Start = nullptr; bool viewCoalesced = VirtualFree(MemoryBase, VirtmemAreaSize, MEM_RELEASE|MEM_COALESCE_PLACEHOLDERS);
} assert(viewCoalesced);
bool freeEverything = VirtualFree(MemoryBase, 0, MEM_RELEASE);
assert(freeEverything);
if (MemoryFile) MemoryBase = nullptr;
{ FastMem9Start = nullptr;
CloseHandle(MemoryFile); FastMem7Start = nullptr;
MemoryFile = INVALID_HANDLE_VALUE; printf("unmappinged everything\n");
} }
if (ExceptionHandlerHandle) if (MemoryFile)
{
CloseHandle(MemoryFile);
MemoryFile = INVALID_HANDLE_VALUE;
}
}
else
{ {
RemoveVectoredExceptionHandler(ExceptionHandlerHandle); delete[] MemoryBase;
ExceptionHandlerHandle = nullptr;
} }
#else #else
sigaction(SIGSEGV, &OldSaSegv, nullptr);
#ifdef __APPLE__
sigaction(SIGBUS, &OldSaBus, nullptr);
#endif
if (MemoryBase) if (MemoryBase)
{ {
munmap(MemoryBase, MemoryTotalSize); munmap(MemoryBase, VirtmemAreaSize);
MemoryBase = nullptr; MemoryBase = nullptr;
FastMem9Start = nullptr; FastMem9Start = nullptr;
FastMem7Start = nullptr; FastMem7Start = nullptr;
@ -812,6 +988,8 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept
#endif #endif
#endif #endif
ARMJIT_Global::DeInit();
} }
void ARMJIT_Memory::Reset() noexcept void ARMJIT_Memory::Reset() noexcept
@ -834,17 +1012,6 @@ void ARMJIT_Memory::Reset() noexcept
bool ARMJIT_Memory::IsFastmemCompatible(int region) const noexcept bool ARMJIT_Memory::IsFastmemCompatible(int region) const noexcept
{ {
#ifdef _WIN32
/*
TODO: with some hacks, the smaller shared WRAM regions
could be mapped in some occaisons as well
*/
if (region == memregion_DTCM
|| region == memregion_SharedWRAM
|| region == memregion_NewSharedWRAM_B
|| region == memregion_NewSharedWRAM_C)
return false;
#endif
return OffsetsPerRegion[region] != UINT32_MAX; return OffsetsPerRegion[region] != UINT32_MAX;
} }

View File

@ -23,6 +23,7 @@
#include "MemConstants.h" #include "MemConstants.h"
#ifdef JIT_ENABLED #ifdef JIT_ENABLED
# include <mutex>
# include "TinyVector.h" # include "TinyVector.h"
# include "ARM.h" # include "ARM.h"
# if defined(__SWITCH__) # if defined(__SWITCH__)
@ -48,23 +49,22 @@ class Compiler;
class ARMJIT; class ARMJIT;
#endif #endif
static constexpr u32 LargePageSize = 0x4000;
static constexpr u32 RegularPageSize = 0x1000;
constexpr u32 RoundUp(u32 size) noexcept constexpr u32 RoundUp(u32 size) noexcept
{ {
#ifdef _WIN32 return (size + LargePageSize - 1) & ~(LargePageSize - 1);
return (size + 0xFFFF) & ~0xFFFF;
#else
return size;
#endif
} }
const u32 MemBlockMainRAMOffset = 0; static constexpr u32 MemBlockMainRAMOffset = 0;
const u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize); static constexpr u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize);
const u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize); static constexpr u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize);
const u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize); static constexpr u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize);
const u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize); static constexpr u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize);
const u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize); static constexpr u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize);
const u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize); static constexpr u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize);
const u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize); static constexpr u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize);
class ARMJIT_Memory class ARMJIT_Memory
{ {
@ -137,6 +137,14 @@ public:
bool IsFastmemCompatible(int region) const noexcept; bool IsFastmemCompatible(int region) const noexcept;
void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept; void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept;
bool MapAtAddress(u32 addr) noexcept; bool MapAtAddress(u32 addr) noexcept;
static bool IsFastMemSupported();
static void RegisterFaultHandler();
static void UnregisterFaultHandler();
static u32 PageSize;
static u32 PageShift;
private: private:
friend class Compiler; friend class Compiler;
struct Mapping struct Mapping
@ -162,14 +170,22 @@ private:
void* FastMem9Start; void* FastMem9Start;
void* FastMem7Start; void* FastMem7Start;
u8* MemoryBase = nullptr; u8* MemoryBase = nullptr;
#if defined(__SWITCH__) #if defined(__SWITCH__)
VirtmemReservation* FastMem9Reservation, *FastMem7Reservation; VirtmemReservation* FastMem9Reservation, *FastMem7Reservation;
u8* MemoryBaseCodeMem; u8* MemoryBaseCodeMem;
#elif defined(_WIN32) #elif defined(_WIN32)
struct VirtmemPlaceholder
{
uintptr_t Start;
size_t Size;
};
std::vector<VirtmemPlaceholder> VirtmemPlaceholders;
static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo); static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo);
HANDLE MemoryFile = INVALID_HANDLE_VALUE; HANDLE MemoryFile = INVALID_HANDLE_VALUE;
LPVOID ExceptionHandlerHandle = nullptr;
#else #else
static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext); static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext);
int MemoryFile = -1; int MemoryFile = -1;
#endif #endif

View File

@ -176,9 +176,9 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
else else
MOV(32, R(ABI_PARAM3), Imm32(true)); // what a waste MOV(32, R(ABI_PARAM3), Imm32(true)); // what a waste
if (Num == 0) if (Num == 0)
CALL((void*)&ARMv5JumpToTrampoline); ABI_CallFunction(ARMv5JumpToTrampoline);
else else
CALL((void*)&ARMv4JumpToTrampoline); ABI_CallFunction(ARMv4JumpToTrampoline);
PopRegs(restoreCPSR, true); PopRegs(restoreCPSR, true);

View File

@ -21,19 +21,13 @@
#include "../ARMJIT.h" #include "../ARMJIT.h"
#include "../ARMInterpreter.h" #include "../ARMInterpreter.h"
#include "../NDS.h" #include "../NDS.h"
#include "../ARMJIT_Global.h"
#include <assert.h> #include <assert.h>
#include <stdarg.h> #include <stdarg.h>
#include "../dolphin/CommonFuncs.h" #include "../dolphin/CommonFuncs.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
using namespace Gen; using namespace Gen;
using namespace Common; using namespace Common;
@ -222,46 +216,21 @@ void Compiler::A_Comp_MSR()
MOV(32, R(ABI_PARAM3), R(RCPSR)); MOV(32, R(ABI_PARAM3), R(RCPSR));
MOV(32, R(ABI_PARAM2), R(RSCRATCH3)); MOV(32, R(ABI_PARAM2), R(RSCRATCH3));
MOV(64, R(ABI_PARAM1), R(RCPU)); MOV(64, R(ABI_PARAM1), R(RCPU));
CALL((void*)&UpdateModeTrampoline); ABI_CallFunction(UpdateModeTrampoline);
PopRegs(true, true); PopRegs(true, true);
} }
} }
} }
/*
We'll repurpose this .bss memory
*/
u8 CodeMemory[1024 * 1024 * 32];
Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
{ {
{ ARMJIT_Global::Init();
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
u64 pageSize = (u64)sysInfo.dwPageSize; CodeMemBase = static_cast<u8*>(ARMJIT_Global::AllocateCodeMem());
#else CodeMemSize = ARMJIT_Global::CodeMemorySliceSize;
u64 pageSize = sysconf(_SC_PAGE_SIZE);
#endif
u8* pageAligned = (u8*)(((u64)CodeMemory & ~(pageSize - 1)) + pageSize); ResetStart = CodeMemBase;
u64 alignedSize = (((u64)CodeMemory + sizeof(CodeMemory)) & ~(pageSize - 1)) - (u64)pageAligned;
#ifdef _WIN32
DWORD dummy;
VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
#elif defined(__APPLE__)
pageAligned = (u8*)mmap(NULL, 1024*1024*32, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS ,-1, 0);
#else
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
#endif
ResetStart = pageAligned;
CodeMemSize = alignedSize;
}
Reset(); Reset();
@ -475,6 +444,13 @@ Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
FarSize = (ResetStart + CodeMemSize) - FarStart; FarSize = (ResetStart + CodeMemSize) - FarStart;
} }
Compiler::~Compiler()
{
ARMJIT_Global::FreeCodeMem(CodeMemBase);
ARMJIT_Global::DeInit();
}
void Compiler::LoadCPSR() void Compiler::LoadCPSR()
{ {
assert(!CPSRDirty); assert(!CPSRDirty);
@ -684,7 +660,7 @@ void Compiler::Comp_SpecialBranchBehaviour(bool taken)
if (ConstantCycles) if (ConstantCycles)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles)); ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
JMP((u8*)&ARM_Ret, true); ABI_TailCall(ARM_Ret);
} }
} }
@ -846,7 +822,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
if (ConstantCycles) if (ConstantCycles)
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles)); ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
JMP((u8*)ARM_Ret, true); ABI_TailCall(ARM_Ret);
#ifdef JIT_PROFILING_ENABLED #ifdef JIT_PROFILING_ENABLED
CreateMethod("JIT_Block_%d_%d_%08X", (void*)res, Num, Thumb, instrs[0].Addr); CreateMethod("JIT_Block_%d_%d_%08X", (void*)res, Num, Thumb, instrs[0].Addr);

View File

@ -84,6 +84,7 @@ class Compiler : public Gen::XEmitter
{ {
public: public:
explicit Compiler(melonDS::NDS& nds); explicit Compiler(melonDS::NDS& nds);
~Compiler();
void Reset(); void Reset();
@ -256,6 +257,7 @@ public:
std::unordered_map<u8*, LoadStorePatch> LoadStorePatches {}; std::unordered_map<u8*, LoadStorePatch> LoadStorePatches {};
u8* CodeMemBase;
u8* ResetStart {}; u8* ResetStart {};
u32 CodeMemSize {}; u32 CodeMemSize {};

View File

@ -316,24 +316,24 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
{ {
switch (size | NDS.ConsoleType) switch (size | NDS.ConsoleType)
{ {
case 32: CALL((void*)&SlowWrite9<u32, 0>); break; case 32: ABI_CallFunction(SlowWrite9<u32, 0>); break;
case 16: CALL((void*)&SlowWrite9<u16, 0>); break; case 16: ABI_CallFunction(SlowWrite9<u16, 0>); break;
case 8: CALL((void*)&SlowWrite9<u8, 0>); break; case 8: ABI_CallFunction(&SlowWrite9<u8, 0>); break;
case 33: CALL((void*)&SlowWrite9<u32, 1>); break; case 33: ABI_CallFunction(&SlowWrite9<u32, 1>); break;
case 17: CALL((void*)&SlowWrite9<u16, 1>); break; case 17: ABI_CallFunction(&SlowWrite9<u16, 1>); break;
case 9: CALL((void*)&SlowWrite9<u8, 1>); break; case 9: ABI_CallFunction(&SlowWrite9<u8, 1>); break;
} }
} }
else else
{ {
switch (size | NDS.ConsoleType) switch (size | NDS.ConsoleType)
{ {
case 32: CALL((void*)&SlowRead9<u32, 0>); break; case 32: ABI_CallFunction(&SlowRead9<u32, 0>); break;
case 16: CALL((void*)&SlowRead9<u16, 0>); break; case 16: ABI_CallFunction(&SlowRead9<u16, 0>); break;
case 8: CALL((void*)&SlowRead9<u8, 0>); break; case 8: ABI_CallFunction(&SlowRead9<u8, 0>); break;
case 33: CALL((void*)&SlowRead9<u32, 1>); break; case 33: ABI_CallFunction(&SlowRead9<u32, 1>); break;
case 17: CALL((void*)&SlowRead9<u16, 1>); break; case 17: ABI_CallFunction(&SlowRead9<u16, 1>); break;
case 9: CALL((void*)&SlowRead9<u8, 1>); break; case 9: ABI_CallFunction(&SlowRead9<u8, 1>); break;
} }
} }
} }
@ -347,24 +347,24 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
switch (size | NDS.ConsoleType) switch (size | NDS.ConsoleType)
{ {
case 32: CALL((void*)&SlowWrite7<u32, 0>); break; case 32: ABI_CallFunction(&SlowWrite7<u32, 0>); break;
case 16: CALL((void*)&SlowWrite7<u16, 0>); break; case 16: ABI_CallFunction(&SlowWrite7<u16, 0>); break;
case 8: CALL((void*)&SlowWrite7<u8, 0>); break; case 8: ABI_CallFunction(&SlowWrite7<u8, 0>); break;
case 33: CALL((void*)&SlowWrite7<u32, 1>); break; case 33: ABI_CallFunction(&SlowWrite7<u32, 1>); break;
case 17: CALL((void*)&SlowWrite7<u16, 1>); break; case 17: ABI_CallFunction(&SlowWrite7<u16, 1>); break;
case 9: CALL((void*)&SlowWrite7<u8, 1>); break; case 9: ABI_CallFunction(&SlowWrite7<u8, 1>); break;
} }
} }
else else
{ {
switch (size | NDS.ConsoleType) switch (size | NDS.ConsoleType)
{ {
case 32: CALL((void*)&SlowRead7<u32, 0>); break; case 32: ABI_CallFunction(&SlowRead7<u32, 0>); break;
case 16: CALL((void*)&SlowRead7<u16, 0>); break; case 16: ABI_CallFunction(&SlowRead7<u16, 0>); break;
case 8: CALL((void*)&SlowRead7<u8, 0>); break; case 8: ABI_CallFunction(&SlowRead7<u8, 0>); break;
case 33: CALL((void*)&SlowRead7<u32, 1>); break; case 33: ABI_CallFunction(&SlowRead7<u32, 1>); break;
case 17: CALL((void*)&SlowRead7<u16, 1>); break; case 17: ABI_CallFunction(&SlowRead7<u16, 1>); break;
case 9: CALL((void*)&SlowRead7<u8, 1>); break; case 9: ABI_CallFunction(&SlowRead7<u8, 1>); break;
} }
} }
} }
@ -526,10 +526,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
switch (Num * 2 | NDS.ConsoleType) switch (Num * 2 | NDS.ConsoleType)
{ {
case 0: CALL((void*)&SlowBlockTransfer9<false, 0>); break; case 0: ABI_CallFunction(&SlowBlockTransfer9<false, 0>); break;
case 1: CALL((void*)&SlowBlockTransfer9<false, 1>); break; case 1: ABI_CallFunction(&SlowBlockTransfer9<false, 1>); break;
case 2: CALL((void*)&SlowBlockTransfer7<false, 0>); break; case 2: ABI_CallFunction(&SlowBlockTransfer7<false, 0>); break;
case 3: CALL((void*)&SlowBlockTransfer7<false, 1>); break; case 3: ABI_CallFunction(&SlowBlockTransfer7<false, 1>); break;
} }
PopRegs(false, false); PopRegs(false, false);
@ -630,10 +630,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
switch (Num * 2 | NDS.ConsoleType) switch (Num * 2 | NDS.ConsoleType)
{ {
case 0: CALL((void*)&SlowBlockTransfer9<true, 0>); break; case 0: ABI_CallFunction(&SlowBlockTransfer9<true, 0>); break;
case 1: CALL((void*)&SlowBlockTransfer9<true, 1>); break; case 1: ABI_CallFunction(&SlowBlockTransfer9<true, 1>); break;
case 2: CALL((void*)&SlowBlockTransfer7<true, 0>); break; case 2: ABI_CallFunction(&SlowBlockTransfer7<true, 0>); break;
case 3: CALL((void*)&SlowBlockTransfer7<true, 1>); break; case 3: ABI_CallFunction(&SlowBlockTransfer7<true, 1>); break;
} }
ADD(64, R(RSP), stackAlloc <= INT8_MAX ? Imm8(stackAlloc) : Imm32(stackAlloc)); ADD(64, R(RSP), stackAlloc <= INT8_MAX ? Imm8(stackAlloc) : Imm32(stackAlloc));

View File

@ -85,18 +85,6 @@ struct GDBArgs
/// New fields here should have default values if possible. /// New fields here should have default values if possible.
struct NDSArgs struct NDSArgs
{ {
/// NDS ROM to install.
/// Defaults to nullptr, which means no cart.
/// Should be populated with the desired save data beforehand,
/// including an SD card if applicable.
std::unique_ptr<NDSCart::CartCommon> NDSROM = nullptr;
/// GBA ROM to install.
/// Defaults to nullptr, which means no cart.
/// Should be populated with the desired save data beforehand.
/// Ignored in DSi mode.
std::unique_ptr<GBACart::CartCommon> GBAROM = nullptr;
/// NDS ARM9 BIOS to install. /// NDS ARM9 BIOS to install.
/// Defaults to FreeBIOS, which is not compatible with DSi mode. /// Defaults to FreeBIOS, which is not compatible with DSi mode.
std::unique_ptr<ARM9BIOSImage> ARM9BIOS = std::make_unique<ARM9BIOSImage>(bios_arm9_bin); std::unique_ptr<ARM9BIOSImage> ARM9BIOS = std::make_unique<ARM9BIOSImage>(bios_arm9_bin);

View File

@ -97,8 +97,13 @@ if (ENABLE_JIT)
ARMJIT.cpp ARMJIT.cpp
ARMJIT_Memory.cpp ARMJIT_Memory.cpp
ARMJIT_Global.cpp
dolphin/CommonFuncs.cpp) dolphin/CommonFuncs.cpp)
if (WIN32)
target_link_libraries(core PRIVATE onecore)
endif()
if (ARCHITECTURE STREQUAL x86_64) if (ARCHITECTURE STREQUAL x86_64)
target_sources(core PRIVATE target_sources(core PRIVATE

View File

@ -44,6 +44,7 @@ void ARMv5::CP15Reset()
CP15Control = 0x2078; // dunno CP15Control = 0x2078; // dunno
RNGSeed = 44203; RNGSeed = 44203;
TraceProcessID = 0;
DTCMSetting = 0; DTCMSetting = 0;
ITCMSetting = 0; ITCMSetting = 0;
@ -643,6 +644,10 @@ void ARMv5::CP15Write(u32 id, u32 val)
UpdateITCMSetting(); UpdateITCMSetting();
return; return;
case 0xD01:
TraceProcessID = val;
return;
case 0xF00: case 0xF00:
//printf("cache debug index register %08X\n", val); //printf("cache debug index register %08X\n", val);
return; return;
@ -760,6 +765,9 @@ u32 ARMv5::CP15Read(u32 id) const
return DTCMSetting; return DTCMSetting;
case 0x911: case 0x911:
return ITCMSetting; return ITCMSetting;
case 0xD01:
return TraceProcessID;
} }
if ((id & 0xF00) == 0xF00) // test/debug shit? if ((id & 0xF00) == 0xF00) // test/debug shit?

View File

@ -74,8 +74,6 @@ const u32 NDMAModes[] =
DSi( DSi(
DSiArgs { DSiArgs {
NDSArgs { NDSArgs {
nullptr,
nullptr,
bios_arm9_bin, bios_arm9_bin,
bios_arm7_bin, bios_arm7_bin,
Firmware(0), Firmware(0),

View File

@ -40,8 +40,8 @@ const u32 DSi_CamModule::kTransferStart = 60000;
DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi) DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi)
{ {
DSi.RegisterEventFunc(Event_DSi_CamIRQ, 0, MemberEventFunc(DSi_CamModule, IRQ)); DSi.RegisterEventFuncs(Event_DSi_CamIRQ, this, {MakeEventThunk(DSi_CamModule, IRQ)});
DSi.RegisterEventFunc(Event_DSi_CamTransfer, 0, MemberEventFunc(DSi_CamModule, TransferScanline)); DSi.RegisterEventFuncs(Event_DSi_CamTransfer, this, {MakeEventThunk(DSi_CamModule, TransferScanline)});
Camera0 = DSi.I2C.GetOuterCamera(); Camera0 = DSi.I2C.GetOuterCamera();
Camera1 = DSi.I2C.GetInnerCamera(); Camera1 = DSi.I2C.GetInnerCamera();
@ -52,8 +52,8 @@ DSi_CamModule::~DSi_CamModule()
Camera0 = nullptr; Camera0 = nullptr;
Camera1 = nullptr; Camera1 = nullptr;
DSi.UnregisterEventFunc(Event_DSi_CamIRQ, 0); DSi.UnregisterEventFuncs(Event_DSi_CamIRQ);
DSi.UnregisterEventFunc(Event_DSi_CamTransfer, 0); DSi.UnregisterEventFuncs(Event_DSi_CamTransfer);
} }
void DSi_CamModule::Reset() void DSi_CamModule::Reset()

View File

@ -109,7 +109,7 @@ void DSi_DSP::AudioCb(std::array<s16, 2> frame)
DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi) DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi)
{ {
DSi.RegisterEventFunc(Event_DSi_DSP, 0, MemberEventFunc(DSi_DSP, DSPCatchUpU32)); DSi.RegisterEventFuncs(Event_DSi_DSP, this, {MakeEventThunk(DSi_DSP, DSPCatchUpU32)});
TeakraCore = new Teakra::Teakra(); TeakraCore = new Teakra::Teakra();
SCFG_RST = false; SCFG_RST = false;
@ -156,7 +156,7 @@ DSi_DSP::~DSi_DSP()
//PDATAWriteFifo = NULL; //PDATAWriteFifo = NULL;
TeakraCore = NULL; TeakraCore = NULL;
DSi.UnregisterEventFunc(Event_DSi_DSP, 0); DSi.UnregisterEventFuncs(Event_DSi_DSP);
} }
void DSi_DSP::Reset() void DSi_DSP::Reset()

View File

@ -189,20 +189,18 @@ void NANDImage::SetupFATCrypto(AES_ctx* ctx, u32 ctr)
u8 iv[16]; u8 iv[16];
memcpy(iv, FATIV.data(), sizeof(iv)); memcpy(iv, FATIV.data(), sizeof(iv));
u32 res; u8 ctr_value[16] = {0};
res = iv[15] + (ctr & 0xFF); ctr_value[15] = ctr & 0xFF;
iv[15] = (res & 0xFF); ctr_value[14] = (ctr >> 8) & 0xFF;
res = iv[14] + ((ctr >> 8) & 0xFF) + (res >> 8); ctr_value[13] = (ctr >> 16) & 0xFF;
iv[14] = (res & 0xFF); ctr_value[12] = (ctr >> 24) & 0xFF;
res = iv[13] + ((ctr >> 16) & 0xFF) + (res >> 8);
iv[13] = (res & 0xFF); unsigned carry = 0;
res = iv[12] + (ctr >> 24) + (res >> 8); for (unsigned i = 0; i < 16; i ++) {
iv[12] = (res & 0xFF); unsigned j = 15-i;
iv[11] += (res >> 8); unsigned x = iv[j] + ctr_value[j] + carry;
for (int i = 10; i >= 0; i--) carry = x >= 0x100;
{ iv[j] = x;
if (iv[i+1] == 0) iv[i]++;
else break;
} }
AES_init_ctx_iv(ctx, FATKey.data(), iv); AES_init_ctx_iv(ctx, FATKey.data(), iv);

View File

@ -31,7 +31,7 @@ using Platform::Log;
using Platform::LogLevel; using Platform::LogLevel;
const u8 CIS0[256] = u8 CIS0[256] =
{ {
0x01, 0x03, 0xD9, 0x01, 0xFF, 0x01, 0x03, 0xD9, 0x01, 0xFF,
0x20, 0x04, 0x71, 0x02, 0x00, 0x02, 0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
@ -70,7 +70,7 @@ const u8 CIS0[256] =
0x00, 0x00, 0x00 0x00, 0x00, 0x00
}; };
const u8 CIS1[256] = u8 CIS1[256] =
{ {
0x20, 0x04, 0x71, 0x02, 0x00, 0x02, 0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
0x21, 0x02, 0x0C, 0x00, 0x21, 0x02, 0x0C, 0x00,
@ -134,7 +134,7 @@ DSi_NWifi::DSi_NWifi(melonDS::DSi& dsi, DSi_SDHost* host) :
}, },
DSi(dsi) DSi(dsi)
{ {
DSi.RegisterEventFunc(Event_DSi_NWifi, 0, MemberEventFunc(DSi_NWifi, MSTimer)); DSi.RegisterEventFuncs(Event_DSi_NWifi, this, {MakeEventThunk(DSi_NWifi, MSTimer)});
// this seems to control whether the firmware upload is done // this seems to control whether the firmware upload is done
EEPROMReady = 0; EEPROMReady = 0;
@ -144,7 +144,7 @@ DSi_NWifi::~DSi_NWifi()
{ {
DSi.CancelEvent(Event_DSi_NWifi); DSi.CancelEvent(Event_DSi_NWifi);
DSi.UnregisterEventFunc(Event_DSi_NWifi, 0); DSi.UnregisterEventFuncs(Event_DSi_NWifi);
} }
void DSi_NWifi::Reset() void DSi_NWifi::Reset()
@ -201,6 +201,9 @@ void DSi_NWifi::Reset()
break; break;
} }
CIS0[9] = ChipID >= 0x0D000000;
CIS1[4] = CIS0[9];
memset(EEPROM, 0, 0x400); memset(EEPROM, 0, 0x400);
*(u32*)&EEPROM[0x000] = 0x300; *(u32*)&EEPROM[0x000] = 0x300;
@ -227,6 +230,8 @@ void DSi_NWifi::Reset()
BeaconTimer = 0x10A2220ULL; BeaconTimer = 0x10A2220ULL;
ConnectionStatus = 0; ConnectionStatus = 0;
SendBSSInfo = true;
DSi.CancelEvent(Event_DSi_NWifi); DSi.CancelEvent(Event_DSi_NWifi);
} }
@ -1001,7 +1006,7 @@ void DSi_NWifi::WMI_Command()
} }
// checkme // checkme
ScanTimer = scantime*5; ScanTimer = scantime*8;
} }
break; break;
@ -1036,6 +1041,7 @@ void DSi_NWifi::WMI_Command()
// TODO: store it somewhere // TODO: store it somewhere
Log(LogLevel::Debug, "WMI: set probed SSID: id=%d, flags=%02X, len=%d, SSID=%s\n", id, flags, len, ssid); Log(LogLevel::Debug, "WMI: set probed SSID: id=%d, flags=%02X, len=%d, SSID=%s\n", id, flags, len, ssid);
SendBSSInfo = flags == 0 || strcmp(ssid, WifiAP::APName) == 0;
} }
break; break;
@ -1405,6 +1411,11 @@ void DSi_NWifi::SendWMIAck(u8 ep)
void DSi_NWifi::SendWMIBSSInfo(u8 type, u8* data, u32 len) void DSi_NWifi::SendWMIBSSInfo(u8 type, u8* data, u32 len)
{ {
if (!SendBSSInfo) {
Log(LogLevel::Info, "NWifi: melonAP filtered, not sending WMI BSSINFO event\n");
return;
}
if (!Mailbox[8].CanFit(6+len+2+16)) if (!Mailbox[8].CanFit(6+len+2+16))
{ {
Log(LogLevel::Error, "NWifi: !! not enough space in RX buffer for WMI BSSINFO event\n"); Log(LogLevel::Error, "NWifi: !! not enough space in RX buffer for WMI BSSINFO event\n");

View File

@ -147,6 +147,8 @@ private:
u32 ConnectionStatus; u32 ConnectionStatus;
u8 LANBuffer[2048]; u8 LANBuffer[2048];
bool SendBSSInfo;
}; };
} }

View File

@ -64,10 +64,9 @@ enum
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional<FATStorage>&& sdcard) noexcept : DSi(dsi), Num(0) DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional<FATStorage>&& sdcard) noexcept : DSi(dsi), Num(0)
{ {
DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer, DSi.RegisterEventFuncs(Event_DSi_SDMMCTransfer, this,
Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX)); {MakeEventThunk(DSi_SDHost, FinishTX),
DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer, MakeEventThunk(DSi_SDHost, FinishRX)});
Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
Ports[0] = sdcard ? std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard)) : nullptr; Ports[0] = sdcard ? std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard)) : nullptr;
sdcard = std::nullopt; // to ensure that sdcard isn't left with a moved-from object sdcard = std::nullopt; // to ensure that sdcard isn't left with a moved-from object
@ -77,10 +76,9 @@ DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optio
// Creates an SDIO host // Creates an SDIO host
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1) DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1)
{ {
DSi.RegisterEventFunc(Event_DSi_SDIOTransfer , DSi.RegisterEventFuncs(Event_DSi_SDIOTransfer, this,
Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX)); {MakeEventThunk(DSi_SDHost, FinishTX),
DSi.RegisterEventFunc(Event_DSi_SDIOTransfer, MakeEventThunk(DSi_SDHost, FinishRX)});
Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
Ports[0] = std::make_unique<DSi_NWifi>(DSi, this); Ports[0] = std::make_unique<DSi_NWifi>(DSi, this);
Ports[1] = nullptr; Ports[1] = nullptr;
@ -88,10 +86,7 @@ DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1)
DSi_SDHost::~DSi_SDHost() DSi_SDHost::~DSi_SDHost()
{ {
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, DSi.UnregisterEventFuncs(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer);
Transfer_TX);
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
Transfer_RX);
// unique_ptr's destructor will clean up the ports // unique_ptr's destructor will clean up the ports
} }

View File

@ -832,6 +832,27 @@ std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen
return cart; return cart;
} }
std::unique_ptr<CartCommon> LoadAddon(int type, void* userdata)
{
std::unique_ptr<CartCommon> cart;
switch (type)
{
case GBAAddon_RAMExpansion:
cart = std::make_unique<CartRAMExpansion>();
break;
case GBAAddon_RumblePak:
cart = std::make_unique<CartRumblePak>(userdata);
break;
default:
Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type);
return nullptr;
}
cart->Reset();
return cart;
}
void GBACartSlot::SetCart(std::unique_ptr<CartCommon>&& cart) noexcept void GBACartSlot::SetCart(std::unique_ptr<CartCommon>&& cart) noexcept
{ {
Cart = std::move(cart); Cart = std::move(cart);
@ -864,23 +885,6 @@ void GBACartSlot::SetSaveMemory(const u8* savedata, u32 savelen) noexcept
} }
} }
void GBACartSlot::LoadAddon(void* userdata, int type) noexcept
{
switch (type)
{
case GBAAddon_RAMExpansion:
Cart = std::make_unique<CartRAMExpansion>();
break;
case GBAAddon_RumblePak:
Cart = std::make_unique<CartRumblePak>(userdata);
break;
default:
Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type);
return;
}
}
std::unique_ptr<CartCommon> GBACartSlot::EjectCart() noexcept std::unique_ptr<CartCommon> GBACartSlot::EjectCart() noexcept
{ {
return std::move(Cart); return std::move(Cart);

View File

@ -241,8 +241,6 @@ public:
[[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); } [[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); } [[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
void LoadAddon(void* userdata, int type) noexcept;
/// @return The cart that was in the cart slot if any, /// @return The cart that was in the cart slot if any,
/// or \c nullptr if the cart slot was empty. /// or \c nullptr if the cart slot was empty.
std::unique_ptr<CartCommon> EjectCart() noexcept; std::unique_ptr<CartCommon> EjectCart() noexcept;
@ -309,6 +307,8 @@ std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen, const u8* sr
/// or \c nullptr if there was an error. /// or \c nullptr if there was an error.
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::unique_ptr<u8[]>&& sramdata, u32 sramlen, void* userdata = nullptr); std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::unique_ptr<u8[]>&& sramdata, u32 sramlen, void* userdata = nullptr);
std::unique_ptr<CartCommon> LoadAddon(int type, void* userdata);
} }
#endif // GBACART_H #endif // GBACART_H

View File

@ -70,10 +70,13 @@ GPU::GPU(melonDS::NDS& nds, std::unique_ptr<Renderer3D>&& renderer3d, std::uniqu
GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique<SoftRenderer>()), GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique<SoftRenderer>()),
GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique<GPU2D::SoftRenderer>(*this)) GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique<GPU2D::SoftRenderer>(*this))
{ {
NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank)); NDS.RegisterEventFuncs(Event_LCD, this,
NDS.RegisterEventFunc(Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline)); {
NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame)); MakeEventThunk(GPU, StartHBlank),
NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO)); MakeEventThunk(GPU, StartScanline),
MakeEventThunk(GPU, FinishFrame)
});
NDS.RegisterEventFuncs(Event_DisplayFIFO, this, {MakeEventThunk(GPU, DisplayFIFO)});
InitFramebuffers(); InitFramebuffers();
} }
@ -82,10 +85,8 @@ GPU::~GPU() noexcept
{ {
// All unique_ptr fields are automatically cleaned up // All unique_ptr fields are automatically cleaned up
NDS.UnregisterEventFunc(Event_LCD, LCD_StartHBlank); NDS.UnregisterEventFuncs(Event_LCD);
NDS.UnregisterEventFunc(Event_LCD, LCD_StartScanline); NDS.UnregisterEventFuncs(Event_DisplayFIFO);
NDS.UnregisterEventFunc(Event_LCD, LCD_FinishFrame);
NDS.UnregisterEventFunc(Event_DisplayFIFO, 0);
} }
void GPU::ResetVRAMCache() noexcept void GPU::ResetVRAMCache() noexcept

View File

@ -914,6 +914,9 @@ void SoftRenderer::DrawBG_3D()
template<bool mosaic, SoftRenderer::DrawPixel drawPixel> template<bool mosaic, SoftRenderer::DrawPixel drawPixel>
void SoftRenderer::DrawBG_Text(u32 line, u32 bgnum) void SoftRenderer::DrawBG_Text(u32 line, u32 bgnum)
{ {
// workaround for backgrounds missing on aarch64 with lto build
asm volatile ("" : : : "memory");
u16 bgcnt = CurUnit->BGCnt[bgnum]; u16 bgcnt = CurUnit->BGCnt[bgnum];
u32 tilesetaddr, tilemapaddr; u32 tilesetaddr, tilemapaddr;

View File

@ -74,13 +74,11 @@ const s32 kIterationCycleMargin = 8;
// //
// timings for GBA slot and wifi are set up at runtime // timings for GBA slot and wifi are set up at runtime
NDS* NDS::Current = nullptr; thread_local NDS* NDS::Current = nullptr;
NDS::NDS() noexcept : NDS::NDS() noexcept :
NDS( NDS(
NDSArgs { NDSArgs {
nullptr,
nullptr,
std::make_unique<ARM9BIOSImage>(bios_arm9_bin), std::make_unique<ARM9BIOSImage>(bios_arm9_bin),
std::make_unique<ARM7BIOSImage>(bios_arm7_bin), std::make_unique<ARM7BIOSImage>(bios_arm7_bin),
Firmware(0), Firmware(0),
@ -102,8 +100,8 @@ NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept :
SPI(*this, std::move(args.Firmware)), SPI(*this, std::move(args.Firmware)),
RTC(*this), RTC(*this),
Wifi(*this), Wifi(*this),
NDSCartSlot(*this, std::move(args.NDSROM)), NDSCartSlot(*this, nullptr),
GBACartSlot(*this, type == 1 ? nullptr : std::move(args.GBAROM)), GBACartSlot(*this, nullptr),
AREngine(*this), AREngine(*this),
ARM9(*this, args.GDB, args.JIT.has_value()), ARM9(*this, args.GDB, args.JIT.has_value()),
ARM7(*this, args.GDB, args.JIT.has_value()), ARM7(*this, args.GDB, args.JIT.has_value()),
@ -124,8 +122,8 @@ NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept :
DMA(1, 3, *this), DMA(1, 3, *this),
} }
{ {
RegisterEventFunc(Event_Div, 0, MemberEventFunc(NDS, DivDone)); RegisterEventFuncs(Event_Div, this, {MakeEventThunk(NDS, DivDone)});
RegisterEventFunc(Event_Sqrt, 0, MemberEventFunc(NDS, SqrtDone)); RegisterEventFuncs(Event_Sqrt, this, {MakeEventThunk(NDS, SqrtDone)});
MainRAM = JIT.Memory.GetMainRAM(); MainRAM = JIT.Memory.GetMainRAM();
SharedWRAM = JIT.Memory.GetSharedWRAM(); SharedWRAM = JIT.Memory.GetSharedWRAM();
@ -134,8 +132,8 @@ NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept :
NDS::~NDS() noexcept NDS::~NDS() noexcept
{ {
UnregisterEventFunc(Event_Div, 0); UnregisterEventFuncs(Event_Div);
UnregisterEventFunc(Event_Sqrt, 0); UnregisterEventFuncs(Event_Sqrt);
// The destructor for each component is automatically called by the compiler // The destructor for each component is automatically called by the compiler
} }
@ -229,6 +227,15 @@ void NDS::SetJITArgs(std::optional<JITArgs> args) noexcept
} }
#endif #endif
#ifdef GDBSTUB_ENABLED
void NDS::SetGdbArgs(std::optional<GDBArgs> args) noexcept
{
ARM9.SetGdbArgs(args);
ARM7.SetGdbArgs(args);
EnableGDBStub = args.has_value();
}
#endif
void NDS::InitTimings() void NDS::InitTimings()
{ {
// TODO, eventually: // TODO, eventually:
@ -752,11 +759,6 @@ void NDS::SetGBASave(const u8* savedata, u32 savelen)
} }
void NDS::LoadGBAAddon(int type)
{
GBACartSlot.LoadAddon(UserData, type);
}
void NDS::LoadBIOS() void NDS::LoadBIOS()
{ {
Reset(); Reset();
@ -816,7 +818,7 @@ void NDS::RunSystem(u64 timestamp)
SchedListMask &= ~(1<<i); SchedListMask &= ~(1<<i);
EventFunc func = evt.Funcs[evt.FuncID]; EventFunc func = evt.Funcs[evt.FuncID];
func(evt.Param); func(evt.That, evt.Param);
} }
} }
@ -873,7 +875,7 @@ void NDS::RunSystemSleep(u64 timestamp)
param = evt.Param; param = evt.Param;
EventFunc func = evt.Funcs[evt.FuncID]; EventFunc func = evt.Funcs[evt.FuncID];
func(param); func(this, param);
} }
} }
} }
@ -892,6 +894,8 @@ void NDS::RunSystemSleep(u64 timestamp)
template <CPUExecuteMode cpuMode> template <CPUExecuteMode cpuMode>
u32 NDS::RunFrame() u32 NDS::RunFrame()
{ {
Current = this;
FrameStartTimestamp = SysTimestamp; FrameStartTimestamp = SysTimestamp;
GPU.TotalScanlines = 0; GPU.TotalScanlines = 0;
@ -1069,18 +1073,26 @@ void NDS::Reschedule(u64 target)
} }
} }
void NDS::RegisterEventFunc(u32 id, u32 funcid, EventFunc func) void NDS::RegisterEventFuncs(u32 id, void* that, const std::initializer_list<EventFunc>& funcs)
{ {
SchedEvent& evt = SchedList[id]; SchedEvent& evt = SchedList[id];
evt.Funcs[funcid] = func; evt.That = that;
assert(funcs.size() <= MaxEventFunctions);
int i = 0;
for (EventFunc func : funcs)
{
evt.Funcs[i++] = func;
}
} }
void NDS::UnregisterEventFunc(u32 id, u32 funcid) void NDS::UnregisterEventFuncs(u32 id)
{ {
SchedEvent& evt = SchedList[id]; SchedEvent& evt = SchedList[id];
evt.Funcs.erase(funcid); evt.That = nullptr;
for (int i = 0; i < MaxEventFunctions; i++)
evt.Funcs[i] = nullptr;
} }
void NDS::ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param) void NDS::ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param)
@ -1088,7 +1100,7 @@ void NDS::ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param)
if (SchedListMask & (1<<id)) if (SchedListMask & (1<<id))
{ {
Log(LogLevel::Debug, "!! EVENT %d ALREADY SCHEDULED\n", id); Log(LogLevel::Debug, "!! EVENT %d ALREADY SCHEDULED\n", id);
return; return;
} }
SchedEvent& evt = SchedList[id]; SchedEvent& evt = SchedList[id];
@ -2734,6 +2746,9 @@ u8 NDS::ARM9IORead8(u32 addr)
case 0x04000132: return KeyCnt[0] & 0xFF; case 0x04000132: return KeyCnt[0] & 0xFF;
case 0x04000133: return KeyCnt[0] >> 8; case 0x04000133: return KeyCnt[0] >> 8;
case 0x04000180: return IPCSync9 & 0xFF;
case 0x04000181: return IPCSync9 >> 8;
case 0x040001A0: case 0x040001A0:
if (!(ExMemCnt[0] & (1<<11))) if (!(ExMemCnt[0] & (1<<11)))
return NDSCartSlot.GetSPICnt() & 0xFF; return NDSCartSlot.GetSPICnt() & 0xFF;
@ -3173,6 +3188,17 @@ void NDS::ARM9IOWrite8(u32 addr, u8 val)
KeyCnt[0] = (KeyCnt[0] & 0x00FF) | (val << 8); KeyCnt[0] = (KeyCnt[0] & 0x00FF) | (val << 8);
return; return;
case 0x04000181:
IPCSync7 &= 0xFFF0;
IPCSync7 |= (val & 0x0F);
IPCSync9 &= 0xB0FF;
IPCSync9 |= ((val & 0x4F) << 8);
if ((val & 0x20) && (IPCSync7 & 0x4000))
{
SetIRQ(1, IRQ_IPCSync);
}
return;
case 0x04000188: case 0x04000188:
NDS::ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); NDS::ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24));
return; return;
@ -3664,6 +3690,9 @@ u8 NDS::ARM7IORead8(u32 addr)
case 0x04000138: return RTC.Read() & 0xFF; case 0x04000138: return RTC.Read() & 0xFF;
case 0x04000180: return IPCSync7 & 0xFF;
case 0x04000181: return IPCSync7 >> 8;
case 0x040001A0: case 0x040001A0:
if (ExMemCnt[0] & (1<<11)) if (ExMemCnt[0] & (1<<11))
return NDSCartSlot.GetSPICnt() & 0xFF; return NDSCartSlot.GetSPICnt() & 0xFF;
@ -3972,6 +4001,17 @@ void NDS::ARM7IOWrite8(u32 addr, u8 val)
case 0x04000138: RTC.Write(val, true); return; case 0x04000138: RTC.Write(val, true); return;
case 0x04000181:
IPCSync9 &= 0xFFF0;
IPCSync9 |= (val & 0x0F);
IPCSync7 &= 0xB0FF;
IPCSync7 |= ((val & 0x4F) << 8);
if ((val & 0x20) && (IPCSync9 & 0x4000))
{
SetIRQ(0, IRQ_IPCSync);
}
return;
case 0x04000188: case 0x04000188:
NDS::ARM7IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); NDS::ARM7IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24));
return; return;

View File

@ -76,11 +76,15 @@ enum
Event_MAX Event_MAX
}; };
typedef std::function<void(u32)> EventFunc; static constexpr u32 MaxEventFunctions = 3;
#define MemberEventFunc(cls,func) std::bind(&cls::func,this,std::placeholders::_1)
typedef void (*EventFunc)(void* that, u32 param);
#define MakeEventThunk(class, func) [](void* that, u32 param) { static_cast<class*>(that)->func(param); }
struct SchedEvent struct SchedEvent
{ {
std::map<u32, EventFunc> Funcs; std::array<EventFunc, MaxEventFunctions> Funcs;
void* That;
u64 Timestamp; u64 Timestamp;
u32 FuncID; u32 FuncID;
u32 Param; u32 Param;
@ -384,7 +388,6 @@ public: // TODO: Encapsulate the rest of these members
u32 GetGBASaveLength() const { return GBACartSlot.GetSaveMemoryLength(); } u32 GetGBASaveLength() const { return GBACartSlot.GetSaveMemoryLength(); }
void SetGBASave(const u8* savedata, u32 savelen); void SetGBASave(const u8* savedata, u32 savelen);
void LoadGBAAddon(int type);
std::unique_ptr<GBACart::CartCommon> EjectGBACart() { return GBACartSlot.EjectCart(); } std::unique_ptr<GBACart::CartCommon> EjectGBACart() { return GBACartSlot.EjectCart(); }
u32 RunFrame(); u32 RunFrame();
@ -402,8 +405,8 @@ public: // TODO: Encapsulate the rest of these members
virtual void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) {} virtual void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) {}
void MicInputFrame(s16* data, int samples); void MicInputFrame(s16* data, int samples);
void RegisterEventFunc(u32 id, u32 funcid, EventFunc func); void RegisterEventFuncs(u32 id, void* that, const std::initializer_list<EventFunc>& funcs);
void UnregisterEventFunc(u32 id, u32 funcid); void UnregisterEventFuncs(u32 id);
void ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param); void ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param);
void CancelEvent(u32 id); void CancelEvent(u32 id);
@ -477,6 +480,12 @@ public: // TODO: Encapsulate the rest of these members
void SetJITArgs(std::optional<JITArgs> args) noexcept {} void SetJITArgs(std::optional<JITArgs> args) noexcept {}
#endif #endif
#ifdef GDBSTUB_ENABLED
void SetGdbArgs(std::optional<GDBArgs> args) noexcept;
#else
void SetGdbArgs(std::optional<GDBArgs> args) noexcept {}
#endif
private: private:
void InitTimings(); void InitTimings();
u32 SchedListMask; u32 SchedListMask;
@ -536,8 +545,8 @@ public:
NDS& operator=(const NDS&) = delete; NDS& operator=(const NDS&) = delete;
NDS(NDS&&) = delete; NDS(NDS&&) = delete;
NDS& operator=(NDS&&) = delete; NDS& operator=(NDS&&) = delete;
// The frontend should set and unset this manually after creating and destroying the NDS object.
[[deprecated("Temporary workaround until JIT code generation is revised to accommodate multiple NDS objects.")]] static NDS* Current; static thread_local NDS* Current;
protected: protected:
explicit NDS(NDSArgs&& args, int type, void* userdata) noexcept; explicit NDS(NDSArgs&& args, int type, void* userdata) noexcept;
virtual void DoSavestateExtra(Savestate* file) {} virtual void DoSavestateExtra(Savestate* file) {}

View File

@ -109,36 +109,52 @@ void NDSCartSlot::Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept
} }
} }
void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept void NDSCartSlot::Key1_LoadKeyBuf(bool dsimode) noexcept
{ {
if (NDS.IsLoadedARM7BIOSKnownNative()) if (NDS.ConsoleType == 1)
{ {
u32 expected_bios_length = dsi ? 0x10000 : 0x4000; // DSi mode: grab the right key depending on the requested cart mode
if (biosLength != expected_bios_length)
auto& dsi = static_cast<DSi&>(NDS);
if (dsimode)
{ {
Platform::Log(LogLevel::Error, "NDSCart: Expected an ARM7 BIOS of %u bytes, got %u bytes\n", expected_bios_length, biosLength); // load from ARM7 BIOS at 0xC6D0
}
else if (bios == nullptr) const u8* bios = dsi.ARM7iBIOS.data();
{ memcpy(Key1_KeyBuf.data(), bios + 0xC6D0, sizeof(Key1_KeyBuf));
Platform::Log(LogLevel::Error, "NDSCart: Expected an ARM7 BIOS of %u bytes, got nullptr\n", expected_bios_length); Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from ARM7i BIOS\n");
} }
else else
{ {
memcpy(Key1_KeyBuf.data(), bios + (dsi ? 0xC6D0 : 0x0030), sizeof(Key1_KeyBuf)); // load from ARM9 BIOS at 0x99A0
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from memory\n");
const u8* bios = dsi.ARM9iBIOS.data();
memcpy(Key1_KeyBuf.data(), bios + 0x99A0, sizeof(Key1_KeyBuf));
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from ARM9i BIOS\n");
} }
} }
else else
{ {
// well // DS mode: load from ARM7 BIOS at 0x0030
memset(Key1_KeyBuf.data(), 0, sizeof(Key1_KeyBuf));
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf to zero\n"); if (NDS.IsLoadedARM7BIOSKnownNative())
{
const u8* bios = NDS.GetARM7BIOS().data();
memcpy(Key1_KeyBuf.data(), bios + 0x0030, sizeof(Key1_KeyBuf));
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from ARM7 BIOS\n");
}
else
{
// well
memset(Key1_KeyBuf.data(), 0, sizeof(Key1_KeyBuf));
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf to zero\n");
}
} }
} }
void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, const u8 *bios, u32 biosLength) noexcept void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) noexcept
{ {
Key1_LoadKeyBuf(dsi, bios, biosLength); Key1_LoadKeyBuf(dsi);
u32 keycode[3] = {idcode, idcode>>1, idcode<<1}; u32 keycode[3] = {idcode, idcode>>1, idcode<<1};
if (level >= 1) Key1_ApplyKeycode(keycode, mod); if (level >= 1) Key1_ApplyKeycode(keycode, mod);
@ -262,16 +278,15 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, const u8* cmd,
case 0x3C: case 0x3C:
CmdEncMode = 1; CmdEncMode = 1;
cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, nds.GetARM7BIOS().data(), ARM7BIOSSize); cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2);
DSiMode = false; DSiMode = false;
return 0; return 0;
case 0x3D: case 0x3D:
if (IsDSi) if (IsDSi)
{ {
auto& dsi = static_cast<DSi&>(nds);
CmdEncMode = 1; CmdEncMode = 1;
cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, &dsi.ARM7iBIOS[0], sizeof(DSi::ARM7iBIOS)); cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2);
DSiMode = true; DSiMode = true;
} }
return 0; return 0;
@ -1426,9 +1441,12 @@ void CartHomebrew::ROMCommandFinish(const u8* cmd, u8* data, u32 len)
NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& rom) noexcept : NDS(nds) NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& rom) noexcept : NDS(nds)
{ {
NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData, MemberEventFunc(NDSCartSlot, ROMPrepareData)); NDS.RegisterEventFuncs(Event_ROMTransfer, this,
NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_End, MemberEventFunc(NDSCartSlot, ROMEndTransfer)); {
NDS.RegisterEventFunc(Event_ROMSPITransfer, 0, MemberEventFunc(NDSCartSlot, SPITransferDone)); MakeEventThunk(NDSCartSlot, ROMPrepareData),
MakeEventThunk(NDSCartSlot, ROMEndTransfer)
});
NDS.RegisterEventFuncs(Event_ROMSPITransfer, this, {MakeEventThunk(NDSCartSlot, SPITransferDone)});
// All fields are default-constructed because they're listed as such in the class declaration // All fields are default-constructed because they're listed as such in the class declaration
if (rom) if (rom)
@ -1437,9 +1455,8 @@ NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& rom) n
NDSCartSlot::~NDSCartSlot() noexcept NDSCartSlot::~NDSCartSlot() noexcept
{ {
NDS.UnregisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData); NDS.UnregisterEventFuncs(Event_ROMTransfer);
NDS.UnregisterEventFunc(Event_ROMTransfer, ROMTransfer_End); NDS.UnregisterEventFuncs(Event_ROMSPITransfer);
NDS.UnregisterEventFunc(Event_ROMSPITransfer, 0);
// Cart is cleaned up automatically because it's a unique_ptr // Cart is cleaned up automatically because it's a unique_ptr
} }
@ -1552,10 +1569,10 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept
memcpy(out, &cartrom[arm9base], 0x800); memcpy(out, &cartrom[arm9base], 0x800);
Key1_InitKeycode(false, gamecode, 2, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); Key1_InitKeycode(false, gamecode, 2, 2);
Key1_Decrypt((u32*)&out[0]); Key1_Decrypt((u32*)&out[0]);
Key1_InitKeycode(false, gamecode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); Key1_InitKeycode(false, gamecode, 3, 2);
for (u32 i = 0; i < 0x800; i += 8) for (u32 i = 0; i < 0x800; i += 8)
Key1_Decrypt((u32*)&out[i]); Key1_Decrypt((u32*)&out[i]);
@ -1714,11 +1731,11 @@ void NDSCartSlot::SetCart(std::unique_ptr<CartCommon>&& cart) noexcept
strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8); strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8);
Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); Key1_InitKeycode(false, romparams.GameCode, 3, 2);
for (u32 i = 0; i < 0x800; i += 8) for (u32 i = 0; i < 0x800; i += 8)
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]); Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]);
Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); Key1_InitKeycode(false, romparams.GameCode, 2, 2);
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]); Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]);
Log(LogLevel::Debug, "Re-encrypted cart secure area\n"); Log(LogLevel::Debug, "Re-encrypted cart secure area\n");

View File

@ -445,8 +445,8 @@ private:
void Key1_Encrypt(u32* data) const noexcept; void Key1_Encrypt(u32* data) const noexcept;
void Key1_Decrypt(u32* data) const noexcept; void Key1_Decrypt(u32* data) const noexcept;
void Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept; void Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept;
void Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept; void Key1_LoadKeyBuf(bool dsi) noexcept;
void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, const u8 *bios, u32 biosLength) noexcept; void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) noexcept;
void Key2_Encrypt(const u8* data, u32 len) noexcept; void Key2_Encrypt(const u8* data, u32 len) noexcept;
void ROMEndTransfer(u32 param) noexcept; void ROMEndTransfer(u32 param) noexcept;
void ROMPrepareData(u32 param) noexcept; void ROMPrepareData(u32 param) noexcept;

View File

@ -34,7 +34,7 @@ void WriteDateTime(int num, u8 val);
RTC::RTC(melonDS::NDS& nds) : NDS(nds) RTC::RTC(melonDS::NDS& nds) : NDS(nds)
{ {
NDS.RegisterEventFunc(Event_RTC, 0, MemberEventFunc(RTC, ClockTimer)); NDS.RegisterEventFuncs(Event_RTC, this, {MakeEventThunk(RTC, ClockTimer)});
ResetState(); ResetState();
@ -45,7 +45,7 @@ RTC::RTC(melonDS::NDS& nds) : NDS(nds)
RTC::~RTC() RTC::~RTC()
{ {
NDS.UnregisterEventFunc(Event_RTC, 0); NDS.UnregisterEventFuncs(Event_RTC);
} }
void RTC::Reset() void RTC::Reset()

View File

@ -474,7 +474,7 @@ void TSC::Write(u8 val)
SPIHost::SPIHost(melonDS::NDS& nds, Firmware&& firmware) : NDS(nds) SPIHost::SPIHost(melonDS::NDS& nds, Firmware&& firmware) : NDS(nds)
{ {
NDS.RegisterEventFunc(Event_SPITransfer, 0, MemberEventFunc(SPIHost, TransferDone)); NDS.RegisterEventFuncs(Event_SPITransfer, this, {MakeEventThunk(SPIHost, TransferDone)});
Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS, std::move(firmware)); Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS, std::move(firmware));
Devices[SPIDevice_PowerMan] = new PowerMan(NDS); Devices[SPIDevice_PowerMan] = new PowerMan(NDS);
@ -495,7 +495,7 @@ SPIHost::~SPIHost()
Devices[i] = nullptr; Devices[i] = nullptr;
} }
NDS.UnregisterEventFunc(Event_SPITransfer, 0); NDS.UnregisterEventFuncs(Event_SPITransfer);
} }
void SPIHost::Reset() void SPIHost::Reset()

View File

@ -202,7 +202,7 @@ SPU::SPU(melonDS::NDS& nds, AudioBitDepth bitdepth, AudioInterpolation interpola
AudioLock(Platform::Mutex_Create()), AudioLock(Platform::Mutex_Create()),
Degrade10Bit(bitdepth == AudioBitDepth::_10Bit || (nds.ConsoleType == 1 && bitdepth == AudioBitDepth::Auto)) Degrade10Bit(bitdepth == AudioBitDepth::_10Bit || (nds.ConsoleType == 1 && bitdepth == AudioBitDepth::Auto))
{ {
NDS.RegisterEventFunc(Event_SPU, 0, MemberEventFunc(SPU, Mix)); NDS.RegisterEventFuncs(Event_SPU, this, {MakeEventThunk(SPU, Mix)});
ApplyBias = true; ApplyBias = true;
Degrade10Bit = false; Degrade10Bit = false;
@ -219,7 +219,7 @@ SPU::~SPU()
Platform::Mutex_Free(AudioLock); Platform::Mutex_Free(AudioLock);
AudioLock = nullptr; AudioLock = nullptr;
NDS.UnregisterEventFunc(Event_SPU, 0); NDS.UnregisterEventFuncs(Event_SPU);
} }
void SPU::Reset() void SPU::Reset()

View File

@ -91,7 +91,7 @@ bool MACIsBroadcast(const u8* a)
Wifi::Wifi(melonDS::NDS& nds) : NDS(nds) Wifi::Wifi(melonDS::NDS& nds) : NDS(nds)
{ {
NDS.RegisterEventFunc(Event_Wifi, 0, MemberEventFunc(Wifi, USTimer)); NDS.RegisterEventFuncs(Event_Wifi, this, {MakeEventThunk(Wifi, USTimer)});
WifiAP = new class WifiAP(this, NDS.UserData); WifiAP = new class WifiAP(this, NDS.UserData);
} }
@ -100,7 +100,7 @@ Wifi::~Wifi()
{ {
delete WifiAP; WifiAP = nullptr; delete WifiAP; WifiAP = nullptr;
NDS.UnregisterEventFunc(Event_Wifi, 0); NDS.UnregisterEventFuncs(Event_Wifi);
} }
void Wifi::Reset() void Wifi::Reset()

View File

@ -51,16 +51,17 @@ static int SocketSetBlocking(int fd, bool block)
namespace Gdb namespace Gdb
{ {
GdbStub::GdbStub(StubCallbacks* cb, int port) GdbStub::GdbStub(StubCallbacks* cb)
: Cb(cb), Port(port) : Cb(cb), Port(0)
, SockFd(0), ConnFd(0) , SockFd(0), ConnFd(0)
, Stat(TgtStatus::None), CurBkpt(0), CurWatchpt(0), StatFlag(false), NoAck(false) , Stat(TgtStatus::None), CurBkpt(0), CurWatchpt(0), StatFlag(false), NoAck(false)
, ServerSA((void*)new struct sockaddr_in()) , ServerSA((void*)new struct sockaddr_in())
, ClientSA((void*)new struct sockaddr_in()) , ClientSA((void*)new struct sockaddr_in())
{ } { }
bool GdbStub::Init() bool GdbStub::Init(int port)
{ {
Port = port;
Log(LogLevel::Info, "[GDB] initializing GDB stub for core %d on port %d\n", Log(LogLevel::Info, "[GDB] initializing GDB stub for core %d on port %d\n",
Cb->GetCPU(), Port); Cb->GetCPU(), Port);

View File

@ -115,10 +115,10 @@ public:
int kind; int kind;
}; };
GdbStub(StubCallbacks* cb, int port); GdbStub(StubCallbacks* cb);
~GdbStub(); ~GdbStub();
bool Init(); bool Init(int port);
void Close(); void Close();
StubState Poll(bool wait = false); StubState Poll(bool wait = false);

View File

@ -1019,6 +1019,28 @@ public:
CALL(ptr); CALL(ptr);
} }
} }
template <typename FunctionPointer>
void ABI_TailCall(FunctionPointer func)
{
static_assert(std::is_pointer<FunctionPointer>() &&
std::is_function<std::remove_pointer_t<FunctionPointer>>(),
"Supplied type must be a function pointer.");
const u8* ptr = reinterpret_cast<const u8*>(func);
const u64 address = reinterpret_cast<u64>(ptr);
const u64 distance = address - (reinterpret_cast<u64>(code) + 5);
if (distance >= 0x0000000080000000ULL && distance < 0xFFFFFFFF80000000ULL)
{
// Far call
MOV(64, R(RAX), Imm64(address));
JMPptr(R(RAX));
}
else
{
JMP(ptr, true);
}
}
template <typename FunctionPointer> template <typename FunctionPointer>
void ABI_CallFunctionC16(FunctionPointer func, u16 param1) void ABI_CallFunctionC16(FunctionPointer func, u16 param1)

View File

@ -22,8 +22,8 @@
enum ScreenLayoutType enum ScreenLayoutType
{ {
screenLayout_Natural, // top screen above bottom screen always screenLayout_Natural, // top screen above bottom screen always
screenLayout_Horizontal,
screenLayout_Vertical, screenLayout_Vertical,
screenLayout_Horizontal,
screenLayout_Hybrid, screenLayout_Hybrid,
screenLayout_MAX, screenLayout_MAX,
}; };

View File

@ -195,6 +195,7 @@ if (WIN32)
target_compile_definitions(melonDS PRIVATE WIN32_PORTABLE) target_compile_definitions(melonDS PRIVATE WIN32_PORTABLE)
endif() endif()
string(REPLACE . , MELON_RC_VERSION ${melonDS_VERSION})
configure_file("${CMAKE_SOURCE_DIR}/res/melon.rc.in" "${CMAKE_BINARY_DIR}/res/melon.rc") configure_file("${CMAKE_SOURCE_DIR}/res/melon.rc.in" "${CMAKE_BINARY_DIR}/res/melon.rc")
target_sources(melonDS PUBLIC "${CMAKE_BINARY_DIR}/res/melon.rc") target_sources(melonDS PUBLIC "${CMAKE_BINARY_DIR}/res/melon.rc")
target_include_directories(melonDS PRIVATE "${CMAKE_BINARY_DIR}/res") target_include_directories(melonDS PRIVATE "${CMAKE_BINARY_DIR}/res")

View File

@ -632,7 +632,8 @@ void Table::SetString(const std::string& path, const std::string& val)
void Table::SetDouble(const std::string& path, double val) void Table::SetDouble(const std::string& path, double val)
{ {
toml::value& tval = ResolvePath(path); toml::value& tval = ResolvePath(path);
tval = val; toml::floating_format_info info = {.prec=10};
tval = toml::value(val, info);
} }
toml::value& Table::ResolvePath(const std::string& path) toml::value& Table::ResolvePath(const std::string& path)
@ -816,7 +817,7 @@ Table GetLocalTable(int instance)
std::string key = "Instance" + std::to_string(instance); std::string key = "Instance" + std::to_string(instance);
toml::value& tbl = RootTable[key]; toml::value& tbl = RootTable[key];
if (tbl.is_uninitialized()) if (tbl.is_empty())
RootTable[key] = RootTable["Instance0"]; RootTable[key] = RootTable["Instance0"];
return Table(tbl, key); return Table(tbl, key);

View File

@ -25,7 +25,7 @@
#include <unordered_map> #include <unordered_map>
#include <tuple> #include <tuple>
#include "toml/toml/value.hpp" #include "toml/toml11/types.hpp"
namespace Config namespace Config
{ {

View File

@ -29,7 +29,6 @@
#include <fstream> #include <fstream>
#include <QDateTime> #include <QDateTime>
#include <QMessageBox>
#include <zstd.h> #include <zstd.h>
#ifdef ARCHIVE_SUPPORT_ENABLED #ifdef ARCHIVE_SUPPORT_ENABLED
@ -77,12 +76,16 @@ EmuInstance::EmuInstance(int inst) : deleting(false),
baseROMDir = ""; baseROMDir = "";
baseROMName = ""; baseROMName = "";
baseAssetName = ""; baseAssetName = "";
nextCart = nullptr;
changeCart = false;
gbaSave = nullptr; gbaSave = nullptr;
gbaCartType = -1; gbaCartType = -1;
baseGBAROMDir = ""; baseGBAROMDir = "";
baseGBAROMName = ""; baseGBAROMName = "";
baseGBAAssetName = ""; baseGBAAssetName = "";
nextGBACart = nullptr;
changeGBACart = false;
cheatFile = nullptr; cheatFile = nullptr;
cheatsOn = localCfg.GetBool("EnableCheats"); cheatsOn = localCfg.GetBool("EnableCheats");
@ -118,7 +121,7 @@ EmuInstance::EmuInstance(int inst) : deleting(false),
mpAudioMode = globalCfg.GetInt("MP.AudioMode"); mpAudioMode = globalCfg.GetInt("MP.AudioMode");
nds = nullptr; nds = nullptr;
//updateConsole(nullptr, nullptr); //updateConsole();
audioInit(); audioInit();
inputInit(); inputInit();
@ -161,7 +164,6 @@ EmuInstance::~EmuInstance()
audioDeInit(); audioDeInit();
inputDeInit(); inputDeInit();
NDS::Current = nullptr;
if (nds) if (nds)
{ {
saveRTCData(); saveRTCData();
@ -242,6 +244,8 @@ void EmuInstance::deleteWindow(int id, bool close)
if (close) if (close)
win->close(); win->close();
if (deleting) return;
if (numWindows == 0) if (numWindows == 0)
{ {
// if we closed the last window, delete the instance // if we closed the last window, delete the instance
@ -683,7 +687,7 @@ std::string EmuInstance::getSavestateName(int slot)
{ {
std::string ext = ".ml"; std::string ext = ".ml";
ext += (char)('0'+slot); ext += (char)('0'+slot);
return getAssetPath(false, globalCfg.GetString("SavestatePath"), ext); return getAssetPath(false, localCfg.GetString("SavestatePath"), ext);
} }
bool EmuInstance::savestateExists(int slot) bool EmuInstance::savestateExists(int slot)
@ -749,7 +753,7 @@ bool EmuInstance::loadState(const std::string& filename)
previousSaveFile = ndsSave->GetPath(); previousSaveFile = ndsSave->GetPath();
std::string savefile = filename.substr(lastSep(filename)+1); std::string savefile = filename.substr(lastSep(filename)+1);
savefile = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav", savefile); savefile = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav", savefile);
savefile += instanceFileSuffix(); savefile += instanceFileSuffix();
ndsSave->SetPath(savefile, true); ndsSave->SetPath(savefile, true);
} }
@ -800,7 +804,7 @@ bool EmuInstance::saveState(const std::string& filename)
if (globalCfg.GetBool("Savestate.RelocSRAM") && ndsSave) if (globalCfg.GetBool("Savestate.RelocSRAM") && ndsSave)
{ {
std::string savefile = filename.substr(lastSep(filename)+1); std::string savefile = filename.substr(lastSep(filename)+1);
savefile = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav", savefile); savefile = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav", savefile);
savefile += instanceFileSuffix(); savefile += instanceFileSuffix();
ndsSave->SetPath(savefile, false); ndsSave->SetPath(savefile, false);
} }
@ -836,7 +840,7 @@ void EmuInstance::loadCheats()
{ {
unloadCheats(); unloadCheats();
std::string filename = getAssetPath(false, globalCfg.GetString("CheatFilePath"), ".mch"); std::string filename = getAssetPath(false, localCfg.GetString("CheatFilePath"), ".mch");
// TODO: check for error (malformed cheat file, ...) // TODO: check for error (malformed cheat file, ...)
cheatFile = std::make_unique<ARCodeFile>(filename); cheatFile = std::make_unique<ARCodeFile>(filename);
@ -855,7 +859,7 @@ std::unique_ptr<ARM9BIOSImage> EmuInstance::loadARM9BIOS() noexcept
{ {
if (!globalCfg.GetBool("Emu.ExternalBIOSEnable")) if (!globalCfg.GetBool("Emu.ExternalBIOSEnable"))
{ {
return globalCfg.GetInt("Emu.ConsoleType") == 0 ? std::make_unique<ARM9BIOSImage>(bios_arm9_bin) : nullptr; return std::make_unique<ARM9BIOSImage>(bios_arm9_bin);
} }
string path = globalCfg.GetString("DS.BIOS9Path"); string path = globalCfg.GetString("DS.BIOS9Path");
@ -878,7 +882,7 @@ std::unique_ptr<ARM7BIOSImage> EmuInstance::loadARM7BIOS() noexcept
{ {
if (!globalCfg.GetBool("Emu.ExternalBIOSEnable")) if (!globalCfg.GetBool("Emu.ExternalBIOSEnable"))
{ {
return globalCfg.GetInt("Emu.ConsoleType") == 0 ? std::make_unique<ARM7BIOSImage>(bios_arm7_bin) : nullptr; return std::make_unique<ARM7BIOSImage>(bios_arm7_bin);
} }
string path = globalCfg.GetString("DS.BIOS7Path"); string path = globalCfg.GetString("DS.BIOS7Path");
@ -1002,11 +1006,12 @@ std::optional<Firmware> EmuInstance::loadFirmware(int type) noexcept
{ // If we're using built-in firmware... { // If we're using built-in firmware...
if (type == 1) if (type == 1)
{ {
Log(Error, "DSi firmware: cannot use built-in firmware in DSi mode!\n"); // TODO: support generating a firmware for DSi mode
return std::nullopt; }
else
{
return generateFirmware(type);
} }
return generateFirmware(type);
} }
//const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath; //const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath;
string firmwarepath; string firmwarepath;
@ -1215,23 +1220,22 @@ void EmuInstance::setDateTime()
time.time().hour(), time.time().minute(), time.time().second()); time.time().hour(), time.time().minute(), time.time().second());
} }
bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGBAArgs&& _gbaargs) noexcept bool EmuInstance::updateConsole() noexcept
{ {
// update the console type // update the console type
consoleType = globalCfg.GetInt("Emu.ConsoleType"); consoleType = globalCfg.GetInt("Emu.ConsoleType");
// Let's get the cart we want to use; // Let's get the cart we want to use;
// if we wnat to keep the cart, we'll eject it from the existing console first. // if we want to keep the cart, we'll eject it from the existing console first.
std::unique_ptr<NDSCart::CartCommon> nextndscart; std::unique_ptr<NDSCart::CartCommon> nextndscart;
if (std::holds_alternative<Keep>(_ndsargs)) if (!changeCart)
{ // If we want to keep the existing cart (if any)... { // If we want to keep the existing cart (if any)...
nextndscart = nds ? nds->EjectCart() : nullptr; nextndscart = nds ? nds->EjectCart() : nullptr;
_ndsargs = {};
} }
else if (const auto ptr = std::get_if<std::unique_ptr<NDSCart::CartCommon>>(&_ndsargs)) else
{ {
nextndscart = std::move(*ptr); nextndscart = std::move(nextCart);
_ndsargs = {}; changeCart = false;
} }
if (auto* cartsd = dynamic_cast<NDSCart::CartSD*>(nextndscart.get())) if (auto* cartsd = dynamic_cast<NDSCart::CartSD*>(nextndscart.get()))
@ -1242,14 +1246,14 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
} }
std::unique_ptr<GBACart::CartCommon> nextgbacart; std::unique_ptr<GBACart::CartCommon> nextgbacart;
if (std::holds_alternative<Keep>(_gbaargs)) if (!changeGBACart)
{ {
nextgbacart = nds ? nds->EjectGBACart() : nullptr; nextgbacart = nds ? nds->EjectGBACart() : nullptr;
} }
else if (const auto ptr = std::get_if<std::unique_ptr<GBACart::CartCommon>>(&_gbaargs)) else
{ {
nextgbacart = std::move(*ptr); nextgbacart = std::move(nextGBACart);
_gbaargs = {}; changeGBACart = false;
} }
@ -1292,8 +1296,6 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
#endif #endif
NDSArgs ndsargs { NDSArgs ndsargs {
std::move(nextndscart),
std::move(nextgbacart),
std::move(arm9bios), std::move(arm9bios),
std::move(arm7bios), std::move(arm7bios),
std::move(*firmware), std::move(*firmware),
@ -1307,8 +1309,6 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
std::optional<DSiArgs> dsiargs = std::nullopt; std::optional<DSiArgs> dsiargs = std::nullopt;
if (consoleType == 1) if (consoleType == 1)
{ {
ndsargs.GBAROM = nullptr;
auto arm7ibios = loadDSiARM7BIOS(); auto arm7ibios = loadDSiARM7BIOS();
if (!arm7ibios) if (!arm7ibios)
return false; return false;
@ -1339,7 +1339,6 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
renderLock.lock(); renderLock.lock();
if ((!nds) || (consoleType != nds->ConsoleType)) if ((!nds) || (consoleType != nds->ConsoleType))
{ {
NDS::Current = nullptr;
if (nds) if (nds)
{ {
saveRTCData(); saveRTCData();
@ -1351,7 +1350,6 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
else else
nds = new NDS(std::move(ndsargs), this); nds = new NDS(std::move(ndsargs), this);
NDS::Current = nds;
nds->Reset(); nds->Reset();
loadRTCData(); loadRTCData();
//emuThread->updateVideoRenderer(); // not actually needed? //emuThread->updateVideoRenderer(); // not actually needed?
@ -1361,10 +1359,8 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
nds->SetARM7BIOS(*args->ARM7BIOS); nds->SetARM7BIOS(*args->ARM7BIOS);
nds->SetARM9BIOS(*args->ARM9BIOS); nds->SetARM9BIOS(*args->ARM9BIOS);
nds->SetFirmware(std::move(args->Firmware)); nds->SetFirmware(std::move(args->Firmware));
nds->SetNDSCart(std::move(args->NDSROM));
nds->SetGBACart(std::move(args->GBAROM));
nds->SetJITArgs(args->JIT); nds->SetJITArgs(args->JIT);
// TODO GDB stub shit nds->SetGdbArgs(args->GDB);
nds->SPU.SetInterpolation(args->Interpolation); nds->SPU.SetInterpolation(args->Interpolation);
nds->SPU.SetDegrade10Bit(args->BitDepth); nds->SPU.SetDegrade10Bit(args->BitDepth);
@ -1380,18 +1376,26 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
dsi->SetSDCard(std::move(_dsiargs.DSiSDCard)); dsi->SetSDCard(std::move(_dsiargs.DSiSDCard));
// We're moving the optional, not the card // We're moving the optional, not the card
// (inserting std::nullopt here is okay, it means no card) // (inserting std::nullopt here is okay, it means no card)
dsi->EjectGBACart();
} }
} }
// loads the carts later -- to be sure that everything else is initialized
nds->SetNDSCart(std::move(nextndscart));
if (consoleType == 1)
nds->EjectGBACart();
else
nds->SetGBACart(std::move(nextgbacart));
renderLock.unlock(); renderLock.unlock();
loadCheats();
return true; return true;
} }
void EmuInstance::reset() void EmuInstance::reset()
{ {
updateConsole(Keep {}, Keep {}); updateConsole();
if (consoleType == 1) ejectGBACart(); if (consoleType == 1) ejectGBACart();
@ -1402,7 +1406,7 @@ void EmuInstance::reset()
if ((cartType != -1) && ndsSave) if ((cartType != -1) && ndsSave)
{ {
std::string oldsave = ndsSave->GetPath(); std::string oldsave = ndsSave->GetPath();
std::string newsave = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav"); std::string newsave = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav");
newsave += instanceFileSuffix(); newsave += instanceFileSuffix();
if (oldsave != newsave) if (oldsave != newsave)
ndsSave->SetPath(newsave, false); ndsSave->SetPath(newsave, false);
@ -1411,7 +1415,7 @@ void EmuInstance::reset()
if ((gbaCartType != -1) && gbaSave) if ((gbaCartType != -1) && gbaSave)
{ {
std::string oldsave = gbaSave->GetPath(); std::string oldsave = gbaSave->GetPath();
std::string newsave = getAssetPath(true, globalCfg.GetString("SaveFilePath"), ".sav"); std::string newsave = getAssetPath(true, localCfg.GetString("SaveFilePath"), ".sav");
newsave += instanceFileSuffix(); newsave += instanceFileSuffix();
if (oldsave != newsave) if (oldsave != newsave)
gbaSave->SetPath(newsave, false); gbaSave->SetPath(newsave, false);
@ -1452,16 +1456,22 @@ void EmuInstance::reset()
} }
bool EmuInstance::bootToMenu() bool EmuInstance::bootToMenu(QString& errorstr)
{ {
// Keep whatever cart is in the console, if any. // Keep whatever cart is in the console, if any.
if (!updateConsole(Keep {}, Keep {})) if (!updateConsole())
{
// Try to update the console, but keep the existing cart. If that fails... // Try to update the console, but keep the existing cart. If that fails...
errorstr = "Failed to boot the firmware.";
return false; return false;
}
// BIOS and firmware files are loaded, patched, and installed in UpdateConsole // BIOS and firmware files are loaded, patched, and installed in UpdateConsole
if (nds->NeedsDirectBoot()) if (nds->NeedsDirectBoot())
{
errorstr = "This firmware is not bootable.";
return false; return false;
}
initFirmwareSaveManager(); initFirmwareSaveManager();
nds->Reset(); nds->Reset();
@ -1838,7 +1848,7 @@ QString EmuInstance::getSavErrorString(std::string& filepath, bool gba)
return QString::fromStdString(err1); return QString::fromStdString(err1);
} }
bool EmuInstance::loadROM(QStringList filepath, bool reset) bool EmuInstance::loadROM(QStringList filepath, bool reset, QString& errorstr)
{ {
unique_ptr<u8[]> filedata = nullptr; unique_ptr<u8[]> filedata = nullptr;
u32 filelen; u32 filelen;
@ -1847,7 +1857,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
if (!loadROMData(filepath, filedata, filelen, basepath, romname)) if (!loadROMData(filepath, filedata, filelen, basepath, romname))
{ {
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM."); errorstr = "Failed to load the DS ROM.";
return false; return false;
} }
@ -1860,7 +1870,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
u32 savelen = 0; u32 savelen = 0;
std::unique_ptr<u8[]> savedata = nullptr; std::unique_ptr<u8[]> savedata = nullptr;
std::string savname = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav"); std::string savname = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav");
std::string origsav = savname; std::string origsav = savname;
savname += instanceFileSuffix(); savname += instanceFileSuffix();
@ -1869,7 +1879,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
{ {
if (!Platform::CheckFileWritable(origsav)) if (!Platform::CheckFileWritable(origsav))
{ {
QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(origsav, false)); errorstr = getSavErrorString(origsav, false);
return false; return false;
} }
@ -1877,7 +1887,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
} }
else if (!Platform::CheckFileWritable(savname)) else if (!Platform::CheckFileWritable(savname))
{ {
QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(savname, false)); errorstr = getSavErrorString(savname, false);
return false; return false;
} }
@ -1904,15 +1914,18 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
if (!cart) if (!cart)
{ {
// If we couldn't parse the ROM... // If we couldn't parse the ROM...
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM."); errorstr = "Failed to load the DS ROM.";
return false; return false;
} }
if (reset) if (reset)
{ {
if (!updateConsole(std::move(cart), Keep {})) nextCart = std::move(cart);
changeCart = true;
if (!updateConsole())
{ {
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM."); errorstr = "Failed to load the DS ROM.";
return false; return false;
} }
@ -1929,13 +1942,20 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
} }
else else
{ {
assert(nds != nullptr); if (emuIsActive())
nds->SetNDSCart(std::move(cart)); {
nds->SetNDSCart(std::move(cart));
loadCheats();
}
else
{
nextCart = std::move(cart);
changeCart = true;
}
} }
cartType = 0; cartType = 0;
ndsSave = std::make_unique<SaveManager>(savname); ndsSave = std::make_unique<SaveManager>(savname);
loadCheats();
return true; // success return true; // success
} }
@ -1944,9 +1964,16 @@ void EmuInstance::ejectCart()
{ {
ndsSave = nullptr; ndsSave = nullptr;
unloadCheats(); if (emuIsActive())
{
nds->EjectCart(); nds->EjectCart();
unloadCheats();
}
else
{
nextCart = nullptr;
changeCart = true;
}
cartType = -1; cartType = -1;
baseROMDir = ""; baseROMDir = "";
@ -1974,11 +2001,11 @@ QString EmuInstance::cartLabel()
} }
bool EmuInstance::loadGBAROM(QStringList filepath) bool EmuInstance::loadGBAROM(QStringList filepath, QString& errorstr)
{ {
if (consoleType == 1) if (consoleType == 1)
{ {
QMessageBox::critical(mainWindow, "melonDS", "The DSi doesn't have a GBA slot."); errorstr = "The DSi doesn't have a GBA slot.";
return false; return false;
} }
@ -1989,7 +2016,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
if (!loadROMData(filepath, filedata, filelen, basepath, romname)) if (!loadROMData(filepath, filedata, filelen, basepath, romname))
{ {
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM."); errorstr = "Failed to load the GBA ROM.";
return false; return false;
} }
@ -2002,7 +2029,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
u32 savelen = 0; u32 savelen = 0;
std::unique_ptr<u8[]> savedata = nullptr; std::unique_ptr<u8[]> savedata = nullptr;
std::string savname = getAssetPath(true, globalCfg.GetString("SaveFilePath"), ".sav"); std::string savname = getAssetPath(true, localCfg.GetString("SaveFilePath"), ".sav");
std::string origsav = savname; std::string origsav = savname;
savname += instanceFileSuffix(); savname += instanceFileSuffix();
@ -2011,7 +2038,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
{ {
if (!Platform::CheckFileWritable(origsav)) if (!Platform::CheckFileWritable(origsav))
{ {
QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(origsav, true)); errorstr = getSavErrorString(origsav, true);
return false; return false;
} }
@ -2019,7 +2046,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
} }
else if (!Platform::CheckFileWritable(savname)) else if (!Platform::CheckFileWritable(savname))
{ {
QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(savname, true)); errorstr = getSavErrorString(savname, true);
return false; return false;
} }
@ -2039,24 +2066,47 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen, this); auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen, this);
if (!cart) if (!cart)
{ {
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM."); errorstr = "Failed to load the GBA ROM.";
return false; return false;
} }
nds->SetGBACart(std::move(cart));
gbaCartType = 0; gbaCartType = 0;
gbaSave = std::make_unique<SaveManager>(savname); if (emuIsActive())
{
nds->SetGBACart(std::move(cart));
gbaSave = std::make_unique<SaveManager>(savname);
}
else
{
nextGBACart = std::move(cart);
changeGBACart = true;
}
return true; return true;
} }
void EmuInstance::loadGBAAddon(int type) void EmuInstance::loadGBAAddon(int type, QString& errorstr)
{ {
if (consoleType == 1) return; if (consoleType == 1) return;
auto cart = GBACart::LoadAddon(type, this);
if (!cart)
{
errorstr = "Failed to load the GBA addon.";
return;
}
if (emuIsActive())
{
nds->SetGBACart(std::move(cart));
}
else
{
nextGBACart = std::move(cart);
changeGBACart = true;
}
gbaSave = nullptr; gbaSave = nullptr;
nds->LoadGBAAddon(type);
gbaCartType = type; gbaCartType = type;
baseGBAROMDir = ""; baseGBAROMDir = "";
baseGBAROMName = ""; baseGBAROMName = "";
@ -2067,7 +2117,15 @@ void EmuInstance::ejectGBACart()
{ {
gbaSave = nullptr; gbaSave = nullptr;
nds->EjectGBACart(); if (emuIsActive())
{
nds->EjectGBACart();
}
else
{
nextGBACart = nullptr;
changeGBACart = true;
}
gbaCartType = -1; gbaCartType = -1;
baseGBAROMDir = ""; baseGBAROMDir = "";

View File

@ -120,7 +120,7 @@ public:
// return: empty string = setup OK, non-empty = error message // return: empty string = setup OK, non-empty = error message
QString verifySetup(); QString verifySetup();
bool updateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept; bool updateConsole() noexcept;
void enableCheats(bool enable); void enableCheats(bool enable);
melonDS::ARCodeFile* getCheatFile(); melonDS::ARCodeFile* getCheatFile();
@ -184,20 +184,22 @@ private:
std::optional<melonDS::FATStorage> loadSDCard(const std::string& key) noexcept; std::optional<melonDS::FATStorage> loadSDCard(const std::string& key) noexcept;
void setBatteryLevels(); void setBatteryLevels();
void reset(); void reset();
bool bootToMenu(); bool bootToMenu(QString& errorstr);
melonDS::u32 decompressROM(const melonDS::u8* inContent, const melonDS::u32 inSize, std::unique_ptr<melonDS::u8[]>& outContent); melonDS::u32 decompressROM(const melonDS::u8* inContent, const melonDS::u32 inSize, std::unique_ptr<melonDS::u8[]>& outContent);
void clearBackupState(); void clearBackupState();
std::pair<std::unique_ptr<melonDS::Firmware>, std::string> generateDefaultFirmware(); std::pair<std::unique_ptr<melonDS::Firmware>, std::string> generateDefaultFirmware();
bool parseMacAddress(void* data); bool parseMacAddress(void* data);
void customizeFirmware(melonDS::Firmware& firmware, bool overridesettings) noexcept; void customizeFirmware(melonDS::Firmware& firmware, bool overridesettings) noexcept;
bool loadROMData(const QStringList& filepath, std::unique_ptr<melonDS::u8[]>& filedata, melonDS::u32& filelen, std::string& basepath, std::string& romname) noexcept; bool loadROMData(const QStringList& filepath, std::unique_ptr<melonDS::u8[]>& filedata, melonDS::u32& filelen, std::string& basepath, std::string& romname) noexcept;
QString getSavErrorString(std::string& filepath, bool gba); QString getSavErrorString(std::string& filepath, bool gba);
bool loadROM(QStringList filepath, bool reset); bool loadROM(QStringList filepath, bool reset, QString& errorstr);
void ejectCart(); void ejectCart();
bool cartInserted(); bool cartInserted();
QString cartLabel(); QString cartLabel();
bool loadGBAROM(QStringList filepath);
void loadGBAAddon(int type); bool loadGBAROM(QStringList filepath, QString& errorstr);
void loadGBAAddon(int type, QString& errorstr);
void ejectGBACart(); void ejectGBACart();
bool gbaCartInserted(); bool gbaCartInserted();
QString gbaAddonName(int addon); QString gbaAddonName(int addon);
@ -261,11 +263,15 @@ private:
std::string baseROMDir; std::string baseROMDir;
std::string baseROMName; std::string baseROMName;
std::string baseAssetName; std::string baseAssetName;
bool changeCart;
std::unique_ptr<melonDS::NDSCart::CartCommon> nextCart;
int gbaCartType; int gbaCartType;
std::string baseGBAROMDir; std::string baseGBAROMDir;
std::string baseGBAROMName; std::string baseGBAROMName;
std::string baseGBAAssetName; std::string baseGBAAssetName;
bool changeGBACart;
std::unique_ptr<melonDS::GBACart::CartCommon> nextGBACart;
// HACK // HACK
public: public:

View File

@ -159,6 +159,8 @@ void EmuInstance::audioMute()
void EmuInstance::micOpen() void EmuInstance::micOpen()
{ {
if (micDevice) return;
if (micInputType != micInputType_External) if (micInputType != micInputType_External)
{ {
micDevice = 0; micDevice = 0;
@ -328,7 +330,11 @@ void EmuInstance::micProcess()
micBufferReadPos += len; micBufferReadPos += len;
} }
if (len < kFrameLen) if (len == 0)
{
memset(tmp, 0, sizeof(tmp));
}
else if (len < kFrameLen)
{ {
for (int i = len; i < kFrameLen; i++) for (int i = len; i < kFrameLen; i++)
tmp[i] = tmp[len-1]; tmp[i] = tmp[len-1];

View File

@ -82,9 +82,6 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
ui->chkJITBranchOptimisations->setChecked(cfg.GetBool("JIT.BranchOptimisations")); ui->chkJITBranchOptimisations->setChecked(cfg.GetBool("JIT.BranchOptimisations"));
ui->chkJITLiteralOptimisations->setChecked(cfg.GetBool("JIT.LiteralOptimisations")); ui->chkJITLiteralOptimisations->setChecked(cfg.GetBool("JIT.LiteralOptimisations"));
ui->chkJITFastMemory->setChecked(cfg.GetBool("JIT.FastMemory")); ui->chkJITFastMemory->setChecked(cfg.GetBool("JIT.FastMemory"));
#ifdef __APPLE__
ui->chkJITFastMemory->setDisabled(true);
#endif
ui->spnJITMaximumBlockSize->setValue(cfg.GetInt("JIT.MaxBlockSize")); ui->spnJITMaximumBlockSize->setValue(cfg.GetInt("JIT.MaxBlockSize"));
#else #else
ui->chkEnableJIT->setDisabled(true); ui->chkEnableJIT->setDisabled(true);
@ -539,11 +536,14 @@ void EmuSettingsDialog::on_btnDSiSDFolderBrowse_clicked()
void EmuSettingsDialog::on_chkEnableJIT_toggled() void EmuSettingsDialog::on_chkEnableJIT_toggled()
{ {
bool disabled = !ui->chkEnableJIT->isChecked(); bool disabled = !ui->chkEnableJIT->isChecked();
#ifdef JIT_ENABLED
bool fastmemSupported = ARMJIT_Memory::IsFastMemSupported();
#else
bool fastmemSupported = false;
#endif
ui->chkJITBranchOptimisations->setDisabled(disabled); ui->chkJITBranchOptimisations->setDisabled(disabled);
ui->chkJITLiteralOptimisations->setDisabled(disabled); ui->chkJITLiteralOptimisations->setDisabled(disabled);
#ifndef __APPLE__ ui->chkJITFastMemory->setDisabled(disabled || !fastmemSupported);
ui->chkJITFastMemory->setDisabled(disabled);
#endif
ui->spnJITMaximumBlockSize->setDisabled(disabled); ui->spnJITMaximumBlockSize->setDisabled(disabled);
on_cbGdbEnabled_toggled(); on_cbGdbEnabled_toggled();

View File

@ -109,7 +109,7 @@ void EmuThread::run()
Config::Table& globalCfg = emuInstance->getGlobalConfig(); Config::Table& globalCfg = emuInstance->getGlobalConfig();
u32 mainScreenPos[3]; u32 mainScreenPos[3];
//emuInstance->updateConsole(nullptr, nullptr); //emuInstance->updateConsole();
// No carts are inserted when melonDS first boots // No carts are inserted when melonDS first boots
mainScreenPos[0] = 0; mainScreenPos[0] = 0;
@ -580,7 +580,7 @@ void EmuThread::handleMessages()
case msg_BootROM: case msg_BootROM:
msgResult = 0; msgResult = 0;
if (!emuInstance->loadROM(msg.param.value<QStringList>(), true)) if (!emuInstance->loadROM(msg.param.value<QStringList>(), true, msgError))
break; break;
assert(emuInstance->nds != nullptr); assert(emuInstance->nds != nullptr);
@ -590,7 +590,7 @@ void EmuThread::handleMessages()
case msg_BootFirmware: case msg_BootFirmware:
msgResult = 0; msgResult = 0;
if (!emuInstance->bootToMenu()) if (!emuInstance->bootToMenu(msgError))
break; break;
assert(emuInstance->nds != nullptr); assert(emuInstance->nds != nullptr);
@ -600,7 +600,7 @@ void EmuThread::handleMessages()
case msg_InsertCart: case msg_InsertCart:
msgResult = 0; msgResult = 0;
if (!emuInstance->loadROM(msg.param.value<QStringList>(), false)) if (!emuInstance->loadROM(msg.param.value<QStringList>(), false, msgError))
break; break;
msgResult = 1; msgResult = 1;
@ -612,7 +612,7 @@ void EmuThread::handleMessages()
case msg_InsertGBACart: case msg_InsertGBACart:
msgResult = 0; msgResult = 0;
if (!emuInstance->loadGBAROM(msg.param.value<QStringList>())) if (!emuInstance->loadGBAROM(msg.param.value<QStringList>(), msgError))
break; break;
msgResult = 1; msgResult = 1;
@ -620,7 +620,7 @@ void EmuThread::handleMessages()
case msg_InsertGBAAddon: case msg_InsertGBAAddon:
msgResult = 0; msgResult = 0;
emuInstance->loadGBAAddon(msg.param.value<int>()); emuInstance->loadGBAAddon(msg.param.value<int>(), msgError);
msgResult = 1; msgResult = 1;
break; break;
@ -756,36 +756,45 @@ bool EmuThread::emuIsActive()
return emuActive; return emuActive;
} }
int EmuThread::bootROM(const QStringList& filename) int EmuThread::bootROM(const QStringList& filename, QString& errorstr)
{ {
sendMessage({.type = msg_BootROM, .param = filename}); sendMessage({.type = msg_BootROM, .param = filename});
waitMessage(); waitMessage();
if (!msgResult) if (!msgResult)
{
errorstr = msgError;
return msgResult; return msgResult;
}
sendMessage(msg_EmuRun); sendMessage(msg_EmuRun);
waitMessage(); waitMessage();
errorstr = "";
return msgResult; return msgResult;
} }
int EmuThread::bootFirmware() int EmuThread::bootFirmware(QString& errorstr)
{ {
sendMessage(msg_BootFirmware); sendMessage(msg_BootFirmware);
waitMessage(); waitMessage();
if (!msgResult) if (!msgResult)
{
errorstr = msgError;
return msgResult; return msgResult;
}
sendMessage(msg_EmuRun); sendMessage(msg_EmuRun);
waitMessage(); waitMessage();
errorstr = "";
return msgResult; return msgResult;
} }
int EmuThread::insertCart(const QStringList& filename, bool gba) int EmuThread::insertCart(const QStringList& filename, bool gba, QString& errorstr)
{ {
MessageType msgtype = gba ? msg_InsertGBACart : msg_InsertCart; MessageType msgtype = gba ? msg_InsertGBACart : msg_InsertCart;
sendMessage({.type = msgtype, .param = filename}); sendMessage({.type = msgtype, .param = filename});
waitMessage(); waitMessage();
errorstr = msgResult ? "" : msgError;
return msgResult; return msgResult;
} }
@ -795,10 +804,11 @@ void EmuThread::ejectCart(bool gba)
waitMessage(); waitMessage();
} }
int EmuThread::insertGBAAddon(int type) int EmuThread::insertGBAAddon(int type, QString& errorstr)
{ {
sendMessage({.type = msg_InsertGBAAddon, .param = type}); sendMessage({.type = msg_InsertGBAAddon, .param = type});
waitMessage(); waitMessage();
errorstr = msgResult ? "" : msgError;
return msgResult; return msgResult;
} }

View File

@ -33,9 +33,6 @@
#include "NDSCart.h" #include "NDSCart.h"
#include "GBACart.h" #include "GBACart.h"
using Keep = std::monostate;
using UpdateConsoleNDSArgs = std::variant<Keep, std::unique_ptr<melonDS::NDSCart::CartCommon>>;
using UpdateConsoleGBAArgs = std::variant<Keep, std::unique_ptr<melonDS::GBACart::CartCommon>>;
namespace melonDS namespace melonDS
{ {
class NDS; class NDS;
@ -114,11 +111,11 @@ public:
void emuFrameStep(); void emuFrameStep();
void emuReset(); void emuReset();
int bootROM(const QStringList& filename); int bootROM(const QStringList& filename, QString& errorstr);
int bootFirmware(); int bootFirmware(QString& errorstr);
int insertCart(const QStringList& filename, bool gba); int insertCart(const QStringList& filename, bool gba, QString& errorstr);
void ejectCart(bool gba); void ejectCart(bool gba);
int insertGBAAddon(int type); int insertGBAAddon(int type, QString& errorstr);
int saveState(const QString& filename); int saveState(const QString& filename);
int loadState(const QString& filename); int loadState(const QString& filename);
@ -184,6 +181,7 @@ private:
int emuPauseStack; int emuPauseStack;
int msgResult = 0; int msgResult = 0;
QString msgError;
QMutex msgMutex; QMutex msgMutex;
QSemaphore msgSemaphore; QSemaphore msgSemaphore;

View File

@ -35,9 +35,9 @@ InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(pare
auto& cfg = emuInstance->getGlobalConfig(); auto& cfg = emuInstance->getGlobalConfig();
ui->cbMouseHide->setChecked(cfg.GetBool("MouseHide")); ui->cbMouseHide->setChecked(cfg.GetBool("Mouse.Hide"));
ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked()); ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked());
ui->spinMouseHideSeconds->setValue(cfg.GetInt("MouseHideSeconds")); ui->spinMouseHideSeconds->setValue(cfg.GetInt("Mouse.HideSeconds"));
ui->cbPauseLostFocus->setChecked(cfg.GetBool("PauseLostFocus")); ui->cbPauseLostFocus->setChecked(cfg.GetBool("PauseLostFocus"));
ui->spinTargetFPS->setValue(cfg.GetDouble("TargetFPS")); ui->spinTargetFPS->setValue(cfg.GetDouble("TargetFPS"));
ui->spinFFW->setValue(cfg.GetDouble("FastForwardFPS")); ui->spinFFW->setValue(cfg.GetDouble("FastForwardFPS"));
@ -115,8 +115,8 @@ void InterfaceSettingsDialog::done(int r)
{ {
auto& cfg = emuInstance->getGlobalConfig(); auto& cfg = emuInstance->getGlobalConfig();
cfg.SetBool("MouseHide", ui->cbMouseHide->isChecked()); cfg.SetBool("Mouse.Hide", ui->cbMouseHide->isChecked());
cfg.SetInt("MouseHideSeconds", ui->spinMouseHideSeconds->value()); cfg.SetInt("Mouse.HideSeconds", ui->spinMouseHideSeconds->value());
cfg.SetBool("PauseLostFocus", ui->cbPauseLostFocus->isChecked()); cfg.SetBool("PauseLostFocus", ui->cbPauseLostFocus->isChecked());
double val = ui->spinTargetFPS->value(); double val = ui->spinTargetFPS->value();

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>547</width> <width>547</width>
<height>409</height> <height>407</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -48,7 +48,7 @@
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Orientation::Horizontal</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
@ -63,17 +63,24 @@
<item> <item>
<widget class="QTreeView" name="tvAvailableGames"> <widget class="QTreeView" name="tvAvailableGames">
<property name="editTriggers"> <property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set> <set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Warning: LAN requires low network latency to work.&lt;/p&gt;&lt;p&gt;Do not expect it to work through a VPN or any sort of tunnel.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Orientation::Horizontal</enum>
</property> </property>
<property name="standardButtons"> <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>389</width> <width>389</width>
<height>228</height> <height>202</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -21,7 +21,7 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint"> <property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum> <enum>QLayout::SizeConstraint::SetFixedSize</enum>
</property> </property>
<item> <item>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
@ -45,15 +45,29 @@
<item row="1" column="1"> <item row="1" column="1">
<widget class="QSpinBox" name="sbNumPlayers"/> <widget class="QSpinBox" name="sbNumPlayers"/>
</item> </item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Warning: LAN requires low network latency to work.&lt;/p&gt;&lt;p&gt;Do not expect it to work through a VPN or any sort of tunnel.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Orientation::Horizontal</enum>
</property> </property>
<property name="standardButtons"> <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -45,7 +45,7 @@ PathSettingsDialog::PathSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne
emuInstance = ((MainWindow*)parent)->getEmuInstance(); emuInstance = ((MainWindow*)parent)->getEmuInstance();
auto& cfg = emuInstance->getGlobalConfig(); auto& cfg = emuInstance->getLocalConfig();
ui->txtSaveFilePath->setText(cfg.GetQString("SaveFilePath")); ui->txtSaveFilePath->setText(cfg.GetQString("SaveFilePath"));
ui->txtSavestatePath->setText(cfg.GetQString("SavestatePath")); ui->txtSavestatePath->setText(cfg.GetQString("SavestatePath"));
ui->txtCheatFilePath->setText(cfg.GetQString("CheatFilePath")); ui->txtCheatFilePath->setText(cfg.GetQString("CheatFilePath"));
@ -108,7 +108,7 @@ void PathSettingsDialog::done(int r)
QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok)
return; return;
auto& cfg = emuInstance->getGlobalConfig(); auto& cfg = emuInstance->getLocalConfig();
cfg.SetQString("SaveFilePath", ui->txtSaveFilePath->text()); cfg.SetQString("SaveFilePath", ui->txtSaveFilePath->text());
cfg.SetQString("SavestatePath", ui->txtSavestatePath->text()); cfg.SetQString("SavestatePath", ui->txtSavestatePath->text());
cfg.SetQString("CheatFilePath", ui->txtCheatFilePath->text()); cfg.SetQString("CheatFilePath", ui->txtCheatFilePath->text());

View File

@ -826,6 +826,8 @@ void MainWindow::saveEnabled(bool enabled)
void MainWindow::closeEvent(QCloseEvent* event) void MainWindow::closeEvent(QCloseEvent* event)
{ {
if (!emuInstance) return;
if (windowID == 0) if (windowID == 0)
emuInstance->saveEnabledWindows(); emuInstance->saveEnabledWindows();
else else
@ -862,7 +864,7 @@ void MainWindow::createScreenPanel()
// Check that creating the context hasn't failed // Check that creating the context hasn't failed
if (panelGL->createContext() == false) if (panelGL->createContext() == false)
{ {
Log(LogLevel::Error, "Failed to create OpenGL context, falling back to Software Renderer.\n"); Log(Platform::LogLevel::Error, "Failed to create OpenGL context, falling back to Software Renderer.\n");
hasOGL = false; hasOGL = false;
globalCfg.SetBool("Screen.UseGL", false); globalCfg.SetBool("Screen.UseGL", false);
@ -991,10 +993,12 @@ void MainWindow::dropEvent(QDropEvent* event)
isNdsRom |= ZstdNdsRomByExtension(filename); isNdsRom |= ZstdNdsRomByExtension(filename);
isGbaRom |= ZstdGbaRomByExtension(filename); isGbaRom |= ZstdGbaRomByExtension(filename);
QString errorstr;
if (isNdsRom) if (isNdsRom)
{ {
if (!emuThread->bootROM(file)) if (!emuThread->bootROM(file, errorstr))
{ {
QMessageBox::critical(this, "melonDS", errorstr);
return; return;
} }
@ -1007,8 +1011,9 @@ void MainWindow::dropEvent(QDropEvent* event)
} }
else if (isGbaRom) else if (isGbaRom)
{ {
if (!emuThread->insertCart(file, true)) if (!emuThread->insertCart(file, true, errorstr))
{ {
QMessageBox::critical(this, "melonDS", errorstr);
return; return;
} }
@ -1076,6 +1081,8 @@ bool MainWindow::verifySetup()
bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot)
{ {
QString errorstr;
if (file.isEmpty() && gbafile.isEmpty()) if (file.isEmpty() && gbafile.isEmpty())
return false; return false;
@ -1087,8 +1094,11 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot)
bool gbaloaded = false; bool gbaloaded = false;
if (!gbafile.isEmpty()) if (!gbafile.isEmpty())
{ {
if (!emuThread->insertCart(gbafile, true)) if (!emuThread->insertCart(gbafile, true, errorstr))
{
QMessageBox::critical(this, "melonDS", errorstr);
return false; return false;
}
gbaloaded = true; gbaloaded = true;
} }
@ -1098,13 +1108,19 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot)
{ {
if (boot) if (boot)
{ {
if (!emuThread->bootROM(file)) if (!emuThread->bootROM(file, errorstr))
{
QMessageBox::critical(this, "melonDS", errorstr);
return false; return false;
}
} }
else else
{ {
if (!emuThread->insertCart(file, false)) if (!emuThread->insertCart(file, false, errorstr))
{
QMessageBox::critical(this, "melonDS", errorstr);
return false; return false;
}
} }
recentFileList.removeAll(file.join("|")); recentFileList.removeAll(file.join("|"));
@ -1114,8 +1130,11 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot)
} }
else if (boot) else if (boot)
{ {
if (!emuThread->bootFirmware()) if (!emuThread->bootFirmware(errorstr))
{
QMessageBox::critical(this, "melonDS", errorstr);
return false; return false;
}
} }
updateCartInserted(false); updateCartInserted(false);
@ -1313,8 +1332,10 @@ void MainWindow::onOpenFile()
if (file.isEmpty()) if (file.isEmpty())
return; return;
if (!emuThread->bootROM(file)) QString errorstr;
if (!emuThread->bootROM(file, errorstr))
{ {
QMessageBox::critical(this, "melonDS", errorstr);
return; return;
} }
@ -1424,8 +1445,10 @@ void MainWindow::onClickRecentFile()
if (file.isEmpty()) if (file.isEmpty())
return; return;
if (!emuThread->bootROM(file)) QString errorstr;
if (!emuThread->bootROM(file, errorstr))
{ {
QMessageBox::critical(this, "melonDS", errorstr);
return; return;
} }
@ -1441,9 +1464,10 @@ void MainWindow::onBootFirmware()
if (!verifySetup()) if (!verifySetup())
return; return;
if (!emuThread->bootFirmware()) QString errorstr;
if (!emuThread->bootFirmware(errorstr))
{ {
QMessageBox::critical(this, "melonDS", "This firmware is not bootable."); QMessageBox::critical(this, "melonDS", errorstr);
return; return;
} }
} }
@ -1454,8 +1478,10 @@ void MainWindow::onInsertCart()
if (file.isEmpty()) if (file.isEmpty())
return; return;
if (!emuThread->insertCart(file, false)) QString errorstr;
if (!emuThread->insertCart(file, false, errorstr))
{ {
QMessageBox::critical(this, "melonDS", errorstr);
return; return;
} }
@ -1474,8 +1500,10 @@ void MainWindow::onInsertGBACart()
if (file.isEmpty()) if (file.isEmpty())
return; return;
if (!emuThread->insertCart(file, true)) QString errorstr;
if (!emuThread->insertCart(file, true, errorstr))
{ {
QMessageBox::critical(this, "melonDS", errorstr);
return; return;
} }
@ -1487,7 +1515,13 @@ void MainWindow::onInsertGBAAddon()
QAction* act = (QAction*)sender(); QAction* act = (QAction*)sender();
int type = act->data().toInt(); int type = act->data().toInt();
emuThread->insertGBAAddon(type); QString errorstr;
if (!emuThread->insertGBAAddon(type, errorstr))
{
QMessageBox::critical(this, "melonDS", errorstr);
return;
}
updateCartInserted(true); updateCartInserted(true);
} }
@ -2003,8 +2037,8 @@ void MainWindow::onUpdateInterfaceSettings()
emuInstance->targetFPS = globalCfg.GetDouble("TargetFPS"); emuInstance->targetFPS = globalCfg.GetDouble("TargetFPS");
emuInstance->fastForwardFPS = globalCfg.GetDouble("FastForwardFPS"); emuInstance->fastForwardFPS = globalCfg.GetDouble("FastForwardFPS");
emuInstance->slowmoFPS = globalCfg.GetDouble("SlowmoFPS"); emuInstance->slowmoFPS = globalCfg.GetDouble("SlowmoFPS");
panel->setMouseHide(globalCfg.GetBool("MouseHide"), panel->setMouseHide(globalCfg.GetBool("Mouse.Hide"),
globalCfg.GetInt("MouseHideSeconds")*1000); globalCfg.GetInt("Mouse.HideSeconds")*1000);
} }
void MainWindow::onInterfaceSettingsFinished(int res) void MainWindow::onInterfaceSettingsFinished(int res)

View File

@ -1,38 +1,62 @@
/* #ifndef TOML11_TOML_HPP
* The MIT License (MIT) #define TOML11_TOML_HPP
*
* Copyright (c) 2017 Toru Niina
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef TOML_FOR_MODERN_CPP // The MIT License (MIT)
#define TOML_FOR_MODERN_CPP //
// Copyright (c) 2017-now Toru Niina
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#define TOML11_VERSION_MAJOR 3 // IWYU pragma: begin_exports
#define TOML11_VERSION_MINOR 7 #include "toml11/color.hpp"
#define TOML11_VERSION_PATCH 1 #include "toml11/comments.hpp"
#include "toml11/compat.hpp"
#include "toml11/context.hpp"
#include "toml11/conversion.hpp"
#include "toml11/datetime.hpp"
#include "toml11/error_info.hpp"
#include "toml11/exception.hpp"
#include "toml11/find.hpp"
#include "toml11/format.hpp"
#include "toml11/from.hpp"
#include "toml11/get.hpp"
#include "toml11/into.hpp"
#include "toml11/literal.hpp"
#include "toml11/location.hpp"
#include "toml11/ordered_map.hpp"
#include "toml11/parser.hpp"
#include "toml11/region.hpp"
#include "toml11/result.hpp"
#include "toml11/scanner.hpp"
#include "toml11/serializer.hpp"
#include "toml11/skip.hpp"
#include "toml11/source_location.hpp"
#include "toml11/spec.hpp"
#include "toml11/storage.hpp"
#include "toml11/syntax.hpp"
#include "toml11/traits.hpp"
#include "toml11/types.hpp"
#include "toml11/utility.hpp"
#include "toml11/value.hpp"
#include "toml11/value_t.hpp"
#include "toml11/version.hpp"
#include "toml11/visit.hpp"
// IWYU pragma: end_exports
#include "toml/parser.hpp" #endif// TOML11_TOML_HPP
#include "toml/literal.hpp"
#include "toml/serializer.hpp"
#include "toml/get.hpp"
#include "toml/macros.hpp"
#endif// TOML_FOR_MODERN_CPP

View File

@ -1,64 +0,0 @@
#ifndef TOML11_COLOR_HPP
#define TOML11_COLOR_HPP
#include <cstdint>
#include <ostream>
#ifdef TOML11_COLORIZE_ERROR_MESSAGE
#define TOML11_ERROR_MESSAGE_COLORIZED true
#else
#define TOML11_ERROR_MESSAGE_COLORIZED false
#endif
namespace toml
{
// put ANSI escape sequence to ostream
namespace color_ansi
{
namespace detail
{
inline int colorize_index()
{
static const int index = std::ios_base::xalloc();
return index;
}
} // detail
inline std::ostream& colorize(std::ostream& os)
{
// by default, it is zero.
os.iword(detail::colorize_index()) = 1;
return os;
}
inline std::ostream& nocolorize(std::ostream& os)
{
os.iword(detail::colorize_index()) = 0;
return os;
}
inline std::ostream& reset (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;}
inline std::ostream& bold (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;}
inline std::ostream& grey (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;}
inline std::ostream& red (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;}
inline std::ostream& green (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;}
inline std::ostream& yellow (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;}
inline std::ostream& blue (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;}
inline std::ostream& magenta(std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;}
inline std::ostream& cyan (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;}
inline std::ostream& white (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;}
} // color_ansi
// ANSI escape sequence is the only and default colorization method currently
namespace color = color_ansi;
} // toml
#endif// TOML11_COLOR_HPP

View File

@ -1,306 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_COMBINATOR_HPP
#define TOML11_COMBINATOR_HPP
#include <cassert>
#include <cctype>
#include <cstdio>
#include <array>
#include <iomanip>
#include <iterator>
#include <limits>
#include <type_traits>
#include "region.hpp"
#include "result.hpp"
#include "traits.hpp"
#include "utility.hpp"
// they scans characters and returns region if it matches to the condition.
// when they fail, it does not change the location.
// in lexer.hpp, these are used.
namespace toml
{
namespace detail
{
// to output character as an error message.
inline std::string show_char(const char c)
{
// It suppresses an error that occurs only in Debug mode of MSVC++ on Windows.
// I'm not completely sure but they check the value of char to be in the
// range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes
// has negative value (if char has sign). So here it re-interprets c as
// unsigned char through pointer. In general, converting pointer to a
// pointer that has different type cause UB, but `(signed|unsigned)?char`
// are one of the exceptions. Converting pointer only to char and std::byte
// (c++17) are valid.
if(std::isgraph(*reinterpret_cast<unsigned char const*>(std::addressof(c))))
{
return std::string(1, c);
}
else
{
std::array<char, 5> buf;
buf.fill('\0');
const auto r = std::snprintf(
buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF);
(void) r; // Unused variable warning
assert(r == static_cast<int>(buf.size()) - 1);
return std::string(buf.data());
}
}
template<char C>
struct character
{
static constexpr char target = C;
static result<region, none_t>
invoke(location& loc)
{
if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter();
const char c = *(loc.iter());
if(c != target)
{
return none();
}
loc.advance(); // update location
return ok(region(loc, first, loc.iter()));
}
};
template<char C>
constexpr char character<C>::target;
// closed interval [Low, Up]. both Low and Up are included.
template<char Low, char Up>
struct in_range
{
// assuming ascii part of UTF-8...
static_assert(Low <= Up, "lower bound should be less than upper bound.");
static constexpr char upper = Up;
static constexpr char lower = Low;
static result<region, none_t>
invoke(location& loc)
{
if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter();
const char c = *(loc.iter());
if(c < lower || upper < c)
{
return none();
}
loc.advance();
return ok(region(loc, first, loc.iter()));
}
};
template<char L, char U> constexpr char in_range<L, U>::upper;
template<char L, char U> constexpr char in_range<L, U>::lower;
// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char.
// for detecting invalid characters, like control sequences in toml string.
template<typename Combinator>
struct exclude
{
static result<region, none_t>
invoke(location& loc)
{
if(loc.iter() == loc.end()) {return none();}
auto first = loc.iter();
auto rslt = Combinator::invoke(loc);
if(rslt.is_ok())
{
loc.reset(first);
return none();
}
loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
return ok(region(loc, first, loc.iter()));
}
};
// increment `iter`, if matches. otherwise, just return empty string.
template<typename Combinator>
struct maybe
{
static result<region, none_t>
invoke(location& loc)
{
const auto rslt = Combinator::invoke(loc);
if(rslt.is_ok())
{
return rslt;
}
return ok(region(loc));
}
};
template<typename ... Ts>
struct sequence;
template<typename Head, typename ... Tail>
struct sequence<Head, Tail...>
{
static result<region, none_t>
invoke(location& loc)
{
const auto first = loc.iter();
auto rslt = Head::invoke(loc);
if(rslt.is_err())
{
loc.reset(first);
return none();
}
return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first);
}
// called from the above function only, recursively.
template<typename Iterator>
static result<region, none_t>
invoke(location& loc, region reg, Iterator first)
{
const auto rslt = Head::invoke(loc);
if(rslt.is_err())
{
loc.reset(first);
return none();
}
reg += rslt.unwrap(); // concat regions
return sequence<Tail...>::invoke(loc, std::move(reg), first);
}
};
template<typename Head>
struct sequence<Head>
{
// would be called from sequence<T ...>::invoke only.
template<typename Iterator>
static result<region, none_t>
invoke(location& loc, region reg, Iterator first)
{
const auto rslt = Head::invoke(loc);
if(rslt.is_err())
{
loc.reset(first);
return none();
}
reg += rslt.unwrap(); // concat regions
return ok(reg);
}
};
template<typename ... Ts>
struct either;
template<typename Head, typename ... Tail>
struct either<Head, Tail...>
{
static result<region, none_t>
invoke(location& loc)
{
const auto rslt = Head::invoke(loc);
if(rslt.is_ok()) {return rslt;}
return either<Tail...>::invoke(loc);
}
};
template<typename Head>
struct either<Head>
{
static result<region, none_t>
invoke(location& loc)
{
return Head::invoke(loc);
}
};
template<typename T, typename N>
struct repeat;
template<std::size_t N> struct exactly{};
template<std::size_t N> struct at_least{};
struct unlimited{};
template<typename T, std::size_t N>
struct repeat<T, exactly<N>>
{
static result<region, none_t>
invoke(location& loc)
{
region retval(loc);
const auto first = loc.iter();
for(std::size_t i=0; i<N; ++i)
{
auto rslt = T::invoke(loc);
if(rslt.is_err())
{
loc.reset(first);
return none();
}
retval += rslt.unwrap();
}
return ok(std::move(retval));
}
};
template<typename T, std::size_t N>
struct repeat<T, at_least<N>>
{
static result<region, none_t>
invoke(location& loc)
{
region retval(loc);
const auto first = loc.iter();
for(std::size_t i=0; i<N; ++i)
{
auto rslt = T::invoke(loc);
if(rslt.is_err())
{
loc.reset(first);
return none();
}
retval += rslt.unwrap();
}
while(true)
{
auto rslt = T::invoke(loc);
if(rslt.is_err())
{
return ok(std::move(retval));
}
retval += rslt.unwrap();
}
}
};
template<typename T>
struct repeat<T, unlimited>
{
static result<region, none_t>
invoke(location& loc)
{
region retval(loc);
while(true)
{
auto rslt = T::invoke(loc);
if(rslt.is_err())
{
return ok(std::move(retval));
}
retval += rslt.unwrap();
}
}
};
} // detail
} // toml
#endif// TOML11_COMBINATOR_HPP

View File

@ -1,631 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_DATETIME_HPP
#define TOML11_DATETIME_HPP
#include <cstdint>
#include <cstdlib>
#include <ctime>
#include <array>
#include <chrono>
#include <iomanip>
#include <ostream>
#include <tuple>
namespace toml
{
// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is
// provided in the absolutely same purpose, but C++11 is actually not compatible
// with C11. We need to dispatch the function depending on the OS.
namespace detail
{
// TODO: find more sophisticated way to handle this
#if defined(_MSC_VER)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_s(&dst, src);
if (result) { throw std::runtime_error("localtime_s failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_s(&dst, src);
if (result) { throw std::runtime_error("gmtime_s failed."); }
return dst;
}
#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_r(src, &dst);
if (!result) { throw std::runtime_error("localtime_r failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_r(src, &dst);
if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst;
}
#else // fallback. not threadsafe
inline std::tm localtime_s(const std::time_t* src)
{
const auto result = std::localtime(src);
if (!result) { throw std::runtime_error("localtime failed."); }
return *result;
}
inline std::tm gmtime_s(const std::time_t* src)
{
const auto result = std::gmtime(src);
if (!result) { throw std::runtime_error("gmtime failed."); }
return *result;
}
#endif
} // detail
enum class month_t : std::uint8_t
{
Jan = 0,
Feb = 1,
Mar = 2,
Apr = 3,
May = 4,
Jun = 5,
Jul = 6,
Aug = 7,
Sep = 8,
Oct = 9,
Nov = 10,
Dec = 11
};
struct local_date
{
std::int16_t year; // A.D. (like, 2018)
std::uint8_t month; // [0, 11]
std::uint8_t day; // [1, 31]
local_date(int y, month_t m, int d)
: year (static_cast<std::int16_t>(y)),
month(static_cast<std::uint8_t>(m)),
day (static_cast<std::uint8_t>(d))
{}
explicit local_date(const std::tm& t)
: year (static_cast<std::int16_t>(t.tm_year + 1900)),
month(static_cast<std::uint8_t>(t.tm_mon)),
day (static_cast<std::uint8_t>(t.tm_mday))
{}
explicit local_date(const std::chrono::system_clock::time_point& tp)
{
const auto t = std::chrono::system_clock::to_time_t(tp);
const auto time = detail::localtime_s(&t);
*this = local_date(time);
}
explicit local_date(const std::time_t t)
: local_date(std::chrono::system_clock::from_time_t(t))
{}
operator std::chrono::system_clock::time_point() const
{
// std::mktime returns date as local time zone. no conversion needed
std::tm t;
t.tm_sec = 0;
t.tm_min = 0;
t.tm_hour = 0;
t.tm_mday = static_cast<int>(this->day);
t.tm_mon = static_cast<int>(this->month);
t.tm_year = static_cast<int>(this->year) - 1900;
t.tm_wday = 0; // the value will be ignored
t.tm_yday = 0; // the value will be ignored
t.tm_isdst = -1;
return std::chrono::system_clock::from_time_t(std::mktime(&t));
}
operator std::time_t() const
{
return std::chrono::system_clock::to_time_t(
std::chrono::system_clock::time_point(*this));
}
local_date() = default;
~local_date() = default;
local_date(local_date const&) = default;
local_date(local_date&&) = default;
local_date& operator=(local_date const&) = default;
local_date& operator=(local_date&&) = default;
};
inline bool operator==(const local_date& lhs, const local_date& rhs)
{
return std::make_tuple(lhs.year, lhs.month, lhs.day) ==
std::make_tuple(rhs.year, rhs.month, rhs.day);
}
inline bool operator!=(const local_date& lhs, const local_date& rhs)
{
return !(lhs == rhs);
}
inline bool operator< (const local_date& lhs, const local_date& rhs)
{
return std::make_tuple(lhs.year, lhs.month, lhs.day) <
std::make_tuple(rhs.year, rhs.month, rhs.day);
}
inline bool operator<=(const local_date& lhs, const local_date& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
inline bool operator> (const local_date& lhs, const local_date& rhs)
{
return !(lhs <= rhs);
}
inline bool operator>=(const local_date& lhs, const local_date& rhs)
{
return !(lhs < rhs);
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const local_date& date)
{
os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day ) ;
return os;
}
struct local_time
{
std::uint8_t hour; // [0, 23]
std::uint8_t minute; // [0, 59]
std::uint8_t second; // [0, 60]
std::uint16_t millisecond; // [0, 999]
std::uint16_t microsecond; // [0, 999]
std::uint16_t nanosecond; // [0, 999]
local_time(int h, int m, int s,
int ms = 0, int us = 0, int ns = 0)
: hour (static_cast<std::uint8_t>(h)),
minute(static_cast<std::uint8_t>(m)),
second(static_cast<std::uint8_t>(s)),
millisecond(static_cast<std::uint16_t>(ms)),
microsecond(static_cast<std::uint16_t>(us)),
nanosecond (static_cast<std::uint16_t>(ns))
{}
explicit local_time(const std::tm& t)
: hour (static_cast<std::uint8_t>(t.tm_hour)),
minute(static_cast<std::uint8_t>(t.tm_min)),
second(static_cast<std::uint8_t>(t.tm_sec)),
millisecond(0), microsecond(0), nanosecond(0)
{}
template<typename Rep, typename Period>
explicit local_time(const std::chrono::duration<Rep, Period>& t)
{
const auto h = std::chrono::duration_cast<std::chrono::hours>(t);
this->hour = static_cast<std::uint8_t>(h.count());
const auto t2 = t - h;
const auto m = std::chrono::duration_cast<std::chrono::minutes>(t2);
this->minute = static_cast<std::uint8_t>(m.count());
const auto t3 = t2 - m;
const auto s = std::chrono::duration_cast<std::chrono::seconds>(t3);
this->second = static_cast<std::uint8_t>(s.count());
const auto t4 = t3 - s;
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t4);
this->millisecond = static_cast<std::uint16_t>(ms.count());
const auto t5 = t4 - ms;
const auto us = std::chrono::duration_cast<std::chrono::microseconds>(t5);
this->microsecond = static_cast<std::uint16_t>(us.count());
const auto t6 = t5 - us;
const auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(t6);
this->nanosecond = static_cast<std::uint16_t>(ns.count());
}
operator std::chrono::nanoseconds() const
{
return std::chrono::nanoseconds (this->nanosecond) +
std::chrono::microseconds(this->microsecond) +
std::chrono::milliseconds(this->millisecond) +
std::chrono::seconds(this->second) +
std::chrono::minutes(this->minute) +
std::chrono::hours(this->hour);
}
local_time() = default;
~local_time() = default;
local_time(local_time const&) = default;
local_time(local_time&&) = default;
local_time& operator=(local_time const&) = default;
local_time& operator=(local_time&&) = default;
};
inline bool operator==(const local_time& lhs, const local_time& rhs)
{
return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) ==
std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond);
}
inline bool operator!=(const local_time& lhs, const local_time& rhs)
{
return !(lhs == rhs);
}
inline bool operator< (const local_time& lhs, const local_time& rhs)
{
return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) <
std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond);
}
inline bool operator<=(const local_time& lhs, const local_time& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
inline bool operator> (const local_time& lhs, const local_time& rhs)
{
return !(lhs <= rhs);
}
inline bool operator>=(const local_time& lhs, const local_time& rhs)
{
return !(lhs < rhs);
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const local_time& time)
{
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.hour ) << ':';
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.minute) << ':';
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.second);
if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0)
{
os << '.';
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.millisecond);
if(time.microsecond != 0 || time.nanosecond != 0)
{
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.microsecond);
if(time.nanosecond != 0)
{
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.nanosecond);
}
}
}
return os;
}
struct time_offset
{
std::int8_t hour; // [-12, 12]
std::int8_t minute; // [-59, 59]
time_offset(int h, int m)
: hour (static_cast<std::int8_t>(h)),
minute(static_cast<std::int8_t>(m))
{}
operator std::chrono::minutes() const
{
return std::chrono::minutes(this->minute) +
std::chrono::hours(this->hour);
}
time_offset() = default;
~time_offset() = default;
time_offset(time_offset const&) = default;
time_offset(time_offset&&) = default;
time_offset& operator=(time_offset const&) = default;
time_offset& operator=(time_offset&&) = default;
};
inline bool operator==(const time_offset& lhs, const time_offset& rhs)
{
return std::make_tuple(lhs.hour, lhs.minute) ==
std::make_tuple(rhs.hour, rhs.minute);
}
inline bool operator!=(const time_offset& lhs, const time_offset& rhs)
{
return !(lhs == rhs);
}
inline bool operator< (const time_offset& lhs, const time_offset& rhs)
{
return std::make_tuple(lhs.hour, lhs.minute) <
std::make_tuple(rhs.hour, rhs.minute);
}
inline bool operator<=(const time_offset& lhs, const time_offset& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
inline bool operator> (const time_offset& lhs, const time_offset& rhs)
{
return !(lhs <= rhs);
}
inline bool operator>=(const time_offset& lhs, const time_offset& rhs)
{
return !(lhs < rhs);
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const time_offset& offset)
{
if(offset.hour == 0 && offset.minute == 0)
{
os << 'Z';
return os;
}
int minute = static_cast<int>(offset.hour) * 60 + offset.minute;
if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';}
os << std::setfill('0') << std::setw(2) << minute / 60 << ':';
os << std::setfill('0') << std::setw(2) << minute % 60;
return os;
}
struct local_datetime
{
local_date date;
local_time time;
local_datetime(local_date d, local_time t): date(d), time(t) {}
explicit local_datetime(const std::tm& t): date(t), time(t){}
explicit local_datetime(const std::chrono::system_clock::time_point& tp)
{
const auto t = std::chrono::system_clock::to_time_t(tp);
std::tm ltime = detail::localtime_s(&t);
this->date = local_date(ltime);
this->time = local_time(ltime);
// std::tm lacks subsecond information, so diff between tp and tm
// can be used to get millisecond & microsecond information.
const auto t_diff = tp -
std::chrono::system_clock::from_time_t(std::mktime(&ltime));
this->time.millisecond = static_cast<std::uint16_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(t_diff).count());
this->time.microsecond = static_cast<std::uint16_t>(
std::chrono::duration_cast<std::chrono::microseconds>(t_diff).count());
this->time.nanosecond = static_cast<std::uint16_t>(
std::chrono::duration_cast<std::chrono::nanoseconds >(t_diff).count());
}
explicit local_datetime(const std::time_t t)
: local_datetime(std::chrono::system_clock::from_time_t(t))
{}
operator std::chrono::system_clock::time_point() const
{
using internal_duration =
typename std::chrono::system_clock::time_point::duration;
// Normally DST begins at A.M. 3 or 4. If we re-use conversion operator
// of local_date and local_time independently, the conversion fails if
// it is the day when DST begins or ends. Since local_date considers the
// time is 00:00 A.M. and local_time does not consider DST because it
// does not have any date information. We need to consider both date and
// time information at the same time to convert it correctly.
std::tm t;
t.tm_sec = static_cast<int>(this->time.second);
t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(this->date.year) - 1900;
t.tm_wday = 0; // the value will be ignored
t.tm_yday = 0; // the value will be ignored
t.tm_isdst = -1;
// std::mktime returns date as local time zone. no conversion needed
auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t));
dt += std::chrono::duration_cast<internal_duration>(
std::chrono::milliseconds(this->time.millisecond) +
std::chrono::microseconds(this->time.microsecond) +
std::chrono::nanoseconds (this->time.nanosecond));
return dt;
}
operator std::time_t() const
{
return std::chrono::system_clock::to_time_t(
std::chrono::system_clock::time_point(*this));
}
local_datetime() = default;
~local_datetime() = default;
local_datetime(local_datetime const&) = default;
local_datetime(local_datetime&&) = default;
local_datetime& operator=(local_datetime const&) = default;
local_datetime& operator=(local_datetime&&) = default;
};
inline bool operator==(const local_datetime& lhs, const local_datetime& rhs)
{
return std::make_tuple(lhs.date, lhs.time) ==
std::make_tuple(rhs.date, rhs.time);
}
inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs)
{
return !(lhs == rhs);
}
inline bool operator< (const local_datetime& lhs, const local_datetime& rhs)
{
return std::make_tuple(lhs.date, lhs.time) <
std::make_tuple(rhs.date, rhs.time);
}
inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
inline bool operator> (const local_datetime& lhs, const local_datetime& rhs)
{
return !(lhs <= rhs);
}
inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs)
{
return !(lhs < rhs);
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const local_datetime& dt)
{
os << dt.date << 'T' << dt.time;
return os;
}
struct offset_datetime
{
local_date date;
local_time time;
time_offset offset;
offset_datetime(local_date d, local_time t, time_offset o)
: date(d), time(t), offset(o)
{}
offset_datetime(const local_datetime& dt, time_offset o)
: date(dt.date), time(dt.time), offset(o)
{}
explicit offset_datetime(const local_datetime& ld)
: date(ld.date), time(ld.time), offset(get_local_offset(nullptr))
// use the current local timezone offset
{}
explicit offset_datetime(const std::chrono::system_clock::time_point& tp)
: offset(0, 0) // use gmtime
{
const auto timet = std::chrono::system_clock::to_time_t(tp);
const auto tm = detail::gmtime_s(&timet);
this->date = local_date(tm);
this->time = local_time(tm);
}
explicit offset_datetime(const std::time_t& t)
: offset(0, 0) // use gmtime
{
const auto tm = detail::gmtime_s(&t);
this->date = local_date(tm);
this->time = local_time(tm);
}
explicit offset_datetime(const std::tm& t)
: offset(0, 0) // assume gmtime
{
this->date = local_date(t);
this->time = local_time(t);
}
operator std::chrono::system_clock::time_point() const
{
// get date-time
using internal_duration =
typename std::chrono::system_clock::time_point::duration;
// first, convert it to local date-time information in the same way as
// local_datetime does. later we will use time_t to adjust time offset.
std::tm t;
t.tm_sec = static_cast<int>(this->time.second);
t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(this->date.year) - 1900;
t.tm_wday = 0; // the value will be ignored
t.tm_yday = 0; // the value will be ignored
t.tm_isdst = -1;
const std::time_t tp_loc = std::mktime(std::addressof(t));
auto tp = std::chrono::system_clock::from_time_t(tp_loc);
tp += std::chrono::duration_cast<internal_duration>(
std::chrono::milliseconds(this->time.millisecond) +
std::chrono::microseconds(this->time.microsecond) +
std::chrono::nanoseconds (this->time.nanosecond));
// Since mktime uses local time zone, it should be corrected.
// `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if
// we are in `+09:00` timezone. To represent `12:00:00Z` there, we need
// to add `+09:00` to `03:00:00Z`.
// Here, it uses the time_t converted from date-time info to handle
// daylight saving time.
const auto ofs = get_local_offset(std::addressof(tp_loc));
tp += std::chrono::hours (ofs.hour);
tp += std::chrono::minutes(ofs.minute);
// We got `12:00:00Z` by correcting local timezone applied by mktime.
// Then we will apply the offset. Let's say `12:00:00-08:00` is given.
// And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`.
// So we need to subtract the offset.
tp -= std::chrono::minutes(this->offset);
return tp;
}
operator std::time_t() const
{
return std::chrono::system_clock::to_time_t(
std::chrono::system_clock::time_point(*this));
}
offset_datetime() = default;
~offset_datetime() = default;
offset_datetime(offset_datetime const&) = default;
offset_datetime(offset_datetime&&) = default;
offset_datetime& operator=(offset_datetime const&) = default;
offset_datetime& operator=(offset_datetime&&) = default;
private:
static time_offset get_local_offset(const std::time_t* tp)
{
// get local timezone with the same date-time information as mktime
const auto t = detail::localtime_s(tp);
std::array<char, 6> buf;
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0
if(result != 5)
{
throw std::runtime_error("toml::offset_datetime: cannot obtain "
"timezone information of current env");
}
const int ofs = std::atoi(buf.data());
const int ofs_h = ofs / 100;
const int ofs_m = ofs - (ofs_h * 100);
return time_offset(ofs_h, ofs_m);
}
};
inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs)
{
return std::make_tuple(lhs.date, lhs.time, lhs.offset) ==
std::make_tuple(rhs.date, rhs.time, rhs.offset);
}
inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs)
{
return !(lhs == rhs);
}
inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs)
{
return std::make_tuple(lhs.date, lhs.time, lhs.offset) <
std::make_tuple(rhs.date, rhs.time, rhs.offset);
}
inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs)
{
return !(lhs <= rhs);
}
inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs)
{
return !(lhs < rhs);
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const offset_datetime& dt)
{
os << dt.date << 'T' << dt.time << dt.offset;
return os;
}
}//toml
#endif// TOML11_DATETIME

View File

@ -1,65 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION_HPP
#include <stdexcept>
#include <string>
#include "source_location.hpp"
namespace toml
{
struct exception : public std::exception
{
public:
explicit exception(const source_location& loc): loc_(loc) {}
virtual ~exception() noexcept override = default;
virtual const char* what() const noexcept override {return "";}
virtual source_location const& location() const noexcept {return loc_;}
protected:
source_location loc_;
};
struct syntax_error : public toml::exception
{
public:
explicit syntax_error(const std::string& what_arg, const source_location& loc)
: exception(loc), what_(what_arg)
{}
virtual ~syntax_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();}
protected:
std::string what_;
};
struct type_error : public toml::exception
{
public:
explicit type_error(const std::string& what_arg, const source_location& loc)
: exception(loc), what_(what_arg)
{}
virtual ~type_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();}
protected:
std::string what_;
};
struct internal_error : public toml::exception
{
public:
explicit internal_error(const std::string& what_arg, const source_location& loc)
: exception(loc), what_(what_arg)
{}
virtual ~internal_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();}
protected:
std::string what_;
};
} // toml
#endif // TOML_EXCEPTION

File diff suppressed because it is too large Load Diff

View File

@ -1,293 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_LEXER_HPP
#define TOML11_LEXER_HPP
#include <istream>
#include <sstream>
#include <stdexcept>
#include <fstream>
#include "combinator.hpp"
namespace toml
{
namespace detail
{
// these scans contents from current location in a container of char
// and extract a region that matches their own pattern.
// to see the implementation of each component, see combinator.hpp.
using lex_wschar = either<character<' '>, character<'\t'>>;
using lex_ws = repeat<lex_wschar, at_least<1>>;
using lex_newline = either<character<'\n'>,
sequence<character<'\r'>, character<'\n'>>>;
using lex_lower = in_range<'a', 'z'>;
using lex_upper = in_range<'A', 'Z'>;
using lex_alpha = either<lex_lower, lex_upper>;
using lex_digit = in_range<'0', '9'>;
using lex_nonzero = in_range<'1', '9'>;
using lex_oct_dig = in_range<'0', '7'>;
using lex_bin_dig = in_range<'0', '1'>;
using lex_hex_dig = either<lex_digit, in_range<'A', 'F'>, in_range<'a', 'f'>>;
using lex_hex_prefix = sequence<character<'0'>, character<'x'>>;
using lex_oct_prefix = sequence<character<'0'>, character<'o'>>;
using lex_bin_prefix = sequence<character<'0'>, character<'b'>>;
using lex_underscore = character<'_'>;
using lex_plus = character<'+'>;
using lex_minus = character<'-'>;
using lex_sign = either<lex_plus, lex_minus>;
// digit | nonzero 1*(digit | _ digit)
using lex_unsigned_dec_int = either<sequence<lex_nonzero, repeat<
either<lex_digit, sequence<lex_underscore, lex_digit>>, at_least<1>>>,
lex_digit>;
// (+|-)? unsigned_dec_int
using lex_dec_int = sequence<maybe<lex_sign>, lex_unsigned_dec_int>;
// hex_prefix hex_dig *(hex_dig | _ hex_dig)
using lex_hex_int = sequence<lex_hex_prefix, sequence<lex_hex_dig, repeat<
either<lex_hex_dig, sequence<lex_underscore, lex_hex_dig>>, unlimited>>>;
// oct_prefix oct_dig *(oct_dig | _ oct_dig)
using lex_oct_int = sequence<lex_oct_prefix, sequence<lex_oct_dig, repeat<
either<lex_oct_dig, sequence<lex_underscore, lex_oct_dig>>, unlimited>>>;
// bin_prefix bin_dig *(bin_dig | _ bin_dig)
using lex_bin_int = sequence<lex_bin_prefix, sequence<lex_bin_dig, repeat<
either<lex_bin_dig, sequence<lex_underscore, lex_bin_dig>>, unlimited>>>;
// (dec_int | hex_int | oct_int | bin_int)
using lex_integer = either<lex_bin_int, lex_oct_int, lex_hex_int, lex_dec_int>;
// ===========================================================================
using lex_inf = sequence<character<'i'>, character<'n'>, character<'f'>>;
using lex_nan = sequence<character<'n'>, character<'a'>, character<'n'>>;
using lex_special_float = sequence<maybe<lex_sign>, either<lex_inf, lex_nan>>;
using lex_zero_prefixable_int = sequence<lex_digit, repeat<either<lex_digit,
sequence<lex_underscore, lex_digit>>, unlimited>>;
using lex_fractional_part = sequence<character<'.'>, lex_zero_prefixable_int>;
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>,
maybe<lex_sign>, lex_zero_prefixable_int>;
using lex_float = either<lex_special_float,
sequence<lex_dec_int, either<lex_exponent_part,
sequence<lex_fractional_part, maybe<lex_exponent_part>>>>>;
// ===========================================================================
using lex_true = sequence<character<'t'>, character<'r'>,
character<'u'>, character<'e'>>;
using lex_false = sequence<character<'f'>, character<'a'>, character<'l'>,
character<'s'>, character<'e'>>;
using lex_boolean = either<lex_true, lex_false>;
// ===========================================================================
using lex_date_fullyear = repeat<lex_digit, exactly<4>>;
using lex_date_month = repeat<lex_digit, exactly<2>>;
using lex_date_mday = repeat<lex_digit, exactly<2>>;
using lex_time_delim = either<character<'T'>, character<'t'>, character<' '>>;
using lex_time_hour = repeat<lex_digit, exactly<2>>;
using lex_time_minute = repeat<lex_digit, exactly<2>>;
using lex_time_second = repeat<lex_digit, exactly<2>>;
using lex_time_secfrac = sequence<character<'.'>,
repeat<lex_digit, at_least<1>>>;
using lex_time_numoffset = sequence<either<character<'+'>, character<'-'>>,
sequence<lex_time_hour, character<':'>,
lex_time_minute>>;
using lex_time_offset = either<character<'Z'>, character<'z'>,
lex_time_numoffset>;
using lex_partial_time = sequence<lex_time_hour, character<':'>,
lex_time_minute, character<':'>,
lex_time_second, maybe<lex_time_secfrac>>;
using lex_full_date = sequence<lex_date_fullyear, character<'-'>,
lex_date_month, character<'-'>,
lex_date_mday>;
using lex_full_time = sequence<lex_partial_time, lex_time_offset>;
using lex_offset_date_time = sequence<lex_full_date, lex_time_delim, lex_full_time>;
using lex_local_date_time = sequence<lex_full_date, lex_time_delim, lex_partial_time>;
using lex_local_date = lex_full_date;
using lex_local_time = lex_partial_time;
// ===========================================================================
using lex_quotation_mark = character<'"'>;
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab) is allowed
in_range<0x0A, 0x1F>,
character<0x22>, character<0x5C>,
character<0x7F>>>;
using lex_escape = character<'\\'>;
using lex_escape_unicode_short = sequence<character<'u'>,
repeat<lex_hex_dig, exactly<4>>>;
using lex_escape_unicode_long = sequence<character<'U'>,
repeat<lex_hex_dig, exactly<8>>>;
using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
character<'b'>, character<'f'>,
character<'n'>, character<'r'>,
character<'t'>,
lex_escape_unicode_short,
lex_escape_unicode_long
>;
using lex_escaped = sequence<lex_escape, lex_escape_seq_char>;
using lex_basic_char = either<lex_basic_unescaped, lex_escaped>;
using lex_basic_string = sequence<lex_quotation_mark,
repeat<lex_basic_char, unlimited>,
lex_quotation_mark>;
// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings
// are allowed to be used.
// After this, the following strings are *explicitly* allowed.
// - One or two `"`s in a multi-line basic string is allowed wherever it is.
// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter.
// - One or two `"`s can appear just before or after the delimiter.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// str7 = """"This," she said, "is just a pointless statement.""""
// ```
// In the current implementation (v3.3.0), it is difficult to parse `str7` in
// the above example. It is difficult to recognize `"` at the end of string body
// collectly. It will be misunderstood as a `"""` delimiter and an additional,
// invalid `"`. Like this:
// ```console
// what(): [error] toml::parse_table: invalid line format
// --> hoge.toml
// |
// 13 | str7 = """"This," she said, "is just a pointless statement.""""
// | ^- expected newline, but got '"'.
// ```
// As a quick workaround for this problem, `lex_ml_basic_string_delim` was
// split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
// In parse_ml_basic_string() function, the trailing `"`s will be attached to
// the string body.
//
using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>;
using lex_ml_basic_string_open = lex_ml_basic_string_delim;
using lex_ml_basic_string_close = sequence<
repeat<lex_quotation_mark, exactly<3>>,
maybe<lex_quotation_mark>, maybe<lex_quotation_mark>
>;
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 is tab
in_range<0x0A, 0x1F>,
character<0x5C>, // backslash
character<0x7F>, // DEL
lex_ml_basic_string_delim>>;
using lex_ml_basic_escaped_newline = sequence<
lex_escape, maybe<lex_ws>, lex_newline,
repeat<either<lex_ws, lex_newline>, unlimited>>;
using lex_ml_basic_char = either<lex_ml_basic_unescaped, lex_escaped>;
using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline,
lex_ml_basic_escaped_newline>,
unlimited>;
using lex_ml_basic_string = sequence<lex_ml_basic_string_open,
lex_ml_basic_body,
lex_ml_basic_string_close>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>, in_range<0x0A, 0x1F>,
character<0x7F>, character<0x27>>>;
using lex_apostrophe = character<'\''>;
using lex_literal_string = sequence<lex_apostrophe,
repeat<lex_literal_char, unlimited>,
lex_apostrophe>;
// the same reason as above.
using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>;
using lex_ml_literal_string_open = lex_ml_literal_string_delim;
using lex_ml_literal_string_close = sequence<
repeat<lex_apostrophe, exactly<3>>,
maybe<lex_apostrophe>, maybe<lex_apostrophe>
>;
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x0A, 0x1F>,
character<0x7F>,
lex_ml_literal_string_delim>>;
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
unlimited>;
using lex_ml_literal_string = sequence<lex_ml_literal_string_open,
lex_ml_literal_body,
lex_ml_literal_string_close>;
using lex_string = either<lex_ml_basic_string, lex_basic_string,
lex_ml_literal_string, lex_literal_string>;
// ===========================================================================
using lex_dot_sep = sequence<maybe<lex_ws>, character<'.'>, maybe<lex_ws>>;
using lex_unquoted_key = repeat<either<lex_alpha, lex_digit,
character<'-'>, character<'_'>>,
at_least<1>>;
using lex_quoted_key = either<lex_basic_string, lex_literal_string>;
using lex_simple_key = either<lex_unquoted_key, lex_quoted_key>;
using lex_dotted_key = sequence<lex_simple_key,
repeat<sequence<lex_dot_sep, lex_simple_key>,
at_least<1>
>
>;
using lex_key = either<lex_dotted_key, lex_simple_key>;
using lex_keyval_sep = sequence<maybe<lex_ws>,
character<'='>,
maybe<lex_ws>>;
using lex_std_table_open = character<'['>;
using lex_std_table_close = character<']'>;
using lex_std_table = sequence<lex_std_table_open,
maybe<lex_ws>,
lex_key,
maybe<lex_ws>,
lex_std_table_close>;
using lex_array_table_open = sequence<lex_std_table_open, lex_std_table_open>;
using lex_array_table_close = sequence<lex_std_table_close, lex_std_table_close>;
using lex_array_table = sequence<lex_array_table_open,
maybe<lex_ws>,
lex_key,
maybe<lex_ws>,
lex_array_table_close>;
using lex_utf8_1byte = in_range<0x00, 0x7F>;
using lex_utf8_2byte = sequence<
in_range<static_cast<char>(0xC2), static_cast<char>(0xDF)>,
in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>
>;
using lex_utf8_3byte = sequence<either<
sequence<character<static_cast<char>(0xE0)>, in_range<static_cast<char>(0xA0), static_cast<char>(0xBF)>>,
sequence<in_range <static_cast<char>(0xE1), static_cast<char>(0xEC)>, in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>>,
sequence<character<static_cast<char>(0xED)>, in_range<static_cast<char>(0x80), static_cast<char>(0x9F)>>,
sequence<in_range <static_cast<char>(0xEE), static_cast<char>(0xEF)>, in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>>
>, in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>>;
using lex_utf8_4byte = sequence<either<
sequence<character<static_cast<char>(0xF0)>, in_range<static_cast<char>(0x90), static_cast<char>(0xBF)>>,
sequence<in_range <static_cast<char>(0xF1), static_cast<char>(0xF3)>, in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>>,
sequence<character<static_cast<char>(0xF4)>, in_range<static_cast<char>(0x80), static_cast<char>(0x8F)>>
>, in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>,
in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>>;
using lex_utf8_code = either<
lex_utf8_1byte,
lex_utf8_2byte,
lex_utf8_3byte,
lex_utf8_4byte
>;
using lex_comment_start_symbol = character<'#'>;
using lex_non_eol_ascii = either<character<0x09>, in_range<0x20, 0x7E>>;
using lex_comment = sequence<lex_comment_start_symbol, repeat<either<
lex_non_eol_ascii, lex_utf8_2byte, lex_utf8_3byte, lex_utf8_4byte>, unlimited>>;
} // detail
} // toml
#endif // TOML_LEXER_HPP

View File

@ -1,113 +0,0 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_LITERAL_HPP
#define TOML11_LITERAL_HPP
#include "parser.hpp"
namespace toml
{
inline namespace literals
{
inline namespace toml_literals
{
// implementation
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
literal_internal_impl(::toml::detail::location loc)
{
using value_type = ::toml::basic_value<
TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>;
// if there are some comments or empty lines, skip them.
using skip_line = ::toml::detail::repeat<toml::detail::sequence<
::toml::detail::maybe<::toml::detail::lex_ws>,
::toml::detail::maybe<::toml::detail::lex_comment>,
::toml::detail::lex_newline
>, ::toml::detail::at_least<1>>;
skip_line::invoke(loc);
// if there are some whitespaces before a value, skip them.
using skip_ws = ::toml::detail::repeat<
::toml::detail::lex_ws, ::toml::detail::at_least<1>>;
skip_ws::invoke(loc);
// to distinguish arrays and tables, first check it is a table or not.
//
// "[1,2,3]"_toml; // this is an array
// "[table]"_toml; // a table that has an empty table named "table" inside.
// "[[1,2,3]]"_toml; // this is an array of arrays
// "[[table]]"_toml; // this is a table that has an array of tables inside.
//
// "[[1]]"_toml; // this can be both... (currently it becomes a table)
// "1 = [{}]"_toml; // this is a table that has an array of table named 1.
// "[[1,]]"_toml; // this is an array of arrays.
// "[[1],]"_toml; // this also.
const auto the_front = loc.iter();
const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc);
loc.reset(the_front);
const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc);
loc.reset(the_front);
// If it is neither a table-key or a array-of-table-key, it may be a value.
if(!is_table_key && !is_aots_key)
{
if(auto data = ::toml::detail::parse_value<value_type>(loc))
{
return data.unwrap();
}
}
// Note that still it can be a table, because the literal might be something
// like the following.
// ```cpp
// R"( // c++11 raw string literals
// key = "value"
// int = 42
// )"_toml;
// ```
// It is a valid toml file.
// It should be parsed as if we parse a file with this content.
if(auto data = ::toml::detail::parse_toml_file<value_type>(loc))
{
return data.unwrap();
}
else // none of them.
{
throw ::toml::syntax_error(data.unwrap_err(), source_location(loc));
}
}
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
operator"" _toml(const char* str, std::size_t len)
{
::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"),
std::vector<char>(str, str + len));
// literal length does not include the null character at the end.
return literal_internal_impl(std::move(loc));
}
// value of __cplusplus in C++2a/20 mode is not fixed yet along compilers.
// So here we use the feature test macro for `char8_t` itself.
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
// value of u8"" literal has been changed from char to char8_t and char8_t is
// NOT compatible to char
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
operator"" _toml(const char8_t* str, std::size_t len)
{
::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"),
std::vector<char>(reinterpret_cast<const char*>(str),
reinterpret_cast<const char*>(str) + len));
return literal_internal_impl(std::move(loc));
}
#endif
} // toml_literals
} // literals
} // toml
#endif//TOML11_LITERAL_HPP

File diff suppressed because it is too large Load Diff

View File

@ -1,417 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_REGION_HPP
#define TOML11_REGION_HPP
#include <memory>
#include <vector>
#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <iomanip>
#include <cassert>
#include "color.hpp"
namespace toml
{
namespace detail
{
// helper function to avoid std::string(0, 'c') or std::string(iter, iter)
template<typename Iterator>
std::string make_string(Iterator first, Iterator last)
{
if(first == last) {return "";}
return std::string(first, last);
}
inline std::string make_string(std::size_t len, char c)
{
if(len == 0) {return "";}
return std::string(len, c);
}
// region_base is a base class of location and region that are defined below.
// it will be used to generate better error messages.
struct region_base
{
region_base() = default;
virtual ~region_base() = default;
region_base(const region_base&) = default;
region_base(region_base&& ) = default;
region_base& operator=(const region_base&) = default;
region_base& operator=(region_base&& ) = default;
virtual bool is_ok() const noexcept {return false;}
virtual char front() const noexcept {return '\0';}
virtual std::string str() const {return std::string("unknown region");}
virtual std::string name() const {return std::string("unknown file");}
virtual std::string line() const {return std::string("unknown line");}
virtual std::string line_num() const {return std::string("?");}
// length of the region
virtual std::size_t size() const noexcept {return 0;}
// number of characters in the line before the region
virtual std::size_t before() const noexcept {return 0;}
// number of characters in the line after the region
virtual std::size_t after() const noexcept {return 0;}
virtual std::vector<std::string> comments() const {return {};}
// ```toml
// # comment_before
// key = "value" # comment_inline
// ```
};
// location represents a position in a container, which contains a file content.
// it can be considered as a region that contains only one character.
//
// it contains pointer to the file content and iterator that points the current
// location.
struct location final : public region_base
{
using const_iterator = typename std::vector<char>::const_iterator;
using difference_type = typename const_iterator::difference_type;
using source_ptr = std::shared_ptr<const std::vector<char>>;
location(std::string source_name, std::vector<char> cont)
: source_(std::make_shared<std::vector<char>>(std::move(cont))),
line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin())
{}
location(std::string source_name, const std::string& cont)
: source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())),
line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin())
{}
location(const location&) = default;
location(location&&) = default;
location& operator=(const location&) = default;
location& operator=(location&&) = default;
~location() = default;
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
char front() const noexcept override {return *iter_;}
// this const prohibits codes like `++(loc.iter())`.
const const_iterator iter() const noexcept {return iter_;}
const_iterator begin() const noexcept {return source_->cbegin();}
const_iterator end() const noexcept {return source_->cend();}
// XXX `location::line_num()` used to be implemented using `std::count` to
// count a number of '\n'. But with a long toml file (typically, 10k lines),
// it becomes intolerably slow because each time it generates error messages,
// it counts '\n' from thousands of characters. To workaround it, I decided
// to introduce `location::line_number_` member variable and synchronize it
// to the location changes the point to look. So an overload of `iter()`
// which returns mutable reference is removed and `advance()`, `retrace()`
// and `reset()` is added.
void advance(difference_type n = 1) noexcept
{
this->line_number_ += static_cast<std::size_t>(
std::count(this->iter_, std::next(this->iter_, n), '\n'));
this->iter_ += n;
return;
}
void retrace(difference_type n = 1) noexcept
{
this->line_number_ -= static_cast<std::size_t>(
std::count(std::prev(this->iter_, n), this->iter_, '\n'));
this->iter_ -= n;
return;
}
void reset(const_iterator rollback) noexcept
{
// since c++11, std::distance works in both ways for random-access
// iterators and returns a negative value if `first > last`.
if(0 <= std::distance(rollback, this->iter_)) // rollback < iter
{
this->line_number_ -= static_cast<std::size_t>(
std::count(rollback, this->iter_, '\n'));
}
else // iter < rollback [[unlikely]]
{
this->line_number_ += static_cast<std::size_t>(
std::count(this->iter_, rollback, '\n'));
}
this->iter_ = rollback;
return;
}
std::string str() const override {return make_string(1, *this->iter());}
std::string name() const override {return source_name_;}
std::string line_num() const override
{
return std::to_string(this->line_number_);
}
std::string line() const override
{
return make_string(this->line_begin(), this->line_end());
}
const_iterator line_begin() const noexcept
{
using reverse_iterator = std::reverse_iterator<const_iterator>;
return std::find(reverse_iterator(this->iter()),
reverse_iterator(this->begin()), '\n').base();
}
const_iterator line_end() const noexcept
{
return std::find(this->iter(), this->end(), '\n');
}
// location is always points a character. so the size is 1.
std::size_t size() const noexcept override
{
return 1u;
}
std::size_t before() const noexcept override
{
const auto sz = std::distance(this->line_begin(), this->iter());
assert(sz >= 0);
return static_cast<std::size_t>(sz);
}
std::size_t after() const noexcept override
{
const auto sz = std::distance(this->iter(), this->line_end());
assert(sz >= 0);
return static_cast<std::size_t>(sz);
}
source_ptr const& source() const& noexcept {return source_;}
source_ptr&& source() && noexcept {return std::move(source_);}
private:
source_ptr source_;
std::size_t line_number_;
std::string source_name_;
const_iterator iter_;
};
// region represents a range in a container, which contains a file content.
//
// it contains pointer to the file content and iterator that points the first
// and last location.
struct region final : public region_base
{
using const_iterator = typename std::vector<char>::const_iterator;
using source_ptr = std::shared_ptr<const std::vector<char>>;
// delete default constructor. source_ never be null.
region() = delete;
explicit region(const location& loc)
: source_(loc.source()), source_name_(loc.name()),
first_(loc.iter()), last_(loc.iter())
{}
explicit region(location&& loc)
: source_(loc.source()), source_name_(loc.name()),
first_(loc.iter()), last_(loc.iter())
{}
region(const location& loc, const_iterator f, const_iterator l)
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
{}
region(location&& loc, const_iterator f, const_iterator l)
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
{}
region(const region&) = default;
region(region&&) = default;
region& operator=(const region&) = default;
region& operator=(region&&) = default;
~region() = default;
region& operator+=(const region& other)
{
// different regions cannot be concatenated
assert(this->begin() == other.begin() && this->end() == other.end() &&
this->last_ == other.first_);
this->last_ = other.last_;
return *this;
}
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
char front() const noexcept override {return *first_;}
std::string str() const override {return make_string(first_, last_);}
std::string line() const override
{
if(this->contain_newline())
{
return make_string(this->line_begin(),
std::find(this->line_begin(), this->last(), '\n'));
}
return make_string(this->line_begin(), this->line_end());
}
std::string line_num() const override
{
return std::to_string(1 + std::count(this->begin(), this->first(), '\n'));
}
std::size_t size() const noexcept override
{
const auto sz = std::distance(first_, last_);
assert(sz >= 0);
return static_cast<std::size_t>(sz);
}
std::size_t before() const noexcept override
{
const auto sz = std::distance(this->line_begin(), this->first());
assert(sz >= 0);
return static_cast<std::size_t>(sz);
}
std::size_t after() const noexcept override
{
const auto sz = std::distance(this->last(), this->line_end());
assert(sz >= 0);
return static_cast<std::size_t>(sz);
}
bool contain_newline() const noexcept
{
return std::find(this->first(), this->last(), '\n') != this->last();
}
const_iterator line_begin() const noexcept
{
using reverse_iterator = std::reverse_iterator<const_iterator>;
return std::find(reverse_iterator(this->first()),
reverse_iterator(this->begin()), '\n').base();
}
const_iterator line_end() const noexcept
{
return std::find(this->last(), this->end(), '\n');
}
const_iterator begin() const noexcept {return source_->cbegin();}
const_iterator end() const noexcept {return source_->cend();}
const_iterator first() const noexcept {return first_;}
const_iterator last() const noexcept {return last_;}
source_ptr const& source() const& noexcept {return source_;}
source_ptr&& source() && noexcept {return std::move(source_);}
std::string name() const override {return source_name_;}
std::vector<std::string> comments() const override
{
// assuming the current region (`*this`) points a value.
// ```toml
// a = "value"
// ^^^^^^^- this region
// ```
using rev_iter = std::reverse_iterator<const_iterator>;
std::vector<std::string> com{};
{
// find comments just before the current region.
// ```toml
// # this should be collected.
// # this also.
// a = value # not this.
// ```
// # this is a comment for `a`, not array elements.
// a = [1, 2, 3, 4, 5]
if(this->first() == std::find_if(this->line_begin(), this->first(),
[](const char c) noexcept -> bool {return c == '[' || c == '{';}))
{
auto iter = this->line_begin(); // points the first character
while(iter != this->begin())
{
iter = std::prev(iter);
// range [line_start, iter) represents the previous line
const auto line_start = std::find(
rev_iter(iter), rev_iter(this->begin()), '\n').base();
const auto comment_found = std::find(line_start, iter, '#');
if(comment_found == iter)
{
break; // comment not found.
}
// exclude the following case.
// > a = "foo" # comment // <-- this is not a comment for b but a.
// > b = "current value"
if(std::all_of(line_start, comment_found,
[](const char c) noexcept -> bool {
return c == ' ' || c == '\t';
}))
{
// unwrap the first '#' by std::next.
auto s = make_string(std::next(comment_found), iter);
if(!s.empty() && s.back() == '\r') {s.pop_back();}
com.push_back(std::move(s));
}
else
{
break;
}
iter = line_start;
}
}
}
if(com.size() > 1)
{
std::reverse(com.begin(), com.end());
}
{
// find comments just after the current region.
// ```toml
// # not this.
// a = value # this one.
// a = [ # not this (technically difficult)
//
// ] # and this.
// ```
// The reason why it's difficult is that it requires parsing in the
// following case.
// ```toml
// a = [ 10 # this comment is for `10`. not for `a` but `a[0]`.
// # ...
// ] # this is apparently a comment for a.
//
// b = [
// 3.14 ] # there is no way to add a comment to `3.14` currently.
//
// c = [
// 3.14 # do this if you need a comment here.
// ]
// ```
const auto comment_found =
std::find(this->last(), this->line_end(), '#');
if(comment_found != this->line_end()) // '#' found
{
// table = {key = "value"} # what is this for?
// the above comment is not for "value", but {key="value"}.
if(comment_found == std::find_if(this->last(), comment_found,
[](const char c) noexcept -> bool {
return !(c == ' ' || c == '\t' || c == ',');
}))
{
// unwrap the first '#' by std::next.
auto s = make_string(std::next(comment_found), this->line_end());
if(!s.empty() && s.back() == '\r') {s.pop_back();}
com.push_back(std::move(s));
}
}
}
return com;
}
private:
source_ptr source_;
std::string source_name_;
const_iterator first_, last_;
};
} // detail
} // toml
#endif// TOML11_REGION_H

View File

@ -1,717 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_RESULT_HPP
#define TOML11_RESULT_HPP
#include "traits.hpp"
#include <type_traits>
#include <stdexcept>
#include <utility>
#include <new>
#include <string>
#include <sstream>
#include <cassert>
namespace toml
{
template<typename T>
struct success
{
using value_type = T;
value_type value;
explicit success(const value_type& v)
noexcept(std::is_nothrow_copy_constructible<value_type>::value)
: value(v)
{}
explicit success(value_type&& v)
noexcept(std::is_nothrow_move_constructible<value_type>::value)
: value(std::move(v))
{}
template<typename U>
explicit success(U&& v): value(std::forward<U>(v)) {}
template<typename U>
explicit success(const success<U>& v): value(v.value) {}
template<typename U>
explicit success(success<U>&& v): value(std::move(v.value)) {}
~success() = default;
success(const success&) = default;
success(success&&) = default;
success& operator=(const success&) = default;
success& operator=(success&&) = default;
};
template<typename T>
struct failure
{
using value_type = T;
value_type value;
explicit failure(const value_type& v)
noexcept(std::is_nothrow_copy_constructible<value_type>::value)
: value(v)
{}
explicit failure(value_type&& v)
noexcept(std::is_nothrow_move_constructible<value_type>::value)
: value(std::move(v))
{}
template<typename U>
explicit failure(U&& v): value(std::forward<U>(v)) {}
template<typename U>
explicit failure(const failure<U>& v): value(v.value) {}
template<typename U>
explicit failure(failure<U>&& v): value(std::move(v.value)) {}
~failure() = default;
failure(const failure&) = default;
failure(failure&&) = default;
failure& operator=(const failure&) = default;
failure& operator=(failure&&) = default;
};
template<typename T>
success<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
ok(T&& v)
{
return success<
typename std::remove_cv<typename std::remove_reference<T>::type>::type
>(std::forward<T>(v));
}
template<typename T>
failure<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
err(T&& v)
{
return failure<
typename std::remove_cv<typename std::remove_reference<T>::type>::type
>(std::forward<T>(v));
}
inline success<std::string> ok(const char* literal)
{
return success<std::string>(std::string(literal));
}
inline failure<std::string> err(const char* literal)
{
return failure<std::string>(std::string(literal));
}
template<typename T, typename E>
struct result
{
using value_type = T;
using error_type = E;
using success_type = success<value_type>;
using failure_type = failure<error_type>;
result(const success_type& s): is_ok_(true)
{
auto tmp = ::new(std::addressof(this->succ)) success_type(s);
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
result(const failure_type& f): is_ok_(false)
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(f);
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
result(success_type&& s): is_ok_(true)
{
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s));
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
result(failure_type&& f): is_ok_(false)
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f));
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
template<typename U>
result(const success<U>& s): is_ok_(true)
{
auto tmp = ::new(std::addressof(this->succ)) success_type(s.value);
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
template<typename U>
result(const failure<U>& f): is_ok_(false)
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value);
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
template<typename U>
result(success<U>&& s): is_ok_(true)
{
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value));
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
template<typename U>
result(failure<U>&& f): is_ok_(false)
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value));
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
result& operator=(const success_type& s)
{
this->cleanup();
this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(s);
assert(tmp == std::addressof(this->succ));
(void)tmp;
return *this;
}
result& operator=(const failure_type& f)
{
this->cleanup();
this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(f);
assert(tmp == std::addressof(this->fail));
(void)tmp;
return *this;
}
result& operator=(success_type&& s)
{
this->cleanup();
this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s));
assert(tmp == std::addressof(this->succ));
(void)tmp;
return *this;
}
result& operator=(failure_type&& f)
{
this->cleanup();
this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f));
assert(tmp == std::addressof(this->fail));
(void)tmp;
return *this;
}
template<typename U>
result& operator=(const success<U>& s)
{
this->cleanup();
this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(s.value);
assert(tmp == std::addressof(this->succ));
(void)tmp;
return *this;
}
template<typename U>
result& operator=(const failure<U>& f)
{
this->cleanup();
this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value);
assert(tmp == std::addressof(this->fail));
(void)tmp;
return *this;
}
template<typename U>
result& operator=(success<U>&& s)
{
this->cleanup();
this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value));
assert(tmp == std::addressof(this->succ));
(void)tmp;
return *this;
}
template<typename U>
result& operator=(failure<U>&& f)
{
this->cleanup();
this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value));
assert(tmp == std::addressof(this->fail));
(void)tmp;
return *this;
}
~result() noexcept {this->cleanup();}
result(const result& other): is_ok_(other.is_ok())
{
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
}
result(result&& other): is_ok_(other.is_ok())
{
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
}
template<typename U, typename F>
result(const result<U, F>& other): is_ok_(other.is_ok())
{
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
}
template<typename U, typename F>
result(result<U, F>&& other): is_ok_(other.is_ok())
{
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
}
result& operator=(const result& other)
{
this->cleanup();
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
is_ok_ = other.is_ok();
return *this;
}
result& operator=(result&& other)
{
this->cleanup();
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
is_ok_ = other.is_ok();
return *this;
}
template<typename U, typename F>
result& operator=(const result<U, F>& other)
{
this->cleanup();
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
is_ok_ = other.is_ok();
return *this;
}
template<typename U, typename F>
result& operator=(result<U, F>&& other)
{
this->cleanup();
if(other.is_ok())
{
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail));
(void)tmp;
}
is_ok_ = other.is_ok();
return *this;
}
bool is_ok() const noexcept {return is_ok_;}
bool is_err() const noexcept {return !is_ok_;}
operator bool() const noexcept {return is_ok_;}
value_type& unwrap() &
{
if(is_err())
{
throw std::runtime_error("toml::result: bad unwrap: " +
format_error(this->as_err()));
}
return this->succ.value;
}
value_type const& unwrap() const&
{
if(is_err())
{
throw std::runtime_error("toml::result: bad unwrap: " +
format_error(this->as_err()));
}
return this->succ.value;
}
value_type&& unwrap() &&
{
if(is_err())
{
throw std::runtime_error("toml::result: bad unwrap: " +
format_error(this->as_err()));
}
return std::move(this->succ.value);
}
value_type& unwrap_or(value_type& opt) &
{
if(is_err()) {return opt;}
return this->succ.value;
}
value_type const& unwrap_or(value_type const& opt) const&
{
if(is_err()) {return opt;}
return this->succ.value;
}
value_type unwrap_or(value_type opt) &&
{
if(is_err()) {return opt;}
return this->succ.value;
}
error_type& unwrap_err() &
{
if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
return this->fail.value;
}
error_type const& unwrap_err() const&
{
if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
return this->fail.value;
}
error_type&& unwrap_err() &&
{
if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
return std::move(this->fail.value);
}
value_type& as_ok() & noexcept {return this->succ.value;}
value_type const& as_ok() const& noexcept {return this->succ.value;}
value_type&& as_ok() && noexcept {return std::move(this->succ.value);}
error_type& as_err() & noexcept {return this->fail.value;}
error_type const& as_err() const& noexcept {return this->fail.value;}
error_type&& as_err() && noexcept {return std::move(this->fail.value);}
// prerequisities
// F: T -> U
// retval: result<U, E>
template<typename F>
result<detail::return_type_of_t<F, value_type&>, error_type>
map(F&& f) &
{
if(this->is_ok()){return ok(f(this->as_ok()));}
return err(this->as_err());
}
template<typename F>
result<detail::return_type_of_t<F, value_type const&>, error_type>
map(F&& f) const&
{
if(this->is_ok()){return ok(f(this->as_ok()));}
return err(this->as_err());
}
template<typename F>
result<detail::return_type_of_t<F, value_type &&>, error_type>
map(F&& f) &&
{
if(this->is_ok()){return ok(f(std::move(this->as_ok())));}
return err(std::move(this->as_err()));
}
// prerequisities
// F: E -> F
// retval: result<T, F>
template<typename F>
result<value_type, detail::return_type_of_t<F, error_type&>>
map_err(F&& f) &
{
if(this->is_err()){return err(f(this->as_err()));}
return ok(this->as_ok());
}
template<typename F>
result<value_type, detail::return_type_of_t<F, error_type const&>>
map_err(F&& f) const&
{
if(this->is_err()){return err(f(this->as_err()));}
return ok(this->as_ok());
}
template<typename F>
result<value_type, detail::return_type_of_t<F, error_type&&>>
map_err(F&& f) &&
{
if(this->is_err()){return err(f(std::move(this->as_err())));}
return ok(std::move(this->as_ok()));
}
// prerequisities
// F: T -> U
// retval: U
template<typename F, typename U>
detail::return_type_of_t<F, value_type&>
map_or_else(F&& f, U&& opt) &
{
if(this->is_err()){return std::forward<U>(opt);}
return f(this->as_ok());
}
template<typename F, typename U>
detail::return_type_of_t<F, value_type const&>
map_or_else(F&& f, U&& opt) const&
{
if(this->is_err()){return std::forward<U>(opt);}
return f(this->as_ok());
}
template<typename F, typename U>
detail::return_type_of_t<F, value_type&&>
map_or_else(F&& f, U&& opt) &&
{
if(this->is_err()){return std::forward<U>(opt);}
return f(std::move(this->as_ok()));
}
// prerequisities
// F: E -> U
// retval: U
template<typename F, typename U>
detail::return_type_of_t<F, error_type&>
map_err_or_else(F&& f, U&& opt) &
{
if(this->is_ok()){return std::forward<U>(opt);}
return f(this->as_err());
}
template<typename F, typename U>
detail::return_type_of_t<F, error_type const&>
map_err_or_else(F&& f, U&& opt) const&
{
if(this->is_ok()){return std::forward<U>(opt);}
return f(this->as_err());
}
template<typename F, typename U>
detail::return_type_of_t<F, error_type&&>
map_err_or_else(F&& f, U&& opt) &&
{
if(this->is_ok()){return std::forward<U>(opt);}
return f(std::move(this->as_err()));
}
// prerequisities:
// F: func T -> U
// toml::err(error_type) should be convertible to U.
// normally, type U is another result<S, F> and E is convertible to F
template<typename F>
detail::return_type_of_t<F, value_type&>
and_then(F&& f) &
{
if(this->is_ok()){return f(this->as_ok());}
return err(this->as_err());
}
template<typename F>
detail::return_type_of_t<F, value_type const&>
and_then(F&& f) const&
{
if(this->is_ok()){return f(this->as_ok());}
return err(this->as_err());
}
template<typename F>
detail::return_type_of_t<F, value_type&&>
and_then(F&& f) &&
{
if(this->is_ok()){return f(std::move(this->as_ok()));}
return err(std::move(this->as_err()));
}
// prerequisities:
// F: func E -> U
// toml::ok(value_type) should be convertible to U.
// normally, type U is another result<S, F> and T is convertible to S
template<typename F>
detail::return_type_of_t<F, error_type&>
or_else(F&& f) &
{
if(this->is_err()){return f(this->as_err());}
return ok(this->as_ok());
}
template<typename F>
detail::return_type_of_t<F, error_type const&>
or_else(F&& f) const&
{
if(this->is_err()){return f(this->as_err());}
return ok(this->as_ok());
}
template<typename F>
detail::return_type_of_t<F, error_type&&>
or_else(F&& f) &&
{
if(this->is_err()){return f(std::move(this->as_err()));}
return ok(std::move(this->as_ok()));
}
// if *this is error, returns *this. otherwise, returns other.
result and_other(const result& other) const&
{
return this->is_err() ? *this : other;
}
result and_other(result&& other) &&
{
return this->is_err() ? std::move(*this) : std::move(other);
}
// if *this is okay, returns *this. otherwise, returns other.
result or_other(const result& other) const&
{
return this->is_ok() ? *this : other;
}
result or_other(result&& other) &&
{
return this->is_ok() ? std::move(*this) : std::move(other);
}
void swap(result<T, E>& other)
{
result<T, E> tmp(std::move(*this));
*this = std::move(other);
other = std::move(tmp);
return ;
}
private:
static std::string format_error(std::exception const& excpt)
{
return std::string(excpt.what());
}
template<typename U, typename std::enable_if<!std::is_base_of<
std::exception, U>::value, std::nullptr_t>::type = nullptr>
static std::string format_error(U const& others)
{
std::ostringstream oss; oss << others;
return oss.str();
}
void cleanup() noexcept
{
if(this->is_ok_) {this->succ.~success_type();}
else {this->fail.~failure_type();}
return;
}
private:
bool is_ok_;
union
{
success_type succ;
failure_type fail;
};
};
template<typename T, typename E>
void swap(result<T, E>& lhs, result<T, E>& rhs)
{
lhs.swap(rhs);
return;
}
// this might be confusing because it eagerly evaluated, while in the other
// cases operator && and || are short-circuited.
//
// template<typename T, typename E>
// inline result<T, E>
// operator&&(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
// {
// return lhs.is_ok() ? rhs : lhs;
// }
//
// template<typename T, typename E>
// inline result<T, E>
// operator||(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
// {
// return lhs.is_ok() ? lhs : rhs;
// }
// ----------------------------------------------------------------------------
// re-use result<T, E> as a optional<T> with none_t
namespace detail
{
struct none_t {};
inline bool operator==(const none_t&, const none_t&) noexcept {return true;}
inline bool operator!=(const none_t&, const none_t&) noexcept {return false;}
inline bool operator< (const none_t&, const none_t&) noexcept {return false;}
inline bool operator<=(const none_t&, const none_t&) noexcept {return true;}
inline bool operator> (const none_t&, const none_t&) noexcept {return false;}
inline bool operator>=(const none_t&, const none_t&) noexcept {return true;}
template<typename charT, typename traitsT>
std::basic_ostream<charT, traitsT>&
operator<<(std::basic_ostream<charT, traitsT>& os, const none_t&)
{
os << "none";
return os;
}
inline failure<none_t> none() noexcept {return failure<none_t>{none_t{}};}
} // detail
} // toml11
#endif// TOML11_RESULT_H

View File

@ -1,922 +0,0 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_SERIALIZER_HPP
#define TOML11_SERIALIZER_HPP
#include <cmath>
#include <cstdio>
#include <limits>
#include "lexer.hpp"
#include "value.hpp"
namespace toml
{
// This function serialize a key. It checks a string is a bare key and
// escapes special characters if the string is not compatible to a bare key.
// ```cpp
// std::string k("non.bare.key"); // the key itself includes `.`s.
// std::string formatted = toml::format_key(k);
// assert(formatted == "\"non.bare.key\"");
// ```
//
// This function is exposed to make it easy to write a user-defined serializer.
// Since toml restricts characters available in a bare key, generally a string
// should be escaped. But checking whether a string needs to be surrounded by
// a `"` and escaping some special character is boring.
template<typename charT, typename traits, typename Alloc>
std::basic_string<charT, traits, Alloc>
format_key(const std::basic_string<charT, traits, Alloc>& k)
{
if(k.empty())
{
return std::string("\"\"");
}
// check the key can be a bare (unquoted) key
detail::location loc(k, std::vector<char>(k.begin(), k.end()));
detail::lex_unquoted_key::invoke(loc);
if(loc.iter() == loc.end())
{
return k; // all the tokens are consumed. the key is unquoted-key.
}
//if it includes special characters, then format it in a "quoted" key.
std::basic_string<charT, traits, Alloc> serialized("\"");
for(const char c : k)
{
switch(c)
{
case '\\': {serialized += "\\\\"; break;}
case '\"': {serialized += "\\\""; break;}
case '\b': {serialized += "\\b"; break;}
case '\t': {serialized += "\\t"; break;}
case '\f': {serialized += "\\f"; break;}
case '\n': {serialized += "\\n"; break;}
case '\r': {serialized += "\\r"; break;}
default : {serialized += c; break;}
}
}
serialized += "\"";
return serialized;
}
template<typename charT, typename traits, typename Alloc>
std::basic_string<charT, traits, Alloc>
format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys)
{
if(keys.empty())
{
return std::string("\"\"");
}
std::basic_string<charT, traits, Alloc> serialized;
for(const auto& ky : keys)
{
serialized += format_key(ky);
serialized += charT('.');
}
serialized.pop_back(); // remove the last dot '.'
return serialized;
}
template<typename Value>
struct serializer
{
static_assert(detail::is_basic_value<Value>::value,
"toml::serializer is for toml::value and its variants, "
"toml::basic_value<...>.");
using value_type = Value;
using key_type = typename value_type::key_type ;
using comment_type = typename value_type::comment_type ;
using boolean_type = typename value_type::boolean_type ;
using integer_type = typename value_type::integer_type ;
using floating_type = typename value_type::floating_type ;
using string_type = typename value_type::string_type ;
using local_time_type = typename value_type::local_time_type ;
using local_date_type = typename value_type::local_date_type ;
using local_datetime_type = typename value_type::local_datetime_type ;
using offset_datetime_type = typename value_type::offset_datetime_type;
using array_type = typename value_type::array_type ;
using table_type = typename value_type::table_type ;
serializer(const std::size_t w = 80u,
const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
const bool can_be_inlined = false,
const bool no_comment = false,
std::vector<toml::key> ks = {},
const bool value_has_comment = false)
: can_be_inlined_(can_be_inlined), no_comment_(no_comment),
value_has_comment_(value_has_comment && !no_comment),
float_prec_(float_prec), width_(w), keys_(std::move(ks))
{}
~serializer() = default;
std::string operator()(const boolean_type& b) const
{
return b ? "true" : "false";
}
std::string operator()(const integer_type i) const
{
return std::to_string(i);
}
std::string operator()(const floating_type f) const
{
if(std::isnan(f))
{
if(std::signbit(f))
{
return std::string("-nan");
}
else
{
return std::string("nan");
}
}
else if(!std::isfinite(f))
{
if(std::signbit(f))
{
return std::string("-inf");
}
else
{
return std::string("inf");
}
}
const auto fmt = "%.*g";
const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f);
// +1 for null character(\0)
std::vector<char> buf(static_cast<std::size_t>(bsz + 1), '\0');
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
std::string token(buf.begin(), std::prev(buf.end()));
if(!token.empty() && token.back() == '.') // 1. => 1.0
{
token += '0';
}
const auto e = std::find_if(
token.cbegin(), token.cend(), [](const char c) noexcept -> bool {
return c == 'e' || c == 'E';
});
const auto has_exponent = (token.cend() != e);
const auto has_fraction = (token.cend() != std::find(
token.cbegin(), token.cend(), '.'));
if(!has_exponent && !has_fraction)
{
// the resulting value does not have any float specific part!
token += ".0";
}
return token;
}
std::string operator()(const string_type& s) const
{
if(s.kind == string_t::basic)
{
if((std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) &&
this->width_ != (std::numeric_limits<std::size_t>::max)())
{
// if linefeed or double-quote is contained,
// make it multiline basic string.
const auto escaped = this->escape_ml_basic_string(s.str);
std::string open("\"\"\"");
std::string close("\"\"\"");
if(escaped.find('\n') != std::string::npos ||
this->width_ < escaped.size() + 6)
{
// if the string body contains newline or is enough long,
// add newlines after and before delimiters.
open += "\n";
close = std::string("\\\n") + close;
}
return open + escaped + close;
}
// no linefeed. try to make it oneline-string.
std::string oneline = this->escape_basic_string(s.str);
if(oneline.size() + 2 < width_ || width_ < 2)
{
const std::string quote("\"");
return quote + oneline + quote;
}
// the line is too long compared to the specified width.
// split it into multiple lines.
std::string token("\"\"\"\n");
while(!oneline.empty())
{
if(oneline.size() < width_)
{
token += oneline;
oneline.clear();
}
else if(oneline.at(width_-2) == '\\')
{
token += oneline.substr(0, width_-2);
token += "\\\n";
oneline.erase(0, width_-2);
}
else
{
token += oneline.substr(0, width_-1);
token += "\\\n";
oneline.erase(0, width_-1);
}
}
return token + std::string("\\\n\"\"\"");
}
else // the string `s` is literal-string.
{
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
{
std::string open("'''");
if(this->width_ + 6 < s.str.size())
{
open += '\n'; // the first newline is ignored by TOML spec
}
const std::string close("'''");
return open + s.str + close;
}
else
{
const std::string quote("'");
return quote + s.str + quote;
}
}
}
std::string operator()(const local_date_type& d) const
{
std::ostringstream oss;
oss << d;
return oss.str();
}
std::string operator()(const local_time_type& t) const
{
std::ostringstream oss;
oss << t;
return oss.str();
}
std::string operator()(const local_datetime_type& dt) const
{
std::ostringstream oss;
oss << dt;
return oss.str();
}
std::string operator()(const offset_datetime_type& odt) const
{
std::ostringstream oss;
oss << odt;
return oss.str();
}
std::string operator()(const array_type& v) const
{
if(v.empty())
{
return std::string("[]");
}
if(this->is_array_of_tables(v))
{
return make_array_of_tables(v);
}
// not an array of tables. normal array.
// first, try to make it inline if none of the elements have a comment.
if( ! this->has_comment_inside(v))
{
const auto inl = this->make_inline_array(v);
if(inl.size() < this->width_ &&
std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend())
{
return inl;
}
}
// if the length exceeds this->width_, print multiline array.
// key = [
// # ...
// 42,
// ...
// ]
std::string token;
std::string current_line;
token += "[\n";
for(const auto& item : v)
{
if( ! item.comments().empty() && !no_comment_)
{
// if comment exists, the element must be the only element in the line.
// e.g. the following is not allowed.
// ```toml
// array = [
// # comment for what?
// 1, 2, 3, 4, 5
// ]
// ```
if(!current_line.empty())
{
if(current_line.back() != '\n')
{
current_line += '\n';
}
token += current_line;
current_line.clear();
}
for(const auto& c : item.comments())
{
token += '#';
token += c;
token += '\n';
}
token += toml::visit(*this, item);
if(!token.empty() && token.back() == '\n') {token.pop_back();}
token += ",\n";
continue;
}
std::string next_elem;
if(item.is_table())
{
serializer ser(*this);
ser.can_be_inlined_ = true;
ser.width_ = (std::numeric_limits<std::size_t>::max)();
next_elem += toml::visit(ser, item);
}
else
{
next_elem += toml::visit(*this, item);
}
// comma before newline.
if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();}
// if current line does not exceeds the width limit, continue.
if(current_line.size() + next_elem.size() + 1 < this->width_)
{
current_line += next_elem;
current_line += ',';
}
else if(current_line.empty())
{
// if current line was empty, force put the next_elem because
// next_elem is not splittable
token += next_elem;
token += ",\n";
// current_line is kept empty
}
else // reset current_line
{
assert(current_line.back() == ',');
token += current_line;
token += '\n';
current_line = next_elem;
current_line += ',';
}
}
if(!current_line.empty())
{
if(!current_line.empty() && current_line.back() != '\n')
{
current_line += '\n';
}
token += current_line;
}
token += "]\n";
return token;
}
// templatize for any table-like container
std::string operator()(const table_type& v) const
{
// if an element has a comment, then it can't be inlined.
// table = {# how can we write a comment for this? key = "value"}
if(this->can_be_inlined_ && !(this->has_comment_inside(v)))
{
std::string token;
if(!this->keys_.empty())
{
token += format_key(this->keys_.back());
token += " = ";
}
token += this->make_inline_table(v);
if(token.size() < this->width_ &&
token.end() == std::find(token.begin(), token.end(), '\n'))
{
return token;
}
}
std::string token;
if(!keys_.empty())
{
token += '[';
token += format_keys(keys_);
token += "]\n";
}
token += this->make_multiline_table(v);
return token;
}
private:
std::string escape_basic_string(const std::string& s) const
{
//XXX assuming `s` is a valid utf-8 sequence.
std::string retval;
for(const char c : s)
{
switch(c)
{
case '\\': {retval += "\\\\"; break;}
case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;}
case '\n': {retval += "\\n"; break;}
case '\r': {retval += "\\r"; break;}
default :
{
if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F)
{
retval += "\\u00";
retval += char(48 + (c / 16));
retval += char((c % 16 < 10 ? 48 : 55) + (c % 16));
}
else
{
retval += c;
}
}
}
}
return retval;
}
std::string escape_ml_basic_string(const std::string& s) const
{
std::string retval;
for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i)
{
switch(*i)
{
case '\\': {retval += "\\\\"; break;}
// One or two consecutive "s are allowed.
// Later we will check there are no three consecutive "s.
// case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;}
case '\n': {retval += "\n"; break;}
case '\r':
{
if(std::next(i) != e && *std::next(i) == '\n')
{
retval += "\r\n";
++i;
}
else
{
retval += "\\r";
}
break;
}
default :
{
const auto c = *i;
if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F)
{
retval += "\\u00";
retval += char(48 + (c / 16));
retval += char((c % 16 < 10 ? 48 : 55) + (c % 16));
}
else
{
retval += c;
}
}
}
}
// Only 1 or 2 consecutive `"`s are allowed in multiline basic string.
// 3 consecutive `"`s are considered as a closing delimiter.
// We need to check if there are 3 or more consecutive `"`s and insert
// backslash to break them down into several short `"`s like the `str6`
// in the following example.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// # str5 = """Here are three quotation marks: """.""" # INVALID
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// ```
auto found_3_quotes = retval.find("\"\"\"");
while(found_3_quotes != std::string::npos)
{
retval.replace(found_3_quotes, 3, "\"\"\\\"");
found_3_quotes = retval.find("\"\"\"");
}
return retval;
}
// if an element of a table or an array has a comment, it cannot be inlined.
bool has_comment_inside(const array_type& a) const noexcept
{
// if no_comment is set, comments would not be written.
if(this->no_comment_) {return false;}
for(const auto& v : a)
{
if(!v.comments().empty()) {return true;}
}
return false;
}
bool has_comment_inside(const table_type& t) const noexcept
{
// if no_comment is set, comments would not be written.
if(this->no_comment_) {return false;}
for(const auto& kv : t)
{
if(!kv.second.comments().empty()) {return true;}
}
return false;
}
std::string make_inline_array(const array_type& v) const
{
assert(!has_comment_inside(v));
std::string token;
token += '[';
bool is_first = true;
for(const auto& item : v)
{
if(is_first) {is_first = false;} else {token += ',';}
token += visit(serializer(
(std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !item.comments().empty()), item);
}
token += ']';
return token;
}
std::string make_inline_table(const table_type& v) const
{
assert(!has_comment_inside(v));
assert(this->can_be_inlined_);
std::string token;
token += '{';
bool is_first = true;
for(const auto& kv : v)
{
// in inline tables, trailing comma is not allowed (toml-lang #569).
if(is_first) {is_first = false;} else {token += ',';}
token += format_key(kv.first);
token += '=';
token += visit(serializer(
(std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
}
token += '}';
return token;
}
std::string make_multiline_table(const table_type& v) const
{
std::string token;
// print non-table elements first.
// ```toml
// [foo] # a table we're writing now here
// key = "value" # <- non-table element, "key"
// # ...
// [foo.bar] # <- table element, "bar"
// ```
// because after printing [foo.bar], the remaining non-table values will
// be assigned into [foo.bar], not [foo]. Those values should be printed
// earlier.
for(const auto& kv : v)
{
if(kv.second.is_table() || is_array_of_tables(kv.second))
{
continue;
}
token += write_comments(kv.second);
const auto key_and_sep = format_key(kv.first) + " = ";
const auto residual_width = (this->width_ > key_and_sep.size()) ?
this->width_ - key_and_sep.size() : 0;
token += key_and_sep;
token += visit(serializer(residual_width, this->float_prec_,
/*can be inlined*/ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
if(token.back() != '\n')
{
token += '\n';
}
}
// normal tables / array of tables
// after multiline table appeared, the other tables cannot be inline
// because the table would be assigned into the table.
// [foo]
// ...
// bar = {...} # <- bar will be a member of [foo].
bool multiline_table_printed = false;
for(const auto& kv : v)
{
if(!kv.second.is_table() && !is_array_of_tables(kv.second))
{
continue; // other stuff are already serialized. skip them.
}
std::vector<toml::key> ks(this->keys_);
ks.push_back(kv.first);
auto tmp = visit(serializer(this->width_, this->float_prec_,
!multiline_table_printed, this->no_comment_, ks,
/*has_comment*/ !kv.second.comments().empty()), kv.second);
// If it is the first time to print a multi-line table, it would be
// helpful to separate normal key-value pair and subtables by a
// newline.
// (this checks if the current key-value pair contains newlines.
// but it is not perfect because multi-line string can also contain
// a newline. in such a case, an empty line will be written) TODO
if((!multiline_table_printed) &&
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
{
multiline_table_printed = true;
token += '\n'; // separate key-value pairs and subtables
token += write_comments(kv.second);
token += tmp;
// care about recursive tables (all tables in each level prints
// newline and there will be a full of newlines)
if(tmp.substr(tmp.size() - 2, 2) != "\n\n" &&
tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" )
{
token += '\n';
}
}
else
{
token += write_comments(kv.second);
token += tmp;
token += '\n';
}
}
return token;
}
std::string make_array_of_tables(const array_type& v) const
{
// if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined, we can format it as the following.
// ```
// table.key = [
// {...},
// # comment
// {...},
// ]
// ```
// This function checks if inlinization is possible or not, and then
// format the array-of-tables in a proper way.
//
// Note about comments:
//
// If the array itself has a comment (value_has_comment_ == true), we
// should try to make it inline.
// ```toml
// # comment about array
// array = [
// # comment about table element
// {of = "table"}
// ]
// ```
// If it is formatted as a multiline table, the two comments becomes
// indistinguishable.
// ```toml
// # comment about array
// # comment about table element
// [[array]]
// of = "table"
// ```
// So we need to try to make it inline, and it force-inlines regardless
// of the line width limit.
// It may fail if the element of a table has comment. In that case,
// the array-of-tables will be formatted as a multiline table.
if(this->can_be_inlined_ || this->value_has_comment_)
{
std::string token;
if(!keys_.empty())
{
token += format_key(keys_.back());
token += " = ";
}
bool failed = false;
token += "[\n";
for(const auto& item : v)
{
// if an element of the table has a comment, the table
// cannot be inlined.
if(this->has_comment_inside(item.as_table()))
{
failed = true;
break;
}
// write comments for the table itself
token += write_comments(item);
const auto t = this->make_inline_table(item.as_table());
if(t.size() + 1 > width_ || // +1 for the last comma {...},
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
{
// if the value itself has a comment, ignore the line width limit
if( ! this->value_has_comment_)
{
failed = true;
break;
}
}
token += t;
token += ",\n";
}
if( ! failed)
{
token += "]\n";
return token;
}
// if failed, serialize them as [[array.of.tables]].
}
std::string token;
for(const auto& item : v)
{
token += write_comments(item);
token += "[[";
token += format_keys(keys_);
token += "]]\n";
token += this->make_multiline_table(item.as_table());
}
return token;
}
std::string write_comments(const value_type& v) const
{
std::string retval;
if(this->no_comment_) {return retval;}
for(const auto& c : v.comments())
{
retval += '#';
retval += c;
retval += '\n';
}
return retval;
}
bool is_array_of_tables(const value_type& v) const
{
if(!v.is_array() || v.as_array().empty()) {return false;}
return is_array_of_tables(v.as_array());
}
bool is_array_of_tables(const array_type& v) const
{
// Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to
// check all the element in an array to check if the array is an array
// of tables.
return std::all_of(v.begin(), v.end(), [](const value_type& elem) {
return elem.is_table();
});
}
private:
bool can_be_inlined_;
bool no_comment_;
bool value_has_comment_;
int float_prec_;
std::size_t width_;
std::vector<toml::key> keys_;
};
template<typename C,
template<typename ...> class M, template<typename ...> class V>
std::string
format(const basic_value<C, M, V>& v, std::size_t w = 80u,
int fprec = std::numeric_limits<toml::floating>::max_digits10,
bool no_comment = false, bool force_inline = false)
{
using value_type = basic_value<C, M, V>;
// if value is a table, it is considered to be a root object.
// the root object can't be an inline table.
if(v.is_table())
{
std::ostringstream oss;
if(!v.comments().empty())
{
oss << v.comments();
oss << '\n'; // to split the file comment from the first element
}
const auto serialized = visit(serializer<value_type>(w, fprec, false, no_comment), v);
oss << serialized;
return oss.str();
}
return visit(serializer<value_type>(w, fprec, force_inline), v);
}
namespace detail
{
template<typename charT, typename traits>
int comment_index(std::basic_ostream<charT, traits>&)
{
static const int index = std::ios_base::xalloc();
return index;
}
} // detail
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
nocomment(std::basic_ostream<charT, traits>& os)
{
// by default, it is zero. and by default, it shows comments.
os.iword(detail::comment_index(os)) = 1;
return os;
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
showcomment(std::basic_ostream<charT, traits>& os)
{
// by default, it is zero. and by default, it shows comments.
os.iword(detail::comment_index(os)) = 0;
return os;
}
template<typename charT, typename traits, typename C,
template<typename ...> class M, template<typename ...> class V>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
{
using value_type = basic_value<C, M, V>;
// get status of std::setw().
const auto w = static_cast<std::size_t>(os.width());
const int fprec = static_cast<int>(os.precision());
os.width(0);
// by default, iword is initialized by 0. And by default, toml11 outputs
// comments. So `0` means showcomment. 1 means nocommnet.
const bool no_comment = (1 == os.iword(detail::comment_index(os)));
if(!no_comment && v.is_table() && !v.comments().empty())
{
os << v.comments();
os << '\n'; // to split the file comment from the first element
}
// the root object can't be an inline table. so pass `false`.
const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v);
os << serialized;
// if v is a non-table value, and has only one comment, then
// put a comment just after a value. in the following way.
//
// ```toml
// key = "value" # comment.
// ```
//
// Since the top-level toml object is a table, one who want to put a
// non-table toml value must use this in a following way.
//
// ```cpp
// toml::value v;
// std::cout << "user-defined-key = " << v << std::endl;
// ```
//
// In this case, it is impossible to put comments before key-value pair.
// The only way to preserve comments is to put all of them after a value.
if(!no_comment && !v.is_table() && !v.comments().empty())
{
os << " #";
for(const auto& c : v.comments()) {os << c;}
}
return os;
}
} // toml
#endif// TOML11_SERIALIZER_HPP

View File

@ -1,233 +0,0 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_SOURCE_LOCATION_HPP
#define TOML11_SOURCE_LOCATION_HPP
#include <cstdint>
#include <sstream>
#include "region.hpp"
namespace toml
{
// A struct to contain location in a toml file.
// The interface imitates std::experimental::source_location,
// but not completely the same.
//
// It would be constructed by toml::value. It can be used to generate
// user-defined error messages.
//
// - std::uint_least32_t line() const noexcept
// - returns the line number where the region is on.
// - std::uint_least32_t column() const noexcept
// - returns the column number where the region starts.
// - std::uint_least32_t region() const noexcept
// - returns the size of the region.
//
// +-- line() +-- region of interest (region() == 9)
// v .---+---.
// 12 | value = "foo bar"
// ^
// +-- column()
//
// - std::string const& file_name() const noexcept;
// - name of the file.
// - std::string const& line_str() const noexcept;
// - the whole line that contains the region of interest.
//
struct source_location
{
public:
source_location()
: line_num_(1), column_num_(1), region_size_(1),
file_name_("unknown file"), line_str_("")
{}
explicit source_location(const detail::region_base* reg)
: line_num_(1), column_num_(1), region_size_(1),
file_name_("unknown file"), line_str_("")
{
if(reg)
{
if(reg->line_num() != detail::region_base().line_num())
{
line_num_ = static_cast<std::uint_least32_t>(
std::stoul(reg->line_num()));
}
column_num_ = static_cast<std::uint_least32_t>(reg->before() + 1);
region_size_ = static_cast<std::uint_least32_t>(reg->size());
file_name_ = reg->name();
line_str_ = reg->line();
}
}
explicit source_location(const detail::region& reg)
: line_num_(static_cast<std::uint_least32_t>(std::stoul(reg.line_num()))),
column_num_(static_cast<std::uint_least32_t>(reg.before() + 1)),
region_size_(static_cast<std::uint_least32_t>(reg.size())),
file_name_(reg.name()),
line_str_ (reg.line())
{}
explicit source_location(const detail::location& loc)
: line_num_(static_cast<std::uint_least32_t>(std::stoul(loc.line_num()))),
column_num_(static_cast<std::uint_least32_t>(loc.before() + 1)),
region_size_(static_cast<std::uint_least32_t>(loc.size())),
file_name_(loc.name()),
line_str_ (loc.line())
{}
~source_location() = default;
source_location(source_location const&) = default;
source_location(source_location &&) = default;
source_location& operator=(source_location const&) = default;
source_location& operator=(source_location &&) = default;
std::uint_least32_t line() const noexcept {return line_num_;}
std::uint_least32_t column() const noexcept {return column_num_;}
std::uint_least32_t region() const noexcept {return region_size_;}
std::string const& file_name() const noexcept {return file_name_;}
std::string const& line_str() const noexcept {return line_str_;}
private:
std::uint_least32_t line_num_;
std::uint_least32_t column_num_;
std::uint_least32_t region_size_;
std::string file_name_;
std::string line_str_;
};
namespace detail
{
// internal error message generation.
inline std::string format_underline(const std::string& message,
const std::vector<std::pair<source_location, std::string>>& loc_com,
const std::vector<std::string>& helps = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
std::size_t line_num_width = 0;
for(const auto& lc : loc_com)
{
std::uint_least32_t line = lc.first.line();
std::size_t digit = 0;
while(line != 0)
{
line /= 10;
digit += 1;
}
line_num_width = (std::max)(line_num_width, digit);
}
// 1 is the minimum width
line_num_width = std::max<std::size_t>(line_num_width, 1);
std::ostringstream retval;
if(colorize)
{
retval << color::colorize; // turn on ANSI color
}
// XXX
// Here, before `colorize` support, it does not output `[error]` prefix
// automatically. So some user may output it manually and this change may
// duplicate the prefix. To avoid it, check the first 7 characters and
// if it is "[error]", it removes that part from the message shown.
if(message.size() > 7 && message.substr(0, 7) == "[error]")
{
retval << color::bold << color::red << "[error]" << color::reset
<< color::bold << message.substr(7) << color::reset << '\n';
}
else
{
retval << color::bold << color::red << "[error] " << color::reset
<< color::bold << message << color::reset << '\n';
}
const auto format_one_location = [line_num_width]
(std::ostringstream& oss,
const source_location& loc, const std::string& comment) -> void
{
oss << ' ' << color::bold << color::blue
<< std::setw(static_cast<int>(line_num_width))
<< std::right << loc.line() << " | " << color::reset
<< loc.line_str() << '\n';
oss << make_string(line_num_width + 1, ' ')
<< color::bold << color::blue << " | " << color::reset
<< make_string(loc.column()-1 /*1-origin*/, ' ');
if(loc.region() == 1)
{
// invalid
// ^------
oss << color::bold << color::red << "^---" << color::reset;
}
else
{
// invalid
// ~~~~~~~
const auto underline_len = (std::min)(
static_cast<std::size_t>(loc.region()), loc.line_str().size());
oss << color::bold << color::red
<< make_string(underline_len, '~') << color::reset;
}
oss << ' ';
oss << comment;
return;
};
assert(!loc_com.empty());
// --> example.toml
// |
retval << color::bold << color::blue << " --> " << color::reset
<< loc_com.front().first.file_name() << '\n';
retval << make_string(line_num_width + 1, ' ')
<< color::bold << color::blue << " |\n" << color::reset;
// 1 | key value
// | ^--- missing =
format_one_location(retval, loc_com.front().first, loc_com.front().second);
// process the rest of the locations
for(std::size_t i=1; i<loc_com.size(); ++i)
{
const auto& prev = loc_com.at(i-1);
const auto& curr = loc_com.at(i);
retval << '\n';
// if the filenames are the same, print "..."
if(prev.first.file_name() == curr.first.file_name())
{
retval << color::bold << color::blue << " ...\n" << color::reset;
}
else // if filename differs, print " --> filename.toml" again
{
retval << color::bold << color::blue << " --> " << color::reset
<< curr.first.file_name() << '\n';
retval << make_string(line_num_width + 1, ' ')
<< color::bold << color::blue << " |\n" << color::reset;
}
format_one_location(retval, curr.first, curr.second);
}
if(!helps.empty())
{
retval << '\n';
retval << make_string(line_num_width + 1, ' ');
retval << color::bold << color::blue << " |" << color::reset;
for(const auto& help : helps)
{
retval << color::bold << "\nHint: " << color::reset;
retval << help;
}
}
return retval.str();
}
} // detail
} // toml
#endif// TOML11_SOURCE_LOCATION_HPP

View File

@ -1,43 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_STORAGE_HPP
#define TOML11_STORAGE_HPP
#include "utility.hpp"
namespace toml
{
namespace detail
{
// this contains pointer and deep-copy the content if copied.
// to avoid recursive pointer.
template<typename T>
struct storage
{
using value_type = T;
explicit storage(value_type const& v): ptr(toml::make_unique<T>(v)) {}
explicit storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {}
~storage() = default;
storage(const storage& rhs): ptr(toml::make_unique<T>(*rhs.ptr)) {}
storage& operator=(const storage& rhs)
{
this->ptr = toml::make_unique<T>(*rhs.ptr);
return *this;
}
storage(storage&&) = default;
storage& operator=(storage&&) = default;
bool is_ok() const noexcept {return static_cast<bool>(ptr);}
value_type& value() & noexcept {return *ptr;}
value_type const& value() const& noexcept {return *ptr;}
value_type&& value() && noexcept {return std::move(*ptr);}
private:
std::unique_ptr<value_type> ptr;
};
} // detail
} // toml
#endif// TOML11_STORAGE_HPP

View File

@ -1,228 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_STRING_HPP
#define TOML11_STRING_HPP
#include "version.hpp"
#include <cstdint>
#include <algorithm>
#include <string>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
#define TOML11_USING_STRING_VIEW 1
#include <string_view>
#endif
#endif
namespace toml
{
enum class string_t : std::uint8_t
{
basic = 0,
literal = 1,
};
struct string
{
string() = default;
~string() = default;
string(const string& s) = default;
string(string&& s) = default;
string& operator=(const string& s) = default;
string& operator=(string&& s) = default;
string(const std::string& s): kind(string_t::basic), str(s){}
string(const std::string& s, string_t k): kind(k), str(s){}
string(const char* s): kind(string_t::basic), str(s){}
string(const char* s, string_t k): kind(k), str(s){}
string(std::string&& s): kind(string_t::basic), str(std::move(s)){}
string(std::string&& s, string_t k): kind(k), str(std::move(s)){}
string& operator=(const std::string& s)
{kind = string_t::basic; str = s; return *this;}
string& operator=(std::string&& s)
{kind = string_t::basic; str = std::move(s); return *this;}
operator std::string& () & noexcept {return str;}
operator std::string const& () const& noexcept {return str;}
operator std::string&& () && noexcept {return std::move(str);}
string& operator+=(const char* rhs) {str += rhs; return *this;}
string& operator+=(const char rhs) {str += rhs; return *this;}
string& operator+=(const std::string& rhs) {str += rhs; return *this;}
string& operator+=(const string& rhs) {str += rhs.str; return *this;}
#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
explicit string(std::string_view s): kind(string_t::basic), str(s){}
string(std::string_view s, string_t k): kind(k), str(s){}
string& operator=(std::string_view s)
{kind = string_t::basic; str = s; return *this;}
explicit operator std::string_view() const noexcept
{return std::string_view(str);}
string& operator+=(const std::string_view& rhs) {str += rhs; return *this;}
#endif
string_t kind;
std::string str;
};
inline bool operator==(const string& lhs, const string& rhs)
{
return lhs.kind == rhs.kind && lhs.str == rhs.str;
}
inline bool operator!=(const string& lhs, const string& rhs)
{
return !(lhs == rhs);
}
inline bool operator<(const string& lhs, const string& rhs)
{
return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind);
}
inline bool operator>(const string& lhs, const string& rhs)
{
return rhs < lhs;
}
inline bool operator<=(const string& lhs, const string& rhs)
{
return !(rhs < lhs);
}
inline bool operator>=(const string& lhs, const string& rhs)
{
return !(lhs < rhs);
}
inline bool
operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;}
inline bool
operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;}
inline bool
operator< (const string& lhs, const std::string& rhs) {return lhs.str < rhs;}
inline bool
operator> (const string& lhs, const std::string& rhs) {return lhs.str > rhs;}
inline bool
operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;}
inline bool
operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;}
inline bool
operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;}
inline bool
operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;}
inline bool
operator< (const std::string& lhs, const string& rhs) {return lhs < rhs.str;}
inline bool
operator> (const std::string& lhs, const string& rhs) {return lhs > rhs.str;}
inline bool
operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;}
inline bool
operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;}
inline bool
operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);}
inline bool
operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);}
inline bool
operator< (const string& lhs, const char* rhs) {return lhs.str < std::string(rhs);}
inline bool
operator> (const string& lhs, const char* rhs) {return lhs.str > std::string(rhs);}
inline bool
operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);}
inline bool
operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);}
inline bool
operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;}
inline bool
operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;}
inline bool
operator< (const char* lhs, const string& rhs) {return std::string(lhs) < rhs.str;}
inline bool
operator> (const char* lhs, const string& rhs) {return std::string(lhs) > rhs.str;}
inline bool
operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;}
inline bool
operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const string& s)
{
if(s.kind == string_t::basic)
{
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
{
// it contains newline. make it multiline string.
os << "\"\"\"\n";
for(auto i=s.str.cbegin(), e=s.str.cend(); i!=e; ++i)
{
switch(*i)
{
case '\\': {os << "\\\\"; break;}
case '\"': {os << "\\\""; break;}
case '\b': {os << "\\b"; break;}
case '\t': {os << "\\t"; break;}
case '\f': {os << "\\f"; break;}
case '\n': {os << '\n'; break;}
case '\r':
{
// since it is a multiline string,
// CRLF is not needed to be escaped.
if(std::next(i) != e && *std::next(i) == '\n')
{
os << "\r\n";
++i;
}
else
{
os << "\\r";
}
break;
}
default: {os << *i; break;}
}
}
os << "\\\n\"\"\"";
return os;
}
// no newline. make it inline.
os << "\"";
for(const auto c : s.str)
{
switch(c)
{
case '\\': {os << "\\\\"; break;}
case '\"': {os << "\\\""; break;}
case '\b': {os << "\\b"; break;}
case '\t': {os << "\\t"; break;}
case '\f': {os << "\\f"; break;}
case '\n': {os << "\\n"; break;}
case '\r': {os << "\\r"; break;}
default : {os << c; break;}
}
}
os << "\"";
return os;
}
// the string `s` is literal-string.
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
{
// contains newline or single quote. make it multiline.
os << "'''\n" << s.str << "'''";
return os;
}
// normal literal string
os << '\'' << s.str << '\'';
return os;
}
} // toml
#endif// TOML11_STRING_H

View File

@ -1,328 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_TRAITS_HPP
#define TOML11_TRAITS_HPP
#include "from.hpp"
#include "into.hpp"
#include "version.hpp"
#include <chrono>
#include <forward_list>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
#include <string_view>
#endif // has_include(<string_view>)
#endif // cplusplus >= C++17
namespace toml
{
template<typename C, template<typename ...> class T, template<typename ...> class A>
class basic_value;
namespace detail
{
// ---------------------------------------------------------------------------
// check whether type T is a kind of container/map class
struct has_iterator_impl
{
template<typename T> static std::true_type check(typename T::iterator*);
template<typename T> static std::false_type check(...);
};
struct has_value_type_impl
{
template<typename T> static std::true_type check(typename T::value_type*);
template<typename T> static std::false_type check(...);
};
struct has_key_type_impl
{
template<typename T> static std::true_type check(typename T::key_type*);
template<typename T> static std::false_type check(...);
};
struct has_mapped_type_impl
{
template<typename T> static std::true_type check(typename T::mapped_type*);
template<typename T> static std::false_type check(...);
};
struct has_reserve_method_impl
{
template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>().reserve(std::declval<std::size_t>()))*);
};
struct has_push_back_method_impl
{
template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))*);
};
struct is_comparable_impl
{
template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>() < std::declval<T>())*);
};
struct has_from_toml_method_impl
{
template<typename T, typename C,
template<typename ...> class Tb, template<typename ...> class A>
static std::true_type check(
decltype(std::declval<T>().from_toml(
std::declval<::toml::basic_value<C, Tb, A>>()))*);
template<typename T, typename C,
template<typename ...> class Tb, template<typename ...> class A>
static std::false_type check(...);
};
struct has_into_toml_method_impl
{
template<typename T>
static std::true_type check(decltype(std::declval<T>().into_toml())*);
template<typename T>
static std::false_type check(...);
};
struct has_specialized_from_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::from<T>)>
static std::true_type check(::toml::from<T>*);
};
struct has_specialized_into_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
static std::true_type check(::toml::from<T>*);
};
/// Intel C++ compiler can not use decltype in parent class declaration, here
/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076
#ifdef __INTEL_COMPILER
#define decltype(...) std::enable_if<true, decltype(__VA_ARGS__)>::type
#endif
template<typename T>
struct has_iterator : decltype(has_iterator_impl::check<T>(nullptr)){};
template<typename T>
struct has_value_type : decltype(has_value_type_impl::check<T>(nullptr)){};
template<typename T>
struct has_key_type : decltype(has_key_type_impl::check<T>(nullptr)){};
template<typename T>
struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){};
template<typename T>
struct has_reserve_method : decltype(has_reserve_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_push_back_method : decltype(has_push_back_method_impl::check<T>(nullptr)){};
template<typename T>
struct is_comparable : decltype(is_comparable_impl::check<T>(nullptr)){};
template<typename T, typename C,
template<typename ...> class Tb, template<typename ...> class A>
struct has_from_toml_method
: decltype(has_from_toml_method_impl::check<T, C, Tb, A>(nullptr)){};
template<typename T>
struct has_into_toml_method
: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_from : decltype(has_specialized_from_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_into : decltype(has_specialized_into_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER
#undef decltype
#endif
// ---------------------------------------------------------------------------
// C++17 and/or/not
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
using std::conjunction;
using std::disjunction;
using std::negation;
#else
template<typename ...> struct conjunction : std::true_type{};
template<typename T> struct conjunction<T> : T{};
template<typename T, typename ... Ts>
struct conjunction<T, Ts...> :
std::conditional<static_cast<bool>(T::value), conjunction<Ts...>, T>::type
{};
template<typename ...> struct disjunction : std::false_type{};
template<typename T> struct disjunction<T> : T {};
template<typename T, typename ... Ts>
struct disjunction<T, Ts...> :
std::conditional<static_cast<bool>(T::value), T, disjunction<Ts...>>::type
{};
template<typename T>
struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{};
#endif
// ---------------------------------------------------------------------------
// type checkers
template<typename T> struct is_std_pair : std::false_type{};
template<typename T1, typename T2>
struct is_std_pair<std::pair<T1, T2>> : std::true_type{};
template<typename T> struct is_std_tuple : std::false_type{};
template<typename ... Ts>
struct is_std_tuple<std::tuple<Ts...>> : std::true_type{};
template<typename T> struct is_std_forward_list : std::false_type{};
template<typename T>
struct is_std_forward_list<std::forward_list<T>> : std::true_type{};
template<typename T> struct is_chrono_duration: std::false_type{};
template<typename Rep, typename Period>
struct is_chrono_duration<std::chrono::duration<Rep, Period>>: std::true_type{};
template<typename T>
struct is_map : conjunction< // map satisfies all the following conditions
has_iterator<T>, // has T::iterator
has_value_type<T>, // has T::value_type
has_key_type<T>, // has T::key_type
has_mapped_type<T> // has T::mapped_type
>{};
template<typename T> struct is_map<T&> : is_map<T>{};
template<typename T> struct is_map<T const&> : is_map<T>{};
template<typename T> struct is_map<T volatile&> : is_map<T>{};
template<typename T> struct is_map<T const volatile&> : is_map<T>{};
template<typename T>
struct is_container : conjunction<
negation<is_map<T>>, // not a map
negation<std::is_same<T, std::string>>, // not a std::string
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
negation<std::is_same<T, std::string_view>>, // not a std::string_view
#endif // has_include(<string_view>)
#endif
has_iterator<T>, // has T::iterator
has_value_type<T> // has T::value_type
>{};
template<typename T> struct is_container<T&> : is_container<T>{};
template<typename T> struct is_container<T const&> : is_container<T>{};
template<typename T> struct is_container<T volatile&> : is_container<T>{};
template<typename T> struct is_container<T const volatile&> : is_container<T>{};
template<typename T>
struct is_basic_value: std::false_type{};
template<typename T> struct is_basic_value<T&> : is_basic_value<T>{};
template<typename T> struct is_basic_value<T const&> : is_basic_value<T>{};
template<typename T> struct is_basic_value<T volatile&> : is_basic_value<T>{};
template<typename T> struct is_basic_value<T const volatile&> : is_basic_value<T>{};
template<typename C, template<typename ...> class M, template<typename ...> class V>
struct is_basic_value<::toml::basic_value<C, M, V>>: std::true_type{};
// ---------------------------------------------------------------------------
// C++14 index_sequence
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::index_sequence;
using std::make_index_sequence;
#else
template<std::size_t ... Ns> struct index_sequence{};
template<typename IS, std::size_t N> struct push_back_index_sequence{};
template<std::size_t N, std::size_t ... Ns>
struct push_back_index_sequence<index_sequence<Ns...>, N>
{
typedef index_sequence<Ns..., N> type;
};
template<std::size_t N>
struct index_sequence_maker
{
typedef typename push_back_index_sequence<
typename index_sequence_maker<N-1>::type, N>::type type;
};
template<>
struct index_sequence_maker<0>
{
typedef index_sequence<0> type;
};
template<std::size_t N>
using make_index_sequence = typename index_sequence_maker<N-1>::type;
#endif // cplusplus >= 2014
// ---------------------------------------------------------------------------
// C++14 enable_if_t
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::enable_if_t;
#else
template<bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type;
#endif // cplusplus >= 2014
// ---------------------------------------------------------------------------
// return_type_of_t
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703
template<typename F, typename ... Args>
using return_type_of_t = std::invoke_result_t<F, Args...>;
#else
// result_of is deprecated after C++17
template<typename F, typename ... Args>
using return_type_of_t = typename std::result_of<F(Args...)>::type;
#endif
// ---------------------------------------------------------------------------
// is_string_literal
//
// to use this, pass `typename remove_reference<T>::type` to T.
template<typename T>
struct is_string_literal:
disjunction<
std::is_same<const char*, T>,
conjunction<
std::is_array<T>,
std::is_same<const char, typename std::remove_extent<T>::type>
>
>{};
// ---------------------------------------------------------------------------
// C++20 remove_cvref_t
template<typename T>
struct remove_cvref
{
using type = typename std::remove_cv<
typename std::remove_reference<T>::type>::type;
};
template<typename T>
using remove_cvref_t = typename remove_cvref<T>::type;
}// detail
}//toml
#endif // TOML_TRAITS

View File

@ -1,173 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_TYPES_HPP
#define TOML11_TYPES_HPP
#include <unordered_map>
#include <vector>
#include "comments.hpp"
#include "datetime.hpp"
#include "string.hpp"
#include "traits.hpp"
namespace toml
{
template<typename Comment, // discard/preserve_comment
template<typename ...> class Table, // map-like class
template<typename ...> class Array> // vector-like class
class basic_value;
using character = char;
using key = std::string;
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wshadow"
#endif
using boolean = bool;
using integer = std::int64_t;
using floating = double; // "float" is a keyword, cannot use it here.
// the following stuffs are structs defined here, so aliases are not needed.
// - string
// - offset_datetime
// - offset_datetime
// - local_datetime
// - local_date
// - local_time
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
// default toml::value and default array/table. these are defined after defining
// basic_value itself.
// using value = basic_value<discard_comments, std::unordered_map, std::vector>;
// using array = typename value::array_type;
// using table = typename value::table_type;
// to avoid warnings about `value_t::integer` is "shadowing" toml::integer in
// GCC -Wshadow=global.
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# if 7 <= __GNUC__
# pragma GCC diagnostic ignored "-Wshadow=global"
# else // gcc-6 or older
# pragma GCC diagnostic ignored "-Wshadow"
# endif
#endif
enum class value_t : std::uint8_t
{
empty = 0,
boolean = 1,
integer = 2,
floating = 3,
string = 4,
offset_datetime = 5,
local_datetime = 6,
local_date = 7,
local_time = 8,
array = 9,
table = 10,
};
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
template<typename charT, typename traits>
inline std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, value_t t)
{
switch(t)
{
case value_t::boolean : os << "boolean"; return os;
case value_t::integer : os << "integer"; return os;
case value_t::floating : os << "floating"; return os;
case value_t::string : os << "string"; return os;
case value_t::offset_datetime : os << "offset_datetime"; return os;
case value_t::local_datetime : os << "local_datetime"; return os;
case value_t::local_date : os << "local_date"; return os;
case value_t::local_time : os << "local_time"; return os;
case value_t::array : os << "array"; return os;
case value_t::table : os << "table"; return os;
case value_t::empty : os << "empty"; return os;
default : os << "unknown"; return os;
}
}
template<typename charT = char,
typename traits = std::char_traits<charT>,
typename alloc = std::allocator<charT>>
inline std::basic_string<charT, traits, alloc> stringize(value_t t)
{
std::basic_ostringstream<charT, traits, alloc> oss;
oss << t;
return oss.str();
}
namespace detail
{
// helper to define a type that represents a value_t value.
template<value_t V>
using value_t_constant = std::integral_constant<value_t, V>;
// meta-function that convertes from value_t to the exact toml type that corresponds to.
// It takes toml::basic_value type because array and table types depend on it.
template<value_t t, typename Value> struct enum_to_type {using type = void ;};
template<typename Value> struct enum_to_type<value_t::empty , Value>{using type = void ;};
template<typename Value> struct enum_to_type<value_t::boolean , Value>{using type = boolean ;};
template<typename Value> struct enum_to_type<value_t::integer , Value>{using type = integer ;};
template<typename Value> struct enum_to_type<value_t::floating , Value>{using type = floating ;};
template<typename Value> struct enum_to_type<value_t::string , Value>{using type = string ;};
template<typename Value> struct enum_to_type<value_t::offset_datetime, Value>{using type = offset_datetime ;};
template<typename Value> struct enum_to_type<value_t::local_datetime , Value>{using type = local_datetime ;};
template<typename Value> struct enum_to_type<value_t::local_date , Value>{using type = local_date ;};
template<typename Value> struct enum_to_type<value_t::local_time , Value>{using type = local_time ;};
template<typename Value> struct enum_to_type<value_t::array , Value>{using type = typename Value::array_type;};
template<typename Value> struct enum_to_type<value_t::table , Value>{using type = typename Value::table_type;};
// meta-function that converts from an exact toml type to the enum that corresponds to.
template<typename T, typename Value>
struct type_to_enum : std::conditional<
std::is_same<T, typename Value::array_type>::value, // if T == array_type,
value_t_constant<value_t::array>, // then value_t::array
typename std::conditional< // else...
std::is_same<T, typename Value::table_type>::value, // if T == table_type
value_t_constant<value_t::table>, // then value_t::table
value_t_constant<value_t::empty> // else value_t::empty
>::type
>::type {};
template<typename Value> struct type_to_enum<boolean , Value>: value_t_constant<value_t::boolean > {};
template<typename Value> struct type_to_enum<integer , Value>: value_t_constant<value_t::integer > {};
template<typename Value> struct type_to_enum<floating , Value>: value_t_constant<value_t::floating > {};
template<typename Value> struct type_to_enum<string , Value>: value_t_constant<value_t::string > {};
template<typename Value> struct type_to_enum<offset_datetime, Value>: value_t_constant<value_t::offset_datetime> {};
template<typename Value> struct type_to_enum<local_datetime , Value>: value_t_constant<value_t::local_datetime > {};
template<typename Value> struct type_to_enum<local_date , Value>: value_t_constant<value_t::local_date > {};
template<typename Value> struct type_to_enum<local_time , Value>: value_t_constant<value_t::local_time > {};
// meta-function that checks the type T is the same as one of the toml::* types.
template<typename T, typename Value>
struct is_exact_toml_type : disjunction<
std::is_same<T, boolean >,
std::is_same<T, integer >,
std::is_same<T, floating >,
std::is_same<T, string >,
std::is_same<T, offset_datetime>,
std::is_same<T, local_datetime >,
std::is_same<T, local_date >,
std::is_same<T, local_time >,
std::is_same<T, typename Value::array_type>,
std::is_same<T, typename Value::table_type>
>{};
template<typename T, typename V> struct is_exact_toml_type<T&, V> : is_exact_toml_type<T, V>{};
template<typename T, typename V> struct is_exact_toml_type<T const&, V> : is_exact_toml_type<T, V>{};
template<typename T, typename V> struct is_exact_toml_type<T volatile&, V> : is_exact_toml_type<T, V>{};
template<typename T, typename V> struct is_exact_toml_type<T const volatile&, V>: is_exact_toml_type<T, V>{};
} // detail
} // toml
#endif// TOML11_TYPES_H

View File

@ -1,150 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_UTILITY_HPP
#define TOML11_UTILITY_HPP
#include <memory>
#include <sstream>
#include <utility>
#include "traits.hpp"
#include "version.hpp"
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
#elif defined(__GNUC__)
# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg)))
#elif defined(_MSC_VER)
# define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg))
#else
# define TOML11_MARK_AS_DEPRECATED
#endif
namespace toml
{
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::make_unique;
#else
template<typename T, typename ... Ts>
inline std::unique_ptr<T> make_unique(Ts&& ... args)
{
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
}
#endif // TOML11_CPLUSPLUS_STANDARD_VERSION >= 2014
namespace detail
{
template<typename Container>
void try_reserve_impl(Container& container, std::size_t N, std::true_type)
{
container.reserve(N);
return;
}
template<typename Container>
void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept
{
return;
}
} // detail
template<typename Container>
void try_reserve(Container& container, std::size_t N)
{
if(N <= container.size()) {return;}
detail::try_reserve_impl(container, N, detail::has_reserve_method<Container>{});
return;
}
namespace detail
{
inline std::string concat_to_string_impl(std::ostringstream& oss)
{
return oss.str();
}
template<typename T, typename ... Ts>
std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail)
{
oss << std::forward<T>(head);
return concat_to_string_impl(oss, std::forward<Ts>(tail) ... );
}
} // detail
template<typename ... Ts>
std::string concat_to_string(Ts&& ... args)
{
std::ostringstream oss;
oss << std::boolalpha << std::fixed;
return detail::concat_to_string_impl(oss, std::forward<Ts>(args) ...);
}
template<typename T>
T from_string(const std::string& str, T opt)
{
T v(opt);
std::istringstream iss(str);
iss >> v;
return v;
}
namespace detail
{
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
template<typename T>
decltype(auto) last_one(T&& tail) noexcept
{
return std::forward<T>(tail);
}
template<typename T, typename ... Ts>
decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept
{
return last_one(std::forward<Ts>(tail)...);
}
#else // C++11
// The following code
// ```cpp
// 1 | template<typename T, typename ... Ts>
// 2 | auto last_one(T&& /*head*/, Ts&& ... tail)
// 3 | -> decltype(last_one(std::forward<Ts>(tail)...))
// 4 | {
// 5 | return last_one(std::forward<Ts>(tail)...);
// 6 | }
// ```
// does not work because the function `last_one(...)` is not yet defined at
// line #3, so `decltype()` cannot deduce the type returned from `last_one`.
// So we need to determine return type in a different way, like a meta func.
template<typename T, typename ... Ts>
struct last_one_in_pack
{
using type = typename last_one_in_pack<Ts...>::type;
};
template<typename T>
struct last_one_in_pack<T>
{
using type = T;
};
template<typename ... Ts>
using last_one_in_pack_t = typename last_one_in_pack<Ts...>::type;
template<typename T>
T&& last_one(T&& tail) noexcept
{
return std::forward<T>(tail);
}
template<typename T, typename ... Ts>
enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t<Ts&& ...>>
last_one(T&& /*head*/, Ts&& ... tail)
{
return last_one(std::forward<Ts>(tail)...);
}
#endif
} // detail
}// toml
#endif // TOML11_UTILITY

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +0,0 @@
#ifndef TOML11_VERSION_HPP
#define TOML11_VERSION_HPP
// This file checks C++ version.
#ifndef __cplusplus
# error "__cplusplus is not defined"
#endif
// Since MSVC does not define `__cplusplus` correctly unless you pass
// `/Zc:__cplusplus` when compiling, the workaround macros are added.
// Those enables you to define version manually or to use MSVC specific
// version macro automatically.
//
// The value of `__cplusplus` macro is defined in the C++ standard spec, but
// MSVC ignores the value, maybe because of backward compatibility. Instead,
// MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in
// the C++ standard. First we check the manual version definition, and then
// we check if _MSVC_LANG is defined. If neither, use normal `__cplusplus`.
//
// FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170
// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170
//
#if defined(TOML11_ENFORCE_CXX11)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201103L
#elif defined(TOML11_ENFORCE_CXX14)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201402L
#elif defined(TOML11_ENFORCE_CXX17)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201703L
#elif defined(TOML11_ENFORCE_CXX20)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 202002L
#elif defined(_MSVC_LANG) && defined(_MSC_VER) && 1910 <= _MSC_VER
# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG
#else
# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus
#endif
#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L && _MSC_VER < 1900
# error "toml11 requires C++11 or later."
#endif
#endif// TOML11_VERSION_HPP

View File

@ -0,0 +1,10 @@
#ifndef TOML11_COLOR_HPP
#define TOML11_COLOR_HPP
#include "fwd/color_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/color_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_COLOR_HPP

View File

@ -0,0 +1,10 @@
#ifndef TOML11_COMMENTS_HPP
#define TOML11_COMMENTS_HPP
#include "fwd/comments_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/comments_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_COMMENTS_HPP

View File

@ -0,0 +1,751 @@
#ifndef TOML11_COMPAT_HPP
#define TOML11_COMPAT_HPP
#include "version.hpp"
#include <algorithm>
#include <iterator>
#include <memory>
#include <string>
#include <type_traits>
#include <cassert>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
# if __has_include(<bit>)
# include <bit>
# endif
#endif
#include <cstring>
// ----------------------------------------------------------------------------
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
# if __has_cpp_attribute(deprecated)
# define TOML11_HAS_ATTR_DEPRECATED 1
# endif
#endif
#if defined(TOML11_HAS_ATTR_DEPRECATED)
# define TOML11_DEPRECATED(msg) [[deprecated(msg)]]
#elif defined(__GNUC__)
# define TOML11_DEPRECATED(msg) __attribute__((deprecated(msg)))
#elif defined(_MSC_VER)
# define TOML11_DEPRECATED(msg) __declspec(deprecated(msg))
#else
# define TOML11_DEPRECATED(msg)
#endif
// ----------------------------------------------------------------------------
#if defined(__cpp_if_constexpr)
# if __cpp_if_constexpr >= 201606L
# define TOML11_HAS_CONSTEXPR_IF 1
# endif
#endif
#if defined(TOML11_HAS_CONSTEXPR_IF)
# define TOML11_CONSTEXPR_IF if constexpr
#else
# define TOML11_CONSTEXPR_IF if
#endif
// ----------------------------------------------------------------------------
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
# if defined(__cpp_lib_make_unique)
# if __cpp_lib_make_unique >= 201304L
# define TOML11_HAS_STD_MAKE_UNIQUE 1
# endif
# endif
#endif
namespace toml
{
namespace cxx
{
#if defined(TOML11_HAS_STD_MAKE_UNIQUE)
using std::make_unique;
#else
template<typename T, typename ... Ts>
std::unique_ptr<T> make_unique(Ts&& ... args)
{
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
}
#endif // TOML11_HAS_STD_MAKE_UNIQUE
} // cxx
} // toml
// ---------------------------------------------------------------------------
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
# if defined(__cpp_lib_make_reverse_iterator)
# if __cpp_lib_make_reverse_iterator >= 201402L
# define TOML11_HAS_STD_MAKE_REVERSE_ITERATOR 1
# endif
# endif
#endif
namespace toml
{
namespace cxx
{
# if defined(TOML11_HAS_STD_MAKE_REVERSE_ITERATOR)
using std::make_reverse_iterator;
#else
template<typename Iterator>
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator iter)
{
return std::reverse_iterator<Iterator>(iter);
}
#endif // TOML11_HAS_STD_MAKE_REVERSE_ITERATOR
} // cxx
} // toml
// ---------------------------------------------------------------------------
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
# if defined(__cpp_lib_clamp)
# if __cpp_lib_clamp >= 201603L
# define TOML11_HAS_STD_CLAMP 1
# endif
# endif
#endif
namespace toml
{
namespace cxx
{
#if defined(TOML11_HAS_STD_CLAMP)
using std::clamp;
#else
template<typename T>
T clamp(const T& x, const T& low, const T& high) noexcept
{
assert(low <= high);
return (std::min)((std::max)(x, low), high);
}
#endif // TOML11_HAS_STD_CLAMP
} // cxx
} // toml
// ---------------------------------------------------------------------------
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
# if defined(__cpp_lib_bit_cast)
# if __cpp_lib_bit_cast >= 201806L
# define TOML11_HAS_STD_BIT_CAST 1
# endif
# endif
#endif
namespace toml
{
namespace cxx
{
#if defined(TOML11_HAS_STD_BIT_CAST)
using std::bit_cast;
#else
template<typename U, typename T>
U bit_cast(const T& x) noexcept
{
static_assert(sizeof(T) == sizeof(U), "");
static_assert(std::is_default_constructible<T>::value, "");
U z;
std::memcpy(reinterpret_cast<char*>(std::addressof(z)),
reinterpret_cast<const char*>(std::addressof(x)),
sizeof(T));
return z;
}
#endif // TOML11_HAS_STD_BIT_CAST
} // cxx
} // toml
// ---------------------------------------------------------------------------
// C++20 remove_cvref_t
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
# if defined(__cpp_lib_remove_cvref)
# if __cpp_lib_remove_cvref >= 201711L
# define TOML11_HAS_STD_REMOVE_CVREF 1
# endif
# endif
#endif
namespace toml
{
namespace cxx
{
#if defined(TOML11_HAS_STD_REMOVE_CVREF)
using std::remove_cvref;
using std::remove_cvref_t;
#else
template<typename T>
struct remove_cvref
{
using type = typename std::remove_cv<
typename std::remove_reference<T>::type>::type;
};
template<typename T>
using remove_cvref_t = typename remove_cvref<T>::type;
#endif // TOML11_HAS_STD_REMOVE_CVREF
} // cxx
} // toml
// ---------------------------------------------------------------------------
// C++17 and/or/not
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
# if defined(__cpp_lib_logical_traits)
# if __cpp_lib_logical_traits >= 201510L
# define TOML11_HAS_STD_CONJUNCTION 1
# endif
# endif
#endif
namespace toml
{
namespace cxx
{
#if defined(TOML11_HAS_STD_CONJUNCTION)
using std::conjunction;
using std::disjunction;
using std::negation;
#else
template<typename ...> struct conjunction : std::true_type{};
template<typename T> struct conjunction<T> : T{};
template<typename T, typename ... Ts>
struct conjunction<T, Ts...> :
std::conditional<static_cast<bool>(T::value), conjunction<Ts...>, T>::type
{};
template<typename ...> struct disjunction : std::false_type{};
template<typename T> struct disjunction<T> : T {};
template<typename T, typename ... Ts>
struct disjunction<T, Ts...> :
std::conditional<static_cast<bool>(T::value), T, disjunction<Ts...>>::type
{};
template<typename T>
struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{};
#endif // TOML11_HAS_STD_CONJUNCTION
} // cxx
} // toml
// ---------------------------------------------------------------------------
// C++14 index_sequence
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
# if defined(__cpp_lib_integer_sequence)
# if __cpp_lib_integer_sequence >= 201304L
# define TOML11_HAS_STD_INTEGER_SEQUENCE 1
# endif
# endif
#endif
namespace toml
{
namespace cxx
{
#if defined(TOML11_HAS_STD_INTEGER_SEQUENCE)
using std::index_sequence;
using std::make_index_sequence;
#else
template<std::size_t ... Ns> struct index_sequence{};
template<bool B, std::size_t N, typename T>
struct double_index_sequence;
template<std::size_t N, std::size_t ... Is>
struct double_index_sequence<true, N, index_sequence<Is...>>
{
using type = index_sequence<Is..., (Is+N)..., N*2>;
};
template<std::size_t N, std::size_t ... Is>
struct double_index_sequence<false, N, index_sequence<Is...>>
{
using type = index_sequence<Is..., (Is+N)...>;
};
template<std::size_t N>
struct index_sequence_maker
{
using type = typename double_index_sequence<
N % 2 == 1, N/2, typename index_sequence_maker<N/2>::type
>::type;
};
template<>
struct index_sequence_maker<0>
{
using type = index_sequence<>;
};
template<std::size_t N>
using make_index_sequence = typename index_sequence_maker<N>::type;
#endif // TOML11_HAS_STD_INTEGER_SEQUENCE
} // cxx
} // toml
// ---------------------------------------------------------------------------
// C++14 enable_if_t
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
# if defined(__cpp_lib_transformation_trait_aliases)
# if __cpp_lib_transformation_trait_aliases >= 201304L
# define TOML11_HAS_STD_ENABLE_IF_T 1
# endif
# endif
#endif
namespace toml
{
namespace cxx
{
#if defined(TOML11_HAS_STD_ENABLE_IF_T)
using std::enable_if_t;
#else
template<bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type;
#endif // TOML11_HAS_STD_ENABLE_IF_T
} // cxx
} // toml
// ---------------------------------------------------------------------------
// return_type_of_t
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
# if defined(__cpp_lib_is_invocable)
# if __cpp_lib_is_invocable >= 201703
# define TOML11_HAS_STD_INVOKE_RESULT 1
# endif
# endif
#endif
namespace toml
{
namespace cxx
{
#if defined(TOML11_HAS_STD_INVOKE_RESULT)
template<typename F, typename ... Args>
using return_type_of_t = std::invoke_result_t<F, Args...>;
#else
// result_of is deprecated after C++17
template<typename F, typename ... Args>
using return_type_of_t = typename std::result_of<F(Args...)>::type;
#endif // TOML11_HAS_STD_INVOKE_RESULT
} // cxx
} // toml
// ----------------------------------------------------------------------------
// (subset of) source_location
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 202002L
# if __has_include(<source_location>)
# define TOML11_HAS_STD_SOURCE_LOCATION
# endif // has_include
#endif // c++20
#if ! defined(TOML11_HAS_STD_SOURCE_LOCATION)
# if defined(__GNUC__) && ! defined(__clang__)
# if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
# if __has_include(<experimental/source_location>)
# define TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION
# endif
# endif
# endif // GNU g++
#endif // not TOML11_HAS_STD_SOURCE_LOCATION
#if ! defined(TOML11_HAS_STD_SOURCE_LOCATION) && ! defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION)
# if defined(__GNUC__) && ! defined(__clang__)
# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))
# define TOML11_HAS_BUILTIN_FILE_LINE 1
# define TOML11_BUILTIN_LINE_TYPE int
# endif
# elif defined(__clang__) // clang 9.0.0 implements builtin_FILE/LINE
# if __has_builtin(__builtin_FILE) && __has_builtin(__builtin_LINE)
# define TOML11_HAS_BUILTIN_FILE_LINE 1
# define TOML11_BUILTIN_LINE_TYPE unsigned int
# endif
# elif defined(_MSVC_LANG) && defined(_MSC_VER)
# if _MSC_VER > 1926
# define TOML11_HAS_BUILTIN_FILE_LINE 1
# define TOML11_BUILTIN_LINE_TYPE int
# endif
# endif
#endif
#if defined(TOML11_HAS_STD_SOURCE_LOCATION)
#include <source_location>
namespace toml
{
namespace cxx
{
using source_location = std::source_location;
inline std::string to_string(const source_location& loc)
{
return std::string(" at line ") + std::to_string(loc.line()) +
std::string(" in file ") + std::string(loc.file_name());
}
} // cxx
} // toml
#elif defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION)
#include <experimental/source_location>
namespace toml
{
namespace cxx
{
using source_location = std::experimental::source_location;
inline std::string to_string(const source_location& loc)
{
return std::string(" at line ") + std::to_string(loc.line()) +
std::string(" in file ") + std::string(loc.file_name());
}
} // cxx
} // toml
#elif defined(TOML11_HAS_BUILTIN_FILE_LINE)
namespace toml
{
namespace cxx
{
struct source_location
{
using line_type = TOML11_BUILTIN_LINE_TYPE;
static source_location current(const line_type line = __builtin_LINE(),
const char* file = __builtin_FILE())
{
return source_location(line, file);
}
source_location(const line_type line, const char* file)
: line_(line), file_name_(file)
{}
line_type line() const noexcept {return line_;}
const char* file_name() const noexcept {return file_name_;}
private:
line_type line_;
const char* file_name_;
};
inline std::string to_string(const source_location& loc)
{
return std::string(" at line ") + std::to_string(loc.line()) +
std::string(" in file ") + std::string(loc.file_name());
}
} // cxx
} // toml
#else // no builtin
namespace toml
{
namespace cxx
{
struct source_location
{
static source_location current() { return source_location{}; }
};
inline std::string to_string(const source_location&)
{
return std::string("");
}
} // cxx
} // toml
#endif // TOML11_HAS_STD_SOURCE_LOCATION
// ----------------------------------------------------------------------------
// (subset of) optional
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
# if __has_include(<optional>)
# include <optional>
# endif // has_include(optional)
#endif // C++17
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
# if defined(__cpp_lib_optional)
# if __cpp_lib_optional >= 201606L
# define TOML11_HAS_STD_OPTIONAL 1
# endif
# endif
#endif
#if defined(TOML11_HAS_STD_OPTIONAL)
namespace toml
{
namespace cxx
{
using std::optional;
inline std::nullopt_t make_nullopt() {return std::nullopt;}
template<typename charT, typename traitsT>
std::basic_ostream<charT, traitsT>&
operator<<(std::basic_ostream<charT, traitsT>& os, const std::nullopt_t&)
{
os << "nullopt";
return os;
}
} // cxx
} // toml
#else // TOML11_HAS_STD_OPTIONAL
namespace toml
{
namespace cxx
{
struct nullopt_t{};
inline nullopt_t make_nullopt() {return nullopt_t{};}
inline bool operator==(const nullopt_t&, const nullopt_t&) noexcept {return true;}
inline bool operator!=(const nullopt_t&, const nullopt_t&) noexcept {return false;}
inline bool operator< (const nullopt_t&, const nullopt_t&) noexcept {return false;}
inline bool operator<=(const nullopt_t&, const nullopt_t&) noexcept {return true;}
inline bool operator> (const nullopt_t&, const nullopt_t&) noexcept {return false;}
inline bool operator>=(const nullopt_t&, const nullopt_t&) noexcept {return true;}
template<typename charT, typename traitsT>
std::basic_ostream<charT, traitsT>&
operator<<(std::basic_ostream<charT, traitsT>& os, const nullopt_t&)
{
os << "nullopt";
return os;
}
template<typename T>
class optional
{
public:
using value_type = T;
public:
optional() noexcept : has_value_(false), null_('\0') {}
optional(nullopt_t) noexcept : has_value_(false), null_('\0') {}
optional(const T& x): has_value_(true), value_(x) {}
optional(T&& x): has_value_(true), value_(std::move(x)) {}
template<typename U, enable_if_t<std::is_constructible<T, U>::value, std::nullptr_t> = nullptr>
explicit optional(U&& x): has_value_(true), value_(std::forward<U>(x)) {}
optional(const optional& rhs): has_value_(rhs.has_value_)
{
if(rhs.has_value_)
{
this->assigner(rhs.value_);
}
}
optional(optional&& rhs): has_value_(rhs.has_value_)
{
if(this->has_value_)
{
this->assigner(std::move(rhs.value_));
}
}
optional& operator=(const optional& rhs)
{
if(this == std::addressof(rhs)) {return *this;}
this->cleanup();
this->has_value_ = rhs.has_value_;
if(this->has_value_)
{
this->assigner(rhs.value_);
}
return *this;
}
optional& operator=(optional&& rhs)
{
if(this == std::addressof(rhs)) {return *this;}
this->cleanup();
this->has_value_ = rhs.has_value_;
if(this->has_value_)
{
this->assigner(std::move(rhs.value_));
}
return *this;
}
template<typename U, enable_if_t<conjunction<
negation<std::is_same<T, U>>, std::is_constructible<T, U>
>::value, std::nullptr_t> = nullptr>
explicit optional(const optional<U>& rhs): has_value_(rhs.has_value_), null_('\0')
{
if(rhs.has_value_)
{
this->assigner(rhs.value_);
}
}
template<typename U, enable_if_t<conjunction<
negation<std::is_same<T, U>>, std::is_constructible<T, U>
>::value, std::nullptr_t> = nullptr>
explicit optional(optional<U>&& rhs): has_value_(rhs.has_value_), null_('\0')
{
if(this->has_value_)
{
this->assigner(std::move(rhs.value_));
}
}
template<typename U, enable_if_t<conjunction<
negation<std::is_same<T, U>>, std::is_constructible<T, U>
>::value, std::nullptr_t> = nullptr>
optional& operator=(const optional<U>& rhs)
{
if(this == std::addressof(rhs)) {return *this;}
this->cleanup();
this->has_value_ = rhs.has_value_;
if(this->has_value_)
{
this->assigner(rhs.value_);
}
return *this;
}
template<typename U, enable_if_t<conjunction<
negation<std::is_same<T, U>>, std::is_constructible<T, U>
>::value, std::nullptr_t> = nullptr>
optional& operator=(optional<U>&& rhs)
{
if(this == std::addressof(rhs)) {return *this;}
this->cleanup();
this->has_value_ = rhs.has_value_;
if(this->has_value_)
{
this->assigner(std::move(rhs.value_));
}
return *this;
}
~optional() noexcept
{
this->cleanup();
}
explicit operator bool() const noexcept
{
return has_value_;
}
bool has_value() const noexcept {return has_value_;}
value_type const& value(source_location loc = source_location::current()) const
{
if( ! this->has_value_)
{
throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc));
}
return this->value_;
}
value_type& value(source_location loc = source_location::current())
{
if( ! this->has_value_)
{
throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc));
}
return this->value_;
}
value_type const& value_or(const value_type& opt) const
{
if(this->has_value_) {return this->value_;} else {return opt;}
}
value_type& value_or(value_type& opt)
{
if(this->has_value_) {return this->value_;} else {return opt;}
}
private:
void cleanup() noexcept
{
if(this->has_value_)
{
value_.~T();
}
}
template<typename U>
void assigner(U&& x)
{
const auto tmp = ::new(std::addressof(this->value_)) value_type(std::forward<U>(x));
assert(tmp == std::addressof(this->value_));
(void)tmp;
}
private:
bool has_value_;
union
{
char null_;
T value_;
};
};
} // cxx
} // toml
#endif // TOML11_HAS_STD_OPTIONAL
#endif // TOML11_COMPAT_HPP

View File

@ -0,0 +1,68 @@
#ifndef TOML11_CONTEXT_HPP
#define TOML11_CONTEXT_HPP
#include "error_info.hpp"
#include "spec.hpp"
#include <vector>
namespace toml
{
namespace detail
{
template<typename TypeConfig>
class context
{
public:
explicit context(const spec& toml_spec)
: toml_spec_(toml_spec), errors_{}
{}
bool has_error() const noexcept {return !errors_.empty();}
std::vector<error_info> const& errors() const noexcept {return errors_;}
semantic_version& toml_version() noexcept {return toml_spec_.version;}
semantic_version const& toml_version() const noexcept {return toml_spec_.version;}
spec& toml_spec() noexcept {return toml_spec_;}
spec const& toml_spec() const noexcept {return toml_spec_;}
void report_error(error_info err)
{
this->errors_.push_back(std::move(err));
}
error_info pop_last_error()
{
assert( ! errors_.empty());
auto e = std::move(errors_.back());
errors_.pop_back();
return e;
}
private:
spec toml_spec_;
std::vector<error_info> errors_;
};
} // detail
} // toml
#if defined(TOML11_COMPILE_SOURCES)
namespace toml
{
struct type_config;
struct ordered_type_config;
namespace detail
{
extern template class context<::toml::type_config>;
extern template class context<::toml::ordered_type_config>;
} // detail
} // toml
#endif // TOML11_COMPILE_SOURCES
#endif // TOML11_CONTEXT_HPP

View File

@ -1,5 +1,105 @@
#ifndef TOML11_MACROS_HPP #ifndef TOML11_CONVERSION_HPP
#define TOML11_MACROS_HPP #define TOML11_CONVERSION_HPP
#include "find.hpp"
#include "from.hpp" // IWYU pragma: keep
#include "into.hpp" // IWYU pragma: keep
#if defined(TOML11_HAS_OPTIONAL)
#include <optional>
namespace toml
{
namespace detail
{
template<typename T>
inline constexpr bool is_optional_v = false;
template<typename T>
inline constexpr bool is_optional_v<std::optional<T>> = true;
template<typename T, typename TC>
void find_member_variable_from_value(T& obj, const basic_value<TC>& v, const char* var_name)
{
if constexpr(is_optional_v<T>)
{
if(v.contains(var_name))
{
obj = toml::find<typename T::value_type>(v, var_name);
}
else
{
obj = std::nullopt;
}
}
else
{
obj = toml::find<T>(v, var_name);
}
}
template<typename T, typename TC>
void assign_member_variable_to_value(const T& obj, basic_value<TC>& v, const char* var_name)
{
if constexpr(is_optional_v<T>)
{
if(obj.has_value())
{
v[var_name] = obj.value();
}
}
else
{
v[var_name] = obj;
}
}
} // detail
} // toml
#else
namespace toml
{
namespace detail
{
template<typename T, typename TC>
void find_member_variable_from_value(T& obj, const basic_value<TC>& v, const char* var_name)
{
obj = toml::find<T>(v, var_name);
}
template<typename T, typename TC>
void assign_member_variable_to_value(const T& obj, basic_value<TC>& v, const char* var_name)
{
v[var_name] = obj;
}
} // detail
} // toml
#endif // optional
// use it in the following way.
// ```cpp
// namespace foo
// {
// struct Foo
// {
// std::string s;
// double d;
// int i;
// };
// } // foo
//
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i)
// ```
//
// And then you can use `toml::get<foo::Foo>(v)` and `toml::find<foo::Foo>(file, "foo");`
//
#define TOML11_STRINGIZE_AUX(x) #x #define TOML11_STRINGIZE_AUX(x) #x
#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) #define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x)
@ -65,39 +165,20 @@
#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ #define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\
TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__)
// ----------------------------------------------------------------------------
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
// use it in the following way.
// ```cpp
// namespace foo
// {
// struct Foo
// {
// std::string s;
// double d;
// int i;
// };
// } // foo
//
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i)
// ```
// And then you can use `toml::find<foo::Foo>(file, "foo");`
//
#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ #define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\
obj.VAR_NAME = toml::find<decltype(obj.VAR_NAME)>(v, TOML11_STRINGIZE(VAR_NAME)); toml::detail::find_member_variable_from_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME));
#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ #define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\
v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME; toml::detail::assign_member_variable_to_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME));
#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ #define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\
namespace toml { \ namespace toml { \
template<> \ template<> \
struct from<NAME> \ struct from<NAME> \
{ \ { \
template<typename C, template<typename ...> class T, \ template<typename TC> \
template<typename ...> class A> \ static NAME from_toml(const basic_value<TC>& v) \
static NAME from_toml(const basic_value<C, T, A>& v) \
{ \ { \
NAME obj; \ NAME obj; \
TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \
@ -107,9 +188,10 @@
template<> \ template<> \
struct into<NAME> \ struct into<NAME> \
{ \ { \
static value into_toml(const NAME& obj) \ template<typename TC> \
static basic_value<TC> into_toml(const NAME& obj) \
{ \ { \
::toml::value v = ::toml::table{}; \ ::toml::basic_value<TC> v = typename ::toml::basic_value<TC>::table_type{}; \
TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \
return v; \ return v; \
} \ } \
@ -118,4 +200,4 @@
#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE #endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
#endif// TOML11_MACROS_HPP #endif // TOML11_CONVERSION_HPP

View File

@ -0,0 +1,10 @@
#ifndef TOML11_DATETIME_HPP
#define TOML11_DATETIME_HPP
#include "fwd/datetime_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/datetime_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_DATETIME_HPP

View File

@ -0,0 +1,10 @@
#ifndef TOML11_ERROR_INFO_HPP
#define TOML11_ERROR_INFO_HPP
#include "fwd/error_info_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/error_info_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_ERROR_INFO_HPP

View File

@ -0,0 +1,17 @@
#ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION_HPP
#include <exception>
namespace toml
{
struct exception : public std::exception
{
public:
virtual ~exception() noexcept override = default;
virtual const char* what() const noexcept override {return "";}
};
} // toml
#endif // TOMl11_EXCEPTION_HPP

377
src/frontend/qt_sdl/toml/toml11/find.hpp vendored Normal file
View File

@ -0,0 +1,377 @@
#ifndef TOML11_FIND_HPP
#define TOML11_FIND_HPP
#include <algorithm>
#include "get.hpp"
#include "value.hpp"
#if defined(TOML11_HAS_STRING_VIEW)
#include <string_view>
#endif
namespace toml
{
// ----------------------------------------------------------------------------
// find<T>(value, key);
template<typename T, typename TC>
decltype(::toml::get<T>(std::declval<basic_value<TC> const&>()))
find(const basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
{
return ::toml::get<T>(v.at(ky));
}
template<typename T, typename TC>
decltype(::toml::get<T>(std::declval<basic_value<TC>&>()))
find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
{
return ::toml::get<T>(v.at(ky));
}
template<typename T, typename TC>
decltype(::toml::get<T>(std::declval<basic_value<TC>&&>()))
find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky)
{
return ::toml::get<T>(std::move(v.at(ky)));
}
// ----------------------------------------------------------------------------
// find<T>(value, idx)
template<typename T, typename TC>
decltype(::toml::get<T>(std::declval<basic_value<TC> const&>()))
find(const basic_value<TC>& v, const std::size_t idx)
{
return ::toml::get<T>(v.at(idx));
}
template<typename T, typename TC>
decltype(::toml::get<T>(std::declval<basic_value<TC>&>()))
find(basic_value<TC>& v, const std::size_t idx)
{
return ::toml::get<T>(v.at(idx));
}
template<typename T, typename TC>
decltype(::toml::get<T>(std::declval<basic_value<TC>&&>()))
find(basic_value<TC>&& v, const std::size_t idx)
{
return ::toml::get<T>(std::move(v.at(idx)));
}
// ----------------------------------------------------------------------------
// find(value, key/idx), w/o conversion
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
{
return v.at(ky);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
find(basic_value<TC> const& v, const typename basic_value<TC>::key_type& ky)
{
return v.at(ky);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky)
{
return basic_value<TC>(std::move(v.at(ky)));
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
find(basic_value<TC>& v, const std::size_t idx)
{
return v.at(idx);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
find(basic_value<TC> const& v, const std::size_t idx)
{
return v.at(idx);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
find(basic_value<TC>&& v, const std::size_t idx)
{
return basic_value<TC>(std::move(v.at(idx)));
}
// --------------------------------------------------------------------------
// toml::find(toml::value, toml::key, Ts&& ... keys)
namespace detail
{
// It suppresses warnings by -Wsign-conversion when we pass integer literal
// to toml::find. integer literal `0` is deduced as an int, and will be
// converted to std::size_t. This causes sign-conversion.
template<typename TC>
std::size_t key_cast(const std::size_t& v) noexcept
{
return v;
}
template<typename TC, typename T>
cxx::enable_if_t<std::is_integral<cxx::remove_cvref_t<T>>::value, std::size_t>
key_cast(const T& v) noexcept
{
return static_cast<std::size_t>(v);
}
// for string-like (string, string literal, string_view)
template<typename TC>
typename basic_value<TC>::key_type const&
key_cast(const typename basic_value<TC>::key_type& v) noexcept
{
return v;
}
template<typename TC>
typename basic_value<TC>::key_type
key_cast(const typename basic_value<TC>::key_type::value_type* v)
{
return typename basic_value<TC>::key_type(v);
}
#if defined(TOML11_HAS_STRING_VIEW)
template<typename TC>
typename basic_value<TC>::key_type
key_cast(const std::string_view v)
{
return typename basic_value<TC>::key_type(v);
}
#endif // string_view
} // detail
// ----------------------------------------------------------------------------
// find(v, keys...)
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
return find(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
}
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
return find(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
}
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
return find(std::move(v.at(detail::key_cast<TC>(k1))), detail::key_cast<TC>(k2), ks...);
}
// ----------------------------------------------------------------------------
// find<T>(v, keys...)
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
decltype(::toml::get<T>(std::declval<const basic_value<TC>&>()))
find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
return find<T>(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
}
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
decltype(::toml::get<T>(std::declval<basic_value<TC>&>()))
find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
return find<T>(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
}
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
decltype(::toml::get<T>(std::declval<basic_value<TC>&&>()))
find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
return find<T>(std::move(v.at(detail::key_cast<TC>(k1))), detail::key_cast<TC>(k2), ks...);
}
// ===========================================================================
// find_or<T>(value, key, fallback)
// ---------------------------------------------------------------------------
// find_or(v, key, other_v)
template<typename TC, typename K>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
find_or(basic_value<TC>& v, const K& k, basic_value<TC>& opt) noexcept
{
try
{
return ::toml::find(v, detail::key_cast<TC>(k));
}
catch(...)
{
return opt;
}
}
template<typename TC, typename K>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
find_or(const basic_value<TC>& v, const K& k, const basic_value<TC>& opt) noexcept
{
try
{
return ::toml::find(v, detail::key_cast<TC>(k));
}
catch(...)
{
return opt;
}
}
template<typename TC, typename K>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
find_or(basic_value<TC>&& v, const K& k, basic_value<TC>&& opt) noexcept
{
try
{
return ::toml::find(v, detail::key_cast<TC>(k));
}
catch(...)
{
return opt;
}
}
// ---------------------------------------------------------------------------
// toml types (return type can be a reference)
template<typename T, typename TC, typename K>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
cxx::remove_cvref_t<T> const&>
find_or(const basic_value<TC>& v, const K& k, const T& opt)
{
try
{
return ::toml::get<T>(v.at(detail::key_cast<TC>(k)));
}
catch(...)
{
return opt;
}
}
template<typename T, typename TC, typename K>
cxx::enable_if_t<cxx::conjunction<
cxx::negation<std::is_const<T>>,
detail::is_exact_toml_type<T, basic_value<TC>>
>::value, cxx::remove_cvref_t<T>&>
find_or(basic_value<TC>& v, const K& k, T& opt)
{
try
{
return ::toml::get<T>(v.at(detail::key_cast<TC>(k)));
}
catch(...)
{
return opt;
}
}
template<typename T, typename TC, typename K>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
cxx::remove_cvref_t<T>>
find_or(basic_value<TC>&& v, const K& k, T opt)
{
try
{
return ::toml::get<T>(std::move(v.at(detail::key_cast<TC>(k))));
}
catch(...)
{
return T(std::move(opt));
}
}
// ---------------------------------------------------------------------------
// string literal (deduced as std::string)
// XXX to avoid confusion when T is explicitly specified in find_or<T>(),
// we restrict the string type as std::string.
template<typename TC, typename K>
cxx::enable_if_t<detail::is_type_config<TC>::value, std::string>
find_or(const basic_value<TC>& v, const K& k, const char* opt)
{
try
{
return ::toml::get<std::string>(v.at(detail::key_cast<TC>(k)));
}
catch(...)
{
return std::string(opt);
}
}
// ---------------------------------------------------------------------------
// other types (requires type conversion and return type cannot be a reference)
template<typename T, typename TC, typename K>
cxx::enable_if_t<cxx::conjunction<
cxx::negation<detail::is_basic_value<cxx::remove_cvref_t<T>>>,
detail::is_not_toml_type<cxx::remove_cvref_t<T>, basic_value<TC>>,
cxx::negation<std::is_same<cxx::remove_cvref_t<T>,
const typename basic_value<TC>::string_type::value_type*>>
>::value, cxx::remove_cvref_t<T>>
find_or(const basic_value<TC>& v, const K& ky, T opt)
{
try
{
return ::toml::get<cxx::remove_cvref_t<T>>(v.at(detail::key_cast<TC>(ky)));
}
catch(...)
{
return cxx::remove_cvref_t<T>(std::move(opt));
}
}
// ----------------------------------------------------------------------------
// recursive
namespace detail
{
template<typename ...Ts>
auto last_one(Ts&&... args)
-> decltype(std::get<sizeof...(Ts)-1>(std::forward_as_tuple(std::forward<Ts>(args)...)))
{
return std::get<sizeof...(Ts)-1>(std::forward_as_tuple(std::forward<Ts>(args)...));
}
} // detail
template<typename Value, typename K1, typename K2, typename K3, typename ... Ks>
auto find_or(Value&& v, const K1& k1, const K2& k2, K3&& k3, Ks&& ... keys) noexcept
-> cxx::enable_if_t<
detail::is_basic_value<cxx::remove_cvref_t<Value>>::value,
decltype(find_or(v, k2, std::forward<K3>(k3), std::forward<Ks>(keys)...))
>
{
try
{
return find_or(v.at(k1), k2, std::forward<K3>(k3), std::forward<Ks>(keys)...);
}
catch(...)
{
return detail::last_one(k3, keys...);
}
}
template<typename T, typename TC, typename K1, typename K2, typename K3, typename ... Ks>
T find_or(const basic_value<TC>& v, const K1& k1, const K2& k2, const K3& k3, const Ks& ... keys) noexcept
{
try
{
return find_or<T>(v.at(k1), k2, k3, keys...);
}
catch(...)
{
return static_cast<T>(detail::last_one(k3, keys...));
}
}
} // toml
#endif // TOML11_FIND_HPP

View File

@ -0,0 +1,10 @@
#ifndef TOML11_FORMAT_HPP
#define TOML11_FORMAT_HPP
#include "fwd/format_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/format_impl.hpp" // IWYU pragma: export
#endif
#endif// TOML11_FORMAT_HPP

View File

@ -1,5 +1,3 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_FROM_HPP #ifndef TOML11_FROM_HPP
#define TOML11_FROM_HPP #define TOML11_FROM_HPP

View File

@ -0,0 +1,88 @@
#ifndef TOML11_COLOR_FWD_HPP
#define TOML11_COLOR_FWD_HPP
#include <iosfwd>
#ifdef TOML11_COLORIZE_ERROR_MESSAGE
#define TOML11_ERROR_MESSAGE_COLORIZED true
#else
#define TOML11_ERROR_MESSAGE_COLORIZED false
#endif
#ifdef TOML11_USE_THREAD_LOCAL_COLORIZATION
#define TOML11_THREAD_LOCAL_COLORIZATION thread_local
#else
#define TOML11_THREAD_LOCAL_COLORIZATION
#endif
namespace toml
{
namespace color
{
// put ANSI escape sequence to ostream
inline namespace ansi
{
namespace detail
{
// Control color mode globally
class color_mode
{
public:
void enable() noexcept
{
should_color_ = true;
}
void disable() noexcept
{
should_color_ = false;
}
bool should_color() const noexcept
{
return should_color_;
}
private:
bool should_color_ = TOML11_ERROR_MESSAGE_COLORIZED;
};
inline color_mode& color_status() noexcept
{
static TOML11_THREAD_LOCAL_COLORIZATION color_mode status;
return status;
}
} // detail
std::ostream& reset (std::ostream& os);
std::ostream& bold (std::ostream& os);
std::ostream& grey (std::ostream& os);
std::ostream& gray (std::ostream& os);
std::ostream& red (std::ostream& os);
std::ostream& green (std::ostream& os);
std::ostream& yellow (std::ostream& os);
std::ostream& blue (std::ostream& os);
std::ostream& magenta(std::ostream& os);
std::ostream& cyan (std::ostream& os);
std::ostream& white (std::ostream& os);
} // ansi
inline void enable()
{
return detail::color_status().enable();
}
inline void disable()
{
return detail::color_status().disable();
}
inline bool should_color()
{
return detail::color_status().should_color();
}
} // color
} // toml
#endif // TOML11_COLOR_FWD_HPP

View File

@ -1,7 +1,10 @@
// Copyright Toru Niina 2019. #ifndef TOML11_COMMENTS_FWD_HPP
// Distributed under the MIT License. #define TOML11_COMMENTS_FWD_HPP
#ifndef TOML11_COMMENTS_HPP
#define TOML11_COMMENTS_HPP // to use __has_builtin
#include "../version.hpp" // IWYU pragma: keep
#include <exception>
#include <initializer_list> #include <initializer_list>
#include <iterator> #include <iterator>
#include <stdexcept> #include <stdexcept>
@ -9,12 +12,7 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <ostream>
#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments
#else
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments
#endif
// This file provides mainly two classes, `preserve_comments` and `discard_comments`. // This file provides mainly two classes, `preserve_comments` and `discard_comments`.
// Those two are a container that have the same interface as `std::vector<std::string>` // Those two are a container that have the same interface as `std::vector<std::string>`
@ -25,16 +23,11 @@
// error whenever you access to the element. // error whenever you access to the element.
namespace toml namespace toml
{ {
struct discard_comments; // forward decl class discard_comments; // forward decl
// use it in the following way class preserve_comments
//
// const toml::basic_value<toml::preserve_comments> data =
// toml::parse<toml::preserve_comments>("example.toml");
//
// the interface is almost the same as std::vector<std::string>.
struct preserve_comments
{ {
public:
// `container_type` is not provided in discard_comments. // `container_type` is not provided in discard_comments.
// do not use this inner-type in a generic code. // do not use this inner-type in a generic code.
using container_type = std::vector<std::string>; using container_type = std::vector<std::string>;
@ -51,6 +44,8 @@ struct preserve_comments
using reverse_iterator = container_type::reverse_iterator; using reverse_iterator = container_type::reverse_iterator;
using const_reverse_iterator = container_type::const_reverse_iterator; using const_reverse_iterator = container_type::const_reverse_iterator;
public:
preserve_comments() = default; preserve_comments() = default;
~preserve_comments() = default; ~preserve_comments() = default;
preserve_comments(preserve_comments const&) = default; preserve_comments(preserve_comments const&) = default;
@ -90,11 +85,9 @@ struct preserve_comments
// Related to the issue #97. // Related to the issue #97.
// //
// It is known that `std::vector::insert` and `std::vector::erase` in // `std::vector::insert` and `std::vector::erase` in the STL implementation
// the standard library implementation included in GCC 4.8.5 takes // included in GCC 4.8.5 takes `std::vector::iterator` instead of
// `std::vector::iterator` instead of `std::vector::const_iterator`. // `std::vector::const_iterator`. It causes compilation error in GCC 4.8.5.
// Because of the const-correctness, we cannot convert a `const_iterator` to
// an `iterator`. It causes compilation error in GCC 4.8.5.
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) #if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__)
# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 # if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805
# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION # define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
@ -233,39 +226,18 @@ struct preserve_comments
container_type comments; container_type comments;
}; };
inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} bool operator==(const preserve_comments& lhs, const preserve_comments& rhs);
inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs);
inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} bool operator< (const preserve_comments& lhs, const preserve_comments& rhs);
inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs);
inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} bool operator> (const preserve_comments& lhs, const preserve_comments& rhs);
inline bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs);
inline void swap(preserve_comments& lhs, preserve_comments& rhs) void swap(preserve_comments& lhs, preserve_comments& rhs);
{ void swap(preserve_comments& lhs, std::vector<std::string>& rhs);
lhs.swap(rhs); void swap(std::vector<std::string>& lhs, preserve_comments& rhs);
return;
}
inline void swap(preserve_comments& lhs, std::vector<std::string>& rhs)
{
lhs.comments.swap(rhs);
return;
}
inline void swap(std::vector<std::string>& lhs, preserve_comments& rhs)
{
lhs.swap(rhs.comments);
return;
}
template<typename charT, typename traits> std::ostream& operator<<(std::ostream& os, const preserve_comments& com);
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const preserve_comments& com)
{
for(const auto& c : com)
{
os << '#' << c << '\n';
}
return os;
}
namespace detail namespace detail
{ {
@ -349,8 +321,9 @@ operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::differ
// efficiency, this is chosen as a default. // efficiency, this is chosen as a default.
// //
// To reduce the memory footprint, later we can try empty base optimization (EBO). // To reduce the memory footprint, later we can try empty base optimization (EBO).
struct discard_comments class discard_comments
{ {
public:
using size_type = std::size_t; using size_type = std::size_t;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using value_type = std::string; using value_type = std::string;
@ -363,6 +336,7 @@ struct discard_comments
using reverse_iterator = detail::empty_iterator<std::string, false>; using reverse_iterator = detail::empty_iterator<std::string, false>;
using const_reverse_iterator = detail::empty_iterator<std::string, true>; using const_reverse_iterator = detail::empty_iterator<std::string, true>;
public:
discard_comments() = default; discard_comments() = default;
~discard_comments() = default; ~discard_comments() = default;
discard_comments(discard_comments const&) = default; discard_comments(discard_comments const&) = default;
@ -425,14 +399,14 @@ struct discard_comments
// empty, so accessing through operator[], front/back, data causes address // empty, so accessing through operator[], front/back, data causes address
// error. // error.
reference operator[](const size_type) noexcept {return *data();} reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");}
const_reference operator[](const size_type) const noexcept {return *data();} const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");}
reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");}
const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");}
reference front() noexcept {return *data();} reference front() noexcept {never_call("toml::discard_comment::front");}
const_reference front() const noexcept {return *data();} const_reference front() const noexcept {never_call("toml::discard_comment::front");}
reference back() noexcept {return *data();} reference back() noexcept {never_call("toml::discard_comment::back");}
const_reference back() const noexcept {return *data();} const_reference back() const noexcept {never_call("toml::discard_comment::back");}
pointer data() noexcept {return nullptr;} pointer data() noexcept {return nullptr;}
const_pointer data() const noexcept {return nullptr;} const_pointer data() const noexcept {return nullptr;}
@ -450,6 +424,16 @@ struct discard_comments
const_reverse_iterator rend() const noexcept {return const_iterator{};} const_reverse_iterator rend() const noexcept {return const_iterator{};}
const_reverse_iterator crbegin() const noexcept {return const_iterator{};} const_reverse_iterator crbegin() const noexcept {return const_iterator{};}
const_reverse_iterator crend() const noexcept {return const_iterator{};} const_reverse_iterator crend() const noexcept {return const_iterator{};}
private:
[[noreturn]] static void never_call(const char *const this_function)
{
#if __has_builtin(__builtin_unreachable)
__builtin_unreachable();
#endif
throw std::logic_error{this_function};
}
}; };
inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;}
@ -461,12 +445,7 @@ inline bool operator>=(const discard_comments&, const discard_comments&) noexcep
inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} inline void swap(const discard_comments&, const discard_comments&) noexcept {return;}
template<typename charT, typename traits> inline std::ostream& operator<<(std::ostream& os, const discard_comments&) {return os;}
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const discard_comments&)
{
return os;
}
} // toml11 } // toml11
#endif// TOML11_COMMENTS_HPP #endif // TOML11_COMMENTS_FWD_HPP

View File

@ -0,0 +1,261 @@
#ifndef TOML11_DATETIME_FWD_HPP
#define TOML11_DATETIME_FWD_HPP
#include <chrono>
#include <iosfwd>
#include <string>
#include <cstdint>
#include <cstdlib>
#include <ctime>
namespace toml
{
enum class month_t : std::uint8_t
{
Jan = 0,
Feb = 1,
Mar = 2,
Apr = 3,
May = 4,
Jun = 5,
Jul = 6,
Aug = 7,
Sep = 8,
Oct = 9,
Nov = 10,
Dec = 11
};
// ----------------------------------------------------------------------------
struct local_date
{
std::int16_t year{0}; // A.D. (like, 2018)
std::uint8_t month{0}; // [0, 11]
std::uint8_t day{0}; // [1, 31]
local_date(int y, month_t m, int d)
: year {static_cast<std::int16_t>(y)},
month{static_cast<std::uint8_t>(m)},
day {static_cast<std::uint8_t>(d)}
{}
explicit local_date(const std::tm& t)
: year {static_cast<std::int16_t>(t.tm_year + 1900)},
month{static_cast<std::uint8_t>(t.tm_mon)},
day {static_cast<std::uint8_t>(t.tm_mday)}
{}
explicit local_date(const std::chrono::system_clock::time_point& tp);
explicit local_date(const std::time_t t);
operator std::chrono::system_clock::time_point() const;
operator std::time_t() const;
local_date() = default;
~local_date() = default;
local_date(local_date const&) = default;
local_date(local_date&&) = default;
local_date& operator=(local_date const&) = default;
local_date& operator=(local_date&&) = default;
};
bool operator==(const local_date& lhs, const local_date& rhs);
bool operator!=(const local_date& lhs, const local_date& rhs);
bool operator< (const local_date& lhs, const local_date& rhs);
bool operator<=(const local_date& lhs, const local_date& rhs);
bool operator> (const local_date& lhs, const local_date& rhs);
bool operator>=(const local_date& lhs, const local_date& rhs);
std::ostream& operator<<(std::ostream& os, const local_date& date);
std::string to_string(const local_date& date);
// -----------------------------------------------------------------------------
struct local_time
{
std::uint8_t hour{0}; // [0, 23]
std::uint8_t minute{0}; // [0, 59]
std::uint8_t second{0}; // [0, 60]
std::uint16_t millisecond{0}; // [0, 999]
std::uint16_t microsecond{0}; // [0, 999]
std::uint16_t nanosecond{0}; // [0, 999]
local_time(int h, int m, int s,
int ms = 0, int us = 0, int ns = 0)
: hour {static_cast<std::uint8_t>(h)},
minute{static_cast<std::uint8_t>(m)},
second{static_cast<std::uint8_t>(s)},
millisecond{static_cast<std::uint16_t>(ms)},
microsecond{static_cast<std::uint16_t>(us)},
nanosecond {static_cast<std::uint16_t>(ns)}
{}
explicit local_time(const std::tm& t)
: hour {static_cast<std::uint8_t>(t.tm_hour)},
minute{static_cast<std::uint8_t>(t.tm_min )},
second{static_cast<std::uint8_t>(t.tm_sec )},
millisecond{0}, microsecond{0}, nanosecond{0}
{}
template<typename Rep, typename Period>
explicit local_time(const std::chrono::duration<Rep, Period>& t)
{
const auto h = std::chrono::duration_cast<std::chrono::hours>(t);
this->hour = static_cast<std::uint8_t>(h.count());
const auto t2 = t - h;
const auto m = std::chrono::duration_cast<std::chrono::minutes>(t2);
this->minute = static_cast<std::uint8_t>(m.count());
const auto t3 = t2 - m;
const auto s = std::chrono::duration_cast<std::chrono::seconds>(t3);
this->second = static_cast<std::uint8_t>(s.count());
const auto t4 = t3 - s;
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t4);
this->millisecond = static_cast<std::uint16_t>(ms.count());
const auto t5 = t4 - ms;
const auto us = std::chrono::duration_cast<std::chrono::microseconds>(t5);
this->microsecond = static_cast<std::uint16_t>(us.count());
const auto t6 = t5 - us;
const auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(t6);
this->nanosecond = static_cast<std::uint16_t>(ns.count());
}
operator std::chrono::nanoseconds() const;
local_time() = default;
~local_time() = default;
local_time(local_time const&) = default;
local_time(local_time&&) = default;
local_time& operator=(local_time const&) = default;
local_time& operator=(local_time&&) = default;
};
bool operator==(const local_time& lhs, const local_time& rhs);
bool operator!=(const local_time& lhs, const local_time& rhs);
bool operator< (const local_time& lhs, const local_time& rhs);
bool operator<=(const local_time& lhs, const local_time& rhs);
bool operator> (const local_time& lhs, const local_time& rhs);
bool operator>=(const local_time& lhs, const local_time& rhs);
std::ostream& operator<<(std::ostream& os, const local_time& time);
std::string to_string(const local_time& time);
// ----------------------------------------------------------------------------
struct time_offset
{
std::int8_t hour{0}; // [-12, 12]
std::int8_t minute{0}; // [-59, 59]
time_offset(int h, int m)
: hour {static_cast<std::int8_t>(h)},
minute{static_cast<std::int8_t>(m)}
{}
operator std::chrono::minutes() const;
time_offset() = default;
~time_offset() = default;
time_offset(time_offset const&) = default;
time_offset(time_offset&&) = default;
time_offset& operator=(time_offset const&) = default;
time_offset& operator=(time_offset&&) = default;
};
bool operator==(const time_offset& lhs, const time_offset& rhs);
bool operator!=(const time_offset& lhs, const time_offset& rhs);
bool operator< (const time_offset& lhs, const time_offset& rhs);
bool operator<=(const time_offset& lhs, const time_offset& rhs);
bool operator> (const time_offset& lhs, const time_offset& rhs);
bool operator>=(const time_offset& lhs, const time_offset& rhs);
std::ostream& operator<<(std::ostream& os, const time_offset& offset);
std::string to_string(const time_offset& offset);
// -----------------------------------------------------------------------------
struct local_datetime
{
local_date date{};
local_time time{};
local_datetime(local_date d, local_time t): date{d}, time{t} {}
explicit local_datetime(const std::tm& t): date{t}, time{t}{}
explicit local_datetime(const std::chrono::system_clock::time_point& tp);
explicit local_datetime(const std::time_t t);
operator std::chrono::system_clock::time_point() const;
operator std::time_t() const;
local_datetime() = default;
~local_datetime() = default;
local_datetime(local_datetime const&) = default;
local_datetime(local_datetime&&) = default;
local_datetime& operator=(local_datetime const&) = default;
local_datetime& operator=(local_datetime&&) = default;
};
bool operator==(const local_datetime& lhs, const local_datetime& rhs);
bool operator!=(const local_datetime& lhs, const local_datetime& rhs);
bool operator< (const local_datetime& lhs, const local_datetime& rhs);
bool operator<=(const local_datetime& lhs, const local_datetime& rhs);
bool operator> (const local_datetime& lhs, const local_datetime& rhs);
bool operator>=(const local_datetime& lhs, const local_datetime& rhs);
std::ostream& operator<<(std::ostream& os, const local_datetime& dt);
std::string to_string(const local_datetime& dt);
// -----------------------------------------------------------------------------
struct offset_datetime
{
local_date date{};
local_time time{};
time_offset offset{};
offset_datetime(local_date d, local_time t, time_offset o)
: date{d}, time{t}, offset{o}
{}
offset_datetime(const local_datetime& dt, time_offset o)
: date{dt.date}, time{dt.time}, offset{o}
{}
// use the current local timezone offset
explicit offset_datetime(const local_datetime& ld);
explicit offset_datetime(const std::chrono::system_clock::time_point& tp);
explicit offset_datetime(const std::time_t& t);
explicit offset_datetime(const std::tm& t);
operator std::chrono::system_clock::time_point() const;
operator std::time_t() const;
offset_datetime() = default;
~offset_datetime() = default;
offset_datetime(offset_datetime const&) = default;
offset_datetime(offset_datetime&&) = default;
offset_datetime& operator=(offset_datetime const&) = default;
offset_datetime& operator=(offset_datetime&&) = default;
private:
static time_offset get_local_offset(const std::time_t* tp);
};
bool operator==(const offset_datetime& lhs, const offset_datetime& rhs);
bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs);
bool operator< (const offset_datetime& lhs, const offset_datetime& rhs);
bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs);
bool operator> (const offset_datetime& lhs, const offset_datetime& rhs);
bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs);
std::ostream& operator<<(std::ostream& os, const offset_datetime& dt);
std::string to_string(const offset_datetime& dt);
}//toml
#endif // TOML11_DATETIME_FWD_HPP

View File

@ -0,0 +1,97 @@
#ifndef TOML11_ERROR_INFO_FWD_HPP
#define TOML11_ERROR_INFO_FWD_HPP
#include "../source_location.hpp"
#include "../utility.hpp"
namespace toml
{
// error info returned from parser.
struct error_info
{
error_info(std::string t, source_location l, std::string m, std::string s = "")
: title_(std::move(t)), locations_{std::make_pair(std::move(l), std::move(m))},
suffix_(std::move(s))
{}
error_info(std::string t, std::vector<std::pair<source_location, std::string>> l,
std::string s = "")
: title_(std::move(t)), locations_(std::move(l)), suffix_(std::move(s))
{}
std::string const& title() const noexcept {return title_;}
std::string & title() noexcept {return title_;}
std::vector<std::pair<source_location, std::string>> const&
locations() const noexcept {return locations_;}
void add_locations(source_location loc, std::string msg) noexcept
{
locations_.emplace_back(std::move(loc), std::move(msg));
}
std::string const& suffix() const noexcept {return suffix_;}
std::string & suffix() noexcept {return suffix_;}
private:
std::string title_;
std::vector<std::pair<source_location, std::string>> locations_;
std::string suffix_; // hint or something like that
};
// forward decl
template<typename TypeConfig>
class basic_value;
namespace detail
{
inline error_info make_error_info_rec(error_info e)
{
return e;
}
inline error_info make_error_info_rec(error_info e, std::string s)
{
e.suffix() = s;
return e;
}
template<typename TC, typename ... Ts>
error_info make_error_info_rec(error_info e,
const basic_value<TC>& v, std::string msg, Ts&& ... tail);
template<typename ... Ts>
error_info make_error_info_rec(error_info e,
source_location loc, std::string msg, Ts&& ... tail)
{
e.add_locations(std::move(loc), std::move(msg));
return make_error_info_rec(std::move(e), std::forward<Ts>(tail)...);
}
} // detail
template<typename ... Ts>
error_info make_error_info(
std::string title, source_location loc, std::string msg, Ts&& ... tail)
{
error_info ei(std::move(title), std::move(loc), std::move(msg));
return detail::make_error_info_rec(ei, std::forward<Ts>(tail) ... );
}
std::string format_error(const std::string& errkind, const error_info& err);
std::string format_error(const error_info& err);
// for custom error message
template<typename ... Ts>
std::string format_error(std::string title,
source_location loc, std::string msg, Ts&& ... tail)
{
return format_error("", make_error_info(std::move(title),
std::move(loc), std::move(msg), std::forward<Ts>(tail)...));
}
std::ostream& operator<<(std::ostream& os, const error_info& e);
} // toml
#endif // TOML11_ERROR_INFO_FWD_HPP

Some files were not shown because too many files have changed in this diff Show More