From f3bd58f75ebc87306f9c0bfba9a277f1b85ddbd8 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 31 Oct 2024 22:10:25 +0100 Subject: [PATCH 001/106] do touchscreen input more properly --- src/frontend/qt_sdl/EmuInstance.h | 6 ++++++ src/frontend/qt_sdl/EmuInstanceInput.cpp | 16 ++++++++++++++++ src/frontend/qt_sdl/EmuThread.cpp | 5 +++++ src/frontend/qt_sdl/Screen.cpp | 21 +++++++-------------- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 658247f0..9ab12d93 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -147,6 +147,9 @@ public: int getJoystickID() { return joystickID; } SDL_Joystick* getJoystick() { return joystick; } + void touchScreen(int x, int y); + void releaseScreen(); + private: static int lastSep(const std::string& path); std::string getAssetPath(bool gba, const std::string& configpath, const std::string& ext, const std::string& file); @@ -328,6 +331,9 @@ private: melonDS::u32 inputMask; + bool isTouching; + melonDS::u16 touchX, touchY; + friend class EmuThread; friend class MainWindow; }; diff --git a/src/frontend/qt_sdl/EmuInstanceInput.cpp b/src/frontend/qt_sdl/EmuInstanceInput.cpp index bb06c242..aa1c529f 100644 --- a/src/frontend/qt_sdl/EmuInstanceInput.cpp +++ b/src/frontend/qt_sdl/EmuInstanceInput.cpp @@ -74,6 +74,10 @@ void EmuInstance::inputInit() hotkeyMask = 0; lastHotkeyMask = 0; + isTouching = false; + touchX = 0; + touchY = 0; + joystick = nullptr; controller = nullptr; hasRumble = false; @@ -353,3 +357,15 @@ void EmuInstance::inputProcess() hotkeyRelease = lastHotkeyMask & ~hotkeyMask; lastHotkeyMask = hotkeyMask; } + +void EmuInstance::touchScreen(int x, int y) +{ + touchX = x; + touchY = y; + isTouching = true; +} + +void EmuInstance::releaseScreen() +{ + isTouching = false; +} diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index f4ce8c94..f0207f1a 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -251,6 +251,11 @@ void EmuThread::run() // process input and hotkeys emuInstance->nds->SetKeyMask(emuInstance->inputMask); + if (emuInstance->isTouching) + emuInstance->nds->TouchScreen(emuInstance->touchX, emuInstance->touchY); + else + emuInstance->nds->ReleaseScreen(); + if (emuInstance->hotkeyPressed(HK_Lid)) { bool lid = !emuInstance->nds->IsLidClosed(); diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 0d05a065..eab5feee 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -258,8 +258,7 @@ void ScreenPanel::mousePressEvent(QMouseEvent* event) if (layout.GetTouchCoords(x, y, false)) { touching = true; - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->TouchScreen(x, y); + emuInstance->touchScreen(x, y); } } @@ -272,8 +271,7 @@ void ScreenPanel::mouseReleaseEvent(QMouseEvent* event) if (touching) { touching = false; - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->ReleaseScreen(); + emuInstance->releaseScreen(); } } @@ -292,8 +290,7 @@ void ScreenPanel::mouseMoveEvent(QMouseEvent* event) if (layout.GetTouchCoords(x, y, true)) { - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->TouchScreen(x, y); + emuInstance->touchScreen(x, y); } } @@ -318,16 +315,14 @@ void ScreenPanel::tabletEvent(QTabletEvent* event) if (layout.GetTouchCoords(x, y, event->type()==QEvent::TabletMove)) { touching = true; - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->TouchScreen(x, y); + emuInstance->touchScreen(x, y); } } break; case QEvent::TabletRelease: if (touching) { - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->ReleaseScreen(); + emuInstance->releaseScreen(); touching = false; } break; @@ -365,16 +360,14 @@ void ScreenPanel::touchEvent(QTouchEvent* event) if (layout.GetTouchCoords(x, y, event->type()==QEvent::TouchUpdate)) { touching = true; - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->TouchScreen(x, y); + emuInstance->touchScreen(x, y); } } break; case QEvent::TouchEnd: if (touching) { - assert(emuInstance->getNDS() != nullptr); - emuInstance->getNDS()->ReleaseScreen(); + emuInstance->releaseScreen(); touching = false; } break; From 9c8f229fed59e0b2f9035d185742c3ab04b921f7 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 31 Oct 2024 22:37:46 +0100 Subject: [PATCH 002/106] misc shit --- src/frontend/qt_sdl/AudioSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/EmuThread.cpp | 8 ++++---- src/frontend/qt_sdl/EmuThread.h | 4 ++-- src/frontend/qt_sdl/Screen.cpp | 10 +++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index 5c0c9330..5f2aef19 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -41,7 +41,7 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui( emuInstance = ((MainWindow*)parent)->getEmuInstance(); auto& cfg = emuInstance->getGlobalConfig(); auto& instcfg = emuInstance->getLocalConfig(); - bool emuActive = emuInstance->getEmuThread()->emuIsActive(); + bool emuActive = emuInstance->emuIsActive(); oldInterp = cfg.GetInt("Audio.Interpolation"); oldBitDepth = cfg.GetInt("Audio.BitDepth"); diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index f0207f1a..7e1b040c 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -319,13 +319,13 @@ void EmuThread::run() if (!useOpenGL) { - FrontBufferLock.lock(); - FrontBuffer = emuInstance->nds->GPU.FrontBuffer; - FrontBufferLock.unlock(); + frontBufferLock.lock(); + frontBuffer = emuInstance->nds->GPU.FrontBuffer; + frontBufferLock.unlock(); } else { - FrontBuffer = emuInstance->nds->GPU.FrontBuffer; + frontBuffer = emuInstance->nds->GPU.FrontBuffer; emuInstance->drawScreenGL(); } diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index 18bff6dd..f28c0604 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -136,8 +136,8 @@ public: void updateVideoSettings() { videoSettingsDirty = true; } void updateVideoRenderer() { videoSettingsDirty = true; lastVideoRenderer = -1; } - int FrontBuffer = 0; - QMutex FrontBufferLock; + int frontBuffer = 0; + QMutex frontBufferLock; signals: void windowUpdate(); diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index eab5feee..3eb37183 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -782,17 +782,17 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) auto nds = emuInstance->getNDS(); assert(nds != nullptr); - emuThread->FrontBufferLock.lock(); - int frontbuf = emuThread->FrontBuffer; + emuThread->frontBufferLock.lock(); + int frontbuf = emuThread->frontBuffer; if (!nds->GPU.Framebuffer[frontbuf][0] || !nds->GPU.Framebuffer[frontbuf][1]) { - emuThread->FrontBufferLock.unlock(); + emuThread->frontBufferLock.unlock(); return; } memcpy(screen[0].scanLine(0), nds->GPU.Framebuffer[frontbuf][0].get(), 256 * 192 * 4); memcpy(screen[1].scanLine(0), nds->GPU.Framebuffer[frontbuf][1].get(), 256 * 192 * 4); - emuThread->FrontBufferLock.unlock(); + emuThread->frontBufferLock.unlock(); QRect screenrc(0, 0, 256, 192); @@ -1106,7 +1106,7 @@ void ScreenPanelGL::drawScreenGL() glUseProgram(screenShaderProgram); glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor); - int frontbuf = emuThread->FrontBuffer; + int frontbuf = emuThread->frontBuffer; glActiveTexture(GL_TEXTURE0); #ifdef OGLRENDERER_ENABLED From e3e561da3fd2fca66b2d09ba6ed418eb4e0029b1 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 31 Oct 2024 22:51:18 +0100 Subject: [PATCH 003/106] lock framebuffer stuff to prevent conflicts when reiniting the core or changing the renderer --- src/frontend/qt_sdl/EmuInstance.cpp | 2 ++ src/frontend/qt_sdl/EmuInstance.h | 2 ++ src/frontend/qt_sdl/EmuThread.cpp | 2 ++ src/frontend/qt_sdl/Screen.cpp | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 9114a141..f4092b61 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1340,6 +1340,7 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB args = &(*dsiargs); } + renderLock.lock(); if ((!nds) || (consoleType != nds->ConsoleType)) { NDS::Current = nullptr; @@ -1387,6 +1388,7 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB dsi->EjectGBACart(); } } + renderLock.unlock(); return true; } diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 9ab12d93..33e5fd95 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -150,6 +150,8 @@ public: void touchScreen(int x, int y); void releaseScreen(); + QMutex renderLock; + private: static int lastSep(const std::string& path); std::string getAssetPath(bool gba, const std::string& configpath, const std::string& ext, const std::string& file); diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 7e1b040c..6417fa69 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -231,6 +231,7 @@ void EmuThread::run() // update render settings if needed if (videoSettingsDirty) { + emuInstance->renderLock.lock(); if (useOpenGL) { emuInstance->setVSyncGL(true); @@ -246,6 +247,7 @@ void EmuThread::run() updateRenderer(); videoSettingsDirty = false; + emuInstance->renderLock.unlock(); } // process input and hotkeys diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 3eb37183..a1048018 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -779,6 +779,7 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) if (emuThread->emuIsActive()) { + emuInstance->renderLock.lock(); auto nds = emuInstance->getNDS(); assert(nds != nullptr); @@ -801,6 +802,7 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) painter.setTransform(screenTrans[i]); painter.drawImage(screenrc, screen[screenKind[i]]); } + emuInstance->renderLock.unlock(); } osdUpdate(); From 09e4400f3cfc7654181a76264203f0c6c5d9315a Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 1 Nov 2024 00:40:09 +0100 Subject: [PATCH 004/106] fix hang when closing an instance that is engaged into local multiplayer --- src/frontend/qt_sdl/EmuInstance.cpp | 2 -- src/frontend/qt_sdl/EmuThread.cpp | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index f4092b61..89d4108b 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -154,8 +154,6 @@ EmuInstance::~EmuInstance() deleting = true; deleteAllWindows(); - MPInterface::Get().End(instanceID); - emuThread->emuExit(); emuThread->wait(); delete emuThread; diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 6417fa69..f767c6db 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -478,7 +478,8 @@ void EmuThread::waitMessage(int num) void EmuThread::waitAllMessages() { if (QThread::currentThread() == this) return; - msgSemaphore.acquire(msgSemaphore.available()); + while (!msgQueue.empty()) + msgSemaphore.acquire(); } void EmuThread::handleMessages() @@ -494,6 +495,7 @@ void EmuThread::handleMessages() emuPauseStack = emuPauseStackRunning; emuInstance->audioDisable(); + MPInterface::Get().End(emuInstance->instanceID); break; case msg_EmuRun: From 78aae252d510711a3d828ce42085350323736af1 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 1 Nov 2024 00:41:55 +0100 Subject: [PATCH 005/106] fix bug where opening a new instance would pause other instances --- src/frontend/qt_sdl/EmuInstance.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 89d4108b..2ca5d09f 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -136,7 +136,6 @@ EmuInstance::EmuInstance(int inst) : deleting(false), createWindow(); emuThread->start(); - emuThread->emuPause(); // if any extra windows were saved as enabled, open them for (int i = 1; i < kMaxWindows; i++) From 58ee191cc8d9b89e7f128b5602105f7efed7b1eb Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 1 Nov 2024 01:31:45 +0100 Subject: [PATCH 006/106] make mic input less shitty (and less dangerous) --- src/frontend/qt_sdl/EmuInstance.cpp | 1 - src/frontend/qt_sdl/EmuInstance.h | 5 +- src/frontend/qt_sdl/EmuInstanceAudio.cpp | 59 ++++++++++++++++++------ 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 2ca5d09f..2ac3f42d 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -140,7 +140,6 @@ EmuInstance::EmuInstance(int inst) : deleting(false), // if any extra windows were saved as enabled, open them for (int i = 1; i < kMaxWindows; i++) { - //Config::Table tbl = localCfg.GetTable("Window"+std::to_string(i), "Window0"); std::string key = "Window" + std::to_string(i) + ".Enabled"; bool enable = localCfg.GetBool(key); if (enable) diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 33e5fd95..a135a52c 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -298,8 +298,9 @@ private: int mpAudioMode; SDL_AudioDeviceID micDevice; - melonDS::s16 micExtBuffer[2048]; + melonDS::s16 micExtBuffer[4096]; melonDS::u32 micExtBufferWritePos; + melonDS::u32 micExtBufferCount; melonDS::u32 micWavLength; melonDS::s16* micWavBuffer; @@ -308,6 +309,8 @@ private: melonDS::u32 micBufferLength; melonDS::u32 micBufferReadPos; + SDL_mutex* micLock; + //int audioInterp; int audioVolume; bool audioDSiVolumeSync; diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp index a9bf1fb7..ac6e7d5b 100644 --- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp +++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp @@ -107,8 +107,12 @@ void EmuInstance::micCallback(void* data, Uint8* stream, int len) s16* input = (s16*)stream; len /= sizeof(s16); + SDL_LockMutex(inst->micLock); int maxlen = sizeof(micExtBuffer) / sizeof(s16); + if ((inst->micExtBufferCount + len) > maxlen) + len = maxlen - inst->micExtBufferCount; + if ((inst->micExtBufferWritePos + len) > maxlen) { u32 len1 = maxlen - inst->micExtBufferWritePos; @@ -121,6 +125,9 @@ void EmuInstance::micCallback(void* data, Uint8* stream, int len) memcpy(&inst->micExtBuffer[inst->micExtBufferWritePos], input, len*sizeof(s16)); inst->micExtBufferWritePos += len; } + + inst->micExtBufferCount += len; + SDL_UnlockMutex(inst->micLock); } void EmuInstance::audioMute() @@ -270,6 +277,8 @@ void EmuInstance::micLoadWav(const std::string& name) void EmuInstance::micProcess() { + SDL_LockMutex(micLock); + int type = micInputType; bool cmd = hotkeyDown(HK_Mic); @@ -278,6 +287,8 @@ void EmuInstance::micProcess() type = micInputType_Silence; } + const int kFrameLen = 735; + switch (type) { case micInputType_Silence: // no mic @@ -289,21 +300,35 @@ void EmuInstance::micProcess() case micInputType_Wav: // WAV if (micBuffer) { - if ((micBufferReadPos + 735) > micBufferLength) - { - s16 tmp[735]; - u32 len1 = micBufferLength - micBufferReadPos; - memcpy(&tmp[0], &micBuffer[micBufferReadPos], len1*sizeof(s16)); - memcpy(&tmp[len1], &micBuffer[0], (735 - len1)*sizeof(s16)); + int len = kFrameLen; + if (micExtBufferCount < len) + len = micExtBufferCount; - nds->MicInputFrame(tmp, 735); - micBufferReadPos = 735 - len1; + s16 tmp[kFrameLen]; + + if ((micBufferReadPos + len) > micBufferLength) + { + u32 part1 = micBufferLength - micBufferReadPos; + memcpy(&tmp[0], &micBuffer[micBufferReadPos], part1*sizeof(s16)); + memcpy(&tmp[part1], &micBuffer[0], (len - part1)*sizeof(s16)); + + micBufferReadPos = len - part1; } else { - nds->MicInputFrame(&micBuffer[micBufferReadPos], 735); - micBufferReadPos += 735; + memcpy(&tmp[0], &micBuffer[micBufferReadPos], len*sizeof(s16)); + + micBufferReadPos += len; } + + if (len < kFrameLen) + { + for (int i = len; i < kFrameLen; i++) + tmp[i] = tmp[len-1]; + } + nds->MicInputFrame(tmp, 735); + + micExtBufferCount -= len; } else { @@ -317,19 +342,21 @@ void EmuInstance::micProcess() int sample_len = sizeof(mic_blow) / sizeof(u16); static int sample_pos = 0; - s16 tmp[735]; + s16 tmp[kFrameLen]; - for (int i = 0; i < 735; i++) + for (int i = 0; i < kFrameLen; i++) { tmp[i] = mic_blow[sample_pos] ^ 0x8000; sample_pos++; if (sample_pos >= sample_len) sample_pos = 0; } - nds->MicInputFrame(tmp, 735); + nds->MicInputFrame(tmp, kFrameLen); } break; } + + SDL_UnlockMutex(micLock); } void EmuInstance::setupMicInputData() @@ -402,12 +429,15 @@ void EmuInstance::audioInit() memset(micExtBuffer, 0, sizeof(micExtBuffer)); micExtBufferWritePos = 0; + micExtBufferCount = 0; micWavBuffer = nullptr; micBuffer = nullptr; micBufferLength = 0; micBufferReadPos = 0; + micLock = SDL_CreateMutex(); + setupMicInputData(); } @@ -425,6 +455,9 @@ void EmuInstance::audioDeInit() if (micWavBuffer) delete[] micWavBuffer; micWavBuffer = nullptr; + + if (micLock) SDL_DestroyMutex(micLock); + micLock = nullptr; } void EmuInstance::audioSync() From 7740634e6a8269d1327fdf3dad4d88de2ac4d330 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 1 Nov 2024 02:19:29 +0100 Subject: [PATCH 007/106] reimplement MP audio mode 2 (active instance only) --- src/frontend/qt_sdl/EmuInstanceAudio.cpp | 15 +++++++++++---- src/frontend/qt_sdl/Screen.cpp | 4 ++++ src/frontend/qt_sdl/Window.cpp | 18 ++++++++++++++++-- src/frontend/qt_sdl/Window.h | 6 ++++++ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp index ac6e7d5b..3b222ba0 100644 --- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp +++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp @@ -133,6 +133,7 @@ void EmuInstance::micCallback(void* data, Uint8* stream, int len) void EmuInstance::audioMute() { audioMuted = false; + if (numEmuInstances() < 2) return; switch (mpAudioMode) { @@ -141,10 +142,16 @@ void EmuInstance::audioMute() break; case 2: // only currently focused instance - //if (mainWindow != nullptr) - // audioMuted = !mainWindow->isActiveWindow(); - // TODO!! - printf("TODO!! audioMute mode 2\n"); + audioMuted = true; + for (int i = 0; i < kMaxWindows; i++) + { + if (!windowList[i]) continue; + if (windowList[i]->isFocused()) + { + audioMuted = false; + break; + } + } break; } } diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index a1048018..10abe1ce 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -385,6 +385,10 @@ bool ScreenPanel::event(QEvent* event) touchEvent((QTouchEvent*)event); return true; } + else if (event->type() == QEvent::FocusIn) + mainWindow->onFocusIn(); + else if (event->type() == QEvent::FocusOut) + mainWindow->onFocusOut(); return QWidget::event(event); } diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index aed8076a..596a0f5c 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -235,7 +235,8 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : localCfg(inst->localCfg), windowCfg(localCfg.GetTable("Window"+std::to_string(id), "Window0")), emuThread(inst->getEmuThread()), - enabledSaved(false) + enabledSaved(false), + focused(true) { #ifndef _WIN32 if (!parent) @@ -1015,13 +1016,26 @@ void MainWindow::dropEvent(QDropEvent* event) void MainWindow::focusInEvent(QFocusEvent* event) { - emuInstance->audioMute(); + onFocusIn(); } void MainWindow::focusOutEvent(QFocusEvent* event) +{ + onFocusOut(); +} + +void MainWindow::onFocusIn() +{ + focused = true; + if (emuInstance) + emuInstance->audioMute(); +} + +void MainWindow::onFocusOut() { // focusOutEvent is called through the window close event handler // prevent use after free + focused = false; if (emuInstance) emuInstance->audioMute(); } diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 121115c0..9f652f54 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -131,6 +131,10 @@ public: void onAppStateChanged(Qt::ApplicationState state); + void onFocusIn(); + void onFocusOut(); + bool isFocused() { return focused; } + void osdAddMessage(unsigned int color, const char* msg); // called when the MP interface is changed @@ -264,6 +268,8 @@ private: int windowID; bool enabledSaved; + bool focused; + EmuInstance* emuInstance; EmuThread* emuThread; From a5389286e861ac13ec1f21412b1160d494c2cfbe Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sat, 2 Nov 2024 05:44:36 +0100 Subject: [PATCH 008/106] Make macOS OpenGL deprecation warnings shut up --- src/frontend/qt_sdl/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 56f82d89..7dc4a00c 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -122,6 +122,10 @@ elseif (APPLE) target_sources(melonDS PRIVATE ../duckstation/gl/context_agl.mm ) + set_source_files_properties( + ../duckstation/gl/context_agl.mm + PROPERTIES COMPILE_OPTIONS "-Wno-deprecated-declarations" + ) else() find_package(X11 REQUIRED) find_package(EGL REQUIRED) From d8f1d106f0965e469c84c661e0f80dafd418e0fd Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 5 Nov 2024 07:58:31 +0100 Subject: [PATCH 009/106] flake: remove workaround no longer needed with Darwin SDK changes also add the Qt tools to the dev shell since they're needed for Qt Designer and such --- flake.lock | 6 +++--- flake.nix | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/flake.lock b/flake.lock index be75f57f..d7dd5bc6 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1729665710, - "narHash": "sha256-AlcmCXJZPIlO5dmFzV3V2XF6x/OpNWUV8Y/FMPGd8Z4=", + "lastModified": 1730531603, + "narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2768c7d042a37de65bb1b5b3268fc987e534c49d", + "rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 8d500c03..a7768e4e 100644 --- a/flake.nix +++ b/flake.nix @@ -19,7 +19,7 @@ then sourceInfo.dirtyShortRev else sourceInfo.shortRev; - melonDS = pkgs.qt6.qtbase.stdenv.mkDerivation { + melonDS = pkgs.stdenv.mkDerivation { pname = "melonDS"; version = "0.9.5-${shortRevision}"; src = ./.; @@ -74,8 +74,11 @@ drv = self.packages.${system}.default; }; devShells = { - default = pkgs.mkShell.override { stdenv = pkgs.qt6.qtbase.stdenv; } { + default = pkgs.mkShell { inputsFrom = [ self.packages.${system}.default ]; + packages = with pkgs; [ + qt6.qttools + ]; }; # Shell for building static melonDS release builds with vcpkg From 5959009ebd600a13578a7be6a6f2baffbcf2b436 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 5 Nov 2024 17:03:07 +0100 Subject: [PATCH 010/106] Use Qt 6 by default on all platforms and update build instructions (#2187) --- .github/workflows/build-ubuntu.yml | 3 +- BUILD.md | 81 ++++++++++++++++++++++++++++++ CMakePresets.json | 4 -- README.md | 70 +------------------------- cmake/ConfigureVcpkg.cmake | 6 +-- src/frontend/qt_sdl/CMakeLists.txt | 6 +-- 6 files changed, 85 insertions(+), 85 deletions(-) create mode 100644 BUILD.md diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 044d01ee..1104142d 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -28,7 +28,7 @@ jobs: sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev libenet-dev \ qt6-{base,base-private,multimedia}-dev libqt6svg6-dev libarchive-dev libzstd-dev libfuse2 - name: Configure - run: cmake -B build -G Ninja -DUSE_QT6=ON -DCMAKE_INSTALL_PREFIX=/usr -DMELONDS_EMBED_BUILD_INFO=ON + run: cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DMELONDS_EMBED_BUILD_INFO=ON - name: Build run: | cmake --build build @@ -79,7 +79,6 @@ jobs: -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config \ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \ -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \ - -DUSE_QT6=ON \ -DMELONDS_EMBED_BUILD_INFO=ON - name: Build shell: bash diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 00000000..51bc4cac --- /dev/null +++ b/BUILD.md @@ -0,0 +1,81 @@ +# Building melonDS + +* [Linux](#linux) +* [Windows](#windows) +* [macOS](#macos) + +## Linux +1. Install dependencies: + * Ubuntu: + * All versions: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev libarchive-dev libenet-dev libzstd-dev` + * 24.04: `sudo apt install qt6-{base,base-private,multimedia,svg}-dev` + * 22.04: `sudo apt install qtbase6-dev qtbase6-private-dev qtmultimedia6-dev libqt6svg6-dev` + * Older versions: `sudo apt install qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev` + Also add `-DUSE_QT6=OFF` to the first CMake command below. + * Fedora: `sudo dnf install gcc-c++ cmake extra-cmake-modules SDL2-devel libarchive-devel enet-devel libzstd-devel qt6-{qtbase,qtmultimedia,qtsvg}-devel wayland-devel` + * Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt6-{base,multimedia,svg} libarchive enet zstd` +2. Download the melonDS repository and prepare: + ```bash + git clone https://github.com/melonDS-emu/melonDS + cd melonDS + ``` +3. Compile: + ```bash + cmake -B build + cmake --build build -j$(nproc --all) + ``` + +## Windows +1. Install [MSYS2](https://www.msys2.org/) +2. Open the MSYS2 terminal from the Start menu: + * For x64 systems (most common), use **MSYS2 UCRT64** + * For ARM64 systems, use **MSYS2 CLANGARM64** +3. Update the packages using `pacman -Syu` and reopen the same terminal if it asks you to +4. Install git and clone the repository + ```bash + pacman -S git + git clone https://github.com/melonDS-emu/melonDS + cd melonDS + ``` +5. Install dependencies: + Replace `` below with `mingw-w64-ucrt-x86_64` on x64 systems, or `mingw-w64-clang-aarch64` on ARM64 systems. + ```bash + pacman -S -{toolchain,cmake,SDL2,libarchive,enet,zstd}` + ``` +6. Install Qt and configure the build directory + * Dynamic builds (with DLLs) + 1. Install Qt: `pacman -S -{qt6-base,qt6-svg,qt6-multimedia,qt6-svg,qt6-tools}` + 2. Set up the build directory with `cmake -B build` + * Static builds (without DLLs, standalone executable) + 1. Install Qt: `pacman -S -qt5-static` + (Note: As of writing, the `qt6-static` package does not work.) + 2. Set up the build directory with `cmake -B build -DBUILD_STATIC=ON -DUSE_QT6=OFF -DCMAKE_PREFIX_PATH=$MSYSTEM_PREFIX/qt5-static` +7. Compile: `cmake --build build` + +If everything went well, melonDS should now be in the `build` folder. For dynamic builds, you may need to run melonDS from the MSYS2 terminal in order for it to find the required DLLs. + +## macOS +1. Install the [Homebrew Package Manager](https://brew.sh) +2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libarchive enet zstd` +3. Download the melonDS repository and prepare: + ```zsh + git clone https://github.com/melonDS-emu/melonDS + cd melonDS + ``` +4. Compile: + ```zsh + cmake -B build -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" + cmake --build build -j$(sysctl -n hw.logicalcpu) + ``` +If everything went well, melonDS.app should now be in the `build` directory. + +### Self-contained app bundle +If you want an app bundle that can be distributed to other computers without needing to install dependencies through Homebrew, you can additionally run ` +../tools/mac-libs.rb .` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command. + +## Nix (macOS/Linux) + +melonDS provides a Nix flake with support for both macOS and Linux. The [Nix package manager](https://nixos.org) needs to be installed to use it. + +* To run melonDS, just type `nix run github:melonDS-emu/melonDS`. +* To get a shell for development, clone the melonDS repository and type `nix develop` in its directory. \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json index 2144417b..b506b88a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -27,10 +27,6 @@ "binaryDir": "${sourceDir}/build/release-mingw-x86_64", "generator": "Ninja", "cacheVariables": { - "USE_QT6": { - "type": "BOOL", - "value": "ON" - }, "BUILD_STATIC": { "type": "BOOL", "value": "ON" diff --git a/README.md b/README.md index eb8b1358..2cf42c2d 100644 --- a/README.md +++ b/README.md @@ -32,75 +32,7 @@ DS BIOS dumps from a DSi or 3DS can be used with no compatibility issues. DSi BI As for the rest, the interface should be pretty straightforward. If you have a question, don't hesitate to ask, though! ## How to build - -### Linux -1. Install dependencies: - * Ubuntu 22.04: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev libarchive-dev libenet-dev libzstd-dev` - * Older Ubuntu: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev libarchive-dev libenet-dev libzstd-dev` - * Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt5-base qt5-multimedia qt5-svg libarchive enet zstd` -3. Download the melonDS repository and prepare: - ```bash - git clone https://github.com/melonDS-emu/melonDS - cd melonDS - ``` - -3. Compile: - ```bash - cmake -B build - cmake --build build -j$(nproc --all) - ``` - -### Windows -1. Install [MSYS2](https://www.msys2.org/) -2. Open the **MSYS2 MinGW 64-bit** terminal -3. Update the packages using `pacman -Syu` and reopen the terminal if it asks you to -4. Install git to clone the repository - ```bash - pacman -S git - ``` -5. Download the melonDS repository and prepare: - ```bash - git clone https://github.com/melonDS-emu/melonDS - cd melonDS - ``` -#### Dynamic builds (with DLLs) -5. Install dependencies: `pacman -S mingw-w64-x86_64-{cmake,SDL2,toolchain,qt5-base,qt5-svg,qt5-multimedia,qt5-svg,qt5-tools,libarchive,enet,zstd}` -6. Compile: - ```bash - cmake -B build - cmake --build build - cd build - ../tools/msys-dist.sh - ``` -If everything went well, melonDS and the libraries it needs should now be in the `dist` folder. - -#### Static builds (without DLLs, standalone executable) -5. Install dependencies: `pacman -S mingw-w64-x86_64-{cmake,SDL2,toolchain,qt5-static,libarchive,enet,zstd}` -6. Compile: - ```bash - cmake -B build -DBUILD_STATIC=ON -DCMAKE_PREFIX_PATH=/mingw64/qt5-static - cmake --build build - ``` -If everything went well, melonDS should now be in the `build` folder. - -### macOS -1. Install the [Homebrew Package Manager](https://brew.sh) -2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libarchive enet zstd` -3. Download the melonDS repository and prepare: - ```zsh - git clone https://github.com/melonDS-emu/melonDS - cd melonDS - ``` -4. Compile: - ```zsh - cmake -B build -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" - cmake --build build -j$(sysctl -n hw.logicalcpu) - ``` -If everything went well, melonDS.app should now be in the `build` directory. - -#### Self-contained app bundle -If you want an app bundle that can be distributed to other computers without needing to install dependencies through Homebrew, you can additionally run ` -../tools/mac-libs.rb .` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command. +See [BUILD.md](./BUILD.md) for build instructions. ## TODO LIST diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake index c1eb522d..3fb0786f 100644 --- a/cmake/ConfigureVcpkg.cmake +++ b/cmake/ConfigureVcpkg.cmake @@ -19,11 +19,7 @@ set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_SOURCE_DIR}/cmake/overlay-triplets") option(USE_RECOMMENDED_TRIPLETS "Use the recommended triplets that are used for official builds" ON) # Duplicated here because it needs to be set before project() -if (NOT WIN32) - option(USE_QT6 "Build using Qt 6 instead of 5" ON) -else() - option(USE_QT6 "Build using Qt 6 instead of 5" OFF) -endif() +option(USE_QT6 "Use Qt 6 instead of Qt 5" ON) # Since the Linux build pulls in glib anyway, we can just use upstream libslirp if (UNIX AND NOT APPLE) diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 7dc4a00c..54888c49 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -59,11 +59,7 @@ set(SOURCES_QT_SDL NetplayDialog.cpp ) -if (APPLE) - option(USE_QT6 "Build using Qt 6 instead of 5" ON) -else() - option(USE_QT6 "Build using Qt 6 instead of 5" OFF) -endif() +option(USE_QT6 "Use Qt 6 instead of Qt 5" ON) if (USE_QT6) find_package(Qt6 COMPONENTS Core Gui Widgets Network Multimedia OpenGL OpenGLWidgets Svg REQUIRED) From 8d4f4195463a2b5f0638ce099a84509581390622 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Fri, 8 Nov 2024 18:57:44 +0100 Subject: [PATCH 011/106] correct assert in gdb stub --- src/debug/GdbProto.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/debug/GdbProto.cpp b/src/debug/GdbProto.cpp index a69538db..33f11dd9 100644 --- a/src/debug/GdbProto.cpp +++ b/src/debug/GdbProto.cpp @@ -113,7 +113,7 @@ Gdb::ReadResult GdbStub::ParseAndSetupPacket() ReadResult result, prevResult; while (true) { - ReadResult result = TryParsePacket(i, packetStart, packetSize, packetContentSize); + result = TryParsePacket(i, packetStart, packetSize, packetContentSize); if (result == ReadResult::NoPacket) break; if (result != ReadResult::CmdRecvd && result != ReadResult::Break) From adf143a38da22f3280478373ce2ab4d53823e3ad Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Fri, 8 Nov 2024 21:32:45 +0000 Subject: [PATCH 012/106] Fix link to contributors in About dialog Add openExternalLinks property to the label to allow the hyperlink to open in the user's web browser. --- src/frontend/qt_sdl/AboutDialog.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/qt_sdl/AboutDialog.ui b/src/frontend/qt_sdl/AboutDialog.ui index 89ca7a36..a4a21594 100644 --- a/src/frontend/qt_sdl/AboutDialog.ui +++ b/src/frontend/qt_sdl/AboutDialog.ui @@ -133,6 +133,9 @@ By Arisotura, the melonDS team <a href="https://github.com/melonDS-emu/melonDS/graphs/contributors">and contributors</a>. + + true + From 7041b52ebc68821c132c09b721a7dbb441e857a5 Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Fri, 8 Nov 2024 21:41:27 +0000 Subject: [PATCH 013/106] Remove extra backtick in Windows build instructions --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index 51bc4cac..b052f905 100644 --- a/BUILD.md +++ b/BUILD.md @@ -40,7 +40,7 @@ 5. Install dependencies: Replace `` below with `mingw-w64-ucrt-x86_64` on x64 systems, or `mingw-w64-clang-aarch64` on ARM64 systems. ```bash - pacman -S -{toolchain,cmake,SDL2,libarchive,enet,zstd}` + pacman -S -{toolchain,cmake,SDL2,libarchive,enet,zstd} ``` 6. Install Qt and configure the build directory * Dynamic builds (with DLLs) From 8e3f6cc519dc740074d4bb0574c532d3a316234b Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sat, 9 Nov 2024 08:32:34 +0100 Subject: [PATCH 014/106] add missing qtbase-private-devel for Fedora --- BUILD.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BUILD.md b/BUILD.md index b052f905..dc45a9d5 100644 --- a/BUILD.md +++ b/BUILD.md @@ -12,7 +12,7 @@ * 22.04: `sudo apt install qtbase6-dev qtbase6-private-dev qtmultimedia6-dev libqt6svg6-dev` * Older versions: `sudo apt install qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev` Also add `-DUSE_QT6=OFF` to the first CMake command below. - * Fedora: `sudo dnf install gcc-c++ cmake extra-cmake-modules SDL2-devel libarchive-devel enet-devel libzstd-devel qt6-{qtbase,qtmultimedia,qtsvg}-devel wayland-devel` + * Fedora: `sudo dnf install gcc-c++ cmake extra-cmake-modules SDL2-devel libarchive-devel enet-devel libzstd-devel qt6-{qtbase,qtbase-private,qtmultimedia,qtsvg}-devel wayland-devel` * Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt6-{base,multimedia,svg} libarchive enet zstd` 2. Download the melonDS repository and prepare: ```bash @@ -78,4 +78,4 @@ If you want an app bundle that can be distributed to other computers without nee melonDS provides a Nix flake with support for both macOS and Linux. The [Nix package manager](https://nixos.org) needs to be installed to use it. * To run melonDS, just type `nix run github:melonDS-emu/melonDS`. -* To get a shell for development, clone the melonDS repository and type `nix develop` in its directory. \ No newline at end of file +* To get a shell for development, clone the melonDS repository and type `nix develop` in its directory. From 4528441c74276f45d3e37c0d311fdeeffe29fe85 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Sat, 9 Nov 2024 14:19:02 +0100 Subject: [PATCH 015/106] for OGL renderer W buffer rendering avoid undefined vertex z see https://github.com/melonDS-emu/melonDS/issues/2017 --- src/GPU3D_OpenGL_shaders.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GPU3D_OpenGL_shaders.h b/src/GPU3D_OpenGL_shaders.h index 03bd43f9..1bd8296a 100644 --- a/src/GPU3D_OpenGL_shaders.h +++ b/src/GPU3D_OpenGL_shaders.h @@ -681,6 +681,7 @@ void main() vec4 fpos; fpos.xy = (((vec2(vPosition.xy) ) * 2.0) / uScreenSize) - 1.0; + fpos.z = 0.0; fZ = float(vPosition.z << zshift) / 16777216.0; fpos.w = float(vPosition.w) / 65536.0f; fpos.xy *= fpos.w; From b2f6fab6f49f1ed32e40e565bf911ead79dde0c2 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Mon, 11 Nov 2024 12:05:08 +0100 Subject: [PATCH 016/106] cmake: use interface include directories properly and fix an indent I guess --- src/CMakeLists.txt | 4 +++- src/frontend/qt_sdl/CMakeLists.txt | 11 +++++------ src/net/CMakeLists.txt | 6 +++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1f947d11..6df0e82e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -127,6 +127,8 @@ if (ENABLE_JIT) endif() endif() +target_include_directories(core INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") + set(MELONDS_VERSION_SUFFIX "$ENV{MELONDS_VERSION_SUFFIX}" CACHE STRING "Suffix to add to displayed melonDS version") option(MELONDS_EMBED_BUILD_INFO "Embed detailed build info into the binary" OFF) set(MELONDS_GIT_BRANCH "$ENV{MELONDS_GIT_BRANCH}" CACHE STRING "The Git branch used for this build") @@ -184,7 +186,7 @@ elseif(NOT APPLE AND NOT HAIKU) target_link_libraries(core PRIVATE rt) endif() elseif(HAIKU) - target_link_libraries(core PRIVATE network) + target_link_libraries(core PRIVATE network) endif() if (ENABLE_JIT_PROFILING) diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 54888c49..fa1fafd8 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -91,8 +91,7 @@ add_compile_definitions(ARCHIVE_SUPPORT_ENABLED) add_executable(melonDS ${SOURCES_QT_SDL}) add_subdirectory("../../net" - "${CMAKE_BINARY_DIR}/net" -) + ${CMAKE_BINARY_DIR}/net) target_link_libraries(melonDS PRIVATE net-utils) @@ -171,10 +170,10 @@ if (BUILD_STATIC) endif() endif() -target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..") -target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..") -target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../../net") +target_include_directories(melonDS PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/..") + if (USE_QT6) target_include_directories(melonDS PUBLIC ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) else() diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt index 6ca24de6..657aa9fd 100644 --- a/src/net/CMakeLists.txt +++ b/src/net/CMakeLists.txt @@ -11,9 +11,9 @@ add_library(net-utils STATIC MPInterface.cpp ) -target_include_directories(net-utils PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_include_directories(net-utils PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..") - +target_include_directories(net-utils PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/..") option(USE_SYSTEM_LIBSLIRP "Use system libslirp instead of the bundled version" OFF) if (USE_SYSTEM_LIBSLIRP) From 7c1d2a64f4afe6b02a93c3342a91fc932015742e Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Mon, 11 Nov 2024 14:18:05 +0100 Subject: [PATCH 017/106] Set WIN32_LEAN_AND_MEAN, gets rid of the winsock2 warnings and probably speeds up compilation a tiny bit oh and NOMINMAX too for good measure while we're at it --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6df0e82e..a4cb6f1e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -180,6 +180,7 @@ endif() if (WIN32) target_link_libraries(core PRIVATE ole32 comctl32 wsock32 ws2_32) + target_compile_definitions(core PUBLIC WIN32_LEAN_AND_MEAN NOMINMAX) elseif(NOT APPLE AND NOT HAIKU) check_library_exists(rt shm_open "" NEED_LIBRT) if (NEED_LIBRT) From 5e8beb3ab7277e0fae9a9a1cae8d26a357c570e5 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Wed, 13 Nov 2024 15:23:59 +0100 Subject: [PATCH 018/106] fix a typo --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index dc45a9d5..e7cbdbe8 100644 --- a/BUILD.md +++ b/BUILD.md @@ -47,7 +47,7 @@ 1. Install Qt: `pacman -S -{qt6-base,qt6-svg,qt6-multimedia,qt6-svg,qt6-tools}` 2. Set up the build directory with `cmake -B build` * Static builds (without DLLs, standalone executable) - 1. Install Qt: `pacman -S -qt5-static` + 1. Install Qt: `pacman -S -qt5-static` (Note: As of writing, the `qt6-static` package does not work.) 2. Set up the build directory with `cmake -B build -DBUILD_STATIC=ON -DUSE_QT6=OFF -DCMAKE_PREFIX_PATH=$MSYSTEM_PREFIX/qt5-static` 7. Compile: `cmake --build build` From 0a4287c6adeeb70026a8b5ca2971f2ac917f8b53 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 Nov 2024 15:23:25 +0100 Subject: [PATCH 019/106] fix crashes when inserting/ejecting GBA carts/addons with nothing loaded --- src/GBACart.cpp | 38 +++++++++-------- src/GBACart.h | 4 +- src/NDS.cpp | 5 --- src/NDS.h | 1 - src/frontend/qt_sdl/EmuInstance.cpp | 63 ++++++++++++++++++++++------- src/frontend/qt_sdl/EmuInstance.h | 6 ++- src/frontend/qt_sdl/EmuThread.cpp | 2 +- 7 files changed, 77 insertions(+), 42 deletions(-) diff --git a/src/GBACart.cpp b/src/GBACart.cpp index a62aca6b..c3550207 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -832,6 +832,27 @@ std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen return cart; } +std::unique_ptr LoadAddon(int type, void* userdata) +{ + std::unique_ptr cart; + switch (type) + { + case GBAAddon_RAMExpansion: + cart = std::make_unique(); + break; + case GBAAddon_RumblePak: + cart = std::make_unique(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&& cart) noexcept { 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(); - break; - case GBAAddon_RumblePak: - Cart = std::make_unique(userdata); - break; - - default: - Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type); - return; - } -} - std::unique_ptr GBACartSlot::EjectCart() noexcept { return std::move(Cart); diff --git a/src/GBACart.h b/src/GBACart.h index 726a234d..e6639813 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -241,8 +241,6 @@ public: [[nodiscard]] CartCommon* GetCart() 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, /// or \c nullptr if the cart slot was empty. std::unique_ptr EjectCart() noexcept; @@ -309,6 +307,8 @@ std::unique_ptr ParseROM(const u8* romdata, u32 romlen, const u8* sr /// or \c nullptr if there was an error. std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen, std::unique_ptr&& sramdata, u32 sramlen, void* userdata = nullptr); +std::unique_ptr LoadAddon(int type, void* userdata); + } #endif // GBACART_H diff --git a/src/NDS.cpp b/src/NDS.cpp index 1023d3c0..bf2e4283 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -752,11 +752,6 @@ void NDS::SetGBASave(const u8* savedata, u32 savelen) } -void NDS::LoadGBAAddon(int type) -{ - GBACartSlot.LoadAddon(UserData, type); -} - void NDS::LoadBIOS() { Reset(); diff --git a/src/NDS.h b/src/NDS.h index b2bfb385..5e474807 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -384,7 +384,6 @@ public: // TODO: Encapsulate the rest of these members u32 GetGBASaveLength() const { return GBACartSlot.GetSaveMemoryLength(); } void SetGBASave(const u8* savedata, u32 savelen); - void LoadGBAAddon(int type); std::unique_ptr EjectGBACart() { return GBACartSlot.EjectCart(); } u32 RunFrame(); diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 2ac3f42d..a66396bb 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -83,6 +83,8 @@ EmuInstance::EmuInstance(int inst) : deleting(false), baseGBAROMDir = ""; baseGBAROMName = ""; baseGBAAssetName = ""; + nextGBACart = nullptr; + changeGBACart = false; cheatFile = nullptr; cheatsOn = localCfg.GetBool("EnableCheats"); @@ -118,7 +120,7 @@ EmuInstance::EmuInstance(int inst) : deleting(false), mpAudioMode = globalCfg.GetInt("MP.AudioMode"); nds = nullptr; - //updateConsole(nullptr, nullptr); + //updateConsole(nullptr); audioInit(); inputInit(); @@ -1215,7 +1217,7 @@ void EmuInstance::setDateTime() time.time().hour(), time.time().minute(), time.time().second()); } -bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGBAArgs&& _gbaargs) noexcept +bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs) noexcept { // update the console type consoleType = globalCfg.GetInt("Emu.ConsoleType"); @@ -1242,14 +1244,14 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB } std::unique_ptr nextgbacart; - if (std::holds_alternative(_gbaargs)) + if (!changeGBACart) { nextgbacart = nds ? nds->EjectGBACart() : nullptr; } - else if (const auto ptr = std::get_if>(&_gbaargs)) + else { - nextgbacart = std::move(*ptr); - _gbaargs = {}; + nextgbacart = std::move(nextGBACart); + changeGBACart = false; } @@ -1391,7 +1393,7 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB void EmuInstance::reset() { - updateConsole(Keep {}, Keep {}); + updateConsole(Keep {}); if (consoleType == 1) ejectGBACart(); @@ -1455,7 +1457,7 @@ void EmuInstance::reset() bool EmuInstance::bootToMenu() { // Keep whatever cart is in the console, if any. - if (!updateConsole(Keep {}, Keep {})) + if (!updateConsole(Keep {})) // Try to update the console, but keep the existing cart. If that fails... return false; @@ -1910,7 +1912,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset) if (reset) { - if (!updateConsole(std::move(cart), Keep {})) + if (!updateConsole(std::move(cart))) { QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM."); return false; @@ -2043,9 +2045,18 @@ bool EmuInstance::loadGBAROM(QStringList filepath) return false; } - nds->SetGBACart(std::move(cart)); gbaCartType = 0; - gbaSave = std::make_unique(savname); + if (emuIsActive()) + { + nds->SetGBACart(std::move(cart)); + gbaSave = std::make_unique(savname); + } + else + { + nextGBACart = std::move(cart); + changeGBACart = true; + } + return true; } @@ -2053,10 +2064,24 @@ void EmuInstance::loadGBAAddon(int type) { if (consoleType == 1) return; + auto cart = GBACart::LoadAddon(type, this); + if (!cart) + { + QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA addon."); + return; + } + + if (emuIsActive()) + { + nds->SetGBACart(std::move(cart)); + } + else + { + nextGBACart = std::move(cart); + changeGBACart = true; + } + gbaSave = nullptr; - - nds->LoadGBAAddon(type); - gbaCartType = type; baseGBAROMDir = ""; baseGBAROMName = ""; @@ -2067,7 +2092,15 @@ void EmuInstance::ejectGBACart() { gbaSave = nullptr; - nds->EjectGBACart(); + if (emuIsActive()) + { + nds->EjectGBACart(); + } + else + { + nextGBACart = nullptr; + changeGBACart = true; + } gbaCartType = -1; baseGBAROMDir = ""; diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index a135a52c..fbee4bf4 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -120,7 +120,7 @@ public: // return: empty string = setup OK, non-empty = error message QString verifySetup(); - bool updateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept; + bool updateConsole(UpdateConsoleNDSArgs&& ndsargs) noexcept; void enableCheats(bool enable); melonDS::ARCodeFile* getCheatFile(); @@ -188,12 +188,14 @@ private: std::pair, std::string> generateDefaultFirmware(); bool parseMacAddress(void* data); void customizeFirmware(melonDS::Firmware& firmware, bool overridesettings) noexcept; + bool loadROMData(const QStringList& filepath, std::unique_ptr& filedata, melonDS::u32& filelen, std::string& basepath, std::string& romname) noexcept; QString getSavErrorString(std::string& filepath, bool gba); bool loadROM(QStringList filepath, bool reset); void ejectCart(); bool cartInserted(); QString cartLabel(); + bool loadGBAROM(QStringList filepath); void loadGBAAddon(int type); void ejectGBACart(); @@ -264,6 +266,8 @@ private: std::string baseGBAROMDir; std::string baseGBAROMName; std::string baseGBAAssetName; + bool changeGBACart; + std::unique_ptr nextGBACart; // HACK public: diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index f767c6db..1592c481 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -109,7 +109,7 @@ void EmuThread::run() Config::Table& globalCfg = emuInstance->getGlobalConfig(); u32 mainScreenPos[3]; - //emuInstance->updateConsole(nullptr, nullptr); + //emuInstance->updateConsole(nullptr); // No carts are inserted when melonDS first boots mainScreenPos[0] = 0; From 871a167d8bd36814bfdaafe54041efd28279a91c Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 Nov 2024 15:43:22 +0100 Subject: [PATCH 020/106] also fix crashes when inserting/ejecting a NDS cart while nothing is loaded --- src/frontend/qt_sdl/EmuInstance.cpp | 54 ++++++++++++++++++++--------- src/frontend/qt_sdl/EmuInstance.h | 4 ++- src/frontend/qt_sdl/EmuThread.cpp | 2 +- src/frontend/qt_sdl/EmuThread.h | 3 -- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index a66396bb..4c0d3798 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -77,6 +77,8 @@ EmuInstance::EmuInstance(int inst) : deleting(false), baseROMDir = ""; baseROMName = ""; baseAssetName = ""; + nextCart = nullptr; + changeCart = false; gbaSave = nullptr; gbaCartType = -1; @@ -120,7 +122,7 @@ EmuInstance::EmuInstance(int inst) : deleting(false), mpAudioMode = globalCfg.GetInt("MP.AudioMode"); nds = nullptr; - //updateConsole(nullptr); + //updateConsole(); audioInit(); inputInit(); @@ -1217,23 +1219,22 @@ void EmuInstance::setDateTime() time.time().hour(), time.time().minute(), time.time().second()); } -bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs) noexcept +bool EmuInstance::updateConsole() noexcept { // update the console type consoleType = globalCfg.GetInt("Emu.ConsoleType"); // 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 nextndscart; - if (std::holds_alternative(_ndsargs)) + if (!changeCart) { // If we want to keep the existing cart (if any)... nextndscart = nds ? nds->EjectCart() : nullptr; - _ndsargs = {}; } - else if (const auto ptr = std::get_if>(&_ndsargs)) + else { - nextndscart = std::move(*ptr); - _ndsargs = {}; + nextndscart = std::move(nextCart); + changeCart = false; } if (auto* cartsd = dynamic_cast(nextndscart.get())) @@ -1388,12 +1389,14 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs) noexcept } renderLock.unlock(); + loadCheats(); + return true; } void EmuInstance::reset() { - updateConsole(Keep {}); + updateConsole(); if (consoleType == 1) ejectGBACart(); @@ -1457,7 +1460,7 @@ void EmuInstance::reset() bool EmuInstance::bootToMenu() { // Keep whatever cart is in the console, if any. - if (!updateConsole(Keep {})) + if (!updateConsole()) // Try to update the console, but keep the existing cart. If that fails... return false; @@ -1912,7 +1915,10 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset) if (reset) { - if (!updateConsole(std::move(cart))) + nextCart = std::move(cart); + changeCart = true; + + if (!updateConsole()) { QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM."); return false; @@ -1931,13 +1937,20 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset) } else { - assert(nds != nullptr); - nds->SetNDSCart(std::move(cart)); + if (emuIsActive()) + { + nds->SetNDSCart(std::move(cart)); + loadCheats(); + } + else + { + nextCart = std::move(cart); + changeCart = true; + } } cartType = 0; ndsSave = std::make_unique(savname); - loadCheats(); return true; // success } @@ -1946,9 +1959,16 @@ void EmuInstance::ejectCart() { ndsSave = nullptr; - unloadCheats(); - - nds->EjectCart(); + if (emuIsActive()) + { + nds->EjectCart(); + unloadCheats(); + } + else + { + nextCart = nullptr; + changeCart = true; + } cartType = -1; baseROMDir = ""; diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index fbee4bf4..35943492 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -120,7 +120,7 @@ public: // return: empty string = setup OK, non-empty = error message QString verifySetup(); - bool updateConsole(UpdateConsoleNDSArgs&& ndsargs) noexcept; + bool updateConsole() noexcept; void enableCheats(bool enable); melonDS::ARCodeFile* getCheatFile(); @@ -261,6 +261,8 @@ private: std::string baseROMDir; std::string baseROMName; std::string baseAssetName; + bool changeCart; + std::unique_ptr nextCart; int gbaCartType; std::string baseGBAROMDir; diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 1592c481..a7feffce 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -109,7 +109,7 @@ void EmuThread::run() Config::Table& globalCfg = emuInstance->getGlobalConfig(); u32 mainScreenPos[3]; - //emuInstance->updateConsole(nullptr); + //emuInstance->updateConsole(); // No carts are inserted when melonDS first boots mainScreenPos[0] = 0; diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index f28c0604..9fa95f3e 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -33,9 +33,6 @@ #include "NDSCart.h" #include "GBACart.h" -using Keep = std::monostate; -using UpdateConsoleNDSArgs = std::variant>; -using UpdateConsoleGBAArgs = std::variant>; namespace melonDS { class NDS; From 023dc0c446ef35405e6a7e0b102dd2638f7b9e5b Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 Nov 2024 15:47:55 +0100 Subject: [PATCH 021/106] avoid reopening the microphone if it was already opened --- src/frontend/qt_sdl/EmuInstanceAudio.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp index 3b222ba0..4952d090 100644 --- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp +++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp @@ -159,6 +159,8 @@ void EmuInstance::audioMute() void EmuInstance::micOpen() { + if (micDevice) return; + if (micInputType != micInputType_External) { micDevice = 0; From 584508230f9f3e6cdcb3f7e1d155ce84b476b9ab Mon Sep 17 00:00:00 2001 From: fincs Date: Sun, 17 Nov 2024 15:57:00 +0100 Subject: [PATCH 022/106] Assortment of fixes related to libnds v2/calico (#2197) * Support 8-bit writes to REG_IPCSYNC * Support CP15 Trace Process ID register * NWifi: expose correct manfid information in CIS0/CIS1 area * NWifi: basic support for WMI_SET_PROBED_SSID # Conflicts: # src/DSi_NWifi.cpp * DSi_NAND: fix incorrect CTR IV calculation code --- src/ARM.h | 1 + src/CP15.cpp | 8 ++++++++ src/DSi_NAND.cpp | 26 ++++++++++++-------------- src/DSi_NWifi.cpp | 17 ++++++++++++++--- src/DSi_NWifi.h | 2 ++ src/NDS.cpp | 28 ++++++++++++++++++++++++++++ 6 files changed, 65 insertions(+), 17 deletions(-) diff --git a/src/ARM.h b/src/ARM.h index b652e74d..1403d91a 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -323,6 +323,7 @@ public: u32 CP15Control; u32 RNGSeed; + u32 TraceProcessID; u32 DTCMSetting, ITCMSetting; diff --git a/src/CP15.cpp b/src/CP15.cpp index c271e180..e924bff3 100644 --- a/src/CP15.cpp +++ b/src/CP15.cpp @@ -44,6 +44,7 @@ void ARMv5::CP15Reset() CP15Control = 0x2078; // dunno RNGSeed = 44203; + TraceProcessID = 0; DTCMSetting = 0; ITCMSetting = 0; @@ -643,6 +644,10 @@ void ARMv5::CP15Write(u32 id, u32 val) UpdateITCMSetting(); return; + case 0xD01: + TraceProcessID = val; + return; + case 0xF00: //printf("cache debug index register %08X\n", val); return; @@ -760,6 +765,9 @@ u32 ARMv5::CP15Read(u32 id) const return DTCMSetting; case 0x911: return ITCMSetting; + + case 0xD01: + return TraceProcessID; } if ((id & 0xF00) == 0xF00) // test/debug shit? diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index a6b6c566..13eadd6e 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -189,20 +189,18 @@ void NANDImage::SetupFATCrypto(AES_ctx* ctx, u32 ctr) u8 iv[16]; memcpy(iv, FATIV.data(), sizeof(iv)); - u32 res; - res = iv[15] + (ctr & 0xFF); - iv[15] = (res & 0xFF); - res = iv[14] + ((ctr >> 8) & 0xFF) + (res >> 8); - iv[14] = (res & 0xFF); - res = iv[13] + ((ctr >> 16) & 0xFF) + (res >> 8); - iv[13] = (res & 0xFF); - res = iv[12] + (ctr >> 24) + (res >> 8); - iv[12] = (res & 0xFF); - iv[11] += (res >> 8); - for (int i = 10; i >= 0; i--) - { - if (iv[i+1] == 0) iv[i]++; - else break; + u8 ctr_value[16] = {0}; + ctr_value[15] = ctr & 0xFF; + ctr_value[14] = (ctr >> 8) & 0xFF; + ctr_value[13] = (ctr >> 16) & 0xFF; + ctr_value[12] = (ctr >> 24) & 0xFF; + + unsigned carry = 0; + for (unsigned i = 0; i < 16; i ++) { + unsigned j = 15-i; + unsigned x = iv[j] + ctr_value[j] + carry; + carry = x >= 0x100; + iv[j] = x; } AES_init_ctx_iv(ctx, FATKey.data(), iv); diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 9827bdbe..9cfb0203 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -31,7 +31,7 @@ using Platform::Log; using Platform::LogLevel; -const u8 CIS0[256] = +u8 CIS0[256] = { 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x20, 0x04, 0x71, 0x02, 0x00, 0x02, @@ -70,7 +70,7 @@ const u8 CIS0[256] = 0x00, 0x00, 0x00 }; -const u8 CIS1[256] = +u8 CIS1[256] = { 0x20, 0x04, 0x71, 0x02, 0x00, 0x02, 0x21, 0x02, 0x0C, 0x00, @@ -201,6 +201,9 @@ void DSi_NWifi::Reset() break; } + CIS0[9] = ChipID >= 0x0D000000; + CIS1[4] = CIS0[9]; + memset(EEPROM, 0, 0x400); *(u32*)&EEPROM[0x000] = 0x300; @@ -227,6 +230,8 @@ void DSi_NWifi::Reset() BeaconTimer = 0x10A2220ULL; ConnectionStatus = 0; + SendBSSInfo = true; + DSi.CancelEvent(Event_DSi_NWifi); } @@ -1001,7 +1006,7 @@ void DSi_NWifi::WMI_Command() } // checkme - ScanTimer = scantime*5; + ScanTimer = scantime*8; } break; @@ -1036,6 +1041,7 @@ void DSi_NWifi::WMI_Command() // TODO: store it somewhere 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; @@ -1405,6 +1411,11 @@ void DSi_NWifi::SendWMIAck(u8 ep) 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)) { Log(LogLevel::Error, "NWifi: !! not enough space in RX buffer for WMI BSSINFO event\n"); diff --git a/src/DSi_NWifi.h b/src/DSi_NWifi.h index 84ac8a49..4140820b 100644 --- a/src/DSi_NWifi.h +++ b/src/DSi_NWifi.h @@ -147,6 +147,8 @@ private: u32 ConnectionStatus; u8 LANBuffer[2048]; + + bool SendBSSInfo; }; } diff --git a/src/NDS.cpp b/src/NDS.cpp index bf2e4283..d82da97b 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -2729,6 +2729,9 @@ u8 NDS::ARM9IORead8(u32 addr) case 0x04000132: return KeyCnt[0] & 0xFF; case 0x04000133: return KeyCnt[0] >> 8; + case 0x04000180: return IPCSync9 & 0xFF; + case 0x04000181: return IPCSync9 >> 8; + case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) return NDSCartSlot.GetSPICnt() & 0xFF; @@ -3168,6 +3171,17 @@ void NDS::ARM9IOWrite8(u32 addr, u8 val) KeyCnt[0] = (KeyCnt[0] & 0x00FF) | (val << 8); 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: NDS::ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); return; @@ -3659,6 +3673,9 @@ u8 NDS::ARM7IORead8(u32 addr) case 0x04000138: return RTC.Read() & 0xFF; + case 0x04000180: return IPCSync7 & 0xFF; + case 0x04000181: return IPCSync7 >> 8; + case 0x040001A0: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot.GetSPICnt() & 0xFF; @@ -3967,6 +3984,17 @@ void NDS::ARM7IOWrite8(u32 addr, u8 val) 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: NDS::ARM7IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); return; From 5f8255bc90db5068f247d8e83c80be3769710ef0 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 Nov 2024 18:17:43 +0100 Subject: [PATCH 023/106] allow DSi mode to run with internal DS BIOS --- src/frontend/qt_sdl/EmuInstance.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 4c0d3798..563fa135 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -859,7 +859,7 @@ std::unique_ptr EmuInstance::loadARM9BIOS() noexcept { if (!globalCfg.GetBool("Emu.ExternalBIOSEnable")) { - return globalCfg.GetInt("Emu.ConsoleType") == 0 ? std::make_unique(bios_arm9_bin) : nullptr; + return std::make_unique(bios_arm9_bin); } string path = globalCfg.GetString("DS.BIOS9Path"); @@ -882,7 +882,7 @@ std::unique_ptr EmuInstance::loadARM7BIOS() noexcept { if (!globalCfg.GetBool("Emu.ExternalBIOSEnable")) { - return globalCfg.GetInt("Emu.ConsoleType") == 0 ? std::make_unique(bios_arm7_bin) : nullptr; + return std::make_unique(bios_arm7_bin); } string path = globalCfg.GetString("DS.BIOS7Path"); @@ -1006,11 +1006,12 @@ std::optional EmuInstance::loadFirmware(int type) noexcept { // If we're using built-in firmware... if (type == 1) { - Log(Error, "DSi firmware: cannot use built-in firmware in DSi mode!\n"); - return std::nullopt; + // TODO: support generating a firmware for DSi mode + } + else + { + return generateFirmware(type); } - - return generateFirmware(type); } //const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath; string firmwarepath; @@ -1458,16 +1459,16 @@ void EmuInstance::reset() bool EmuInstance::bootToMenu() -{ +{printf("bootToMenu 1\n"); // Keep whatever cart is in the console, if any. if (!updateConsole()) // Try to update the console, but keep the existing cart. If that fails... return false; - + printf("bootToMenu 2\n"); // BIOS and firmware files are loaded, patched, and installed in UpdateConsole if (nds->NeedsDirectBoot()) return false; - + printf("bootToMenu 3\n"); initFirmwareSaveManager(); nds->Reset(); setBatteryLevels(); From 5e3d2d07c3769965dbda0e3e73797b7f454ee4c4 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 Nov 2024 19:04:13 +0100 Subject: [PATCH 024/106] fix Key1 code to source the DS-mode key data from the ARM9i BIOS, so it works even if no DS BIOSes are provided (had to rework the loading code to make it work -- if carts are passed to the DSi constructor, they get initialized before the DSi stuff is initialized, and can't read the DSi BIOSes) --- src/NDSCart.cpp | 61 ++++++++++++++++++----------- src/NDSCart.h | 4 +- src/frontend/qt_sdl/EmuInstance.cpp | 18 ++++++--- 3 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index b0eef56a..97de4580 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -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; - if (biosLength != expected_bios_length) + // DSi mode: grab the right key depending on the requested cart mode + + auto& dsi = static_cast(NDS); + if (dsimode) { - Platform::Log(LogLevel::Error, "NDSCart: Expected an ARM7 BIOS of %u bytes, got %u bytes\n", expected_bios_length, biosLength); - } - else if (bios == nullptr) - { - Platform::Log(LogLevel::Error, "NDSCart: Expected an ARM7 BIOS of %u bytes, got nullptr\n", expected_bios_length); + // load from ARM7 BIOS at 0xC6D0 + + const u8* bios = dsi.ARM7iBIOS.data(); + memcpy(Key1_KeyBuf.data(), bios + 0xC6D0, sizeof(Key1_KeyBuf)); + Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from ARM7i BIOS\n"); } else { - memcpy(Key1_KeyBuf.data(), bios + (dsi ? 0xC6D0 : 0x0030), sizeof(Key1_KeyBuf)); - Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from memory\n"); + // load from ARM9 BIOS at 0x99A0 + + 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 { - // well - memset(Key1_KeyBuf.data(), 0, sizeof(Key1_KeyBuf)); - Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf to zero\n"); + // DS mode: load from ARM7 BIOS at 0x0030 + + 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}; if (level >= 1) Key1_ApplyKeycode(keycode, mod); @@ -262,16 +278,15 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, const u8* cmd, case 0x3C: 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; return 0; case 0x3D: if (IsDSi) { - auto& dsi = static_cast(nds); 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; } return 0; @@ -1552,10 +1567,10 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept 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_InitKeycode(false, gamecode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); + Key1_InitKeycode(false, gamecode, 3, 2); for (u32 i = 0; i < 0x800; i += 8) Key1_Decrypt((u32*)&out[i]); @@ -1714,11 +1729,11 @@ void NDSCartSlot::SetCart(std::unique_ptr&& cart) noexcept 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) 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]); Log(LogLevel::Debug, "Re-encrypted cart secure area\n"); diff --git a/src/NDSCart.h b/src/NDSCart.h index 3c4bb4f5..3704f659 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -445,8 +445,8 @@ private: void Key1_Encrypt(u32* data) const noexcept; void Key1_Decrypt(u32* data) const noexcept; void Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept; - void Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept; - void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, const u8 *bios, u32 biosLength) noexcept; + void Key1_LoadKeyBuf(bool dsi) noexcept; + void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) noexcept; void Key2_Encrypt(const u8* data, u32 len) noexcept; void ROMEndTransfer(u32 param) noexcept; void ROMPrepareData(u32 param) noexcept; diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 563fa135..7760ba68 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1296,8 +1296,10 @@ bool EmuInstance::updateConsole() noexcept #endif NDSArgs ndsargs { - std::move(nextndscart), - std::move(nextgbacart), + //std::move(nextndscart), + //std::move(nextgbacart), + nullptr, + nullptr, std::move(arm9bios), std::move(arm7bios), std::move(*firmware), @@ -1365,8 +1367,6 @@ bool EmuInstance::updateConsole() noexcept nds->SetARM7BIOS(*args->ARM7BIOS); nds->SetARM9BIOS(*args->ARM9BIOS); nds->SetFirmware(std::move(args->Firmware)); - nds->SetNDSCart(std::move(args->NDSROM)); - nds->SetGBACart(std::move(args->GBAROM)); nds->SetJITArgs(args->JIT); // TODO GDB stub shit nds->SPU.SetInterpolation(args->Interpolation); @@ -1384,10 +1384,16 @@ bool EmuInstance::updateConsole() noexcept dsi->SetSDCard(std::move(_dsiargs.DSiSDCard)); // We're moving the optional, not the 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(); loadCheats(); From 99aa5676dba0b573fb82008ae079dca6e34f7741 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 Nov 2024 19:38:36 +0100 Subject: [PATCH 025/106] actually remove NDS/GBA ROM args from NDSArgs, since we won't be using them --- src/Args.h | 12 ------------ src/DSi.cpp | 2 -- src/NDS.cpp | 6 ++---- src/frontend/qt_sdl/EmuInstance.cpp | 6 ------ 4 files changed, 2 insertions(+), 24 deletions(-) diff --git a/src/Args.h b/src/Args.h index 2c405e29..4f4fa1f0 100644 --- a/src/Args.h +++ b/src/Args.h @@ -85,18 +85,6 @@ struct GDBArgs /// New fields here should have default values if possible. 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 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 GBAROM = nullptr; - /// NDS ARM9 BIOS to install. /// Defaults to FreeBIOS, which is not compatible with DSi mode. std::unique_ptr ARM9BIOS = std::make_unique(bios_arm9_bin); diff --git a/src/DSi.cpp b/src/DSi.cpp index 00ed8da0..f9115cbf 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -74,8 +74,6 @@ const u32 NDMAModes[] = DSi( DSiArgs { NDSArgs { - nullptr, - nullptr, bios_arm9_bin, bios_arm7_bin, Firmware(0), diff --git a/src/NDS.cpp b/src/NDS.cpp index d82da97b..e553dd5d 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -79,8 +79,6 @@ NDS* NDS::Current = nullptr; NDS::NDS() noexcept : NDS( NDSArgs { - nullptr, - nullptr, std::make_unique(bios_arm9_bin), std::make_unique(bios_arm7_bin), Firmware(0), @@ -102,8 +100,8 @@ NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept : SPI(*this, std::move(args.Firmware)), RTC(*this), Wifi(*this), - NDSCartSlot(*this, std::move(args.NDSROM)), - GBACartSlot(*this, type == 1 ? nullptr : std::move(args.GBAROM)), + NDSCartSlot(*this, nullptr), + GBACartSlot(*this, nullptr), AREngine(*this), ARM9(*this, args.GDB, args.JIT.has_value()), ARM7(*this, args.GDB, args.JIT.has_value()), diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 7760ba68..ce9912e1 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1296,10 +1296,6 @@ bool EmuInstance::updateConsole() noexcept #endif NDSArgs ndsargs { - //std::move(nextndscart), - //std::move(nextgbacart), - nullptr, - nullptr, std::move(arm9bios), std::move(arm7bios), std::move(*firmware), @@ -1313,8 +1309,6 @@ bool EmuInstance::updateConsole() noexcept std::optional dsiargs = std::nullopt; if (consoleType == 1) { - ndsargs.GBAROM = nullptr; - auto arm7ibios = loadDSiARM7BIOS(); if (!arm7ibios) return false; From 0ea0af3abf69649fa02888f03a019fde00674211 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 Nov 2024 20:00:52 +0100 Subject: [PATCH 026/106] make it possible to change gdb stub settings without destroying/recreating a NDS --- src/ARM.cpp | 28 +++++++++++++++++----------- src/ARM.h | 2 ++ src/NDS.cpp | 9 +++++++++ src/NDS.h | 6 ++++++ src/debug/GdbStub.cpp | 7 ++++--- src/debug/GdbStub.h | 4 ++-- src/frontend/qt_sdl/EmuInstance.cpp | 2 +- 7 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/ARM.cpp b/src/ARM.cpp index b7b703da..0d93dc45 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -109,21 +109,13 @@ const u32 ARM::ConditionTable[16] = ARM::ARM(u32 num, bool jit, std::optional gdb, melonDS::NDS& nds) : #ifdef GDBSTUB_ENABLED - GdbStub(this, gdb ? (num ? gdb->PortARM7 : gdb->PortARM9) : 0), - BreakOnStartup(gdb ? (num ? gdb->ARM7BreakOnStartup : gdb->ARM9BreakOnStartup) : false), + GdbStub(this), + BreakOnStartup(false), #endif Num(num), // well uh NDS(nds) { -#ifdef GDBSTUB_ENABLED - if (gdb -#ifdef JIT_ENABLED - && !jit // TODO: Should we support toggling the GdbStub without destroying the ARM? -#endif - ) - GdbStub.Init(); - IsSingleStep = false; -#endif + SetGdbArgs(jit ? gdb : std::nullopt); } ARM::~ARM() @@ -148,6 +140,20 @@ ARMv5::~ARMv5() // DTCM is owned by Memory, not going to delete it } +void ARM::SetGdbArgs(std::optional 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() { Cycles = 0; diff --git a/src/ARM.h b/src/ARM.h index 1403d91a..f18d7650 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -68,6 +68,8 @@ public: ARM(u32 num, bool jit, std::optional gdb, NDS& nds); virtual ~ARM(); // destroy shit + void SetGdbArgs(std::optional gdb); + virtual void Reset(); virtual void DoSavestate(Savestate* file); diff --git a/src/NDS.cpp b/src/NDS.cpp index e553dd5d..d8e1d216 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -227,6 +227,15 @@ void NDS::SetJITArgs(std::optional args) noexcept } #endif +#ifdef GDBSTUB_ENABLED +void NDS::SetGdbArgs(std::optional args) noexcept +{ + ARM9.SetGdbArgs(args); + ARM7.SetGdbArgs(args); + EnableGDBStub = args.has_value(); +} +#endif + void NDS::InitTimings() { // TODO, eventually: diff --git a/src/NDS.h b/src/NDS.h index 5e474807..da68799f 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -476,6 +476,12 @@ public: // TODO: Encapsulate the rest of these members void SetJITArgs(std::optional args) noexcept {} #endif +#ifdef GDBSTUB_ENABLED + void SetGdbArgs(std::optional args) noexcept; +#else + void SetGdbArgs(std::optional args) noexcept {} +#endif + private: void InitTimings(); u32 SchedListMask; diff --git a/src/debug/GdbStub.cpp b/src/debug/GdbStub.cpp index b055794a..7ce7693b 100644 --- a/src/debug/GdbStub.cpp +++ b/src/debug/GdbStub.cpp @@ -51,16 +51,17 @@ static int SocketSetBlocking(int fd, bool block) namespace Gdb { -GdbStub::GdbStub(StubCallbacks* cb, int port) - : Cb(cb), Port(port) +GdbStub::GdbStub(StubCallbacks* cb) + : Cb(cb), Port(0) , SockFd(0), ConnFd(0) , Stat(TgtStatus::None), CurBkpt(0), CurWatchpt(0), StatFlag(false), NoAck(false) , ServerSA((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", Cb->GetCPU(), Port); diff --git a/src/debug/GdbStub.h b/src/debug/GdbStub.h index 3e4a0efe..85934327 100644 --- a/src/debug/GdbStub.h +++ b/src/debug/GdbStub.h @@ -115,10 +115,10 @@ public: int kind; }; - GdbStub(StubCallbacks* cb, int port); + GdbStub(StubCallbacks* cb); ~GdbStub(); - bool Init(); + bool Init(int port); void Close(); StubState Poll(bool wait = false); diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index ce9912e1..1af8a887 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1362,7 +1362,7 @@ bool EmuInstance::updateConsole() noexcept nds->SetARM9BIOS(*args->ARM9BIOS); nds->SetFirmware(std::move(args->Firmware)); nds->SetJITArgs(args->JIT); - // TODO GDB stub shit + nds->SetGdbArgs(args->GDB); nds->SPU.SetInterpolation(args->Interpolation); nds->SPU.SetDegrade10Bit(args->BitDepth); From c01b2bf7a04a1ab153207b5d1b87ab5f6cb4e020 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Mon, 18 Nov 2024 18:58:14 +0100 Subject: [PATCH 027/106] prevent out of bounds access for microphone data --- src/frontend/qt_sdl/EmuInstanceAudio.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp index 4952d090..a4ac9394 100644 --- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp +++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp @@ -330,7 +330,11 @@ void EmuInstance::micProcess() micBufferReadPos += len; } - if (len < kFrameLen) + if (len == 0) + { + memset(tmp, 0, sizeof(tmp)); + } + else if (len < kFrameLen) { for (int i = len; i < kFrameLen; i++) tmp[i] = tmp[len-1]; From cb7af652f583d54aad07253a0ff78e478c641f1e Mon Sep 17 00:00:00 2001 From: RSDuck Date: Mon, 18 Nov 2024 20:08:49 +0100 Subject: [PATCH 028/106] aarch64 lto broken GPU2D workaround --- src/GPU2D_Soft.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/GPU2D_Soft.cpp b/src/GPU2D_Soft.cpp index 6ad2cd3e..01dade2b 100644 --- a/src/GPU2D_Soft.cpp +++ b/src/GPU2D_Soft.cpp @@ -914,6 +914,9 @@ void SoftRenderer::DrawBG_3D() template 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]; u32 tilesetaddr, tilemapaddr; From 99ce959913bd678781d066dc78a9e30e1daf5f05 Mon Sep 17 00:00:00 2001 From: Kemal Afzal Date: Mon, 18 Nov 2024 20:43:05 +0100 Subject: [PATCH 029/106] Multiinstance jit (#2201) * works on Linux x64 still needs to be fixed for everything else * use lots of PROT_NONE memory to reliably reserve virtual address space * multi instance fastmem on Linux * Windows * blarg * disable fastmem if the page size is not 4kb * fix fast mem dialog option * make aarch64 work as well * fastmem 16kb pages support --- src/ARMJIT.cpp | 40 ++- src/ARMJIT.h | 11 +- src/ARMJIT_A64/ARMJIT_Compiler.cpp | 45 +-- src/ARMJIT_A64/ARMJIT_Compiler.h | 1 + src/ARMJIT_Global.cpp | 118 +++++++ src/ARMJIT_Global.h | 44 +++ src/ARMJIT_Internal.h | 4 +- src/ARMJIT_Memory.cpp | 361 ++++++++++++++++------ src/ARMJIT_Memory.h | 44 ++- src/ARMJIT_x64/ARMJIT_Branch.cpp | 4 +- src/ARMJIT_x64/ARMJIT_Compiler.cpp | 54 +--- src/ARMJIT_x64/ARMJIT_Compiler.h | 2 + src/ARMJIT_x64/ARMJIT_LoadStore.cpp | 64 ++-- src/CMakeLists.txt | 5 + src/NDS.cpp | 5 +- src/NDS.h | 4 +- src/dolphin/x64Emitter.h | 22 ++ src/frontend/qt_sdl/EmuInstance.cpp | 3 - src/frontend/qt_sdl/EmuSettingsDialog.cpp | 7 +- 19 files changed, 573 insertions(+), 265 deletions(-) create mode 100644 src/ARMJIT_Global.cpp create mode 100644 src/ARMJIT_Global.h diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp index 1ebcce8e..9582f7c8 100644 --- a/src/ARMJIT.cpp +++ b/src/ARMJIT.cpp @@ -30,6 +30,7 @@ #include "ARMJIT_Internal.h" #include "ARMJIT_Memory.h" #include "ARMJIT_Compiler.h" +#include "ARMJIT_Global.h" #include "ARMInterpreter_ALU.h" #include "ARMInterpreter_LoadStore.h" @@ -467,6 +468,16 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] = }; #undef F +ARMJIT::ARMJIT(melonDS::NDS& nds, std::optional 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 { auto it = RestoreCandidates.find(block->InstrHash); @@ -483,6 +494,7 @@ void ARMJIT::RetireJitBlock(JitBlock* block) noexcept void ARMJIT::SetJITArgs(JITArgs args) noexcept { + args.FastMemory = args.FastMemory && ARMJIT_Memory::IsFastMemSupported(); args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u); if (MaxBlockSize != args.MaxBlockSize @@ -499,36 +511,22 @@ void ARMJIT::SetJITArgs(JITArgs args) noexcept void ARMJIT::SetMaxBlockSize(int size) noexcept { - size = std::clamp(size, 1, 32); - - if (size != MaxBlockSize) - ResetBlockCache(); - - MaxBlockSize = size; + SetJITArgs(JITArgs{static_cast(size), LiteralOptimizations, LiteralOptimizations, FastMemory}); } void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept { - if (LiteralOptimizations != enabled) - ResetBlockCache(); - - LiteralOptimizations = enabled; + SetJITArgs(JITArgs{static_cast(MaxBlockSize), enabled, BranchOptimizations, FastMemory}); } void ARMJIT::SetBranchOptimizations(bool enabled) noexcept { - if (BranchOptimizations != enabled) - ResetBlockCache(); - - BranchOptimizations = enabled; + SetJITArgs(JITArgs{static_cast(MaxBlockSize), LiteralOptimizations, enabled, FastMemory}); } void ARMJIT::SetFastMemory(bool enabled) noexcept { - if (FastMemory != enabled) - ResetBlockCache(); - - FastMemory = enabled; + SetJITArgs(JITArgs{static_cast(MaxBlockSize), LiteralOptimizations, BranchOptimizations, enabled}); } void ARMJIT::CompileBlock(ARM* cpu) noexcept @@ -918,7 +916,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept AddressRange* region = CodeMemRegions[addressRanges[j] >> 27]; - if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000) / 512])) + if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize)) Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true); AddressRange* range = ®ion[(addressRanges[j] & 0x7FFFFFF) / 512]; @@ -971,7 +969,7 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept range->Blocks.Remove(i); if (range->Blocks.Length == 0 - && !PageContainsCode(®ion[(localAddr & 0x7FFF000) / 512])) + && !PageContainsCode(®ion[(localAddr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize)) { Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false); } @@ -1005,7 +1003,7 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept 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); otherRange->Code = 0; diff --git a/src/ARMJIT.h b/src/ARMJIT.h index a228a4dd..309aa8e8 100644 --- a/src/ARMJIT.h +++ b/src/ARMJIT.h @@ -44,15 +44,7 @@ class JitBlock; class ARMJIT { public: - ARMJIT(melonDS::NDS& nds, std::optional 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(melonDS::NDS& nds, std::optional jit) noexcept; ~ARMJIT() noexcept; void InvalidateByAddr(u32) noexcept; void CheckAndInvalidateWVRAM(int) noexcept; @@ -80,6 +72,7 @@ private: bool LiteralOptimizations = false; bool BranchOptimizations = false; bool FastMemory = false; + public: melonDS::NDS& NDS; TinyVector InvalidLiterals {}; diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.cpp b/src/ARMJIT_A64/ARMJIT_Compiler.cpp index f05de448..2cb0bf3b 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_A64/ARMJIT_Compiler.cpp @@ -22,17 +22,7 @@ #include "../ARMInterpreter.h" #include "../ARMJIT.h" #include "../NDS.h" - -#if defined(__SWITCH__) -#include - -extern char __start__; -#elif defined(_WIN32) -#include -#else -#include -#include -#endif +#include "../ARMJIT_Global.h" #include @@ -66,11 +56,6 @@ const int RegisterCache::NativeRegsAvailable = 15; 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() { ADD(MapReg(15), MapReg(15), Thumb ? 2 : 4); @@ -260,29 +245,12 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds) SetCodeBase((u8*)JitRWStart, (u8*)JitRXStart); JitMemMainSize = JitMemSize; #else - #ifdef _WIN32 - SYSTEM_INFO sysInfo; - GetSystemInfo(&sysInfo); + ARMJIT_Global::Init(); - u64 pageSize = (u64)sysInfo.dwPageSize; - #else - 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; + CodeMemBase = ARMJIT_Global::AllocateCodeMem(); - #if defined(_WIN32) - DWORD dummy; - 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; + SetCodeBase(reinterpret_cast(CodeMemBase), reinterpret_cast(CodeMemBase)); + JitMemMainSize = ARMJIT_Global::CodeMemorySliceSize; #endif SetCodePtr(0); @@ -493,6 +461,9 @@ Compiler::~Compiler() free(JitRWBase); } #endif + + ARMJIT_Global::FreeCodeMem(CodeMemBase); + ARMJIT_Global::DeInit(); } void Compiler::LoadCycles() diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h index a7b567f6..44886b13 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.h +++ b/src/ARMJIT_A64/ARMJIT_Compiler.h @@ -275,6 +275,7 @@ public: void* JitRWStart; void* JitRXStart; #endif + void* CodeMemBase; void* ReadBanked, *WriteBanked; diff --git a/src/ARMJIT_Global.cpp b/src/ARMJIT_Global.cpp new file mode 100644 index 00000000..b6510379 --- /dev/null +++ b/src/ARMJIT_Global.cpp @@ -0,0 +1,118 @@ +#include "ARMJIT_Global.h" +#include "ARMJIT_Memory.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include +#include + +#include + +namespace melonDS +{ + +namespace ARMJIT_Global +{ + +std::mutex globalMutex; + +#ifndef __APPLE__ +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((reinterpret_cast(CodeMemory) + (16*1024-1)) & ~static_cast(16*1024-1)); +} +#endif + +int RefCounter = 0; + +void* AllocateCodeMem() +{ + std::lock_guard guard(globalMutex); + +#ifndef __APPLE__ + 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); +#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); + + for (int i = 0; i < NumCodeMemSlices; i++) + { + if (codeMem == &GetAlignedCodeMemoryStart()[CodeMemorySliceSize * i]) + { + //printf("freeing slice\n"); + AvailableCodeMemSlices |= 1 << i; + return; + } + } + +#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__) + // Apple 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(); + } +} + +} + +} diff --git a/src/ARMJIT_Global.h b/src/ARMJIT_Global.h new file mode 100644 index 00000000..299d71a6 --- /dev/null +++ b/src/ARMJIT_Global.h @@ -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 + +namespace melonDS +{ + +namespace ARMJIT_Global +{ + +static constexpr size_t CodeMemorySliceSize = 1024*1024*32; + +void Init(); +void DeInit(); + +void* AllocateCodeMem(); +void FreeCodeMem(void* codeMem); + +} + +} + +#endif \ No newline at end of file diff --git a/src/ARMJIT_Internal.h b/src/ARMJIT_Internal.h index 5b393903..12591d30 100644 --- a/src/ARMJIT_Internal.h +++ b/src/ARMJIT_Internal.h @@ -85,9 +85,9 @@ typedef void (*InterpreterFunc)(ARM* cpu); extern InterpreterFunc InterpretARM[]; 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) return true; diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index 51e022d1..ae8391bb 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -39,6 +39,7 @@ #include "ARMJIT_Internal.h" #include "ARMJIT_Compiler.h" +#include "ARMJIT_Global.h" #include "DSi.h" #include "GPU.h" @@ -100,6 +101,9 @@ namespace melonDS { +static constexpr u64 AddrSpaceSize = 0x100000000; +static constexpr u64 VirtmemAreaSize = AddrSpaceSize * 2 + MemoryTotalSize; + using Platform::Log; using Platform::LogLevel; @@ -152,6 +156,15 @@ void __libnx_exception_handler(ThreadExceptionDump* ctx) #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) { if (exceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) @@ -170,6 +183,7 @@ LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) return EXCEPTION_CONTINUE_EXECUTION; } + Log(LogLevel::Debug, "it all returns to nothing\n"); return EXCEPTION_CONTINUE_SEARCH; } @@ -261,18 +275,61 @@ enum memstate_MappedProtected, }; - +#define CHECK_ALIGNED(value) assert(((value) & (PageSize-1)) == 0) 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; #ifdef __SWITCH__ Result r = (svcMapProcessMemory(dst, envGetOwnProcessHandle(), (u64)(MemoryBaseCodeMem + offset), size)); return R_SUCCEEDED(r); #elif defined(_WIN32) - bool r = MapViewOfFileEx(MemoryFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, offset, size, dst) == dst; - return r; + uintptr_t uintptrDst = reinterpret_cast(dst); + 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 return mmap(dst, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, offset) != MAP_FAILED; #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 { + CHECK_ALIGNED(addr); + CHECK_ALIGNED(offset); + CHECK_ALIGNED(size); + u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr; #ifdef __SWITCH__ Result r = svcUnmapProcessMemory(dst, envGetOwnProcessHandle(), (u64)(MemoryBaseCodeMem + offset), size); return R_SUCCEEDED(r); #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(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(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 - return munmap(dst, size) == 0; + return mmap(dst, size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0) != MAP_FAILED; #endif } #ifndef __SWITCH__ 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; #if defined(_WIN32) DWORD winProtection, oldProtection; @@ -305,6 +409,10 @@ void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int prot else winProtection = PAGE_READWRITE; bool success = VirtualProtect(dst, size, winProtection, &oldProtection); + if (!success) + { + Log(LogLevel::Debug, "VirtualProtect failed with %x\n", GetLastError()); + } assert(success); #else int posixProt; @@ -335,14 +443,14 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept else { u32 segmentOffset = offset; - u8 status = statuses[(Addr + offset) >> 12]; - while (statuses[(Addr + offset) >> 12] == status + u8 status = statuses[(Addr + offset) >> PageShift]; + while (statuses[(Addr + offset) >> PageShift] == status && offset < Size && (!skipDTCM || Addr + offset != dtcmStart)) { - assert(statuses[(Addr + offset) >> 12] != memstate_Unmapped); - statuses[(Addr + offset) >> 12] = memstate_Unmapped; - offset += 0x1000; + assert(statuses[(Addr + offset) >> PageShift] != memstate_Unmapped); + statuses[(Addr + offset) >> PageShift] = memstate_Unmapped; + offset += PageSize; } #ifdef __SWITCH__ @@ -358,7 +466,6 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept } #ifndef __SWITCH__ -#ifndef _WIN32 u32 dtcmEnd = dtcmStart + dtcmSize; if (Num == 0 && dtcmEnd >= Addr @@ -378,7 +485,6 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept } } else -#endif { bool succeded = nds.JIT.Memory.UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size); 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 { - offset &= ~0xFFF; + offset &= ~(PageSize - 1); //printf("set code protection %d %x %d\n", region, offset, protect); 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); - //printf("%x %d %x %x %x %d\n", effectiveAddr, mapping.Num, mapping.Addr, mapping.LocalOffset, mapping.Size, states[effectiveAddr >> 12]); - assert(states[effectiveAddr >> 12] == (protect ? memstate_MappedRW : memstate_MappedProtected)); - states[effectiveAddr >> 12] = protect ? memstate_MappedProtected : memstate_MappedRW; + //printf("%x %d %x %x %x %d\n", effectiveAddr, mapping.Num, mapping.Addr, mapping.LocalOffset, mapping.Size, states[effectiveAddr >> PageShift]); + assert(states[effectiveAddr >> PageShift] == (protect ? memstate_MappedRW : memstate_MappedProtected)); + states[effectiveAddr >> PageShift] = protect ? memstate_MappedProtected : memstate_MappedRW; #if defined(__SWITCH__) 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); assert(success); #else - SetCodeProtectionRange(effectiveAddr, 0x1000, mapping.Num, protect ? 1 : 2); + SetCodeProtectionRange(effectiveAddr, PageSize, mapping.Num, protect ? 1 : 2); #endif } } @@ -543,11 +649,19 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept u32 dtcmSize = ~NDS.ARM9.DTCMMask + 1; u32 dtcmEnd = dtcmStart + dtcmSize; #ifndef __SWITCH__ -#ifndef _WIN32 if (num == 0 && dtcmEnd >= mirrorStart && 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; if (dtcmStart > mirrorStart) { @@ -562,7 +676,6 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept } } else -#endif { bool succeded = MapIntoRange(mirrorStart, num, OffsetsPerRegion[region] + memoryOffset, mirrorSize); assert(succeded); @@ -579,22 +692,19 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept { if (skipDTCM && mirrorStart + offset == dtcmStart) { -#ifdef _WIN32 - SetCodeProtectionRange(dtcmStart, dtcmSize, 0, 0); -#endif offset += dtcmSize; } else { u32 sectionOffset = offset; - bool hasCode = isExecutable && PageContainsCode(&range[offset / 512]); + bool hasCode = isExecutable && PageContainsCode(&range[offset / 512], PageSize); while (offset < mirrorSize - && (!isExecutable || PageContainsCode(&range[offset / 512]) == hasCode) + && (!isExecutable || PageContainsCode(&range[offset / 512], PageSize) == hasCode) && (!skipDTCM || mirrorStart + offset != NDS.ARM9.DTCMBase)) { - assert(states[(mirrorStart + offset) >> 12] == memstate_Unmapped); - states[(mirrorStart + offset) >> 12] = hasCode ? memstate_MappedProtected : memstate_MappedRW; - offset += 0x1000; + assert(states[(mirrorStart + offset) >> PageShift] == memstate_Unmapped); + states[(mirrorStart + offset) >> PageShift] = hasCode ? memstate_MappedProtected : memstate_MappedRW; + offset += PageSize; } u32 sectionSize = offset - sectionOffset; @@ -624,6 +734,86 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept 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 = PageShift == 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(GetProcAddress(KernelBaseDll, "VirtualAlloc2")); + mapViewOfFile3Ptr = reinterpret_cast(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) { 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; - if (memStatus[faultDesc.EmulatedFaultAddr >> 12] == memstate_Unmapped) + if (memStatus[faultDesc.EmulatedFaultAddr >> PageShift] == memstate_Unmapped) rewriteToSlowPath = !nds.JIT.Memory.MapAtAddress(faultDesc.EmulatedFaultAddr); if (rewriteToSlowPath) @@ -643,10 +833,9 @@ bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds) return false; } -const u64 AddrSpaceSize = 0x100000000; - ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds) { + ARMJIT_Global::Init(); #if defined(__SWITCH__) MemoryBase = (u8*)aligned_alloc(0x1000, MemoryTotalSize); virtmemLock(); @@ -671,33 +860,27 @@ ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds) u8* basePtr = MemoryBaseCodeMem; #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(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); - VirtualFree(MemoryBase, 0, MEM_RELEASE); - // this is incredible hacky - // but someone else is trying to go into our address space! - // Windows will very likely give them virtual memory starting at the same address - // as it is giving us now. - // 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); + VirtmemPlaceholders.push_back({reinterpret_cast(MemoryBase)+MemoryTotalSize, AddrSpaceSize*2}); + } + else + { + // old Windows version + MemoryBase = new u8[MemoryTotalSize]; + } #else - // this used to be allocated with three different mmaps - // 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; + MemoryBase = (u8*)mmap(nullptr, VirtmemAreaSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); #if defined(__ANDROID__) 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)); } - 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); - - u8* basePtr = MemoryBase; #endif + FastMem9Start = MemoryBase+MemoryTotalSize; + FastMem7Start = static_cast(FastMem9Start)+AddrSpaceSize; } ARMJIT_Memory::~ARMJIT_Memory() noexcept @@ -764,34 +937,37 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept free(MemoryBase); MemoryBase = nullptr; #elif defined(_WIN32) - if (MemoryBase) + if (virtualAlloc2Ptr) { - bool viewUnmapped = UnmapViewOfFile(MemoryBase); - assert(viewUnmapped); - MemoryBase = nullptr; - FastMem9Start = nullptr; - FastMem7Start = nullptr; - } + if (MemoryBase) + { + bool viewUnmapped = UnmapViewOfFileEx(MemoryBase, MEM_PRESERVE_PLACEHOLDER); + assert(viewUnmapped); + bool viewCoalesced = VirtualFree(MemoryBase, VirtmemAreaSize, MEM_RELEASE|MEM_COALESCE_PLACEHOLDERS); + assert(viewCoalesced); + bool freeEverything = VirtualFree(MemoryBase, 0, MEM_RELEASE); + assert(freeEverything); - if (MemoryFile) - { - CloseHandle(MemoryFile); - MemoryFile = INVALID_HANDLE_VALUE; - } + MemoryBase = nullptr; + FastMem9Start = nullptr; + FastMem7Start = nullptr; + printf("unmappinged everything\n"); + } - if (ExceptionHandlerHandle) + if (MemoryFile) + { + CloseHandle(MemoryFile); + MemoryFile = INVALID_HANDLE_VALUE; + } + } + else { - RemoveVectoredExceptionHandler(ExceptionHandlerHandle); - ExceptionHandlerHandle = nullptr; + delete[] MemoryBase; } #else - sigaction(SIGSEGV, &OldSaSegv, nullptr); -#ifdef __APPLE__ - sigaction(SIGBUS, &OldSaBus, nullptr); -#endif if (MemoryBase) { - munmap(MemoryBase, MemoryTotalSize); + munmap(MemoryBase, VirtmemAreaSize); MemoryBase = nullptr; FastMem9Start = nullptr; FastMem7Start = nullptr; @@ -812,6 +988,8 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept #endif #endif + + ARMJIT_Global::DeInit(); } void ARMJIT_Memory::Reset() noexcept @@ -834,17 +1012,6 @@ void ARMJIT_Memory::Reset() 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; } diff --git a/src/ARMJIT_Memory.h b/src/ARMJIT_Memory.h index 88e647d5..cac9dc62 100644 --- a/src/ARMJIT_Memory.h +++ b/src/ARMJIT_Memory.h @@ -23,6 +23,7 @@ #include "MemConstants.h" #ifdef JIT_ENABLED +# include # include "TinyVector.h" # include "ARM.h" # if defined(__SWITCH__) @@ -48,23 +49,22 @@ class Compiler; class ARMJIT; #endif +static constexpr u32 LargePageSize = 0x4000; +static constexpr u32 RegularPageSize = 0x1000; + constexpr u32 RoundUp(u32 size) noexcept { -#ifdef _WIN32 - return (size + 0xFFFF) & ~0xFFFF; -#else - return size; -#endif + return (size + LargePageSize - 1) & ~(LargePageSize - 1); } -const u32 MemBlockMainRAMOffset = 0; -const u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize); -const u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize); -const u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize); -const u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize); -const u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize); -const u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize); -const u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize); +static constexpr u32 MemBlockMainRAMOffset = 0; +static constexpr u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize); +static constexpr u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize); +static constexpr u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize); +static constexpr u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize); +static constexpr u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize); +static constexpr u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize); +static constexpr u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize); class ARMJIT_Memory { @@ -137,6 +137,14 @@ public: bool IsFastmemCompatible(int region) const noexcept; void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept; bool MapAtAddress(u32 addr) noexcept; + + static bool IsFastMemSupported(); + + static void RegisterFaultHandler(); + static void UnregisterFaultHandler(); + + static u32 PageSize; + static u32 PageShift; private: friend class Compiler; struct Mapping @@ -162,14 +170,22 @@ private: void* FastMem9Start; void* FastMem7Start; u8* MemoryBase = nullptr; + #if defined(__SWITCH__) VirtmemReservation* FastMem9Reservation, *FastMem7Reservation; u8* MemoryBaseCodeMem; #elif defined(_WIN32) + struct VirtmemPlaceholder + { + uintptr_t Start; + size_t Size; + }; + std::vector VirtmemPlaceholders; + static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo); HANDLE MemoryFile = INVALID_HANDLE_VALUE; - LPVOID ExceptionHandlerHandle = nullptr; #else + static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext); int MemoryFile = -1; #endif diff --git a/src/ARMJIT_x64/ARMJIT_Branch.cpp b/src/ARMJIT_x64/ARMJIT_Branch.cpp index c32e2b73..bd73ae71 100644 --- a/src/ARMJIT_x64/ARMJIT_Branch.cpp +++ b/src/ARMJIT_x64/ARMJIT_Branch.cpp @@ -176,9 +176,9 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR) else MOV(32, R(ABI_PARAM3), Imm32(true)); // what a waste if (Num == 0) - CALL((void*)&ARMv5JumpToTrampoline); + ABI_CallFunction(ARMv5JumpToTrampoline); else - CALL((void*)&ARMv4JumpToTrampoline); + ABI_CallFunction(ARMv4JumpToTrampoline); PopRegs(restoreCPSR, true); diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.cpp b/src/ARMJIT_x64/ARMJIT_Compiler.cpp index ba6c0fb4..6de4caf6 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_x64/ARMJIT_Compiler.cpp @@ -21,19 +21,13 @@ #include "../ARMJIT.h" #include "../ARMInterpreter.h" #include "../NDS.h" +#include "../ARMJIT_Global.h" #include #include #include "../dolphin/CommonFuncs.h" -#ifdef _WIN32 -#include -#else -#include -#include -#endif - using namespace Gen; using namespace Common; @@ -222,46 +216,21 @@ void Compiler::A_Comp_MSR() MOV(32, R(ABI_PARAM3), R(RCPSR)); MOV(32, R(ABI_PARAM2), R(RSCRATCH3)); MOV(64, R(ABI_PARAM1), R(RCPU)); - CALL((void*)&UpdateModeTrampoline); + ABI_CallFunction(UpdateModeTrampoline); PopRegs(true, true); } } } -/* - We'll repurpose this .bss memory - - */ -u8 CodeMemory[1024 * 1024 * 32]; - Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) { - { - #ifdef _WIN32 - SYSTEM_INFO sysInfo; - GetSystemInfo(&sysInfo); + ARMJIT_Global::Init(); - u64 pageSize = (u64)sysInfo.dwPageSize; - #else - u64 pageSize = sysconf(_SC_PAGE_SIZE); - #endif + CodeMemBase = static_cast(ARMJIT_Global::AllocateCodeMem()); + CodeMemSize = ARMJIT_Global::CodeMemorySliceSize; - u8* pageAligned = (u8*)(((u64)CodeMemory & ~(pageSize - 1)) + pageSize); - 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; - } + ResetStart = CodeMemBase; Reset(); @@ -475,6 +444,13 @@ Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) FarSize = (ResetStart + CodeMemSize) - FarStart; } +Compiler::~Compiler() +{ + ARMJIT_Global::FreeCodeMem(CodeMemBase); + + ARMJIT_Global::DeInit(); +} + void Compiler::LoadCPSR() { assert(!CPSRDirty); @@ -684,7 +660,7 @@ void Compiler::Comp_SpecialBranchBehaviour(bool taken) if (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) ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles)); - JMP((u8*)ARM_Ret, true); + ABI_TailCall(ARM_Ret); #ifdef JIT_PROFILING_ENABLED CreateMethod("JIT_Block_%d_%d_%08X", (void*)res, Num, Thumb, instrs[0].Addr); diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.h b/src/ARMJIT_x64/ARMJIT_Compiler.h index 3965e882..c714a6ba 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.h +++ b/src/ARMJIT_x64/ARMJIT_Compiler.h @@ -84,6 +84,7 @@ class Compiler : public Gen::XEmitter { public: explicit Compiler(melonDS::NDS& nds); + ~Compiler(); void Reset(); @@ -256,6 +257,7 @@ public: std::unordered_map LoadStorePatches {}; + u8* CodeMemBase; u8* ResetStart {}; u32 CodeMemSize {}; diff --git a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp index 219c7271..71cd0770 100644 --- a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp @@ -316,24 +316,24 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag { switch (size | NDS.ConsoleType) { - case 32: CALL((void*)&SlowWrite9); break; - case 16: CALL((void*)&SlowWrite9); break; - case 8: CALL((void*)&SlowWrite9); break; - case 33: CALL((void*)&SlowWrite9); break; - case 17: CALL((void*)&SlowWrite9); break; - case 9: CALL((void*)&SlowWrite9); break; + case 32: ABI_CallFunction(SlowWrite9); break; + case 16: ABI_CallFunction(SlowWrite9); break; + case 8: ABI_CallFunction(&SlowWrite9); break; + case 33: ABI_CallFunction(&SlowWrite9); break; + case 17: ABI_CallFunction(&SlowWrite9); break; + case 9: ABI_CallFunction(&SlowWrite9); break; } } else { switch (size | NDS.ConsoleType) { - case 32: CALL((void*)&SlowRead9); break; - case 16: CALL((void*)&SlowRead9); break; - case 8: CALL((void*)&SlowRead9); break; - case 33: CALL((void*)&SlowRead9); break; - case 17: CALL((void*)&SlowRead9); break; - case 9: CALL((void*)&SlowRead9); break; + case 32: ABI_CallFunction(&SlowRead9); break; + case 16: ABI_CallFunction(&SlowRead9); break; + case 8: ABI_CallFunction(&SlowRead9); break; + case 33: ABI_CallFunction(&SlowRead9); break; + case 17: ABI_CallFunction(&SlowRead9); break; + case 9: ABI_CallFunction(&SlowRead9); break; } } } @@ -347,24 +347,24 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag switch (size | NDS.ConsoleType) { - case 32: CALL((void*)&SlowWrite7); break; - case 16: CALL((void*)&SlowWrite7); break; - case 8: CALL((void*)&SlowWrite7); break; - case 33: CALL((void*)&SlowWrite7); break; - case 17: CALL((void*)&SlowWrite7); break; - case 9: CALL((void*)&SlowWrite7); break; + case 32: ABI_CallFunction(&SlowWrite7); break; + case 16: ABI_CallFunction(&SlowWrite7); break; + case 8: ABI_CallFunction(&SlowWrite7); break; + case 33: ABI_CallFunction(&SlowWrite7); break; + case 17: ABI_CallFunction(&SlowWrite7); break; + case 9: ABI_CallFunction(&SlowWrite7); break; } } else { switch (size | NDS.ConsoleType) { - case 32: CALL((void*)&SlowRead7); break; - case 16: CALL((void*)&SlowRead7); break; - case 8: CALL((void*)&SlowRead7); break; - case 33: CALL((void*)&SlowRead7); break; - case 17: CALL((void*)&SlowRead7); break; - case 9: CALL((void*)&SlowRead7); break; + case 32: ABI_CallFunction(&SlowRead7); break; + case 16: ABI_CallFunction(&SlowRead7); break; + case 8: ABI_CallFunction(&SlowRead7); break; + case 33: ABI_CallFunction(&SlowRead7); break; + case 17: ABI_CallFunction(&SlowRead7); break; + case 9: ABI_CallFunction(&SlowRead7); break; } } } @@ -526,10 +526,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc switch (Num * 2 | NDS.ConsoleType) { - case 0: CALL((void*)&SlowBlockTransfer9); break; - case 1: CALL((void*)&SlowBlockTransfer9); break; - case 2: CALL((void*)&SlowBlockTransfer7); break; - case 3: CALL((void*)&SlowBlockTransfer7); break; + case 0: ABI_CallFunction(&SlowBlockTransfer9); break; + case 1: ABI_CallFunction(&SlowBlockTransfer9); break; + case 2: ABI_CallFunction(&SlowBlockTransfer7); break; + case 3: ABI_CallFunction(&SlowBlockTransfer7); break; } PopRegs(false, false); @@ -630,10 +630,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc switch (Num * 2 | NDS.ConsoleType) { - case 0: CALL((void*)&SlowBlockTransfer9); break; - case 1: CALL((void*)&SlowBlockTransfer9); break; - case 2: CALL((void*)&SlowBlockTransfer7); break; - case 3: CALL((void*)&SlowBlockTransfer7); break; + case 0: ABI_CallFunction(&SlowBlockTransfer9); break; + case 1: ABI_CallFunction(&SlowBlockTransfer9); break; + case 2: ABI_CallFunction(&SlowBlockTransfer7); break; + case 3: ABI_CallFunction(&SlowBlockTransfer7); break; } ADD(64, R(RSP), stackAlloc <= INT8_MAX ? Imm8(stackAlloc) : Imm32(stackAlloc)); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a4cb6f1e..fa8d475c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -97,8 +97,13 @@ if (ENABLE_JIT) ARMJIT.cpp ARMJIT_Memory.cpp + ARMJIT_Global.cpp dolphin/CommonFuncs.cpp) + + if (WIN32) + target_link_libraries(core PRIVATE onecore) + endif() if (ARCHITECTURE STREQUAL x86_64) target_sources(core PRIVATE diff --git a/src/NDS.cpp b/src/NDS.cpp index d8e1d216..b9370c6b 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -74,7 +74,7 @@ const s32 kIterationCycleMargin = 8; // // 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( @@ -128,6 +128,7 @@ NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept : MainRAM = JIT.Memory.GetMainRAM(); SharedWRAM = JIT.Memory.GetSharedWRAM(); ARM7WRAM = JIT.Memory.GetARM7WRAM(); + } NDS::~NDS() noexcept @@ -894,6 +895,8 @@ void NDS::RunSystemSleep(u64 timestamp) template u32 NDS::RunFrame() { + Current = this; + FrameStartTimestamp = SysTimestamp; GPU.TotalScanlines = 0; diff --git a/src/NDS.h b/src/NDS.h index da68799f..8dfbf28b 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -541,8 +541,8 @@ public: NDS& operator=(const NDS&) = delete; NDS(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: explicit NDS(NDSArgs&& args, int type, void* userdata) noexcept; virtual void DoSavestateExtra(Savestate* file) {} diff --git a/src/dolphin/x64Emitter.h b/src/dolphin/x64Emitter.h index 36603218..d83a41f8 100644 --- a/src/dolphin/x64Emitter.h +++ b/src/dolphin/x64Emitter.h @@ -1019,6 +1019,28 @@ public: CALL(ptr); } } + template + void ABI_TailCall(FunctionPointer func) + { + static_assert(std::is_pointer() && + std::is_function>(), + "Supplied type must be a function pointer."); + + const u8* ptr = reinterpret_cast(func); + const u64 address = reinterpret_cast(ptr); + const u64 distance = address - (reinterpret_cast(code) + 5); + + if (distance >= 0x0000000080000000ULL && distance < 0xFFFFFFFF80000000ULL) + { + // Far call + MOV(64, R(RAX), Imm64(address)); + JMPptr(R(RAX)); + } + else + { + JMP(ptr, true); + } + } template void ABI_CallFunctionC16(FunctionPointer func, u16 param1) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 1af8a887..a1d106b8 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -165,7 +165,6 @@ EmuInstance::~EmuInstance() audioDeInit(); inputDeInit(); - NDS::Current = nullptr; if (nds) { saveRTCData(); @@ -1339,7 +1338,6 @@ bool EmuInstance::updateConsole() noexcept renderLock.lock(); if ((!nds) || (consoleType != nds->ConsoleType)) { - NDS::Current = nullptr; if (nds) { saveRTCData(); @@ -1351,7 +1349,6 @@ bool EmuInstance::updateConsole() noexcept else nds = new NDS(std::move(ndsargs), this); - NDS::Current = nds; nds->Reset(); loadRTCData(); //emuThread->updateVideoRenderer(); // not actually needed? diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index b37f7118..ed5eba10 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -82,9 +82,6 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new ui->chkJITBranchOptimisations->setChecked(cfg.GetBool("JIT.BranchOptimisations")); ui->chkJITLiteralOptimisations->setChecked(cfg.GetBool("JIT.LiteralOptimisations")); ui->chkJITFastMemory->setChecked(cfg.GetBool("JIT.FastMemory")); - #ifdef __APPLE__ - ui->chkJITFastMemory->setDisabled(true); - #endif ui->spnJITMaximumBlockSize->setValue(cfg.GetInt("JIT.MaxBlockSize")); #else ui->chkEnableJIT->setDisabled(true); @@ -541,9 +538,7 @@ void EmuSettingsDialog::on_chkEnableJIT_toggled() bool disabled = !ui->chkEnableJIT->isChecked(); ui->chkJITBranchOptimisations->setDisabled(disabled); ui->chkJITLiteralOptimisations->setDisabled(disabled); - #ifndef __APPLE__ - ui->chkJITFastMemory->setDisabled(disabled); - #endif + ui->chkJITFastMemory->setDisabled(disabled || !ARMJIT_Memory::IsFastMemSupported()); ui->spnJITMaximumBlockSize->setDisabled(disabled); on_cbGdbEnabled_toggled(); From f0503a6a2813208c9170318280047ae7019e9ef1 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Mon, 18 Nov 2024 21:21:02 +0100 Subject: [PATCH 030/106] fix 4kb page check oops --- src/ARMJIT_Memory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index ae8391bb..e1c1da43 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -754,7 +754,7 @@ bool ARMJIT_Memory::IsFastMemSupported() PageSize = RegularPageSize; #else PageSize = __sysconf(_SC_PAGESIZE); - isSupported = PageShift == RegularPageSize || PageSize == LargePageSize; + isSupported = PageSize == RegularPageSize || PageSize == LargePageSize; #endif PageShift = __builtin_ctz(PageSize); initialised = true; From 9ad3d422525078a7535f8b49c09292178b93141d Mon Sep 17 00:00:00 2001 From: RSDuck Date: Mon, 18 Nov 2024 21:31:56 +0100 Subject: [PATCH 031/106] hopefully fix macos --- src/ARMJIT_A64/ARMJIT_Compiler.cpp | 1 + src/ARMJIT_Global.cpp | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.cpp b/src/ARMJIT_A64/ARMJIT_Compiler.cpp index 2cb0bf3b..7aa71126 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_A64/ARMJIT_Compiler.cpp @@ -248,6 +248,7 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds) ARMJIT_Global::Init(); CodeMemBase = ARMJIT_Global::AllocateCodeMem(); + nds.JIT.JitEnableWrite(); SetCodeBase(reinterpret_cast(CodeMemBase), reinterpret_cast(CodeMemBase)); JitMemMainSize = ARMJIT_Global::CodeMemorySliceSize; diff --git a/src/ARMJIT_Global.cpp b/src/ARMJIT_Global.cpp index b6510379..c9432cbf 100644 --- a/src/ARMJIT_Global.cpp +++ b/src/ARMJIT_Global.cpp @@ -21,7 +21,11 @@ namespace ARMJIT_Global std::mutex globalMutex; -#ifndef __APPLE__ +#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; @@ -42,7 +46,7 @@ void* AllocateCodeMem() { std::lock_guard guard(globalMutex); -#ifndef __APPLE__ +#ifndef APPLE_AARCH64 if (AvailableCodeMemSlices) { int slice = __builtin_ctz(AvailableCodeMemSlices); @@ -55,6 +59,8 @@ void* AllocateCodeMem() // 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); @@ -65,6 +71,7 @@ void FreeCodeMem(void* codeMem) { std::lock_guard guard(globalMutex); +#ifndef APPLE_AARCH64 for (int i = 0; i < NumCodeMemSlices; i++) { if (codeMem == &GetAlignedCodeMemoryStart()[CodeMemorySliceSize * i]) @@ -74,6 +81,7 @@ void FreeCodeMem(void* codeMem) return; } } +#endif #ifdef _WIN32 VirtualFree(codeMem, CodeMemorySliceSize, MEM_RELEASE|MEM_DECOMMIT); @@ -92,8 +100,8 @@ void Init() #ifdef _WIN32 DWORD dummy; VirtualProtect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PAGE_EXECUTE_READWRITE, &dummy); - #elif defined(__APPLE__) - // Apple always uses dynamic allocation + #elif defined(APPLE_AARCH64) + // Apple aarch64 always uses dynamic allocation #else mprotect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PROT_EXEC | PROT_READ | PROT_WRITE); #endif From bdc8f635de04c6d174b12dad49dcc45c63b3827d Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 18 Nov 2024 22:06:50 +0100 Subject: [PATCH 032/106] change splash logo to 384x384 png --- res/melon.qrc | 2 +- res/melon384.png | Bin 0 -> 27329 bytes res/melon512.png | Bin 0 -> 38826 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 res/melon384.png create mode 100644 res/melon512.png diff --git a/res/melon.qrc b/res/melon.qrc index 3c5824d6..1defcc75 100644 --- a/res/melon.qrc +++ b/res/melon.qrc @@ -2,6 +2,6 @@ icon/melon_256x256.png - melon.svg + melon384.png diff --git a/res/melon384.png b/res/melon384.png new file mode 100644 index 0000000000000000000000000000000000000000..d597d2ed230f76144e4f5c58f8cd9d0092cb6ddc GIT binary patch literal 27329 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 nIy1L{IP|wd%m0smDVP%P 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 o#wbuFkXT0pF literal 0 HcmV?d00001 From f1c96281a9b9d0bc02a7680ffa49557446c44298 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 18 Nov 2024 22:53:55 +0100 Subject: [PATCH 033/106] update toml11. fixes bug with FPS settings corrupting config file --- cmake/toml11Config.cmake__ | 2 + res/icon/melon_192x192.png | Bin 0 -> 100714 bytes res/icon/splash.xcf | Bin 0 -> 246892 bytes src/frontend/qt_sdl/Config.cpp | 2 +- src/frontend/qt_sdl/Config.h | 2 +- src/frontend/qt_sdl/toml/toml.hpp | 94 +- src/frontend/qt_sdl/toml/toml/color.hpp | 64 - src/frontend/qt_sdl/toml/toml/combinator.hpp | 306 -- src/frontend/qt_sdl/toml/toml/datetime.hpp | 631 --- src/frontend/qt_sdl/toml/toml/exception.hpp | 65 - src/frontend/qt_sdl/toml/toml/get.hpp | 1119 ----- src/frontend/qt_sdl/toml/toml/lexer.hpp | 293 -- src/frontend/qt_sdl/toml/toml/literal.hpp | 113 - src/frontend/qt_sdl/toml/toml/parser.hpp | 2416 ----------- src/frontend/qt_sdl/toml/toml/region.hpp | 417 -- src/frontend/qt_sdl/toml/toml/result.hpp | 717 --- src/frontend/qt_sdl/toml/toml/serializer.hpp | 922 ---- .../qt_sdl/toml/toml/source_location.hpp | 233 - src/frontend/qt_sdl/toml/toml/storage.hpp | 43 - src/frontend/qt_sdl/toml/toml/string.hpp | 228 - src/frontend/qt_sdl/toml/toml/traits.hpp | 328 -- src/frontend/qt_sdl/toml/toml/types.hpp | 173 - src/frontend/qt_sdl/toml/toml/utility.hpp | 150 - src/frontend/qt_sdl/toml/toml/value.hpp | 2035 --------- src/frontend/qt_sdl/toml/toml/version.hpp | 42 - src/frontend/qt_sdl/toml/toml11/color.hpp | 10 + src/frontend/qt_sdl/toml/toml11/comments.hpp | 10 + src/frontend/qt_sdl/toml/toml11/compat.hpp | 751 ++++ src/frontend/qt_sdl/toml/toml11/context.hpp | 68 + .../macros.hpp => toml11/conversion.hpp} | 138 +- src/frontend/qt_sdl/toml/toml11/datetime.hpp | 10 + .../qt_sdl/toml/toml11/error_info.hpp | 10 + src/frontend/qt_sdl/toml/toml11/exception.hpp | 17 + src/frontend/qt_sdl/toml/toml11/find.hpp | 377 ++ src/frontend/qt_sdl/toml/toml11/format.hpp | 10 + .../qt_sdl/toml/{toml => toml11}/from.hpp | 2 - .../qt_sdl/toml/toml11/fwd/color_fwd.hpp | 88 + .../fwd/comments_fwd.hpp} | 115 +- .../qt_sdl/toml/toml11/fwd/datetime_fwd.hpp | 261 ++ .../qt_sdl/toml/toml11/fwd/error_info_fwd.hpp | 97 + .../qt_sdl/toml/toml11/fwd/format_fwd.hpp | 250 ++ .../qt_sdl/toml/toml11/fwd/literal_fwd.hpp | 33 + .../qt_sdl/toml/toml11/fwd/location_fwd.hpp | 145 + .../qt_sdl/toml/toml11/fwd/region_fwd.hpp | 104 + .../qt_sdl/toml/toml11/fwd/scanner_fwd.hpp | 391 ++ .../toml/toml11/fwd/source_location_fwd.hpp | 115 + .../qt_sdl/toml/toml11/fwd/syntax_fwd.hpp | 357 ++ .../qt_sdl/toml/toml11/fwd/value_t_fwd.hpp | 117 + src/frontend/qt_sdl/toml/toml11/get.hpp | 632 +++ .../qt_sdl/toml/toml11/impl/color_impl.hpp | 76 + .../qt_sdl/toml/toml11/impl/comments_impl.hpp | 46 + .../qt_sdl/toml/toml11/impl/datetime_impl.hpp | 518 +++ .../toml/toml11/impl/error_info_impl.hpp | 75 + .../qt_sdl/toml/toml11/impl/format_impl.hpp | 297 ++ .../qt_sdl/toml/toml11/impl/literal_impl.hpp | 174 + .../qt_sdl/toml/toml11/impl/location_impl.hpp | 226 + .../qt_sdl/toml/toml11/impl/region_impl.hpp | 188 + .../qt_sdl/toml/toml11/impl/scanner_impl.hpp | 473 ++ .../toml/toml11/impl/source_location_impl.hpp | 197 + .../qt_sdl/toml/toml11/impl/syntax_impl.hpp | 732 ++++ .../qt_sdl/toml/toml11/impl/value_t_impl.hpp | 40 + .../qt_sdl/toml/{toml => toml11}/into.hpp | 2 - src/frontend/qt_sdl/toml/toml11/literal.hpp | 10 + src/frontend/qt_sdl/toml/toml11/location.hpp | 10 + .../qt_sdl/toml/toml11/ordered_map.hpp | 265 ++ src/frontend/qt_sdl/toml/toml11/parser.hpp | 3829 +++++++++++++++++ src/frontend/qt_sdl/toml/toml11/region.hpp | 10 + src/frontend/qt_sdl/toml/toml11/result.hpp | 486 +++ src/frontend/qt_sdl/toml/toml11/scanner.hpp | 10 + .../qt_sdl/toml/toml11/serializer.hpp | 1275 ++++++ src/frontend/qt_sdl/toml/toml11/skip.hpp | 392 ++ .../qt_sdl/toml/toml11/source_location.hpp | 10 + src/frontend/qt_sdl/toml/toml11/spec.hpp | 121 + src/frontend/qt_sdl/toml/toml11/storage.hpp | 49 + src/frontend/qt_sdl/toml/toml11/syntax.hpp | 10 + src/frontend/qt_sdl/toml/toml11/traits.hpp | 240 ++ src/frontend/qt_sdl/toml/toml11/types.hpp | 397 ++ src/frontend/qt_sdl/toml/toml11/utility.hpp | 170 + src/frontend/qt_sdl/toml/toml11/value.hpp | 2257 ++++++++++ src/frontend/qt_sdl/toml/toml11/value_t.hpp | 10 + src/frontend/qt_sdl/toml/toml11/version.hpp | 121 + src/frontend/qt_sdl/toml/toml11/visit.hpp | 81 + src/frontend/qt_sdl/toml/toml_fwd.hpp | 88 + 83 files changed, 16956 insertions(+), 10432 deletions(-) create mode 100644 cmake/toml11Config.cmake__ create mode 100644 res/icon/melon_192x192.png create mode 100644 res/icon/splash.xcf delete mode 100644 src/frontend/qt_sdl/toml/toml/color.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/combinator.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/datetime.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/exception.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/get.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/lexer.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/literal.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/parser.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/region.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/result.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/serializer.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/source_location.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/storage.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/string.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/traits.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/types.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/utility.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/value.hpp delete mode 100644 src/frontend/qt_sdl/toml/toml/version.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/color.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/comments.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/compat.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/context.hpp rename src/frontend/qt_sdl/toml/{toml/macros.hpp => toml11/conversion.hpp} (77%) create mode 100644 src/frontend/qt_sdl/toml/toml11/datetime.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/error_info.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/exception.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/find.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/format.hpp rename src/frontend/qt_sdl/toml/{toml => toml11}/from.hpp (78%) create mode 100644 src/frontend/qt_sdl/toml/toml11/fwd/color_fwd.hpp rename src/frontend/qt_sdl/toml/{toml/comments.hpp => toml11/fwd/comments_fwd.hpp} (86%) create mode 100644 src/frontend/qt_sdl/toml/toml11/fwd/datetime_fwd.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/fwd/error_info_fwd.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/fwd/format_fwd.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/fwd/literal_fwd.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/fwd/location_fwd.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/fwd/region_fwd.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/fwd/scanner_fwd.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/fwd/source_location_fwd.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/fwd/syntax_fwd.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/fwd/value_t_fwd.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/get.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/impl/color_impl.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/impl/comments_impl.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/impl/datetime_impl.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/impl/error_info_impl.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/impl/format_impl.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/impl/literal_impl.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/impl/location_impl.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/impl/region_impl.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/impl/scanner_impl.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/impl/source_location_impl.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/impl/syntax_impl.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/impl/value_t_impl.hpp rename src/frontend/qt_sdl/toml/{toml => toml11}/into.hpp (79%) create mode 100644 src/frontend/qt_sdl/toml/toml11/literal.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/location.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/ordered_map.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/parser.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/region.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/result.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/scanner.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/serializer.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/skip.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/source_location.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/spec.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/storage.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/syntax.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/traits.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/types.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/utility.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/value.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/value_t.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/version.hpp create mode 100644 src/frontend/qt_sdl/toml/toml11/visit.hpp create mode 100644 src/frontend/qt_sdl/toml/toml_fwd.hpp diff --git a/cmake/toml11Config.cmake__ b/cmake/toml11Config.cmake__ new file mode 100644 index 00000000..edc73f69 --- /dev/null +++ b/cmake/toml11Config.cmake__ @@ -0,0 +1,2 @@ +@PACKAGE_INIT@ +include("@PACKAGE_toml11_install_cmake_dir@/toml11Targets.cmake") diff --git a/res/icon/melon_192x192.png b/res/icon/melon_192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d4da5d278a8f6c78495ba1fe01074f2c050543 GIT binary patch literal 100714 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%0DfW}+kqE&hshwtk|CFaoolz3DPSG28d@tL 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

melonDS

- +

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[C; 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 literal 0 HcmV?d00001 diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 02be5b65..7d5331fc 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -816,7 +816,7 @@ Table GetLocalTable(int instance) std::string key = "Instance" + std::to_string(instance); toml::value& tbl = RootTable[key]; - if (tbl.is_uninitialized()) + if (tbl.is_empty()) RootTable[key] = RootTable["Instance0"]; return Table(tbl, key); diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index 9e6d3ea4..39278c36 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -25,7 +25,7 @@ #include #include -#include "toml/toml/value.hpp" +#include "toml/toml11/types.hpp" namespace Config { diff --git a/src/frontend/qt_sdl/toml/toml.hpp b/src/frontend/qt_sdl/toml/toml.hpp index 6b0ca1ed..75cc95be 100644 --- a/src/frontend/qt_sdl/toml/toml.hpp +++ b/src/frontend/qt_sdl/toml/toml.hpp @@ -1,38 +1,62 @@ -/* - * The MIT License (MIT) - * - * 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 TOML11_TOML_HPP +#define TOML11_TOML_HPP -#ifndef TOML_FOR_MODERN_CPP -#define TOML_FOR_MODERN_CPP +// The MIT License (MIT) +// +// 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 -#define TOML11_VERSION_MINOR 7 -#define TOML11_VERSION_PATCH 1 +// IWYU pragma: begin_exports +#include "toml11/color.hpp" +#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" -#include "toml/literal.hpp" -#include "toml/serializer.hpp" -#include "toml/get.hpp" -#include "toml/macros.hpp" - -#endif// TOML_FOR_MODERN_CPP +#endif// TOML11_TOML_HPP diff --git a/src/frontend/qt_sdl/toml/toml/color.hpp b/src/frontend/qt_sdl/toml/toml/color.hpp deleted file mode 100644 index 4cb572cb..00000000 --- a/src/frontend/qt_sdl/toml/toml/color.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef TOML11_COLOR_HPP -#define TOML11_COLOR_HPP -#include -#include - -#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 diff --git a/src/frontend/qt_sdl/toml/toml/combinator.hpp b/src/frontend/qt_sdl/toml/toml/combinator.hpp deleted file mode 100644 index 33ecca1e..00000000 --- a/src/frontend/qt_sdl/toml/toml/combinator.hpp +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_COMBINATOR_HPP -#define TOML11_COMBINATOR_HPP -#include -#include -#include - -#include -#include -#include -#include -#include - -#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(std::addressof(c)))) - { - return std::string(1, c); - } - else - { - std::array buf; - buf.fill('\0'); - const auto r = std::snprintf( - buf.data(), buf.size(), "0x%02x", static_cast(c) & 0xFF); - (void) r; // Unused variable warning - assert(r == static_cast(buf.size()) - 1); - return std::string(buf.data()); - } -} - -template -struct character -{ - static constexpr char target = C; - - static result - 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 -constexpr char character::target; - -// closed interval [Low, Up]. both Low and Up are included. -template -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 - 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 constexpr char in_range::upper; -template constexpr char in_range::lower; - -// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char. -// for detecting invalid characters, like control sequences in toml string. -template -struct exclude -{ - static result - 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 -struct maybe -{ - static result - invoke(location& loc) - { - const auto rslt = Combinator::invoke(loc); - if(rslt.is_ok()) - { - return rslt; - } - return ok(region(loc)); - } -}; - -template -struct sequence; - -template -struct sequence -{ - static result - invoke(location& loc) - { - const auto first = loc.iter(); - auto rslt = Head::invoke(loc); - if(rslt.is_err()) - { - loc.reset(first); - return none(); - } - return sequence::invoke(loc, std::move(rslt.unwrap()), first); - } - - // called from the above function only, recursively. - template - static result - 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::invoke(loc, std::move(reg), first); - } -}; - -template -struct sequence -{ - // would be called from sequence::invoke only. - template - static result - 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 -struct either; - -template -struct either -{ - static result - invoke(location& loc) - { - const auto rslt = Head::invoke(loc); - if(rslt.is_ok()) {return rslt;} - return either::invoke(loc); - } -}; -template -struct either -{ - static result - invoke(location& loc) - { - return Head::invoke(loc); - } -}; - -template -struct repeat; - -template struct exactly{}; -template struct at_least{}; -struct unlimited{}; - -template -struct repeat> -{ - static result - invoke(location& loc) - { - region retval(loc); - const auto first = loc.iter(); - for(std::size_t i=0; i -struct repeat> -{ - static result - invoke(location& loc) - { - region retval(loc); - - const auto first = loc.iter(); - for(std::size_t i=0; i -struct repeat -{ - static result - 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 diff --git a/src/frontend/qt_sdl/toml/toml/datetime.hpp b/src/frontend/qt_sdl/toml/toml/datetime.hpp deleted file mode 100644 index 36db1e10..00000000 --- a/src/frontend/qt_sdl/toml/toml/datetime.hpp +++ /dev/null @@ -1,631 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_DATETIME_HPP -#define TOML11_DATETIME_HPP -#include -#include -#include - -#include -#include -#include -#include -#include - -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(y)), - month(static_cast(m)), - day (static_cast(d)) - {} - - explicit local_date(const std::tm& t) - : year (static_cast(t.tm_year + 1900)), - month(static_cast(t.tm_mon)), - day (static_cast(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(this->day); - t.tm_mon = static_cast(this->month); - t.tm_year = static_cast(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 -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_date& date) -{ - os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; - os << std::setfill('0') << std::setw(2) << static_cast(date.month) + 1 << '-'; - os << std::setfill('0') << std::setw(2) << static_cast(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(h)), - minute(static_cast(m)), - second(static_cast(s)), - millisecond(static_cast(ms)), - microsecond(static_cast(us)), - nanosecond (static_cast(ns)) - {} - - explicit local_time(const std::tm& t) - : hour (static_cast(t.tm_hour)), - minute(static_cast(t.tm_min)), - second(static_cast(t.tm_sec)), - millisecond(0), microsecond(0), nanosecond(0) - {} - - template - explicit local_time(const std::chrono::duration& t) - { - const auto h = std::chrono::duration_cast(t); - this->hour = static_cast(h.count()); - const auto t2 = t - h; - const auto m = std::chrono::duration_cast(t2); - this->minute = static_cast(m.count()); - const auto t3 = t2 - m; - const auto s = std::chrono::duration_cast(t3); - this->second = static_cast(s.count()); - const auto t4 = t3 - s; - const auto ms = std::chrono::duration_cast(t4); - this->millisecond = static_cast(ms.count()); - const auto t5 = t4 - ms; - const auto us = std::chrono::duration_cast(t5); - this->microsecond = static_cast(us.count()); - const auto t6 = t5 - us; - const auto ns = std::chrono::duration_cast(t6); - this->nanosecond = static_cast(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 -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_time& time) -{ - os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; - os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; - os << std::setfill('0') << std::setw(2) << static_cast(time.second); - if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) - { - os << '.'; - os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); - if(time.microsecond != 0 || time.nanosecond != 0) - { - os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); - if(time.nanosecond != 0) - { - os << std::setfill('0') << std::setw(3) << static_cast(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(h)), - minute(static_cast(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 -std::basic_ostream& -operator<<(std::basic_ostream& os, const time_offset& offset) -{ - if(offset.hour == 0 && offset.minute == 0) - { - os << 'Z'; - return os; - } - int minute = static_cast(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(<ime)); - this->time.millisecond = static_cast( - std::chrono::duration_cast(t_diff).count()); - this->time.microsecond = static_cast( - std::chrono::duration_cast(t_diff).count()); - this->time.nanosecond = static_cast( - std::chrono::duration_cast(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(this->time.second); - t.tm_min = static_cast(this->time.minute); - t.tm_hour = static_cast(this->time.hour); - t.tm_mday = static_cast(this->date.day); - t.tm_mon = static_cast(this->date.month); - t.tm_year = static_cast(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( - 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 -std::basic_ostream& -operator<<(std::basic_ostream& 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(this->time.second); - t.tm_min = static_cast(this->time.minute); - t.tm_hour = static_cast(this->time.hour); - t.tm_mday = static_cast(this->date.day); - t.tm_mon = static_cast(this->date.month); - t.tm_year = static_cast(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( - 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 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 -std::basic_ostream& -operator<<(std::basic_ostream& os, const offset_datetime& dt) -{ - os << dt.date << 'T' << dt.time << dt.offset; - return os; -} - -}//toml -#endif// TOML11_DATETIME diff --git a/src/frontend/qt_sdl/toml/toml/exception.hpp b/src/frontend/qt_sdl/toml/toml/exception.hpp deleted file mode 100644 index c64651d0..00000000 --- a/src/frontend/qt_sdl/toml/toml/exception.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_EXCEPTION_HPP -#define TOML11_EXCEPTION_HPP -#include -#include - -#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 diff --git a/src/frontend/qt_sdl/toml/toml/get.hpp b/src/frontend/qt_sdl/toml/toml/get.hpp deleted file mode 100644 index 901790b5..00000000 --- a/src/frontend/qt_sdl/toml/toml/get.hpp +++ /dev/null @@ -1,1119 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_GET_HPP -#define TOML11_GET_HPP -#include - -#include "from.hpp" -#include "result.hpp" -#include "value.hpp" - -namespace toml -{ - -// ============================================================================ -// exact toml::* type - -template class M, template class V> -detail::enable_if_t>::value, T> & -get(basic_value& v) -{ - return v.template cast>::value>(); -} - -template class M, template class V> -detail::enable_if_t>::value, T> const& -get(const basic_value& v) -{ - return v.template cast>::value>(); -} - -template class M, template class V> -detail::enable_if_t>::value, T> -get(basic_value&& v) -{ - return T(std::move(v).template cast>::value>()); -} - -// ============================================================================ -// T == toml::value; identity transformation. - -template class M, template class V> -inline detail::enable_if_t>::value, T>& -get(basic_value& v) -{ - return v; -} - -template class M, template class V> -inline detail::enable_if_t>::value, T> const& -get(const basic_value& v) -{ - return v; -} - -template class M, template class V> -inline detail::enable_if_t>::value, T> -get(basic_value&& v) -{ - return basic_value(std::move(v)); -} - -// ============================================================================ -// T == toml::basic_value; basic_value -> basic_value - -template class M, template class V> -inline detail::enable_if_t, - detail::negation>> - >::value, T> -get(const basic_value& v) -{ - return T(v); -} - -// ============================================================================ -// integer convertible from toml::Integer - -template class M, template class V> -inline detail::enable_if_t, // T is integral - detail::negation>, // but not bool - detail::negation< // but not toml::integer - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - return static_cast(v.as_integer()); -} - -// ============================================================================ -// floating point convertible from toml::Float - -template class M, template class V> -inline detail::enable_if_t, // T is floating_point - detail::negation< // but not toml::floating - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - return static_cast(v.as_floating()); -} - -// ============================================================================ -// std::string; toml uses its own toml::string, but it should be convertible to -// std::string seamlessly - -template class M, template class V> -inline detail::enable_if_t::value, std::string>& -get(basic_value& v) -{ - return v.as_string().str; -} - -template class M, template class V> -inline detail::enable_if_t::value, std::string> const& -get(const basic_value& v) -{ - return v.as_string().str; -} - -template class M, template class V> -inline detail::enable_if_t::value, std::string> -get(basic_value&& v) -{ - return std::string(std::move(v.as_string().str)); -} - -// ============================================================================ -// std::string_view - -#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 -template class M, template class V> -inline detail::enable_if_t::value, std::string_view> -get(const basic_value& v) -{ - return std::string_view(v.as_string().str); -} -#endif - -// ============================================================================ -// std::chrono::duration from toml::local_time. - -template class M, template class V> -inline detail::enable_if_t::value, T> -get(const basic_value& v) -{ - return std::chrono::duration_cast( - std::chrono::nanoseconds(v.as_local_time())); -} - -// ============================================================================ -// std::chrono::system_clock::time_point from toml::datetime variants - -template class M, template class V> -inline detail::enable_if_t< - std::is_same::value, T> -get(const basic_value& v) -{ - switch(v.type()) - { - case value_t::local_date: - { - return std::chrono::system_clock::time_point(v.as_local_date()); - } - case value_t::local_datetime: - { - return std::chrono::system_clock::time_point(v.as_local_datetime()); - } - case value_t::offset_datetime: - { - return std::chrono::system_clock::time_point(v.as_offset_datetime()); - } - default: - { - throw type_error(detail::format_underline("toml::value: " - "bad_cast to std::chrono::system_clock::time_point", { - {v.location(), concat_to_string("the actual type is ", v.type())} - }), v.location()); - } - } -} - -// ============================================================================ -// forward declaration to use this recursively. ignore this and go ahead. - -// array-like type with push_back(value) method -template class M, template class V> -detail::enable_if_t, // T is a container - detail::has_push_back_method, // T::push_back(value) works - detail::negation< // but not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value&); - -// array-like type without push_back(value) method -template class M, template class V> -detail::enable_if_t, // T is a container - detail::negation>, // w/o push_back(...) - detail::negation>, // T does not have special conversion - detail::negation< // not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value&); - -// std::pair -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value&); - -// std::tuple -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value&); - -// map-like classes -template class M, template class V> -detail::enable_if_t, // T is map - detail::negation< // but not toml::table - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value&); - -// T.from_toml(v) -template class M, template class V> -detail::enable_if_t>>, - detail::has_from_toml_method, // but has from_toml(toml::value) - std::is_default_constructible // and default constructible - >::value, T> -get(const basic_value&); - -// toml::from::from_toml(v) -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value&); - -// T(const toml::value&) and T is not toml::basic_value, -// and it does not have `from` nor `from_toml`. -template class M, template class V> -detail::enable_if_t>, - std::is_constructible&>, - detail::negation>, - detail::negation> - >::value, T> -get(const basic_value&); - -// ============================================================================ -// array-like types; most likely STL container, like std::vector, etc. - -template class M, template class V> -detail::enable_if_t, // T is a container - detail::has_push_back_method, // container.push_back(elem) works - detail::negation< // but not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - using value_type = typename T::value_type; - const auto& ary = v.as_array(); - - T container; - try_reserve(container, ary.size()); - - for(const auto& elem : ary) - { - container.push_back(get(elem)); - } - return container; -} - -// ============================================================================ -// std::forward_list does not have push_back, insert, or emplace. -// It has insert_after, emplace_after, push_front. - -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - using value_type = typename T::value_type; - T container; - for(const auto& elem : v.as_array()) - { - container.push_front(get(elem)); - } - container.reverse(); - return container; -} - -// ============================================================================ -// array-like types, without push_back(). most likely [std|boost]::array. - -template class M, template class V> -detail::enable_if_t, // T is a container - detail::negation>, // w/o push_back - detail::negation>, // T does not have special conversion - detail::negation< // T is not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - using value_type = typename T::value_type; - const auto& ar = v.as_array(); - - T container; - if(ar.size() != container.size()) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "toml::get: specified container size is ", container.size(), - " but there are ", ar.size(), " elements in toml array."), { - {v.location(), "here"} - })); - } - for(std::size_t i=0; i(ar[i]); - } - return container; -} - -// ============================================================================ -// std::pair. - -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - using first_type = typename T::first_type; - using second_type = typename T::second_type; - - const auto& ar = v.as_array(); - if(ar.size() != 2) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "toml::get: specified std::pair but there are ", ar.size(), - " elements in toml array."), {{v.location(), "here"}})); - } - return std::make_pair(::toml::get(ar.at(0)), - ::toml::get(ar.at(1))); -} - -// ============================================================================ -// std::tuple. - -namespace detail -{ -template -T get_tuple_impl(const Array& a, index_sequence) -{ - return std::make_tuple( - ::toml::get::type>(a.at(I))...); -} -} // detail - -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - const auto& ar = v.as_array(); - if(ar.size() != std::tuple_size::value) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "toml::get: specified std::tuple with ", - std::tuple_size::value, " elements, but there are ", ar.size(), - " elements in toml array."), {{v.location(), "here"}})); - } - return detail::get_tuple_impl(ar, - detail::make_index_sequence::value>{}); -} - -// ============================================================================ -// map-like types; most likely STL map, like std::map or std::unordered_map. - -template class M, template class V> -detail::enable_if_t, // T is map - detail::negation< // but not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - using key_type = typename T::key_type; - using mapped_type = typename T::mapped_type; - static_assert(std::is_convertible::value, - "toml::get only supports map type of which key_type is " - "convertible from std::string."); - T map; - for(const auto& kv : v.as_table()) - { - map.emplace(key_type(kv.first), get(kv.second)); - } - return map; -} - -// ============================================================================ -// user-defined, but compatible types. - -template class M, template class V> -detail::enable_if_t>>, - detail::has_from_toml_method, // but has from_toml(toml::value) memfn - std::is_default_constructible // and default constructible - >::value, T> -get(const basic_value& v) -{ - T ud; - ud.from_toml(v); - return ud; -} -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - return ::toml::from::from_toml(v); -} - -template class M, template class V> -detail::enable_if_t>, // T is not a toml::value - std::is_constructible&>, // T is constructible from toml::value - detail::negation>, // and T does not have T.from_toml(v); - detail::negation> // and T does not have toml::from{}; - >::value, T> -get(const basic_value& v) -{ - return T(v); -} - -// ============================================================================ -// find - -// ---------------------------------------------------------------------------- -// these overloads do not require to set T. and returns value itself. -template class M, template class V> -basic_value const& find(const basic_value& v, const key& ky) -{ - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return tab.at(ky); -} -template class M, template class V> -basic_value& find(basic_value& v, const key& ky) -{ - auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return tab.at(ky); -} -template class M, template class V> -basic_value find(basic_value&& v, const key& ky) -{ - typename basic_value::table_type tab = std::move(v).as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return basic_value(std::move(tab.at(ky))); -} - -// ---------------------------------------------------------------------------- -// find(value, idx) -template class M, template class V> -basic_value const& -find(const basic_value& v, const std::size_t idx) -{ - const auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ary.at(idx); -} -template class M, template class V> -basic_value& find(basic_value& v, const std::size_t idx) -{ - auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ary.at(idx); -} -template class M, template class V> -basic_value find(basic_value&& v, const std::size_t idx) -{ - auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return basic_value(std::move(ary.at(idx))); -} - -// ---------------------------------------------------------------------------- -// find(value, key); - -template class M, template class V> -decltype(::toml::get(std::declval const&>())) -find(const basic_value& v, const key& ky) -{ - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return ::toml::get(tab.at(ky)); -} - -template class M, template class V> -decltype(::toml::get(std::declval&>())) -find(basic_value& v, const key& ky) -{ - auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return ::toml::get(tab.at(ky)); -} - -template class M, template class V> -decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, const key& ky) -{ - typename basic_value::table_type tab = std::move(v).as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return ::toml::get(std::move(tab.at(ky))); -} - -// ---------------------------------------------------------------------------- -// find(value, idx) -template class M, template class V> -decltype(::toml::get(std::declval const&>())) -find(const basic_value& v, const std::size_t idx) -{ - const auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ::toml::get(ary.at(idx)); -} -template class M, template class V> -decltype(::toml::get(std::declval&>())) -find(basic_value& v, const std::size_t idx) -{ - auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ::toml::get(ary.at(idx)); -} -template class M, template class V> -decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, const std::size_t idx) -{ - typename basic_value::array_type ary = std::move(v).as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ::toml::get(std::move(ary.at(idx))); -} - -// -------------------------------------------------------------------------- -// toml::find(toml::value, toml::key, Ts&& ... keys) - -namespace detail -{ -// It suppresses warnings by -Wsign-conversion. Let's say we have the following -// code. -// ```cpp -// const auto x = toml::find(data, "array", 0); -// ``` -// Here, the type of literal number `0` is `int`. `int` is a signed integer. -// `toml::find` takes `std::size_t` as an index. So it causes implicit sign -// conversion and `-Wsign-conversion` warns about it. Using `0u` instead of `0` -// suppresses the warning, but it makes user code messy. -// To suppress this warning, we need to be aware of type conversion caused -// by `toml::find(v, key1, key2, ... keys)`. But the thing is that the types of -// keys can be any combination of {string-like, size_t-like}. Of course we can't -// write down all the combinations. Thus we need to use some function that -// recognize the type of argument and cast it into `std::string` or -// `std::size_t` depending on the context. -// `key_cast` does the job. It has 2 overloads. One is invoked when the -// argument type is an integer and cast the argument into `std::size_t`. The -// other is invoked when the argument type is not an integer, possibly one of -// std::string, const char[N] or const char*, and construct std::string from -// the argument. -// `toml::find(v, k1, k2, ... ks)` uses `key_cast` before passing `ks` to -// `toml::find(v, k)` to suppress -Wsign-conversion. - -template -enable_if_t>, - negation, bool>>>::value, std::size_t> -key_cast(T&& v) noexcept -{ - return std::size_t(v); -} -template -enable_if_t>, - negation, bool>>>>::value, std::string> -key_cast(T&& v) noexcept -{ - return std::string(std::forward(v)); -} -} // detail - -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -const basic_value& -find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -basic_value& -find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -basic_value -find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(std::move(v), std::forward(k1)), - detail::key_cast(k2), std::forward(keys)...); -} - -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -decltype(::toml::get(std::declval&>())) -find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -decltype(::toml::get(std::declval&>())) -find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(std::move(v), detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} - -// ============================================================================ -// get_or(value, fallback) - -template class M, template class V> -basic_value const& -get_or(const basic_value& v, const basic_value&) -{ - return v; -} -template class M, template class V> -basic_value& -get_or(basic_value& v, basic_value&) -{ - return v; -} -template class M, template class V> -basic_value -get_or(basic_value&& v, basic_value&&) -{ - return v; -} - -// ---------------------------------------------------------------------------- -// specialization for the exact toml types (return type becomes lvalue ref) - -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T> const& -get_or(const basic_value& v, const T& opt) -{ - try - { - return get>(v); - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T>& -get_or(basic_value& v, T& opt) -{ - try - { - return get>(v); - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t, - basic_value>::value, detail::remove_cvref_t> -get_or(basic_value&& v, T&& opt) -{ - try - { - return get>(std::move(v)); - } - catch(...) - { - return detail::remove_cvref_t(std::forward(opt)); - } -} - -// ---------------------------------------------------------------------------- -// specialization for std::string (return type becomes lvalue ref) - -template class M, template class V> -detail::enable_if_t, std::string>::value, - std::string> const& -get_or(const basic_value& v, const T& opt) -{ - try - { - return v.as_string().str; - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t::value, std::string>& -get_or(basic_value& v, T& opt) -{ - try - { - return v.as_string().str; - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t< - std::is_same, std::string>::value, std::string> -get_or(basic_value&& v, T&& opt) -{ - try - { - return std::move(v.as_string().str); - } - catch(...) - { - return std::string(std::forward(opt)); - } -} - -// ---------------------------------------------------------------------------- -// specialization for string literal - -template class M, template class V> -detail::enable_if_t::type>::value, std::string> -get_or(const basic_value& v, T&& opt) -{ - try - { - return std::move(v.as_string().str); - } - catch(...) - { - return std::string(std::forward(opt)); - } -} - -// ---------------------------------------------------------------------------- -// others (require type conversion and return type cannot be lvalue reference) - -template class M, template class V> -detail::enable_if_t, - basic_value>>, - detail::negation>>, - detail::negation::type>> - >::value, detail::remove_cvref_t> -get_or(const basic_value& v, T&& opt) -{ - try - { - return get>(v); - } - catch(...) - { - return detail::remove_cvref_t(std::forward(opt)); - } -} - -// =========================================================================== -// find_or(value, key, fallback) - -template class M, template class V> -basic_value const& -find_or(const basic_value& v, const key& ky, - const basic_value& opt) -{ - if(!v.is_table()) {return opt;} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return tab.at(ky); -} - -template class M, template class V> -basic_value& -find_or(basic_value& v, const toml::key& ky, basic_value& opt) -{ - if(!v.is_table()) {return opt;} - auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return tab.at(ky); -} - -template class M, template class V> -basic_value -find_or(basic_value&& v, const toml::key& ky, basic_value&& opt) -{ - if(!v.is_table()) {return opt;} - auto tab = std::move(v).as_table(); - if(tab.count(ky) == 0) {return opt;} - return basic_value(std::move(tab.at(ky))); -} - -// --------------------------------------------------------------------------- -// exact types (return type can be a reference) -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T> const& -find_or(const basic_value& v, const key& ky, const T& opt) -{ - if(!v.is_table()) {return opt;} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} - -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T>& -find_or(basic_value& v, const toml::key& ky, T& opt) -{ - if(!v.is_table()) {return opt;} - auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} - -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, - detail::remove_cvref_t> -find_or(basic_value&& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::forward(opt);} - auto tab = std::move(v).as_table(); - if(tab.count(ky) == 0) {return std::forward(opt);} - return get_or(std::move(tab.at(ky)), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// std::string (return type can be a reference) - -template class M, template class V> -detail::enable_if_t::value, std::string> const& -find_or(const basic_value& v, const key& ky, const T& opt) -{ - if(!v.is_table()) {return opt;} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} -template class M, template class V> -detail::enable_if_t::value, std::string>& -find_or(basic_value& v, const toml::key& ky, T& opt) -{ - if(!v.is_table()) {return opt;} - auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} -template class M, template class V> -detail::enable_if_t::value, std::string> -find_or(basic_value&& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::forward(opt);} - auto tab = std::move(v).as_table(); - if(tab.count(ky) == 0) {return std::forward(opt);} - return get_or(std::move(tab.at(ky)), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// string literal (deduced as std::string) -template class M, template class V> -detail::enable_if_t< - detail::is_string_literal::type>::value, - std::string> -find_or(const basic_value& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::string(opt);} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return std::string(opt);} - return get_or(tab.at(ky), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// others (require type conversion and return type cannot be lvalue reference) -template class M, template class V> -detail::enable_if_t, basic_value>>, - // T is not std::string - detail::negation>>, - // T is not a string literal - detail::negation::type>> - >::value, detail::remove_cvref_t> -find_or(const basic_value& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::forward(opt);} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return std::forward(opt);} - return get_or(tab.at(ky), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// recursive find-or with type deduction (find_or(value, keys, opt)) - -template 1), std::nullptr_t> = nullptr> - // here we need to add SFINAE in the template parameter to avoid - // infinite recursion in type deduction on gcc -auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) - -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) -{ - if(!v.is_table()) - { - return detail::last_one(std::forward(keys)...); - } - auto&& tab = std::forward(v).as_table(); - if(tab.count(ky) == 0) - { - return detail::last_one(std::forward(keys)...); - } - return find_or(std::forward(tab).at(ky), std::forward(keys)...); -} - -// --------------------------------------------------------------------------- -// recursive find_or with explicit type specialization, find_or(value, keys...) - -template 1), std::nullptr_t> = nullptr> - // here we need to add SFINAE in the template parameter to avoid - // infinite recursion in type deduction on gcc -auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) - -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) -{ - if(!v.is_table()) - { - return detail::last_one(std::forward(keys)...); - } - auto&& tab = std::forward(v).as_table(); - if(tab.count(ky) == 0) - { - return detail::last_one(std::forward(keys)...); - } - return find_or(std::forward(tab).at(ky), std::forward(keys)...); -} - -// ============================================================================ -// expect - -template class M, template class V> -result expect(const basic_value& v) noexcept -{ - try - { - return ok(get(v)); - } - catch(const std::exception& e) - { - return err(e.what()); - } -} -template class M, template class V> -result -expect(const basic_value& v, const toml::key& k) noexcept -{ - try - { - return ok(find(v, k)); - } - catch(const std::exception& e) - { - return err(e.what()); - } -} - -} // toml -#endif// TOML11_GET diff --git a/src/frontend/qt_sdl/toml/toml/lexer.hpp b/src/frontend/qt_sdl/toml/toml/lexer.hpp deleted file mode 100644 index ea5050b8..00000000 --- a/src/frontend/qt_sdl/toml/toml/lexer.hpp +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_LEXER_HPP -#define TOML11_LEXER_HPP -#include -#include -#include -#include - -#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<'\t'>>; -using lex_ws = repeat>; -using lex_newline = either, - sequence, character<'\n'>>>; -using lex_lower = in_range<'a', 'z'>; -using lex_upper = in_range<'A', 'Z'>; -using lex_alpha = either; -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, in_range<'a', 'f'>>; - -using lex_hex_prefix = sequence, character<'x'>>; -using lex_oct_prefix = sequence, character<'o'>>; -using lex_bin_prefix = sequence, character<'b'>>; -using lex_underscore = character<'_'>; -using lex_plus = character<'+'>; -using lex_minus = character<'-'>; -using lex_sign = either; - -// digit | nonzero 1*(digit | _ digit) -using lex_unsigned_dec_int = either>, at_least<1>>>, - lex_digit>; -// (+|-)? unsigned_dec_int -using lex_dec_int = sequence, lex_unsigned_dec_int>; - -// hex_prefix hex_dig *(hex_dig | _ hex_dig) -using lex_hex_int = sequence>, unlimited>>>; -// oct_prefix oct_dig *(oct_dig | _ oct_dig) -using lex_oct_int = sequence>, unlimited>>>; -// bin_prefix bin_dig *(bin_dig | _ bin_dig) -using lex_bin_int = sequence>, unlimited>>>; - -// (dec_int | hex_int | oct_int | bin_int) -using lex_integer = either; - -// =========================================================================== - -using lex_inf = sequence, character<'n'>, character<'f'>>; -using lex_nan = sequence, character<'a'>, character<'n'>>; -using lex_special_float = sequence, either>; - -using lex_zero_prefixable_int = sequence>, unlimited>>; - -using lex_fractional_part = sequence, lex_zero_prefixable_int>; - -using lex_exponent_part = sequence, character<'E'>>, - maybe, lex_zero_prefixable_int>; - -using lex_float = either>>>>; - -// =========================================================================== - -using lex_true = sequence, character<'r'>, - character<'u'>, character<'e'>>; -using lex_false = sequence, character<'a'>, character<'l'>, - character<'s'>, character<'e'>>; -using lex_boolean = either; - -// =========================================================================== - -using lex_date_fullyear = repeat>; -using lex_date_month = repeat>; -using lex_date_mday = repeat>; -using lex_time_delim = either, character<'t'>, character<' '>>; -using lex_time_hour = repeat>; -using lex_time_minute = repeat>; -using lex_time_second = repeat>; -using lex_time_secfrac = sequence, - repeat>>; - -using lex_time_numoffset = sequence, character<'-'>>, - sequence, - lex_time_minute>>; -using lex_time_offset = either, character<'z'>, - lex_time_numoffset>; - -using lex_partial_time = sequence, - lex_time_minute, character<':'>, - lex_time_second, maybe>; -using lex_full_date = sequence, - lex_date_month, character<'-'>, - lex_date_mday>; -using lex_full_time = sequence; - -using lex_offset_date_time = sequence; -using lex_local_date_time = sequence; -using lex_local_date = lex_full_date; -using lex_local_time = lex_partial_time; - -// =========================================================================== - -using lex_quotation_mark = character<'"'>; -using lex_basic_unescaped = exclude, // 0x09 (tab) is allowed - in_range<0x0A, 0x1F>, - character<0x22>, character<0x5C>, - character<0x7F>>>; - -using lex_escape = character<'\\'>; -using lex_escape_unicode_short = sequence, - repeat>>; -using lex_escape_unicode_long = sequence, - repeat>>; -using lex_escape_seq_char = either, character<'\\'>, - character<'b'>, character<'f'>, - character<'n'>, character<'r'>, - character<'t'>, - lex_escape_unicode_short, - lex_escape_unicode_long - >; -using lex_escaped = sequence; -using lex_basic_char = either; -using lex_basic_string = sequence, - 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>; -using lex_ml_basic_string_open = lex_ml_basic_string_delim; -using lex_ml_basic_string_close = sequence< - repeat>, - maybe, maybe - >; - -using lex_ml_basic_unescaped = exclude, // 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_newline, - repeat, unlimited>>; - -using lex_ml_basic_char = either; -using lex_ml_basic_body = repeat, - unlimited>; -using lex_ml_basic_string = sequence; - -using lex_literal_char = exclude, in_range<0x0A, 0x1F>, - character<0x7F>, character<0x27>>>; -using lex_apostrophe = character<'\''>; -using lex_literal_string = sequence, - lex_apostrophe>; - -// the same reason as above. -using lex_ml_literal_string_delim = repeat>; -using lex_ml_literal_string_open = lex_ml_literal_string_delim; -using lex_ml_literal_string_close = sequence< - repeat>, - maybe, maybe - >; - -using lex_ml_literal_char = exclude, - in_range<0x0A, 0x1F>, - character<0x7F>, - lex_ml_literal_string_delim>>; -using lex_ml_literal_body = repeat, - unlimited>; -using lex_ml_literal_string = sequence; - -using lex_string = either; - -// =========================================================================== -using lex_dot_sep = sequence, character<'.'>, maybe>; - -using lex_unquoted_key = repeat, character<'_'>>, - at_least<1>>; -using lex_quoted_key = either; -using lex_simple_key = either; -using lex_dotted_key = sequence, - at_least<1> - > - >; -using lex_key = either; - -using lex_keyval_sep = sequence, - character<'='>, - maybe>; - -using lex_std_table_open = character<'['>; -using lex_std_table_close = character<']'>; -using lex_std_table = sequence, - lex_key, - maybe, - lex_std_table_close>; - -using lex_array_table_open = sequence; -using lex_array_table_close = sequence; -using lex_array_table = sequence, - lex_key, - maybe, - lex_array_table_close>; - -using lex_utf8_1byte = in_range<0x00, 0x7F>; -using lex_utf8_2byte = sequence< - in_range(0xC2), static_cast(0xDF)>, - in_range(0x80), static_cast(0xBF)> - >; -using lex_utf8_3byte = sequence(0xE0)>, in_range(0xA0), static_cast(0xBF)>>, - sequence(0xE1), static_cast(0xEC)>, in_range(0x80), static_cast(0xBF)>>, - sequence(0xED)>, in_range(0x80), static_cast(0x9F)>>, - sequence(0xEE), static_cast(0xEF)>, in_range(0x80), static_cast(0xBF)>> - >, in_range(0x80), static_cast(0xBF)>>; -using lex_utf8_4byte = sequence(0xF0)>, in_range(0x90), static_cast(0xBF)>>, - sequence(0xF1), static_cast(0xF3)>, in_range(0x80), static_cast(0xBF)>>, - sequence(0xF4)>, in_range(0x80), static_cast(0x8F)>> - >, in_range(0x80), static_cast(0xBF)>, - in_range(0x80), static_cast(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, in_range<0x20, 0x7E>>; -using lex_comment = sequence, unlimited>>; - -} // detail -} // toml -#endif // TOML_LEXER_HPP diff --git a/src/frontend/qt_sdl/toml/toml/literal.hpp b/src/frontend/qt_sdl/toml/toml/literal.hpp deleted file mode 100644 index 04fbbc13..00000000 --- a/src/frontend/qt_sdl/toml/toml/literal.hpp +++ /dev/null @@ -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 -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::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(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(loc)) - { - return data.unwrap(); - } - else // none of them. - { - throw ::toml::syntax_error(data.unwrap_err(), source_location(loc)); - } - -} - -inline ::toml::basic_value -operator"" _toml(const char* str, std::size_t len) -{ - ::toml::detail::location loc( - std::string("TOML literal encoded in a C++ code"), - std::vector(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 -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(reinterpret_cast(str), - reinterpret_cast(str) + len)); - return literal_internal_impl(std::move(loc)); -} -#endif - -} // toml_literals -} // literals -} // toml -#endif//TOML11_LITERAL_HPP diff --git a/src/frontend/qt_sdl/toml/toml/parser.hpp b/src/frontend/qt_sdl/toml/toml/parser.hpp deleted file mode 100644 index bfa5531f..00000000 --- a/src/frontend/qt_sdl/toml/toml/parser.hpp +++ /dev/null @@ -1,2416 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_PARSER_HPP -#define TOML11_PARSER_HPP -#include -#include -#include - -#include "combinator.hpp" -#include "lexer.hpp" -#include "region.hpp" -#include "result.hpp" -#include "types.hpp" -#include "value.hpp" - -#ifndef TOML11_DISABLE_STD_FILESYSTEM -#ifdef __cpp_lib_filesystem -#if __has_include() -#define TOML11_HAS_STD_FILESYSTEM -#include -#endif // has_include() -#endif // __cpp_lib_filesystem -#endif // TOML11_DISABLE_STD_FILESYSTEM - -namespace toml -{ -namespace detail -{ - -inline result, std::string> -parse_boolean(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_boolean::invoke(loc)) - { - const auto reg = token.unwrap(); - if (reg.str() == "true") {return ok(std::make_pair(true, reg));} - else if(reg.str() == "false") {return ok(std::make_pair(false, reg));} - else // internal error. - { - throw internal_error(format_underline( - "toml::parse_boolean: internal error", - {{source_location(reg), "invalid token"}}), - source_location(reg)); - } - } - loc.reset(first); //rollback - return err(format_underline("toml::parse_boolean: ", - {{source_location(loc), "the next token is not a boolean"}})); -} - -inline result, std::string> -parse_binary_integer(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_bin_int::invoke(loc)) - { - auto str = token.unwrap().str(); - assert(str.size() > 2); // minimum -> 0b1 - integer retval(0), base(1); - for(auto i(str.rbegin()), e(str.rend() - 2); i!=e; ++i) - { - if (*i == '1'){retval += base; base *= 2;} - else if(*i == '0'){base *= 2;} - else if(*i == '_'){/* do nothing. */} - else // internal error. - { - throw internal_error(format_underline( - "toml::parse_integer: internal error", - {{source_location(token.unwrap()), "invalid token"}}), - source_location(loc)); - } - } - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_binary_integer:", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_octal_integer(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_oct_int::invoke(loc)) - { - auto str = token.unwrap().str(); - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - str.erase(str.begin()); str.erase(str.begin()); // remove `0o` prefix - - std::istringstream iss(str); - integer retval(0); - iss >> std::oct >> retval; - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_octal_integer:", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_hexadecimal_integer(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_hex_int::invoke(loc)) - { - auto str = token.unwrap().str(); - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - str.erase(str.begin()); str.erase(str.begin()); // remove `0x` prefix - - std::istringstream iss(str); - integer retval(0); - iss >> std::hex >> retval; - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_hexadecimal_integer", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_integer(location& loc) -{ - const auto first = loc.iter(); - if(first != loc.end() && *first == '0') - { - const auto second = std::next(first); - if(second == loc.end()) // the token is just zero. - { - loc.advance(); - return ok(std::make_pair(0, region(loc, first, second))); - } - - if(*second == 'b') {return parse_binary_integer (loc);} // 0b1100 - if(*second == 'o') {return parse_octal_integer (loc);} // 0o775 - if(*second == 'x') {return parse_hexadecimal_integer(loc);} // 0xC0FFEE - - if(std::isdigit(*second)) - { - return err(format_underline("toml::parse_integer: " - "leading zero in an Integer is not allowed.", - {{source_location(loc), "leading zero"}})); - } - else if(std::isalpha(*second)) - { - return err(format_underline("toml::parse_integer: " - "unknown integer prefix appeared.", - {{source_location(loc), "none of 0x, 0o, 0b"}})); - } - } - - if(const auto token = lex_dec_int::invoke(loc)) - { - auto str = token.unwrap().str(); - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - - std::istringstream iss(str); - integer retval(0); - iss >> retval; - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_integer: ", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_floating(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_float::invoke(loc)) - { - auto str = token.unwrap().str(); - if(str == "inf" || str == "+inf") - { - if(std::numeric_limits::has_infinity) - { - return ok(std::make_pair( - std::numeric_limits::infinity(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: inf value found" - " but the current environment does not support inf. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - else if(str == "-inf") - { - if(std::numeric_limits::has_infinity) - { - return ok(std::make_pair( - -std::numeric_limits::infinity(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: inf value found" - " but the current environment does not support inf. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - else if(str == "nan" || str == "+nan") - { - if(std::numeric_limits::has_quiet_NaN) - { - return ok(std::make_pair( - std::numeric_limits::quiet_NaN(), token.unwrap())); - } - else if(std::numeric_limits::has_signaling_NaN) - { - return ok(std::make_pair( - std::numeric_limits::signaling_NaN(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: NaN value found" - " but the current environment does not support NaN. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - else if(str == "-nan") - { - if(std::numeric_limits::has_quiet_NaN) - { - return ok(std::make_pair( - -std::numeric_limits::quiet_NaN(), token.unwrap())); - } - else if(std::numeric_limits::has_signaling_NaN) - { - return ok(std::make_pair( - -std::numeric_limits::signaling_NaN(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: NaN value found" - " but the current environment does not support NaN. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - std::istringstream iss(str); - floating v(0.0); - iss >> v; - return ok(std::make_pair(v, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_floating: ", - {{source_location(loc), "the next token is not a float"}})); -} - -inline std::string read_utf8_codepoint(const region& reg, const location& loc) -{ - const auto str = reg.str().substr(1); - std::uint_least32_t codepoint; - std::istringstream iss(str); - iss >> std::hex >> codepoint; - - const auto to_char = [](const std::uint_least32_t i) noexcept -> char { - const auto uc = static_cast(i); - return *reinterpret_cast(std::addressof(uc)); - }; - - std::string character; - if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. - { - character += static_cast(codepoint); - } - else if(codepoint < 0x800) //U+0080 ... U+07FF - { - // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 - character += to_char(0xC0| codepoint >> 6); - character += to_char(0x80|(codepoint & 0x3F)); - } - else if(codepoint < 0x10000) // U+0800...U+FFFF - { - if(0xD800 <= codepoint && codepoint <= 0xDFFF) - { - throw syntax_error(format_underline( - "toml::read_utf8_codepoint: codepoints in the range " - "[0xD800, 0xDFFF] are not valid UTF-8.", {{ - source_location(loc), "not a valid UTF-8 codepoint" - }}), source_location(loc)); - } - assert(codepoint < 0xD800 || 0xDFFF < codepoint); - // 1110yyyy 10yxxxxx 10xxxxxx - character += to_char(0xE0| codepoint >> 12); - character += to_char(0x80|(codepoint >> 6 & 0x3F)); - character += to_char(0x80|(codepoint & 0x3F)); - } - else if(codepoint < 0x110000) // U+010000 ... U+10FFFF - { - // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx - character += to_char(0xF0| codepoint >> 18); - character += to_char(0x80|(codepoint >> 12 & 0x3F)); - character += to_char(0x80|(codepoint >> 6 & 0x3F)); - character += to_char(0x80|(codepoint & 0x3F)); - } - else // out of UTF-8 region - { - throw syntax_error(format_underline("toml::read_utf8_codepoint:" - " input codepoint is too large.", - {{source_location(loc), "should be in [0x00..0x10FFFF]"}}), - source_location(loc)); - } - return character; -} - -inline result parse_escape_sequence(location& loc) -{ - const auto first = loc.iter(); - if(first == loc.end() || *first != '\\') - { - return err(format_underline("toml::parse_escape_sequence: ", {{ - source_location(loc), "the next token is not a backslash \"\\\""}})); - } - loc.advance(); - switch(*loc.iter()) - { - case '\\':{loc.advance(); return ok(std::string("\\"));} - case '"' :{loc.advance(); return ok(std::string("\""));} - case 'b' :{loc.advance(); return ok(std::string("\b"));} - case 't' :{loc.advance(); return ok(std::string("\t"));} - case 'n' :{loc.advance(); return ok(std::string("\n"));} - case 'f' :{loc.advance(); return ok(std::string("\f"));} - case 'r' :{loc.advance(); return ok(std::string("\r"));} - case 'u' : - { - if(const auto token = lex_escape_unicode_short::invoke(loc)) - { - return ok(read_utf8_codepoint(token.unwrap(), loc)); - } - else - { - return err(format_underline("parse_escape_sequence: " - "invalid token found in UTF-8 codepoint uXXXX.", - {{source_location(loc), "here"}})); - } - } - case 'U': - { - if(const auto token = lex_escape_unicode_long::invoke(loc)) - { - return ok(read_utf8_codepoint(token.unwrap(), loc)); - } - else - { - return err(format_underline("parse_escape_sequence: " - "invalid token found in UTF-8 codepoint Uxxxxxxxx", - {{source_location(loc), "here"}})); - } - } - } - - const auto msg = format_underline("parse_escape_sequence: " - "unknown escape sequence appeared.", {{source_location(loc), - "escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}}, - /* Hints = */{"if you want to write backslash as just one backslash, " - "use literal string like: regex = '<\\i\\c*\\s*>'"}); - loc.reset(first); - return err(msg); -} - -inline std::ptrdiff_t check_utf8_validity(const std::string& reg) -{ - location loc("tmp", reg); - const auto u8 = repeat::invoke(loc); - if(!u8 || loc.iter() != loc.end()) - { - const auto error_location = std::distance(loc.begin(), loc.iter()); - assert(0 <= error_location); - return error_location; - } - return -1; -} - -inline result, std::string> -parse_ml_basic_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_ml_basic_string::invoke(loc)) - { - auto inner_loc = loc; - inner_loc.reset(first); - - std::string retval; - retval.reserve(token.unwrap().size()); - - auto delim = lex_ml_basic_string_open::invoke(inner_loc); - if(!delim) - { - throw internal_error(format_underline( - "parse_ml_basic_string: invalid token", - {{source_location(inner_loc), "should be \"\"\""}}), - source_location(inner_loc)); - } - // immediate newline is ignored (if exists) - /* discard return value */ lex_newline::invoke(inner_loc); - - delim = none(); - while(!delim) - { - using lex_unescaped_seq = repeat< - either, unlimited>; - if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) - { - retval += unescaped.unwrap().str(); - } - if(auto escaped = parse_escape_sequence(inner_loc)) - { - retval += escaped.unwrap(); - } - if(auto esc_nl = lex_ml_basic_escaped_newline::invoke(inner_loc)) - { - // ignore newline after escape until next non-ws char - } - if(inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "parse_ml_basic_string: unexpected end of region", - {{source_location(inner_loc), "not sufficient token"}}), - source_location(inner_loc)); - } - delim = lex_ml_basic_string_close::invoke(inner_loc); - } - // `lex_ml_basic_string_close` allows 3 to 5 `"`s to allow 1 or 2 `"`s - // at just before the delimiter. Here, we need to attach `"`s at the - // end of the string body, if it exists. - // For detail, see the definition of `lex_ml_basic_string_close`. - assert(std::all_of(delim.unwrap().first(), delim.unwrap().last(), - [](const char c) noexcept {return c == '\"';})); - switch(delim.unwrap().size()) - { - case 3: {break;} - case 4: {retval += "\""; break;} - case 5: {retval += "\"\""; break;} - default: - { - throw internal_error(format_underline( - "parse_ml_basic_string: closing delimiter has invalid length", - {{source_location(inner_loc), "end of this"}}), - source_location(inner_loc)); - } - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair(toml::string(retval), token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_ml_basic_string: " - "the next token is not a valid multiline string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_basic_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_basic_string::invoke(loc)) - { - auto inner_loc = loc; - inner_loc.reset(first); - - auto quot = lex_quotation_mark::invoke(inner_loc); - if(!quot) - { - throw internal_error(format_underline("parse_basic_string: " - "invalid token", {{source_location(inner_loc), "should be \""}}), - source_location(inner_loc)); - } - - std::string retval; - retval.reserve(token.unwrap().size()); - - quot = none(); - while(!quot) - { - using lex_unescaped_seq = repeat; - if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) - { - retval += unescaped.unwrap().str(); - } - if(auto escaped = parse_escape_sequence(inner_loc)) - { - retval += escaped.unwrap(); - } - if(inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "parse_basic_string: unexpected end of region", - {{source_location(inner_loc), "not sufficient token"}}), - source_location(inner_loc)); - } - quot = lex_quotation_mark::invoke(inner_loc); - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair(toml::string(retval), token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); // rollback - return err(format_underline("toml::parse_basic_string: " - "the next token is not a valid string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_ml_literal_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_ml_literal_string::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_ml_literal_string_open::invoke(inner_loc); - if(!open) - { - throw internal_error(format_underline( - "parse_ml_literal_string: invalid token", - {{source_location(inner_loc), "should be '''"}}), - source_location(inner_loc)); - } - // immediate newline is ignored (if exists) - /* discard return value */ lex_newline::invoke(inner_loc); - - const auto body = lex_ml_literal_body::invoke(inner_loc); - - const auto close = lex_ml_literal_string_close::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "parse_ml_literal_string: invalid token", - {{source_location(inner_loc), "should be '''"}}), - source_location(inner_loc)); - } - // `lex_ml_literal_string_close` allows 3 to 5 `'`s to allow 1 or 2 `'`s - // at just before the delimiter. Here, we need to attach `'`s at the - // end of the string body, if it exists. - // For detail, see the definition of `lex_ml_basic_string_close`. - - std::string retval = body.unwrap().str(); - assert(std::all_of(close.unwrap().first(), close.unwrap().last(), - [](const char c) noexcept {return c == '\'';})); - switch(close.unwrap().size()) - { - case 3: {break;} - case 4: {retval += "'"; break;} - case 5: {retval += "''"; break;} - default: - { - throw internal_error(format_underline( - "parse_ml_literal_string: closing delimiter has invalid length", - {{source_location(inner_loc), "end of this"}}), - source_location(inner_loc)); - } - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair(toml::string(retval, toml::string_t::literal), - token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); // rollback - return err(format_underline("toml::parse_ml_literal_string: " - "the next token is not a valid multiline literal string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_literal_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_literal_string::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_apostrophe::invoke(inner_loc); - if(!open) - { - throw internal_error(format_underline( - "parse_literal_string: invalid token", - {{source_location(inner_loc), "should be '"}}), - source_location(inner_loc)); - } - - const auto body = repeat::invoke(inner_loc); - - const auto close = lex_apostrophe::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "parse_literal_string: invalid token", - {{source_location(inner_loc), "should be '"}}), - source_location(inner_loc)); - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair( - toml::string(body.unwrap().str(), toml::string_t::literal), - token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); // rollback - return err(format_underline("toml::parse_literal_string: " - "the next token is not a valid literal string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_string(location& loc) -{ - if(loc.iter() != loc.end() && *(loc.iter()) == '"') - { - if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '"' && - loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '"') - { - return parse_ml_basic_string(loc); - } - else - { - return parse_basic_string(loc); - } - } - else if(loc.iter() != loc.end() && *(loc.iter()) == '\'') - { - if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '\'' && - loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '\'') - { - return parse_ml_literal_string(loc); - } - else - { - return parse_literal_string(loc); - } - } - return err(format_underline("toml::parse_string: ", - {{source_location(loc), "the next token is not a string"}})); -} - -inline result, std::string> -parse_local_date(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_local_date::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto y = lex_date_fullyear::invoke(inner_loc); - if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') - { - throw internal_error(format_underline( - "toml::parse_inner_local_date: invalid year format", - {{source_location(inner_loc), "should be `-`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto m = lex_date_month::invoke(inner_loc); - if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') - { - throw internal_error(format_underline( - "toml::parse_local_date: invalid month format", - {{source_location(inner_loc), "should be `-`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto d = lex_date_mday::invoke(inner_loc); - if(!d) - { - throw internal_error(format_underline( - "toml::parse_local_date: invalid day format", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - - const auto year = static_cast(from_string(y.unwrap().str(), 0)); - const auto month = static_cast(from_string(m.unwrap().str(), 0)); - const auto day = static_cast(from_string(d.unwrap().str(), 0)); - - // We briefly check whether the input date is valid or not. But here, we - // only check if the RFC3339 compliance. - // Actually there are several special date that does not exist, - // because of historical reasons, such as 1582/10/5-1582/10/14 (only in - // several countries). But here, we do not care about such a complicated - // rule. It makes the code complicated and there is only low probability - // that such a specific date is needed in practice. If someone need to - // validate date accurately, that means that the one need a specialized - // library for their purpose in a different layer. - { - const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); - const auto max_day = (month == 2) ? (is_leap ? 29 : 28) : - ((month == 4 || month == 6 || month == 9 || month == 11) ? 30 : 31); - - if((month < 1 || 12 < month) || (day < 1 || max_day < day)) - { - throw syntax_error(format_underline("toml::parse_date: " - "invalid date: it does not conform RFC3339.", {{ - source_location(loc), "month should be 01-12, day should be" - " 01-28,29,30,31, depending on month/year." - }}), source_location(inner_loc)); - } - } - return ok(std::make_pair(local_date(year, static_cast(month - 1), day), - token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_local_date: ", - {{source_location(loc), "the next token is not a local_date"}})); - } -} - -inline result, std::string> -parse_local_time(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_local_time::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto h = lex_time_hour::invoke(inner_loc); - if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid year format", - {{source_location(inner_loc), "should be `:`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto m = lex_time_minute::invoke(inner_loc); - if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid month format", - {{source_location(inner_loc), "should be `:`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto s = lex_time_second::invoke(inner_loc); - if(!s) - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid second format", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - - const int hour = from_string(h.unwrap().str(), 0); - const int minute = from_string(m.unwrap().str(), 0); - const int second = from_string(s.unwrap().str(), 0); - - if((hour < 0 || 23 < hour) || (minute < 0 || 59 < minute) || - (second < 0 || 60 < second)) // it may be leap second - { - throw syntax_error(format_underline("toml::parse_time: " - "invalid time: it does not conform RFC3339.", {{ - source_location(loc), "hour should be 00-23, minute should be" - " 00-59, second should be 00-60 (depending on the leap" - " second rules.)"}}), source_location(inner_loc)); - } - - local_time time(hour, minute, second, 0, 0); - - const auto before_secfrac = inner_loc.iter(); - if(const auto secfrac = lex_time_secfrac::invoke(inner_loc)) - { - auto sf = secfrac.unwrap().str(); - sf.erase(sf.begin()); // sf.front() == '.' - switch(sf.size() % 3) - { - case 2: sf += '0'; break; - case 1: sf += "00"; break; - case 0: break; - default: break; - } - if(sf.size() >= 9) - { - time.millisecond = from_string(sf.substr(0, 3), 0u); - time.microsecond = from_string(sf.substr(3, 3), 0u); - time.nanosecond = from_string(sf.substr(6, 3), 0u); - } - else if(sf.size() >= 6) - { - time.millisecond = from_string(sf.substr(0, 3), 0u); - time.microsecond = from_string(sf.substr(3, 3), 0u); - } - else if(sf.size() >= 3) - { - time.millisecond = from_string(sf, 0u); - time.microsecond = 0u; - } - } - else - { - if(before_secfrac != inner_loc.iter()) - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid subsecond format", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - return ok(std::make_pair(time, token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_local_time: ", - {{source_location(loc), "the next token is not a local_time"}})); - } -} - -inline result, std::string> -parse_local_datetime(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_local_date_time::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - const auto date = parse_local_date(inner_loc); - if(!date || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "date, not datetime"}}), - source_location(inner_loc)); - } - const char delim = *(inner_loc.iter()); - if(delim != 'T' && delim != 't' && delim != ' ') - { - throw internal_error(format_underline( - "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "should be `T` or ` ` (space)"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto time = parse_local_time(inner_loc); - if(!time) - { - throw internal_error(format_underline( - "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "invalid time format"}}), - source_location(inner_loc)); - } - return ok(std::make_pair( - local_datetime(date.unwrap().first, time.unwrap().first), - token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_local_datetime: ", - {{source_location(loc), "the next token is not a local_datetime"}})); - } -} - -inline result, std::string> -parse_offset_datetime(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_offset_date_time::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - const auto datetime = parse_local_datetime(inner_loc); - if(!datetime || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_offset_datetime: invalid datetime format", - {{source_location(inner_loc), "date, not datetime"}}), - source_location(inner_loc)); - } - time_offset offset(0, 0); - if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) - { - const auto str = ofs.unwrap().str(); - - const auto hour = from_string(str.substr(1,2), 0); - const auto minute = from_string(str.substr(4,2), 0); - - if((hour < 0 || 23 < hour) || (minute < 0 || 59 < minute)) - { - throw syntax_error(format_underline("toml::parse_offset_datetime: " - "invalid offset: it does not conform RFC3339.", {{ - source_location(loc), "month should be 01-12, day should be" - " 01-28,29,30,31, depending on month/year." - }}), source_location(inner_loc)); - } - - if(str.front() == '+') - { - offset = time_offset(hour, minute); - } - else - { - offset = time_offset(-hour, -minute); - } - } - else if(*inner_loc.iter() != 'Z' && *inner_loc.iter() != 'z') - { - throw internal_error(format_underline( - "toml::parse_offset_datetime: invalid datetime format", - {{source_location(inner_loc), "should be `Z` or `+HH:MM`"}}), - source_location(inner_loc)); - } - return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset), - token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_offset_datetime: ", - {{source_location(loc), "the next token is not a offset_datetime"}})); - } -} - -inline result, std::string> -parse_simple_key(location& loc) -{ - if(const auto bstr = parse_basic_string(loc)) - { - return ok(std::make_pair(bstr.unwrap().first.str, bstr.unwrap().second)); - } - if(const auto lstr = parse_literal_string(loc)) - { - return ok(std::make_pair(lstr.unwrap().first.str, lstr.unwrap().second)); - } - if(const auto bare = lex_unquoted_key::invoke(loc)) - { - const auto reg = bare.unwrap(); - return ok(std::make_pair(reg.str(), reg)); - } - return err(format_underline("toml::parse_simple_key: ", - {{source_location(loc), "the next token is not a simple key"}})); -} - -// dotted key become vector of keys -inline result, region>, std::string> -parse_key(location& loc) -{ - const auto first = loc.iter(); - // dotted key -> `foo.bar.baz` where several single keys are chained by - // dots. Whitespaces between keys and dots are allowed. - if(const auto token = lex_dotted_key::invoke(loc)) - { - const auto reg = token.unwrap(); - location inner_loc(loc.name(), reg.str()); - std::vector keys; - - while(inner_loc.iter() != inner_loc.end()) - { - lex_ws::invoke(inner_loc); - if(const auto k = parse_simple_key(inner_loc)) - { - keys.push_back(k.unwrap().first); - } - else - { - throw internal_error(format_underline( - "toml::detail::parse_key: dotted key contains invalid key", - {{source_location(inner_loc), k.unwrap_err()}}), - source_location(inner_loc)); - } - - lex_ws::invoke(inner_loc); - if(inner_loc.iter() == inner_loc.end()) - { - break; - } - else if(*inner_loc.iter() == '.') - { - inner_loc.advance(); // to skip `.` - } - else - { - throw internal_error(format_underline("toml::parse_key: " - "dotted key contains invalid key ", - {{source_location(inner_loc), "should be `.`"}}), - source_location(inner_loc)); - } - } - return ok(std::make_pair(keys, reg)); - } - loc.reset(first); - - // simple_key: a single (basic_string|literal_string|bare key) - if(const auto smpl = parse_simple_key(loc)) - { - return ok(std::make_pair(std::vector(1, smpl.unwrap().first), - smpl.unwrap().second)); - } - return err(format_underline("toml::parse_key: an invalid key appeared.", - {{source_location(loc), "is not a valid key"}}, { - "bare keys : non-empty strings composed only of [A-Za-z0-9_-].", - "quoted keys: same as \"basic strings\" or 'literal strings'.", - "dotted keys: sequence of bare or quoted keys joined with a dot." - })); -} - -// forward-decl to implement parse_array and parse_table -template -result parse_value(location&); - -template -result, std::string> -parse_array(location& loc) -{ - using value_type = Value; - using array_type = typename value_type::array_type; - - const auto first = loc.iter(); - if(loc.iter() == loc.end()) - { - return err("toml::parse_array: input is empty"); - } - if(*loc.iter() != '[') - { - return err("toml::parse_array: token is not an array"); - } - loc.advance(); - - using lex_ws_comment_newline = repeat< - either, unlimited>; - - array_type retval; - while(loc.iter() != loc.end()) - { - lex_ws_comment_newline::invoke(loc); // skip - - if(loc.iter() != loc.end() && *loc.iter() == ']') - { - loc.advance(); // skip ']' - return ok(std::make_pair(retval, - region(loc, first, loc.iter()))); - } - - if(auto val = parse_value(loc)) - { - // After TOML v1.0.0-rc.1, array becomes to be able to have values - // with different types. So here we will omit this by default. - // - // But some of the test-suite checks if the parser accepts a hetero- - // geneous arrays, so we keep this for a while. -#ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS - if(!retval.empty() && retval.front().type() != val.as_ok().type()) - { - auto array_start_loc = loc; - array_start_loc.reset(first); - - throw syntax_error(format_underline("toml::parse_array: " - "type of elements should be the same each other.", { - {source_location(array_start_loc), "array starts here"}, - { - retval.front().location(), - "value has type " + stringize(retval.front().type()) - }, - { - val.unwrap().location(), - "value has different type, " + stringize(val.unwrap().type()) - } - }), source_location(loc)); - } -#endif - retval.push_back(std::move(val.unwrap())); - } - else - { - auto array_start_loc = loc; - array_start_loc.reset(first); - - throw syntax_error(format_underline("toml::parse_array: " - "value having invalid format appeared in an array", { - {source_location(array_start_loc), "array starts here"}, - {source_location(loc), "it is not a valid value."} - }), source_location(loc)); - } - - using lex_array_separator = sequence, character<','>>; - const auto sp = lex_array_separator::invoke(loc); - if(!sp) - { - lex_ws_comment_newline::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == ']') - { - loc.advance(); // skip ']' - return ok(std::make_pair(retval, - region(loc, first, loc.iter()))); - } - else - { - auto array_start_loc = loc; - array_start_loc.reset(first); - - throw syntax_error(format_underline("toml::parse_array:" - " missing array separator `,` after a value", { - {source_location(array_start_loc), "array starts here"}, - {source_location(loc), "should be `,`"} - }), source_location(loc)); - } - } - } - loc.reset(first); - throw syntax_error(format_underline("toml::parse_array: " - "array did not closed by `]`", - {{source_location(loc), "should be closed"}}), - source_location(loc)); -} - -template -result, region>, Value>, std::string> -parse_key_value_pair(location& loc) -{ - using value_type = Value; - - const auto first = loc.iter(); - auto key_reg = parse_key(loc); - if(!key_reg) - { - std::string msg = std::move(key_reg.unwrap_err()); - // if the next token is keyvalue-separator, it means that there are no - // key. then we need to show error as "empty key is not allowed". - if(const auto keyval_sep = lex_keyval_sep::invoke(loc)) - { - loc.reset(first); - msg = format_underline("toml::parse_key_value_pair: " - "empty key is not allowed.", - {{source_location(loc), "key expected before '='"}}); - } - return err(std::move(msg)); - } - - const auto kvsp = lex_keyval_sep::invoke(loc); - if(!kvsp) - { - std::string msg; - // if the line contains '=' after the invalid sequence, possibly the - // error is in the key (like, invalid character in bare key). - const auto line_end = std::find(loc.iter(), loc.end(), '\n'); - if(std::find(loc.iter(), line_end, '=') != line_end) - { - msg = format_underline("toml::parse_key_value_pair: " - "invalid format for key", - {{source_location(loc), "invalid character in key"}}, - {"Did you forget '.' to separate dotted-key?", - "Allowed characters for bare key are [0-9a-zA-Z_-]."}); - } - else // if not, the error is lack of key-value separator. - { - msg = format_underline("toml::parse_key_value_pair: " - "missing key-value separator `=`", - {{source_location(loc), "should be `=`"}}); - } - loc.reset(first); - return err(std::move(msg)); - } - - const auto after_kvsp = loc.iter(); // err msg - auto val = parse_value(loc); - if(!val) - { - std::string msg; - loc.reset(after_kvsp); - // check there is something not a comment/whitespace after `=` - if(sequence, maybe, lex_newline>::invoke(loc)) - { - loc.reset(after_kvsp); - msg = format_underline("toml::parse_key_value_pair: " - "missing value after key-value separator '='", - {{source_location(loc), "expected value, but got nothing"}}); - } - else // there is something not a comment/whitespace, so invalid format. - { - msg = std::move(val.unwrap_err()); - } - loc.reset(first); - return err(msg); - } - return ok(std::make_pair(std::move(key_reg.unwrap()), - std::move(val.unwrap()))); -} - -// for error messages. -template -std::string format_dotted_keys(InputIterator first, const InputIterator last) -{ - static_assert(std::is_same::value_type>::value,""); - - std::string retval(*first++); - for(; first != last; ++first) - { - retval += '.'; - retval += *first; - } - return retval; -} - -// forward decl for is_valid_forward_table_definition -result, region>, std::string> -parse_table_key(location& loc); -template -result, std::string> -parse_inline_table(location& loc); - -// The following toml file is allowed. -// ```toml -// [a.b.c] # here, table `a` has element `b`. -// foo = "bar" -// [a] # merge a = {baz = "qux"} to a = {b = {...}} -// baz = "qux" -// ``` -// But the following is not allowed. -// ```toml -// [a] -// b.c.foo = "bar" -// [a] # error! the same table [a] defined! -// baz = "qux" -// ``` -// The following is neither allowed. -// ```toml -// a = { b.c.foo = "bar"} -// [a] # error! the same table [a] defined! -// baz = "qux" -// ``` -// Here, it parses region of `tab->at(k)` as a table key and check the depth -// of the key. If the key region points deeper node, it would be allowed. -// Otherwise, the key points the same node. It would be rejected. -template -bool is_valid_forward_table_definition(const Value& fwd, const Value& inserting, - Iterator key_first, Iterator key_curr, Iterator key_last) -{ - // ------------------------------------------------------------------------ - // check type of the value to be inserted/merged - - std::string inserting_reg = ""; - if(const auto ptr = detail::get_region(inserting)) - { - inserting_reg = ptr->str(); - } - location inserting_def("internal", std::move(inserting_reg)); - if(const auto inlinetable = parse_inline_table(inserting_def)) - { - // check if we are overwriting existing table. - // ```toml - // # NG - // a.b = 42 - // a = {d = 3.14} - // ``` - // Inserting an inline table to a existing super-table is not allowed in - // any case. If we found it, we can reject it without further checking. - return false; - } - - // Valid and invalid cases when inserting to the [a.b] table: - // - // ## Invalid - // - // ```toml - // # invalid - // [a] - // b.c.d = "foo" - // [a.b] # a.b is already defined and closed - // d = "bar" - // ``` - // ```toml - // # invalid - // a = {b.c.d = "foo"} - // [a.b] # a is already defined and inline table is closed - // d = "bar" - // ``` - // ```toml - // # invalid - // a.b.c.d = "foo" - // [a.b] # a.b is already defined and dotted-key table is closed - // d = "bar" - // ``` - // - // ## Valid - // - // ```toml - // # OK. a.b is defined, but is *overwritable* - // [a.b.c] - // d = "foo" - // [a.b] - // d = "bar" - // ``` - // ```toml - // # OK. a.b is defined, but is *overwritable* - // [a] - // b.c.d = "foo" - // b.e = "bar" - // ``` - - // ------------------------------------------------------------------------ - // check table defined before - - std::string internal = ""; - if(const auto ptr = detail::get_region(fwd)) - { - internal = ptr->str(); - } - location def("internal", std::move(internal)); - if(const auto tabkeys = parse_table_key(def)) // [table.key] - { - // table keys always contains all the nodes from the root. - const auto& tks = tabkeys.unwrap().first; - if(std::size_t(std::distance(key_first, key_last)) == tks.size() && - std::equal(tks.begin(), tks.end(), key_first)) - { - // the keys are equivalent. it is not allowed. - return false; - } - // the keys are not equivalent. it is allowed. - return true; - } - if(const auto dotkeys = parse_key(def)) // a.b.c = "foo" - { - // consider the following case. - // [a] - // b.c = {d = 42} - // [a.b.c] - // e = 2.71 - // this defines the table [a.b.c] twice. no? - if(const auto reopening_dotkey_by_table = parse_table_key(inserting_def)) - { - // re-opening a dotkey-defined table by a table is invalid. - // only dotkey can append a key-val. Like: - // ```toml - // a.b.c = "foo" - // a.b.d = "bar" # OK. reopen `a.b` by dotkey - // [a.b] - // e = "bar" # Invalid. re-opening `a.b` by [a.b] is not allowed. - // ``` - return false; - } - - // a dotted key starts from the node representing a table in which the - // dotted key belongs to. - const auto& dks = dotkeys.unwrap().first; - if(std::size_t(std::distance(key_curr, key_last)) == dks.size() && - std::equal(dks.begin(), dks.end(), key_curr)) - { - // the keys are equivalent. it is not allowed. - return false; - } - // the keys are not equivalent. it is allowed. - return true; - } - return false; -} - -template -result -insert_nested_key(typename Value::table_type& root, const Value& v, - InputIterator iter, const InputIterator last, - region key_reg, - const bool is_array_of_table = false) -{ - static_assert(std::is_same::value_type>::value,""); - - using value_type = Value; - using table_type = typename value_type::table_type; - using array_type = typename value_type::array_type; - - const auto first = iter; - assert(iter != last); - - table_type* tab = std::addressof(root); - for(; iter != last; ++iter) // search recursively - { - const key& k = *iter; - if(std::next(iter) == last) // k is the last key - { - // XXX if the value is array-of-tables, there can be several - // tables that are in the same array. in that case, we need to - // find the last element and insert it to there. - if(is_array_of_table) - { - if(tab->count(k) == 1) // there is already an array of table - { - if(tab->at(k).is_table()) - { - // show special err msg for conflicting table - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), - "\") cannot be defined"), { - {tab->at(k).location(), "table already defined"}, - {v.location(), "this conflicts with the previous table"} - }), v.location()); - } - else if(!(tab->at(k).is_array())) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") collides with" - " existing value"), { - {tab->at(k).location(), - concat_to_string("this ", tab->at(k).type(), - " value already exists")}, - {v.location(), - "while inserting this array-of-tables"} - }), v.location()); - } - // the above if-else-if checks tab->at(k) is an array - auto& a = tab->at(k).as_array(); - // If table element is defined as [[array_of_tables]], it - // cannot be an empty array. If an array of tables is - // defined as `aot = []`, it cannot be appended. - if(a.empty() || !(a.front().is_table())) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") collides with" - " existing value"), { - {tab->at(k).location(), - concat_to_string("this ", tab->at(k).type(), - " value already exists")}, - {v.location(), - "while inserting this array-of-tables"} - }), v.location()); - } - // avoid conflicting array of table like the following. - // ```toml - // a = [{b = 42}] # define a as an array of *inline* tables - // [[a]] # a is an array of *multi-line* tables - // b = 54 - // ``` - // Here, from the type information, these cannot be detected - // because inline table is also a table. - // But toml v0.5.0 explicitly says it is invalid. The above - // array-of-tables has a static size and appending to the - // array is invalid. - // In this library, multi-line table value has a region - // that points to the key of the table (e.g. [[a]]). By - // comparing the first two letters in key, we can detect - // the array-of-table is inline or multiline. - if(const auto ptr = detail::get_region(a.front())) - { - if(ptr->str().substr(0,2) != "[[") - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") collides " - "with existing array-of-tables"), { - {tab->at(k).location(), - concat_to_string("this ", tab->at(k).type(), - " value has static size")}, - {v.location(), - "appending it to the statically sized array"} - }), v.location()); - } - } - a.push_back(v); - return ok(true); - } - else // if not, we need to create the array of table - { - // XXX: Consider the following array of tables. - // ```toml - // # This is a comment. - // [[aot]] - // foo = "bar" - // ``` - // Here, the comment is for `aot`. But here, actually two - // values are defined. An array that contains tables, named - // `aot`, and the 0th element of the `aot`, `{foo = "bar"}`. - // Those two are different from each other. But both of them - // points to the same portion of the TOML file, `[[aot]]`, - // so `key_reg.comments()` returns `# This is a comment`. - // If it is assigned as a comment of `aot` defined here, the - // comment will be duplicated. Both the `aot` itself and - // the 0-th element will have the same comment. This causes - // "duplication of the same comments" bug when the data is - // serialized. - // Next, consider the following. - // ```toml - // # comment 1 - // aot = [ - // # comment 2 - // {foo = "bar"}, - // ] - // ``` - // In this case, we can distinguish those two comments. So - // here we need to add "comment 1" to the `aot` and - // "comment 2" to the 0th element of that. - // To distinguish those two, we check the key region. - std::vector comments{/* empty by default */}; - if(key_reg.str().substr(0, 2) != "[[") - { - comments = key_reg.comments(); - } - value_type aot(array_type(1, v), key_reg, std::move(comments)); - tab->insert(std::make_pair(k, aot)); - return ok(true); - } - } // end if(array of table) - - if(tab->count(k) == 1) - { - if(tab->at(k).is_table() && v.is_table()) - { - if(!is_valid_forward_table_definition( - tab->at(k), v, first, iter, last)) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: table (\"", - format_dotted_keys(first, last), - "\") already exists."), { - {tab->at(k).location(), "table already exists here"}, - {v.location(), "table defined twice"} - }), v.location()); - } - // to allow the following toml file. - // [a.b.c] - // d = 42 - // [a] - // e = 2.71 - auto& t = tab->at(k).as_table(); - for(const auto& kv : v.as_table()) - { - if(tab->at(k).contains(kv.first)) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: value (\"", - format_dotted_keys(first, last), - "\") already exists."), { - {t.at(kv.first).location(), "already exists here"}, - {v.location(), "this defined twice"} - }), v.location()); - } - t[kv.first] = kv.second; - } - detail::change_region(tab->at(k), key_reg); - return ok(true); - } - else if(v.is_table() && - tab->at(k).is_array() && - tab->at(k).as_array().size() > 0 && - tab->at(k).as_array().front().is_table()) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of tables (\"", - format_dotted_keys(first, last), "\") already exists."), { - {tab->at(k).location(), "array of tables defined here"}, - {v.location(), "table conflicts with the previous array of table"} - }), v.location()); - } - else - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: value (\"", - format_dotted_keys(first, last), "\") already exists."), { - {tab->at(k).location(), "value already exists here"}, - {v.location(), "value defined twice"} - }), v.location()); - } - } - tab->insert(std::make_pair(k, v)); - return ok(true); - } - else // k is not the last one, we should insert recursively - { - // if there is no corresponding value, insert it first. - // related: you don't need to write - // # [x] - // # [x.y] - // to write - // [x.y.z] - if(tab->count(k) == 0) - { - // a table that is defined implicitly doesn't have any comments. - (*tab)[k] = value_type(table_type{}, key_reg, {/*no comment*/}); - } - - // type checking... - if(tab->at(k).is_table()) - { - // According to toml-lang/toml:36d3091b3 "Clarify that inline - // tables are immutable", check if it adds key-value pair to an - // inline table. - if(const auto* ptr = get_region(tab->at(k))) - { - // here, if the value is a (multi-line) table, the region - // should be something like `[table-name]`. - if(ptr->front() == '{') - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: inserting to an inline table (", - format_dotted_keys(first, std::next(iter)), - ") but inline tables are immutable"), { - {tab->at(k).location(), "inline tables are immutable"}, - {v.location(), "inserting this"} - }), v.location()); - } - } - tab = std::addressof((*tab)[k].as_table()); - } - else if(tab->at(k).is_array()) // inserting to array-of-tables? - { - auto& a = (*tab)[k].as_array(); - if(!a.back().is_table()) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: target (", - format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), { - {a.back().location(), concat_to_string( - "actual type is ", a.back().type())}, - {v.location(), "inserting this"} - }), v.location()); - } - tab = std::addressof(a.back().as_table()); - } - else - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: target (", - format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), { - {tab->at(k).location(), concat_to_string( - "actual type is ", tab->at(k).type())}, - {v.location(), "inserting this"} - }), v.location()); - } - } - } - return err(std::string("toml::detail::insert_nested_key: never reach here")); -} - -template -result, std::string> -parse_inline_table(location& loc) -{ - using value_type = Value; - using table_type = typename value_type::table_type; - - const auto first = loc.iter(); - table_type retval; - if(!(loc.iter() != loc.end() && *loc.iter() == '{')) - { - return err(format_underline("toml::parse_inline_table: ", - {{source_location(loc), "the next token is not an inline table"}})); - } - loc.advance(); - - // check if the inline table is an empty table = { } - maybe::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == '}') - { - loc.advance(); // skip `}` - return ok(std::make_pair(retval, region(loc, first, loc.iter()))); - } - - // it starts from "{". it should be formatted as inline-table - while(loc.iter() != loc.end()) - { - const auto kv_r = parse_key_value_pair(loc); - if(!kv_r) - { - return err(kv_r.unwrap_err()); - } - - const auto& kvpair = kv_r.unwrap(); - const std::vector& keys = kvpair.first.first; - const auto& key_reg = kvpair.first.second; - const value_type& val = kvpair.second; - - const auto inserted = - insert_nested_key(retval, val, keys.begin(), keys.end(), key_reg); - if(!inserted) - { - throw internal_error("toml::parse_inline_table: " - "failed to insert value into table: " + inserted.unwrap_err(), - source_location(loc)); - } - - using lex_table_separator = sequence, character<','>>; - const auto sp = lex_table_separator::invoke(loc); - - if(!sp) - { - maybe::invoke(loc); - - if(loc.iter() == loc.end()) - { - throw syntax_error(format_underline( - "toml::parse_inline_table: missing table separator `}` ", - {{source_location(loc), "should be `}`"}}), - source_location(loc)); - } - else if(*loc.iter() == '}') - { - loc.advance(); // skip `}` - return ok(std::make_pair( - retval, region(loc, first, loc.iter()))); - } - else if(*loc.iter() == '#' || *loc.iter() == '\r' || *loc.iter() == '\n') - { - throw syntax_error(format_underline( - "toml::parse_inline_table: missing curly brace `}`", - {{source_location(loc), "should be `}`"}}), - source_location(loc)); - } - else - { - throw syntax_error(format_underline( - "toml::parse_inline_table: missing table separator `,` ", - {{source_location(loc), "should be `,`"}}), - source_location(loc)); - } - } - else // `,` is found - { - maybe::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == '}') - { - throw syntax_error(format_underline( - "toml::parse_inline_table: trailing comma is not allowed in" - " an inline table", - {{source_location(loc), "should be `}`"}}), - source_location(loc)); - } - } - } - loc.reset(first); - throw syntax_error(format_underline("toml::parse_inline_table: " - "inline table did not closed by `}`", - {{source_location(loc), "should be closed"}}), - source_location(loc)); -} - -inline result guess_number_type(const location& l) -{ - // This function tries to find some (common) mistakes by checking characters - // that follows the last character of a value. But it is often difficult - // because some non-newline characters can appear after a value. E.g. - // spaces, tabs, commas (in an array or inline table), closing brackets - // (of an array or inline table), comment-sign (#). Since this function - // does not parse further, those characters are always allowed to be there. - location loc = l; - - if(lex_offset_date_time::invoke(loc)) {return ok(value_t::offset_datetime);} - loc.reset(l.iter()); - - if(lex_local_date_time::invoke(loc)) - { - // bad offset may appear after this. - if(loc.iter() != loc.end() && (*loc.iter() == '+' || *loc.iter() == '-' - || *loc.iter() == 'Z' || *loc.iter() == 'z')) - { - return err(format_underline("bad offset: should be [+-]HH:MM or Z", - {{source_location(loc), "[+-]HH:MM or Z"}}, - {"pass: +09:00, -05:30", "fail: +9:00, -5:30"})); - } - return ok(value_t::local_datetime); - } - loc.reset(l.iter()); - - if(lex_local_date::invoke(loc)) - { - // bad time may appear after this. - // A space is allowed as a delimiter between local time. But there are - // both cases in which a space becomes valid or invalid. - // - invalid: 2019-06-16 7:00:00 - // - valid : 2019-06-16 07:00:00 - if(loc.iter() != loc.end()) - { - const auto c = *loc.iter(); - if(c == 'T' || c == 't') - { - return err(format_underline("bad time: should be HH:MM:SS.subsec", - {{source_location(loc), "HH:MM:SS.subsec"}}, - {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", - "fail: 1979-05-27T7:32:00, 1979-05-27 17:32"})); - } - if('0' <= c && c <= '9') - { - return err(format_underline("bad time: missing T", - {{source_location(loc), "T or space required here"}}, - {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", - "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); - } - if(c == ' ' && std::next(loc.iter()) != loc.end() && - ('0' <= *std::next(loc.iter()) && *std::next(loc.iter())<= '9')) - { - loc.advance(); - return err(format_underline("bad time: should be HH:MM:SS.subsec", - {{source_location(loc), "HH:MM:SS.subsec"}}, - {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", - "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); - } - } - return ok(value_t::local_date); - } - loc.reset(l.iter()); - - if(lex_local_time::invoke(loc)) {return ok(value_t::local_time);} - loc.reset(l.iter()); - - if(lex_float::invoke(loc)) - { - if(loc.iter() != loc.end() && *loc.iter() == '_') - { - return err(format_underline("bad float: `_` should be surrounded by digits", - {{source_location(loc), "here"}}, - {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", - "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); - } - return ok(value_t::floating); - } - loc.reset(l.iter()); - - if(lex_integer::invoke(loc)) - { - if(loc.iter() != loc.end()) - { - const auto c = *loc.iter(); - if(c == '_') - { - return err(format_underline("bad integer: `_` should be surrounded by digits", - {{source_location(loc), "here"}}, - {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", - "fail: 1__000, 0123"})); - } - if('0' <= c && c <= '9') - { - // leading zero. point '0' - loc.retrace(); - return err(format_underline("bad integer: leading zero", - {{source_location(loc), "here"}}, - {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", - "fail: 1__000, 0123"})); - } - if(c == ':' || c == '-') - { - return err(format_underline("bad datetime: invalid format", - {{source_location(loc), "here"}}, - {"pass: 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z", - "fail: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30"})); - } - if(c == '.' || c == 'e' || c == 'E') - { - return err(format_underline("bad float: invalid format", - {{source_location(loc), "here"}}, - {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", - "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); - } - } - return ok(value_t::integer); - } - if(loc.iter() != loc.end() && *loc.iter() == '.') - { - return err(format_underline("bad float: invalid format", - {{source_location(loc), "integer part required before this"}}, - {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", - "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); - } - if(loc.iter() != loc.end() && *loc.iter() == '_') - { - return err(format_underline("bad number: `_` should be surrounded by digits", - {{source_location(loc), "`_` is not surrounded by digits"}}, - {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", - "fail: 1__000, 0123"})); - } - return err(format_underline("bad format: unknown value appeared", - {{source_location(loc), "here"}})); -} - -inline result guess_value_type(const location& loc) -{ - switch(*loc.iter()) - { - case '"' : {return ok(value_t::string); } - case '\'': {return ok(value_t::string); } - case 't' : {return ok(value_t::boolean); } - case 'f' : {return ok(value_t::boolean); } - case '[' : {return ok(value_t::array); } - case '{' : {return ok(value_t::table); } - case 'i' : {return ok(value_t::floating);} // inf. - case 'n' : {return ok(value_t::floating);} // nan. - default : {return guess_number_type(loc);} - } -} - -template -result -parse_value_helper(result, std::string> rslt) -{ - if(rslt.is_ok()) - { - auto comments = rslt.as_ok().second.comments(); - return ok(Value(std::move(rslt.as_ok()), std::move(comments))); - } - else - { - return err(std::move(rslt.as_err())); - } -} - -template -result parse_value(location& loc) -{ - const auto first = loc.iter(); - if(first == loc.end()) - { - return err(format_underline("toml::parse_value: input is empty", - {{source_location(loc), ""}})); - } - - const auto type = guess_value_type(loc); - if(!type) - { - return err(type.unwrap_err()); - } - - switch(type.unwrap()) - { - case value_t::boolean : {return parse_value_helper(parse_boolean(loc) );} - case value_t::integer : {return parse_value_helper(parse_integer(loc) );} - case value_t::floating : {return parse_value_helper(parse_floating(loc) );} - case value_t::string : {return parse_value_helper(parse_string(loc) );} - case value_t::offset_datetime: {return parse_value_helper(parse_offset_datetime(loc) );} - case value_t::local_datetime : {return parse_value_helper(parse_local_datetime(loc) );} - case value_t::local_date : {return parse_value_helper(parse_local_date(loc) );} - case value_t::local_time : {return parse_value_helper(parse_local_time(loc) );} - case value_t::array : {return parse_value_helper(parse_array(loc) );} - case value_t::table : {return parse_value_helper(parse_inline_table(loc));} - default: - { - const auto msg = format_underline("toml::parse_value: " - "unknown token appeared", {{source_location(loc), "unknown"}}); - loc.reset(first); - return err(msg); - } - } -} - -inline result, region>, std::string> -parse_table_key(location& loc) -{ - if(auto token = lex_std_table::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_std_table_open::invoke(inner_loc); - if(!open || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_table_key: no `[`", - {{source_location(inner_loc), "should be `[`"}}), - source_location(inner_loc)); - } - // to skip [ a . b . c ] - // ^----------- this whitespace - lex_ws::invoke(inner_loc); - const auto keys = parse_key(inner_loc); - if(!keys) - { - throw internal_error(format_underline( - "toml::parse_table_key: invalid key", - {{source_location(inner_loc), "not key"}}), - source_location(inner_loc)); - } - // to skip [ a . b . c ] - // ^-- this whitespace - lex_ws::invoke(inner_loc); - const auto close = lex_std_table_close::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "toml::parse_table_key: no `]`", - {{source_location(inner_loc), "should be `]`"}}), - source_location(inner_loc)); - } - - // after [table.key], newline or EOF(empty table) required. - if(loc.iter() != loc.end()) - { - using lex_newline_after_table_key = - sequence, maybe, lex_newline>; - const auto nl = lex_newline_after_table_key::invoke(loc); - if(!nl) - { - throw syntax_error(format_underline( - "toml::parse_table_key: newline required after [table.key]", - {{source_location(loc), "expected newline"}}), - source_location(loc)); - } - } - return ok(std::make_pair(keys.unwrap().first, token.unwrap())); - } - else - { - return err(format_underline("toml::parse_table_key: " - "not a valid table key", {{source_location(loc), "here"}})); - } -} - -inline result, region>, std::string> -parse_array_table_key(location& loc) -{ - if(auto token = lex_array_table::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_array_table_open::invoke(inner_loc); - if(!open || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_array_table_key: no `[[`", - {{source_location(inner_loc), "should be `[[`"}}), - source_location(inner_loc)); - } - lex_ws::invoke(inner_loc); - const auto keys = parse_key(inner_loc); - if(!keys) - { - throw internal_error(format_underline( - "toml::parse_array_table_key: invalid key", - {{source_location(inner_loc), "not a key"}}), - source_location(inner_loc)); - } - lex_ws::invoke(inner_loc); - const auto close = lex_array_table_close::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "toml::parse_table_key: no `]]`", - {{source_location(inner_loc), "should be `]]`"}}), - source_location(inner_loc)); - } - - // after [[table.key]], newline or EOF(empty table) required. - if(loc.iter() != loc.end()) - { - using lex_newline_after_table_key = - sequence, maybe, lex_newline>; - const auto nl = lex_newline_after_table_key::invoke(loc); - if(!nl) - { - throw syntax_error(format_underline("toml::" - "parse_array_table_key: newline required after [[table.key]]", - {{source_location(loc), "expected newline"}}), - source_location(loc)); - } - } - return ok(std::make_pair(keys.unwrap().first, token.unwrap())); - } - else - { - return err(format_underline("toml::parse_array_table_key: " - "not a valid table key", {{source_location(loc), "here"}})); - } -} - -// parse table body (key-value pairs until the iter hits the next [tablekey]) -template -result -parse_ml_table(location& loc) -{ - using value_type = Value; - using table_type = typename value_type::table_type; - - const auto first = loc.iter(); - if(first == loc.end()) - { - return ok(table_type{}); - } - - // XXX at lest one newline is needed. - using skip_line = repeat< - sequence, maybe, lex_newline>, at_least<1>>; - skip_line::invoke(loc); - lex_ws::invoke(loc); - - table_type tab; - while(loc.iter() != loc.end()) - { - lex_ws::invoke(loc); - const auto before = loc.iter(); - if(const auto tmp = parse_array_table_key(loc)) // next table found - { - loc.reset(before); - return ok(tab); - } - if(const auto tmp = parse_table_key(loc)) // next table found - { - loc.reset(before); - return ok(tab); - } - - if(const auto kv = parse_key_value_pair(loc)) - { - const auto& kvpair = kv.unwrap(); - const std::vector& keys = kvpair.first.first; - const auto& key_reg = kvpair.first.second; - const value_type& val = kvpair.second; - const auto inserted = - insert_nested_key(tab, val, keys.begin(), keys.end(), key_reg); - if(!inserted) - { - return err(inserted.unwrap_err()); - } - } - else - { - return err(kv.unwrap_err()); - } - - // comment lines are skipped by the above function call. - // However, since the `skip_line` requires at least 1 newline, it fails - // if the file ends with ws and/or comment without newline. - // `skip_line` matches `ws? + comment? + newline`, not `ws` or `comment` - // itself. To skip the last ws and/or comment, call lexers. - // It does not matter if these fails, so the return value is discarded. - lex_ws::invoke(loc); - lex_comment::invoke(loc); - - // skip_line is (whitespace? comment? newline)_{1,}. multiple empty lines - // and comments after the last key-value pairs are allowed. - const auto newline = skip_line::invoke(loc); - if(!newline && loc.iter() != loc.end()) - { - const auto before2 = loc.iter(); - lex_ws::invoke(loc); // skip whitespace - const auto msg = format_underline("toml::parse_table: " - "invalid line format", {{source_location(loc), concat_to_string( - "expected newline, but got '", show_char(*loc.iter()), "'.")}}); - loc.reset(before2); - return err(msg); - } - - // the skip_lines only matches with lines that includes newline. - // to skip the last line that includes comment and/or whitespace - // but no newline, call them one more time. - lex_ws::invoke(loc); - lex_comment::invoke(loc); - } - return ok(tab); -} - -template -result parse_toml_file(location& loc) -{ - using value_type = Value; - using table_type = typename value_type::table_type; - - const auto first = loc.iter(); - if(first == loc.end()) - { - // For empty files, return an empty table with an empty region (zero-length). - // Without the region, error messages would miss the filename. - return ok(value_type(table_type{}, region(loc, first, first), {})); - } - - // put the first line as a region of a file - // Here first != loc.end(), so taking std::next is okay - const region file(loc, first, std::next(loc.iter())); - - // The first successive comments that are separated from the first value - // by an empty line are for a file itself. - // ```toml - // # this is a comment for a file. - // - // key = "the first value" - // ``` - // ```toml - // # this is a comment for "the first value". - // key = "the first value" - // ``` - std::vector comments; - using lex_first_comments = sequence< - repeat, lex_comment, lex_newline>, at_least<1>>, - sequence, lex_newline> - >; - if(const auto token = lex_first_comments::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - while(inner_loc.iter() != inner_loc.end()) - { - maybe::invoke(inner_loc); // remove ws if exists - if(lex_newline::invoke(inner_loc)) - { - assert(inner_loc.iter() == inner_loc.end()); - break; // empty line found. - } - auto com = lex_comment::invoke(inner_loc).unwrap().str(); - com.erase(com.begin()); // remove # sign - comments.push_back(std::move(com)); - lex_newline::invoke(inner_loc); - } - } - - table_type data; - // root object is also a table, but without [tablename] - if(const auto tab = parse_ml_table(loc)) - { - data = std::move(tab.unwrap()); - } - else // failed (empty table is regarded as success in parse_ml_table) - { - return err(tab.unwrap_err()); - } - while(loc.iter() != loc.end()) - { - // here, the region of [table] is regarded as the table-key because - // the table body is normally too big and it is not so informative - // if the first key-value pair of the table is shown in the error - // message. - if(const auto tabkey = parse_array_table_key(loc)) - { - const auto tab = parse_ml_table(loc); - if(!tab){return err(tab.unwrap_err());} - - const auto& tk = tabkey.unwrap(); - const auto& keys = tk.first; - const auto& reg = tk.second; - - const auto inserted = insert_nested_key(data, - value_type(tab.unwrap(), reg, reg.comments()), - keys.begin(), keys.end(), reg, - /*is_array_of_table=*/ true); - if(!inserted) {return err(inserted.unwrap_err());} - - continue; - } - if(const auto tabkey = parse_table_key(loc)) - { - const auto tab = parse_ml_table(loc); - if(!tab){return err(tab.unwrap_err());} - - const auto& tk = tabkey.unwrap(); - const auto& keys = tk.first; - const auto& reg = tk.second; - - const auto inserted = insert_nested_key(data, - value_type(tab.unwrap(), reg, reg.comments()), - keys.begin(), keys.end(), reg); - if(!inserted) {return err(inserted.unwrap_err());} - - continue; - } - return err(format_underline("toml::parse_toml_file: " - "unknown line appeared", {{source_location(loc), "unknown format"}})); - } - - return ok(Value(std::move(data), file, comments)); -} - -} // detail - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value -parse(std::istream& is, const std::string& fname = "unknown file") -{ - using value_type = basic_value; - - const auto beg = is.tellg(); - is.seekg(0, std::ios::end); - const auto end = is.tellg(); - const auto fsize = end - beg; - is.seekg(beg); - - // read whole file as a sequence of char - assert(fsize >= 0); - std::vector letters(static_cast(fsize)); - is.read(letters.data(), fsize); - - // append LF. - // Although TOML does not require LF at the EOF, to make parsing logic - // simpler, we "normalize" the content by adding LF if it does not exist. - // It also checks if the last char is CR, to avoid changing the meaning. - // This is not the *best* way to deal with the last character, but is a - // simple and quick fix. - if(!letters.empty() && letters.back() != '\n' && letters.back() != '\r') - { - letters.push_back('\n'); - } - - detail::location loc(std::move(fname), std::move(letters)); - - // skip BOM if exists. - // XXX component of BOM (like 0xEF) exceeds the representable range of - // signed char, so on some (actually, most) of the environment, these cannot - // be compared to char. However, since we are always out of luck, we need to - // check our chars are equivalent to BOM. To do this, first we need to - // convert char to unsigned char to guarantee the comparability. - if(loc.source()->size() >= 3) - { - std::array BOM; - std::memcpy(BOM.data(), loc.source()->data(), 3); - if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF) - { - loc.advance(3); // BOM found. skip. - } - } - - const auto data = detail::parse_toml_file(loc); - if(!data) - { - throw syntax_error(data.unwrap_err(), source_location(loc)); - } - return data.unwrap(); -} - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value parse(const std::string& fname) -{ - std::ifstream ifs(fname.c_str(), std::ios_base::binary); - if(!ifs.good()) - { - throw std::runtime_error("toml::parse: file open error -> " + fname); - } - return parse(ifs, fname); -} - -#ifdef TOML11_HAS_STD_FILESYSTEM -// This function just forwards `parse("filename.toml")` to std::string version -// to avoid the ambiguity in overload resolution. -// -// Both std::string and std::filesystem::path are convertible from const char*. -// Without this, both parse(std::string) and parse(std::filesystem::path) -// matches to parse("filename.toml"). This breaks the existing code. -// -// This function exactly matches to the invocation with c-string. -// So this function is preferred than others and the ambiguity disappears. -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value parse(const char* fname) -{ - return parse(std::string(fname)); -} - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value parse(const std::filesystem::path& fpath) -{ - std::ifstream ifs(fpath, std::ios_base::binary); - if(!ifs.good()) - { - throw std::runtime_error("toml::parse: file open error -> " + - fpath.string()); - } - return parse(ifs, fpath.string()); -} -#endif // TOML11_HAS_STD_FILESYSTEM - -} // toml -#endif// TOML11_PARSER_HPP diff --git a/src/frontend/qt_sdl/toml/toml/region.hpp b/src/frontend/qt_sdl/toml/toml/region.hpp deleted file mode 100644 index 2e01e51d..00000000 --- a/src/frontend/qt_sdl/toml/toml/region.hpp +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_REGION_HPP -#define TOML11_REGION_HPP -#include -#include -#include -#include -#include -#include -#include -#include "color.hpp" - -namespace toml -{ -namespace detail -{ - -// helper function to avoid std::string(0, 'c') or std::string(iter, iter) -template -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 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::const_iterator; - using difference_type = typename const_iterator::difference_type; - using source_ptr = std::shared_ptr>; - - location(std::string source_name, std::vector cont) - : source_(std::make_shared>(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>(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(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::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::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::count(rollback, this->iter_, '\n')); - } - else // iter < rollback [[unlikely]] - { - this->line_number_ += static_cast( - 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; - 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(sz); - } - std::size_t after() const noexcept override - { - const auto sz = std::distance(this->iter(), this->line_end()); - assert(sz >= 0); - return static_cast(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::const_iterator; - using source_ptr = std::shared_ptr>; - - // 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(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(sz); - } - std::size_t before() const noexcept override - { - const auto sz = std::distance(this->line_begin(), this->first()); - assert(sz >= 0); - return static_cast(sz); - } - std::size_t after() const noexcept override - { - const auto sz = std::distance(this->last(), this->line_end()); - assert(sz >= 0); - return static_cast(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; - 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 comments() const override - { - // assuming the current region (`*this`) points a value. - // ```toml - // a = "value" - // ^^^^^^^- this region - // ``` - using rev_iter = std::reverse_iterator; - - std::vector 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 diff --git a/src/frontend/qt_sdl/toml/toml/result.hpp b/src/frontend/qt_sdl/toml/toml/result.hpp deleted file mode 100644 index 77cd46c6..00000000 --- a/src/frontend/qt_sdl/toml/toml/result.hpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include - -namespace toml -{ - -template -struct success -{ - using value_type = T; - value_type value; - - explicit success(const value_type& v) - noexcept(std::is_nothrow_copy_constructible::value) - : value(v) - {} - explicit success(value_type&& v) - noexcept(std::is_nothrow_move_constructible::value) - : value(std::move(v)) - {} - - template - explicit success(U&& v): value(std::forward(v)) {} - - template - explicit success(const success& v): value(v.value) {} - template - explicit success(success&& 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 -struct failure -{ - using value_type = T; - value_type value; - - explicit failure(const value_type& v) - noexcept(std::is_nothrow_copy_constructible::value) - : value(v) - {} - explicit failure(value_type&& v) - noexcept(std::is_nothrow_move_constructible::value) - : value(std::move(v)) - {} - - template - explicit failure(U&& v): value(std::forward(v)) {} - - template - explicit failure(const failure& v): value(v.value) {} - template - explicit failure(failure&& 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 -success::type>::type> -ok(T&& v) -{ - return success< - typename std::remove_cv::type>::type - >(std::forward(v)); -} -template -failure::type>::type> -err(T&& v) -{ - return failure< - typename std::remove_cv::type>::type - >(std::forward(v)); -} - -inline success ok(const char* literal) -{ - return success(std::string(literal)); -} -inline failure err(const char* literal) -{ - return failure(std::string(literal)); -} - - -template -struct result -{ - using value_type = T; - using error_type = E; - using success_type = success; - using failure_type = failure; - - 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 - result(const success& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - template - result(const failure& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - template - result(success&& 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 - result(failure&& 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 - result& operator=(const success& 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 - result& operator=(const failure& 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 - result& operator=(success&& 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 - result& operator=(failure&& 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 - 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; - } - } - template - 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; - } - } - - 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 - 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; - } - template - 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; - } - - 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 - template - result, error_type> - map(F&& f) & - { - if(this->is_ok()){return ok(f(this->as_ok()));} - return err(this->as_err()); - } - template - result, error_type> - map(F&& f) const& - { - if(this->is_ok()){return ok(f(this->as_ok()));} - return err(this->as_err()); - } - template - result, 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 - template - result> - map_err(F&& f) & - { - if(this->is_err()){return err(f(this->as_err()));} - return ok(this->as_ok()); - } - template - result> - map_err(F&& f) const& - { - if(this->is_err()){return err(f(this->as_err()));} - return ok(this->as_ok()); - } - template - result> - 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 - detail::return_type_of_t - map_or_else(F&& f, U&& opt) & - { - if(this->is_err()){return std::forward(opt);} - return f(this->as_ok()); - } - template - detail::return_type_of_t - map_or_else(F&& f, U&& opt) const& - { - if(this->is_err()){return std::forward(opt);} - return f(this->as_ok()); - } - template - detail::return_type_of_t - map_or_else(F&& f, U&& opt) && - { - if(this->is_err()){return std::forward(opt);} - return f(std::move(this->as_ok())); - } - - // prerequisities - // F: E -> U - // retval: U - template - detail::return_type_of_t - map_err_or_else(F&& f, U&& opt) & - { - if(this->is_ok()){return std::forward(opt);} - return f(this->as_err()); - } - template - detail::return_type_of_t - map_err_or_else(F&& f, U&& opt) const& - { - if(this->is_ok()){return std::forward(opt);} - return f(this->as_err()); - } - template - detail::return_type_of_t - map_err_or_else(F&& f, U&& opt) && - { - if(this->is_ok()){return std::forward(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 and E is convertible to F - template - detail::return_type_of_t - and_then(F&& f) & - { - if(this->is_ok()){return f(this->as_ok());} - return err(this->as_err()); - } - template - detail::return_type_of_t - and_then(F&& f) const& - { - if(this->is_ok()){return f(this->as_ok());} - return err(this->as_err()); - } - template - detail::return_type_of_t - 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 and T is convertible to S - template - detail::return_type_of_t - or_else(F&& f) & - { - if(this->is_err()){return f(this->as_err());} - return ok(this->as_ok()); - } - template - detail::return_type_of_t - or_else(F&& f) const& - { - if(this->is_err()){return f(this->as_err());} - return ok(this->as_ok()); - } - template - detail::return_type_of_t - 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& other) - { - result 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::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 -void swap(result& lhs, result& rhs) -{ - lhs.swap(rhs); - return; -} - -// this might be confusing because it eagerly evaluated, while in the other -// cases operator && and || are short-circuited. -// -// template -// inline result -// operator&&(const result& lhs, const result& rhs) noexcept -// { -// return lhs.is_ok() ? rhs : lhs; -// } -// -// template -// inline result -// operator||(const result& lhs, const result& rhs) noexcept -// { -// return lhs.is_ok() ? lhs : rhs; -// } - -// ---------------------------------------------------------------------------- -// re-use result as a optional 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 -std::basic_ostream& -operator<<(std::basic_ostream& os, const none_t&) -{ - os << "none"; - return os; -} -inline failure none() noexcept {return failure{none_t{}};} -} // detail -} // toml11 -#endif// TOML11_RESULT_H diff --git a/src/frontend/qt_sdl/toml/toml/serializer.hpp b/src/frontend/qt_sdl/toml/toml/serializer.hpp deleted file mode 100644 index 88ae775a..00000000 --- a/src/frontend/qt_sdl/toml/toml/serializer.hpp +++ /dev/null @@ -1,922 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_SERIALIZER_HPP -#define TOML11_SERIALIZER_HPP -#include -#include - -#include - -#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 -std::basic_string -format_key(const std::basic_string& k) -{ - if(k.empty()) - { - return std::string("\"\""); - } - - // check the key can be a bare (unquoted) key - detail::location loc(k, std::vector(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 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 -std::basic_string -format_keys(const std::vector>& keys) -{ - if(keys.empty()) - { - return std::string("\"\""); - } - - std::basic_string serialized; - for(const auto& ky : keys) - { - serialized += format_key(ky); - serialized += charT('.'); - } - serialized.pop_back(); // remove the last dot '.' - return serialized; -} - -template -struct serializer -{ - static_assert(detail::is_basic_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::max_digits10, - const bool can_be_inlined = false, - const bool no_comment = false, - std::vector 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 buf(static_cast(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::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::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::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::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 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 keys_; -}; - -template class M, template class V> -std::string -format(const basic_value& v, std::size_t w = 80u, - int fprec = std::numeric_limits::max_digits10, - bool no_comment = false, bool force_inline = false) -{ - using value_type = basic_value; - // 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(w, fprec, false, no_comment), v); - oss << serialized; - return oss.str(); - } - return visit(serializer(w, fprec, force_inline), v); -} - -namespace detail -{ -template -int comment_index(std::basic_ostream&) -{ - static const int index = std::ios_base::xalloc(); - return index; -} -} // detail - -template -std::basic_ostream& -nocomment(std::basic_ostream& os) -{ - // by default, it is zero. and by default, it shows comments. - os.iword(detail::comment_index(os)) = 1; - return os; -} - -template -std::basic_ostream& -showcomment(std::basic_ostream& os) -{ - // by default, it is zero. and by default, it shows comments. - os.iword(detail::comment_index(os)) = 0; - return os; -} - -template class M, template class V> -std::basic_ostream& -operator<<(std::basic_ostream& os, const basic_value& v) -{ - using value_type = basic_value; - - // get status of std::setw(). - const auto w = static_cast(os.width()); - const int fprec = static_cast(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(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 diff --git a/src/frontend/qt_sdl/toml/toml/source_location.hpp b/src/frontend/qt_sdl/toml/toml/source_location.hpp deleted file mode 100644 index fa175b5b..00000000 --- a/src/frontend/qt_sdl/toml/toml/source_location.hpp +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_SOURCE_LOCATION_HPP -#define TOML11_SOURCE_LOCATION_HPP -#include -#include - -#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::stoul(reg->line_num())); - } - column_num_ = static_cast(reg->before() + 1); - region_size_ = static_cast(reg->size()); - file_name_ = reg->name(); - line_str_ = reg->line(); - } - } - - explicit source_location(const detail::region& reg) - : line_num_(static_cast(std::stoul(reg.line_num()))), - column_num_(static_cast(reg.before() + 1)), - region_size_(static_cast(reg.size())), - file_name_(reg.name()), - line_str_ (reg.line()) - {} - explicit source_location(const detail::location& loc) - : line_num_(static_cast(std::stoul(loc.line_num()))), - column_num_(static_cast(loc.before() + 1)), - region_size_(static_cast(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>& loc_com, - const std::vector& 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(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(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(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 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 diff --git a/src/frontend/qt_sdl/toml/toml/storage.hpp b/src/frontend/qt_sdl/toml/toml/storage.hpp deleted file mode 100644 index 202f9035..00000000 --- a/src/frontend/qt_sdl/toml/toml/storage.hpp +++ /dev/null @@ -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 -struct storage -{ - using value_type = T; - - explicit storage(value_type const& v): ptr(toml::make_unique(v)) {} - explicit storage(value_type&& v): ptr(toml::make_unique(std::move(v))) {} - ~storage() = default; - storage(const storage& rhs): ptr(toml::make_unique(*rhs.ptr)) {} - storage& operator=(const storage& rhs) - { - this->ptr = toml::make_unique(*rhs.ptr); - return *this; - } - storage(storage&&) = default; - storage& operator=(storage&&) = default; - - bool is_ok() const noexcept {return static_cast(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 ptr; -}; - -} // detail -} // toml -#endif// TOML11_STORAGE_HPP diff --git a/src/frontend/qt_sdl/toml/toml/string.hpp b/src/frontend/qt_sdl/toml/toml/string.hpp deleted file mode 100644 index def3e57c..00000000 --- a/src/frontend/qt_sdl/toml/toml/string.hpp +++ /dev/null @@ -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 - -#include -#include - -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L -#if __has_include() -#define TOML11_USING_STRING_VIEW 1 -#include -#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 -std::basic_ostream& -operator<<(std::basic_ostream& 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 diff --git a/src/frontend/qt_sdl/toml/toml/traits.hpp b/src/frontend/qt_sdl/toml/toml/traits.hpp deleted file mode 100644 index 255d9e88..00000000 --- a/src/frontend/qt_sdl/toml/toml/traits.hpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include - -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L -#if __has_include() -#include -#endif // has_include() -#endif // cplusplus >= C++17 - -namespace toml -{ -template class T, template class A> -class basic_value; - -namespace detail -{ -// --------------------------------------------------------------------------- -// check whether type T is a kind of container/map class - -struct has_iterator_impl -{ - template static std::true_type check(typename T::iterator*); - template static std::false_type check(...); -}; -struct has_value_type_impl -{ - template static std::true_type check(typename T::value_type*); - template static std::false_type check(...); -}; -struct has_key_type_impl -{ - template static std::true_type check(typename T::key_type*); - template static std::false_type check(...); -}; -struct has_mapped_type_impl -{ - template static std::true_type check(typename T::mapped_type*); - template static std::false_type check(...); -}; -struct has_reserve_method_impl -{ - template static std::false_type check(...); - template static std::true_type check( - decltype(std::declval().reserve(std::declval()))*); -}; -struct has_push_back_method_impl -{ - template static std::false_type check(...); - template static std::true_type check( - decltype(std::declval().push_back(std::declval()))*); -}; -struct is_comparable_impl -{ - template static std::false_type check(...); - template static std::true_type check( - decltype(std::declval() < std::declval())*); -}; - -struct has_from_toml_method_impl -{ - template class Tb, template class A> - static std::true_type check( - decltype(std::declval().from_toml( - std::declval<::toml::basic_value>()))*); - - template class Tb, template class A> - static std::false_type check(...); -}; -struct has_into_toml_method_impl -{ - template - static std::true_type check(decltype(std::declval().into_toml())*); - template - static std::false_type check(...); -}; - -struct has_specialized_from_impl -{ - template - static std::false_type check(...); - template)> - static std::true_type check(::toml::from*); -}; -struct has_specialized_into_impl -{ - template - static std::false_type check(...); - template)> - static std::true_type check(::toml::from*); -}; - - -/// 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::type -#endif - -template -struct has_iterator : decltype(has_iterator_impl::check(nullptr)){}; -template -struct has_value_type : decltype(has_value_type_impl::check(nullptr)){}; -template -struct has_key_type : decltype(has_key_type_impl::check(nullptr)){}; -template -struct has_mapped_type : decltype(has_mapped_type_impl::check(nullptr)){}; -template -struct has_reserve_method : decltype(has_reserve_method_impl::check(nullptr)){}; -template -struct has_push_back_method : decltype(has_push_back_method_impl::check(nullptr)){}; -template -struct is_comparable : decltype(is_comparable_impl::check(nullptr)){}; - -template class Tb, template class A> -struct has_from_toml_method -: decltype(has_from_toml_method_impl::check(nullptr)){}; - -template -struct has_into_toml_method -: decltype(has_into_toml_method_impl::check(nullptr)){}; - -template -struct has_specialized_from : decltype(has_specialized_from_impl::check(nullptr)){}; -template -struct has_specialized_into : decltype(has_specialized_into_impl::check(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 struct conjunction : std::true_type{}; -template struct conjunction : T{}; -template -struct conjunction : - std::conditional(T::value), conjunction, T>::type -{}; - -template struct disjunction : std::false_type{}; -template struct disjunction : T {}; -template -struct disjunction : - std::conditional(T::value), T, disjunction>::type -{}; - -template -struct negation : std::integral_constant(T::value)>{}; - -#endif - -// --------------------------------------------------------------------------- -// type checkers - -template struct is_std_pair : std::false_type{}; -template -struct is_std_pair> : std::true_type{}; - -template struct is_std_tuple : std::false_type{}; -template -struct is_std_tuple> : std::true_type{}; - -template struct is_std_forward_list : std::false_type{}; -template -struct is_std_forward_list> : std::true_type{}; - -template struct is_chrono_duration: std::false_type{}; -template -struct is_chrono_duration>: std::true_type{}; - -template -struct is_map : conjunction< // map satisfies all the following conditions - has_iterator, // has T::iterator - has_value_type, // has T::value_type - has_key_type, // has T::key_type - has_mapped_type // has T::mapped_type - >{}; -template struct is_map : is_map{}; -template struct is_map : is_map{}; -template struct is_map : is_map{}; -template struct is_map : is_map{}; - -template -struct is_container : conjunction< - negation>, // not a map - negation>, // not a std::string -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L -#if __has_include() - negation>, // not a std::string_view -#endif // has_include() -#endif - has_iterator, // has T::iterator - has_value_type // has T::value_type - >{}; -template struct is_container : is_container{}; -template struct is_container : is_container{}; -template struct is_container : is_container{}; -template struct is_container : is_container{}; - -template -struct is_basic_value: std::false_type{}; -template struct is_basic_value : is_basic_value{}; -template struct is_basic_value : is_basic_value{}; -template struct is_basic_value : is_basic_value{}; -template struct is_basic_value : is_basic_value{}; -template class M, template class V> -struct is_basic_value<::toml::basic_value>: std::true_type{}; - -// --------------------------------------------------------------------------- -// C++14 index_sequence - -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L - -using std::index_sequence; -using std::make_index_sequence; - -#else - -template struct index_sequence{}; - -template struct push_back_index_sequence{}; -template -struct push_back_index_sequence, N> -{ - typedef index_sequence type; -}; - -template -struct index_sequence_maker -{ - typedef typename push_back_index_sequence< - typename index_sequence_maker::type, N>::type type; -}; -template<> -struct index_sequence_maker<0> -{ - typedef index_sequence<0> type; -}; -template -using make_index_sequence = typename index_sequence_maker::type; - -#endif // cplusplus >= 2014 - -// --------------------------------------------------------------------------- -// C++14 enable_if_t - -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L - -using std::enable_if_t; - -#else - -template -using enable_if_t = typename std::enable_if::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 -using return_type_of_t = std::invoke_result_t; - -#else -// result_of is deprecated after C++17 -template -using return_type_of_t = typename std::result_of::type; - -#endif - -// --------------------------------------------------------------------------- -// is_string_literal -// -// to use this, pass `typename remove_reference::type` to T. - -template -struct is_string_literal: -disjunction< - std::is_same, - conjunction< - std::is_array, - std::is_same::type> - > - >{}; - -// --------------------------------------------------------------------------- -// C++20 remove_cvref_t - -template -struct remove_cvref -{ - using type = typename std::remove_cv< - typename std::remove_reference::type>::type; -}; - -template -using remove_cvref_t = typename remove_cvref::type; - -}// detail -}//toml -#endif // TOML_TRAITS diff --git a/src/frontend/qt_sdl/toml/toml/types.hpp b/src/frontend/qt_sdl/toml/toml/types.hpp deleted file mode 100644 index 1e420e7f..00000000 --- a/src/frontend/qt_sdl/toml/toml/types.hpp +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_TYPES_HPP -#define TOML11_TYPES_HPP -#include -#include - -#include "comments.hpp" -#include "datetime.hpp" -#include "string.hpp" -#include "traits.hpp" - -namespace toml -{ - -template class Table, // map-like class - template 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; -// 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 -inline std::basic_ostream& -operator<<(std::basic_ostream& 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 alloc = std::allocator> -inline std::basic_string stringize(value_t t) -{ - std::basic_ostringstream oss; - oss << t; - return oss.str(); -} - -namespace detail -{ - -// helper to define a type that represents a value_t value. -template -using value_t_constant = std::integral_constant; - -// 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 struct enum_to_type {using type = void ;}; -template struct enum_to_type{using type = void ;}; -template struct enum_to_type{using type = boolean ;}; -template struct enum_to_type{using type = integer ;}; -template struct enum_to_type{using type = floating ;}; -template struct enum_to_type{using type = string ;}; -template struct enum_to_type{using type = offset_datetime ;}; -template struct enum_to_type{using type = local_datetime ;}; -template struct enum_to_type{using type = local_date ;}; -template struct enum_to_type{using type = local_time ;}; -template struct enum_to_type{using type = typename Value::array_type;}; -template struct enum_to_type{using type = typename Value::table_type;}; - -// meta-function that converts from an exact toml type to the enum that corresponds to. -template -struct type_to_enum : std::conditional< - std::is_same::value, // if T == array_type, - value_t_constant, // then value_t::array - typename std::conditional< // else... - std::is_same::value, // if T == table_type - value_t_constant, // then value_t::table - value_t_constant // else value_t::empty - >::type - >::type {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; - -// meta-function that checks the type T is the same as one of the toml::* types. -template -struct is_exact_toml_type : disjunction< - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same - >{}; -template struct is_exact_toml_type : is_exact_toml_type{}; -template struct is_exact_toml_type : is_exact_toml_type{}; -template struct is_exact_toml_type : is_exact_toml_type{}; -template struct is_exact_toml_type: is_exact_toml_type{}; - -} // detail -} // toml - -#endif// TOML11_TYPES_H diff --git a/src/frontend/qt_sdl/toml/toml/utility.hpp b/src/frontend/qt_sdl/toml/toml/utility.hpp deleted file mode 100644 index 53a18b94..00000000 --- a/src/frontend/qt_sdl/toml/toml/utility.hpp +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_UTILITY_HPP -#define TOML11_UTILITY_HPP -#include -#include -#include - -#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 -inline std::unique_ptr make_unique(Ts&& ... args) -{ - return std::unique_ptr(new T(std::forward(args)...)); -} - -#endif // TOML11_CPLUSPLUS_STANDARD_VERSION >= 2014 - -namespace detail -{ -template -void try_reserve_impl(Container& container, std::size_t N, std::true_type) -{ - container.reserve(N); - return; -} -template -void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept -{ - return; -} -} // detail - -template -void try_reserve(Container& container, std::size_t N) -{ - if(N <= container.size()) {return;} - detail::try_reserve_impl(container, N, detail::has_reserve_method{}); - return; -} - -namespace detail -{ -inline std::string concat_to_string_impl(std::ostringstream& oss) -{ - return oss.str(); -} -template -std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail) -{ - oss << std::forward(head); - return concat_to_string_impl(oss, std::forward(tail) ... ); -} -} // detail - -template -std::string concat_to_string(Ts&& ... args) -{ - std::ostringstream oss; - oss << std::boolalpha << std::fixed; - return detail::concat_to_string_impl(oss, std::forward(args) ...); -} - -template -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 -decltype(auto) last_one(T&& tail) noexcept -{ - return std::forward(tail); -} - -template -decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept -{ - return last_one(std::forward(tail)...); -} -#else // C++11 -// The following code -// ```cpp -// 1 | template -// 2 | auto last_one(T&& /*head*/, Ts&& ... tail) -// 3 | -> decltype(last_one(std::forward(tail)...)) -// 4 | { -// 5 | return last_one(std::forward(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 -struct last_one_in_pack -{ - using type = typename last_one_in_pack::type; -}; -template -struct last_one_in_pack -{ - using type = T; -}; -template -using last_one_in_pack_t = typename last_one_in_pack::type; - -template -T&& last_one(T&& tail) noexcept -{ - return std::forward(tail); -} -template -enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t> -last_one(T&& /*head*/, Ts&& ... tail) -{ - return last_one(std::forward(tail)...); -} - -#endif -} // detail - -}// toml -#endif // TOML11_UTILITY diff --git a/src/frontend/qt_sdl/toml/toml/value.hpp b/src/frontend/qt_sdl/toml/toml/value.hpp deleted file mode 100644 index 1b43db8d..00000000 --- a/src/frontend/qt_sdl/toml/toml/value.hpp +++ /dev/null @@ -1,2035 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_VALUE_HPP -#define TOML11_VALUE_HPP -#include - -#include "comments.hpp" -#include "exception.hpp" -#include "into.hpp" -#include "region.hpp" -#include "source_location.hpp" -#include "storage.hpp" -#include "traits.hpp" -#include "types.hpp" -#include "utility.hpp" - -namespace toml -{ - -namespace detail -{ - -// to show error messages. not recommended for users. -template -inline region_base const* get_region(const Value& v) -{ - return v.region_info_.get(); -} - -template -void change_region(Value& v, region reg) -{ - v.region_info_ = std::make_shared(std::move(reg)); - return; -} - -template -[[noreturn]] inline void -throw_bad_cast(const std::string& funcname, value_t actual, const Value& v) -{ - throw type_error(detail::format_underline( - concat_to_string(funcname, "bad_cast to ", Expected), { - {v.location(), concat_to_string("the actual type is ", actual)} - }), v.location()); -} - -// Throw `out_of_range` from `toml::value::at()` and `toml::find()` -// after generating an error message. -// -// The implementation is a bit complicated and there are many edge-cases. -// If you are not interested in the error message generation, just skip this. -template -[[noreturn]] void -throw_key_not_found_error(const Value& v, const key& ky) -{ - // The top-level table has its region at the first character of the file. - // That means that, in the case when a key is not found in the top-level - // table, the error message points to the first character. If the file has - // its first table at the first line, the error message would be like this. - // ```console - // [error] key "a" not found - // --> example.toml - // | - // 1 | [table] - // | ^------ in this table - // ``` - // It actually points to the top-level table at the first character, - // not `[table]`. But it is too confusing. To avoid the confusion, the error - // message should explicitly say "key not found in the top-level table", - // or "the parsed file is empty" if there is no content at all (0 bytes in file). - const auto loc = v.location(); - if(loc.line() == 1 && loc.region() == 0) - { - // First line with a zero-length region means "empty file". - // The region will be generated at `parse_toml_file` function - // if the file contains no bytes. - throw std::out_of_range(format_underline(concat_to_string( - "key \"", ky, "\" not found in the top-level table"), { - {loc, "the parsed file is empty"} - })); - } - else if(loc.line() == 1 && loc.region() == 1) - { - // Here it assumes that top-level table starts at the first character. - // The region corresponds to the top-level table will be generated at - // `parse_toml_file` function. - // It also assumes that the top-level table size is just one and - // the line number is `1`. It is always satisfied. And those conditions - // are satisfied only if the table is the top-level table. - // - // 1. one-character dot-key at the first line - // ```toml - // a.b = "c" - // ``` - // toml11 counts whole key as the table key. Here, `a.b` is the region - // of the table "a". It could be counter intuitive, but it works. - // The size of the region is 3, not 1. The above example is the shortest - // dot-key example. The size cannot be 1. - // - // 2. one-character inline-table at the first line - // ```toml - // a = {b = "c"} - // ``` - // toml11 considers the inline table body as the table region. Here, - // `{b = "c"}` is the region of the table "a". The size of the region - // is 9, not 1. The shotest inline table still has two characters, `{` - // and `}`. The size cannot be 1. - // - // 3. one-character table declaration at the first line - // ```toml - // [a] - // ``` - // toml11 considers the whole table key as the table region. Here, - // `[a]` is the table region. The size is 3, not 1. - // - throw std::out_of_range(format_underline(concat_to_string( - "key \"", ky, "\" not found in the top-level table"), { - {loc, "the top-level table starts here"} - })); - } - else - { - // normal table. - throw std::out_of_range(format_underline(concat_to_string( - "key \"", ky, "\" not found"), { {loc, "in this table"} })); - } -} - -// switch by `value_t` at the compile time. -template -struct switch_cast {}; -#define TOML11_GENERATE_SWITCH_CASTER(TYPE) \ - template<> \ - struct switch_cast \ - { \ - template \ - static typename Value::TYPE##_type& invoke(Value& v) \ - { \ - return v.as_##TYPE(); \ - } \ - template \ - static typename Value::TYPE##_type const& invoke(const Value& v) \ - { \ - return v.as_##TYPE(); \ - } \ - template \ - static typename Value::TYPE##_type&& invoke(Value&& v) \ - { \ - return std::move(v).as_##TYPE(); \ - } \ - }; \ - /**/ -TOML11_GENERATE_SWITCH_CASTER(boolean) -TOML11_GENERATE_SWITCH_CASTER(integer) -TOML11_GENERATE_SWITCH_CASTER(floating) -TOML11_GENERATE_SWITCH_CASTER(string) -TOML11_GENERATE_SWITCH_CASTER(offset_datetime) -TOML11_GENERATE_SWITCH_CASTER(local_datetime) -TOML11_GENERATE_SWITCH_CASTER(local_date) -TOML11_GENERATE_SWITCH_CASTER(local_time) -TOML11_GENERATE_SWITCH_CASTER(array) -TOML11_GENERATE_SWITCH_CASTER(table) - -#undef TOML11_GENERATE_SWITCH_CASTER - -}// detail - -template class Table = std::unordered_map, - template class Array = std::vector> -class basic_value -{ - template - static void assigner(T& dst, U&& v) - { - const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); - assert(tmp == std::addressof(dst)); - (void)tmp; - } - - using region_base = detail::region_base; - - template class T, - template class A> - friend class basic_value; - - public: - - using comment_type = Comment; - using key_type = ::toml::key; - using value_type = basic_value; - using boolean_type = ::toml::boolean; - using integer_type = ::toml::integer; - using floating_type = ::toml::floating; - using string_type = ::toml::string; - using local_time_type = ::toml::local_time; - using local_date_type = ::toml::local_date; - using local_datetime_type = ::toml::local_datetime; - using offset_datetime_type = ::toml::offset_datetime; - using array_type = Array; - using table_type = Table; - - public: - - basic_value() noexcept - : type_(value_t::empty), - region_info_(std::make_shared(region_base{})) - {} - ~basic_value() noexcept {this->cleanup();} - - basic_value(const basic_value& v) - : type_(v.type()), region_info_(v.region_info_), comments_(v.comments_) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : assigner(array_ , v.array_ ); break; - case value_t::table : assigner(table_ , v.table_ ); break; - default: break; - } - } - basic_value(basic_value&& v) - : type_(v.type()), region_info_(std::move(v.region_info_)), - comments_(std::move(v.comments_)) - { - switch(this->type_) // here this->type_ is already initialized - { - case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; - case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; - case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; - case value_t::string : assigner(string_ , std::move(v.string_ )); break; - case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; - case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; - case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; - case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; - case value_t::array : assigner(array_ , std::move(v.array_ )); break; - case value_t::table : assigner(table_ , std::move(v.table_ )); break; - default: break; - } - } - basic_value& operator=(const basic_value& v) - { - this->cleanup(); - this->region_info_ = v.region_info_; - this->comments_ = v.comments_; - this->type_ = v.type(); - switch(this->type_) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : assigner(array_ , v.array_ ); break; - case value_t::table : assigner(table_ , v.table_ ); break; - default: break; - } - return *this; - } - basic_value& operator=(basic_value&& v) - { - this->cleanup(); - this->region_info_ = std::move(v.region_info_); - this->comments_ = std::move(v.comments_); - this->type_ = v.type(); - switch(this->type_) - { - case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; - case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; - case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; - case value_t::string : assigner(string_ , std::move(v.string_ )); break; - case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; - case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; - case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; - case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; - case value_t::array : assigner(array_ , std::move(v.array_ )); break; - case value_t::table : assigner(table_ , std::move(v.table_ )); break; - default: break; - } - return *this; - } - - // overwrite comments ---------------------------------------------------- - - basic_value(const basic_value& v, std::vector com) - : type_(v.type()), region_info_(v.region_info_), - comments_(std::move(com)) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : assigner(array_ , v.array_ ); break; - case value_t::table : assigner(table_ , v.table_ ); break; - default: break; - } - } - - basic_value(basic_value&& v, std::vector com) - : type_(v.type()), region_info_(std::move(v.region_info_)), - comments_(std::move(com)) - { - switch(this->type_) // here this->type_ is already initialized - { - case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; - case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; - case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; - case value_t::string : assigner(string_ , std::move(v.string_ )); break; - case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; - case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; - case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; - case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; - case value_t::array : assigner(array_ , std::move(v.array_ )); break; - case value_t::table : assigner(table_ , std::move(v.table_ )); break; - default: break; - } - } - - // ----------------------------------------------------------------------- - // conversion between different basic_values. - template class T, - template class A> - basic_value(const basic_value& v) - : type_(v.type()), region_info_(v.region_info_), comments_(v.comments()) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : - { - array_type tmp(v.as_array(std::nothrow).begin(), - v.as_array(std::nothrow).end()); - assigner(array_, std::move(tmp)); - break; - } - case value_t::table : - { - table_type tmp(v.as_table(std::nothrow).begin(), - v.as_table(std::nothrow).end()); - assigner(table_, std::move(tmp)); - break; - } - default: break; - } - } - template class T, - template class A> - basic_value(const basic_value& v, std::vector com) - : type_(v.type()), region_info_(v.region_info_), - comments_(std::move(com)) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : - { - array_type tmp(v.as_array(std::nothrow).begin(), - v.as_array(std::nothrow).end()); - assigner(array_, std::move(tmp)); - break; - } - case value_t::table : - { - table_type tmp(v.as_table(std::nothrow).begin(), - v.as_table(std::nothrow).end()); - assigner(table_, std::move(tmp)); - break; - } - default: break; - } - } - template class T, - template class A> - basic_value& operator=(const basic_value& v) - { - this->region_info_ = v.region_info_; - this->comments_ = comment_type(v.comments()); - this->type_ = v.type(); - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : - { - array_type tmp(v.as_array(std::nothrow).begin(), - v.as_array(std::nothrow).end()); - assigner(array_, std::move(tmp)); - break; - } - case value_t::table : - { - table_type tmp(v.as_table(std::nothrow).begin(), - v.as_table(std::nothrow).end()); - assigner(table_, std::move(tmp)); - break; - } - default: break; - } - return *this; - } - - // boolean ============================================================== - - basic_value(boolean b) - : type_(value_t::boolean), - region_info_(std::make_shared(region_base{})) - { - assigner(this->boolean_, b); - } - basic_value& operator=(boolean b) - { - this->cleanup(); - this->type_ = value_t::boolean; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->boolean_, b); - return *this; - } - basic_value(boolean b, std::vector com) - : type_(value_t::boolean), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->boolean_, b); - } - - // integer ============================================================== - - template, detail::negation>>::value, - std::nullptr_t>::type = nullptr> - basic_value(T i) - : type_(value_t::integer), - region_info_(std::make_shared(region_base{})) - { - assigner(this->integer_, static_cast(i)); - } - - template, detail::negation>>::value, - std::nullptr_t>::type = nullptr> - basic_value& operator=(T i) - { - this->cleanup(); - this->type_ = value_t::integer; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->integer_, static_cast(i)); - return *this; - } - - template, detail::negation>>::value, - std::nullptr_t>::type = nullptr> - basic_value(T i, std::vector com) - : type_(value_t::integer), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->integer_, static_cast(i)); - } - - // floating ============================================================= - - template::value, std::nullptr_t>::type = nullptr> - basic_value(T f) - : type_(value_t::floating), - region_info_(std::make_shared(region_base{})) - { - assigner(this->floating_, static_cast(f)); - } - - - template::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(T f) - { - this->cleanup(); - this->type_ = value_t::floating; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->floating_, static_cast(f)); - return *this; - } - - template::value, std::nullptr_t>::type = nullptr> - basic_value(T f, std::vector com) - : type_(value_t::floating), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->floating_, f); - } - - // string =============================================================== - - basic_value(toml::string s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, std::move(s)); - } - basic_value& operator=(toml::string s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, s); - return *this; - } - basic_value(toml::string s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, std::move(s)); - } - - basic_value(std::string s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::move(s))); - } - basic_value& operator=(std::string s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, toml::string(std::move(s))); - return *this; - } - basic_value(std::string s, string_t kind) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::move(s), kind)); - } - basic_value(std::string s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::move(s))); - } - basic_value(std::string s, string_t kind, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::move(s), kind)); - } - - basic_value(const char* s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::string(s))); - } - basic_value& operator=(const char* s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, toml::string(std::string(s))); - return *this; - } - basic_value(const char* s, string_t kind) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::string(s), kind)); - } - basic_value(const char* s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::string(s))); - } - basic_value(const char* s, string_t kind, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::string(s), kind)); - } - -#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 - basic_value(std::string_view s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(s)); - } - basic_value& operator=(std::string_view s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, toml::string(s)); - return *this; - } - basic_value(std::string_view s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(s)); - } - basic_value(std::string_view s, string_t kind) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(s, kind)); - } - basic_value(std::string_view s, string_t kind, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(s, kind)); - } -#endif - - // local date =========================================================== - - basic_value(const local_date& ld) - : type_(value_t::local_date), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_date_, ld); - } - basic_value& operator=(const local_date& ld) - { - this->cleanup(); - this->type_ = value_t::local_date; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_date_, ld); - return *this; - } - basic_value(const local_date& ld, std::vector com) - : type_(value_t::local_date), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_date_, ld); - } - - // local time =========================================================== - - basic_value(const local_time& lt) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_time_, lt); - } - basic_value(const local_time& lt, std::vector com) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_time_, lt); - } - basic_value& operator=(const local_time& lt) - { - this->cleanup(); - this->type_ = value_t::local_time; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_time_, lt); - return *this; - } - - template - basic_value(const std::chrono::duration& dur) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_time_, local_time(dur)); - } - template - basic_value(const std::chrono::duration& dur, - std::vector com) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_time_, local_time(dur)); - } - template - basic_value& operator=(const std::chrono::duration& dur) - { - this->cleanup(); - this->type_ = value_t::local_time; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_time_, local_time(dur)); - return *this; - } - - // local datetime ======================================================= - - basic_value(const local_datetime& ldt) - : type_(value_t::local_datetime), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_datetime_, ldt); - } - basic_value(const local_datetime& ldt, std::vector com) - : type_(value_t::local_datetime), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_datetime_, ldt); - } - basic_value& operator=(const local_datetime& ldt) - { - this->cleanup(); - this->type_ = value_t::local_datetime; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_datetime_, ldt); - return *this; - } - - // offset datetime ====================================================== - - basic_value(const offset_datetime& odt) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})) - { - assigner(this->offset_datetime_, odt); - } - basic_value(const offset_datetime& odt, std::vector com) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->offset_datetime_, odt); - } - basic_value& operator=(const offset_datetime& odt) - { - this->cleanup(); - this->type_ = value_t::offset_datetime; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->offset_datetime_, odt); - return *this; - } - basic_value(const std::chrono::system_clock::time_point& tp) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})) - { - assigner(this->offset_datetime_, offset_datetime(tp)); - } - basic_value(const std::chrono::system_clock::time_point& tp, - std::vector com) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->offset_datetime_, offset_datetime(tp)); - } - basic_value& operator=(const std::chrono::system_clock::time_point& tp) - { - this->cleanup(); - this->type_ = value_t::offset_datetime; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->offset_datetime_, offset_datetime(tp)); - return *this; - } - - // array ================================================================ - - basic_value(const array_type& ary) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})) - { - assigner(this->array_, ary); - } - basic_value(const array_type& ary, std::vector com) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->array_, ary); - } - basic_value& operator=(const array_type& ary) - { - this->cleanup(); - this->type_ = value_t::array ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->array_, ary); - return *this; - } - - // array (initializer_list) ---------------------------------------------- - - template::value, - std::nullptr_t>::type = nullptr> - basic_value(std::initializer_list list) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})) - { - array_type ary(list.begin(), list.end()); - assigner(this->array_, std::move(ary)); - } - template::value, - std::nullptr_t>::type = nullptr> - basic_value(std::initializer_list list, std::vector com) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - array_type ary(list.begin(), list.end()); - assigner(this->array_, std::move(ary)); - } - template::value, - std::nullptr_t>::type = nullptr> - basic_value& operator=(std::initializer_list list) - { - this->cleanup(); - this->type_ = value_t::array; - this->region_info_ = std::make_shared(region_base{}); - - array_type ary(list.begin(), list.end()); - assigner(this->array_, std::move(ary)); - return *this; - } - - // array (STL Containers) ------------------------------------------------ - - template>, - detail::is_container - >::value, std::nullptr_t>::type = nullptr> - basic_value(const T& list) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})) - { - static_assert(std::is_convertible::value, - "elements of a container should be convertible to toml::value"); - - array_type ary(list.size()); - std::copy(list.begin(), list.end(), ary.begin()); - assigner(this->array_, std::move(ary)); - } - template>, - detail::is_container - >::value, std::nullptr_t>::type = nullptr> - basic_value(const T& list, std::vector com) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - static_assert(std::is_convertible::value, - "elements of a container should be convertible to toml::value"); - - array_type ary(list.size()); - std::copy(list.begin(), list.end(), ary.begin()); - assigner(this->array_, std::move(ary)); - } - template>, - detail::is_container - >::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(const T& list) - { - static_assert(std::is_convertible::value, - "elements of a container should be convertible to toml::value"); - - this->cleanup(); - this->type_ = value_t::array; - this->region_info_ = std::make_shared(region_base{}); - - array_type ary(list.size()); - std::copy(list.begin(), list.end(), ary.begin()); - assigner(this->array_, std::move(ary)); - return *this; - } - - // table ================================================================ - - basic_value(const table_type& tab) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})) - { - assigner(this->table_, tab); - } - basic_value(const table_type& tab, std::vector com) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->table_, tab); - } - basic_value& operator=(const table_type& tab) - { - this->cleanup(); - this->type_ = value_t::table; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->table_, tab); - return *this; - } - - // initializer-list ------------------------------------------------------ - - basic_value(std::initializer_list> list) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})) - { - table_type tab; - for(const auto& elem : list) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - - basic_value(std::initializer_list> list, - std::vector com) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - table_type tab; - for(const auto& elem : list) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - basic_value& operator=(std::initializer_list> list) - { - this->cleanup(); - this->type_ = value_t::table; - this->region_info_ = std::make_shared(region_base{}); - - table_type tab; - for(const auto& elem : list) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - return *this; - } - - // other table-like ----------------------------------------------------- - - template>, - detail::is_map - >::value, std::nullptr_t>::type = nullptr> - basic_value(const Map& mp) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})) - { - table_type tab; - for(const auto& elem : mp) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - template>, - detail::is_map - >::value, std::nullptr_t>::type = nullptr> - basic_value(const Map& mp, std::vector com) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - table_type tab; - for(const auto& elem : mp) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - template>, - detail::is_map - >::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(const Map& mp) - { - this->cleanup(); - this->type_ = value_t::table; - this->region_info_ = std::make_shared(region_base{}); - - table_type tab; - for(const auto& elem : mp) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - return *this; - } - - // user-defined ========================================================= - - // convert using into_toml() method ------------------------------------- - - template::value, std::nullptr_t>::type = nullptr> - basic_value(const T& ud): basic_value(ud.into_toml()) {} - - template::value, std::nullptr_t>::type = nullptr> - basic_value(const T& ud, std::vector com) - : basic_value(ud.into_toml(), std::move(com)) - {} - template::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(const T& ud) - { - *this = ud.into_toml(); - return *this; - } - - // convert using into struct ----------------------------------------- - - template)> - basic_value(const T& ud): basic_value(::toml::into::into_toml(ud)) {} - template)> - basic_value(const T& ud, std::vector com) - : basic_value(::toml::into::into_toml(ud), std::move(com)) - {} - template)> - basic_value& operator=(const T& ud) - { - *this = ::toml::into::into_toml(ud); - return *this; - } - - // for internal use ------------------------------------------------------ - // - // Those constructors take detail::region that contains parse result. - - basic_value(boolean b, detail::region reg, std::vector cm) - : type_(value_t::boolean), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->boolean_, b); - } - template, detail::negation> - >::value, std::nullptr_t>::type = nullptr> - basic_value(T i, detail::region reg, std::vector cm) - : type_(value_t::integer), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->integer_, static_cast(i)); - } - template::value, std::nullptr_t>::type = nullptr> - basic_value(T f, detail::region reg, std::vector cm) - : type_(value_t::floating), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->floating_, static_cast(f)); - } - basic_value(toml::string s, detail::region reg, - std::vector cm) - : type_(value_t::string), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->string_, std::move(s)); - } - basic_value(const local_date& ld, detail::region reg, - std::vector cm) - : type_(value_t::local_date), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->local_date_, ld); - } - basic_value(const local_time& lt, detail::region reg, - std::vector cm) - : type_(value_t::local_time), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->local_time_, lt); - } - basic_value(const local_datetime& ldt, detail::region reg, - std::vector cm) - : type_(value_t::local_datetime), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->local_datetime_, ldt); - } - basic_value(const offset_datetime& odt, detail::region reg, - std::vector cm) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->offset_datetime_, odt); - } - basic_value(const array_type& ary, detail::region reg, - std::vector cm) - : type_(value_t::array), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->array_, ary); - } - basic_value(const table_type& tab, detail::region reg, - std::vector cm) - : type_(value_t::table), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->table_, tab); - } - - template::value, - std::nullptr_t>::type = nullptr> - basic_value(std::pair parse_result, std::vector com) - : basic_value(std::move(parse_result.first), - std::move(parse_result.second), - std::move(com)) - {} - - // type checking and casting ============================================ - - template::value, - std::nullptr_t>::type = nullptr> - bool is() const noexcept - { - return detail::type_to_enum::value == this->type_; - } - bool is(value_t t) const noexcept {return t == this->type_;} - - bool is_uninitialized() const noexcept {return this->is(value_t::empty );} - bool is_boolean() const noexcept {return this->is(value_t::boolean );} - bool is_integer() const noexcept {return this->is(value_t::integer );} - bool is_floating() const noexcept {return this->is(value_t::floating );} - bool is_string() const noexcept {return this->is(value_t::string );} - bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);} - bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );} - bool is_local_date() const noexcept {return this->is(value_t::local_date );} - bool is_local_time() const noexcept {return this->is(value_t::local_time );} - bool is_array() const noexcept {return this->is(value_t::array );} - bool is_table() const noexcept {return this->is(value_t::table );} - - value_t type() const noexcept {return type_;} - - template - typename detail::enum_to_type::type& cast() & - { - if(this->type_ != T) - { - detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); - } - return detail::switch_cast::invoke(*this); - } - template - typename detail::enum_to_type::type const& cast() const& - { - if(this->type_ != T) - { - detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); - } - return detail::switch_cast::invoke(*this); - } - template - typename detail::enum_to_type::type&& cast() && - { - if(this->type_ != T) - { - detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); - } - return detail::switch_cast::invoke(std::move(*this)); - } - - // ------------------------------------------------------------------------ - // nothrow version - - boolean const& as_boolean (const std::nothrow_t&) const& noexcept {return this->boolean_;} - integer const& as_integer (const std::nothrow_t&) const& noexcept {return this->integer_;} - floating const& as_floating (const std::nothrow_t&) const& noexcept {return this->floating_;} - string const& as_string (const std::nothrow_t&) const& noexcept {return this->string_;} - offset_datetime const& as_offset_datetime(const std::nothrow_t&) const& noexcept {return this->offset_datetime_;} - local_datetime const& as_local_datetime (const std::nothrow_t&) const& noexcept {return this->local_datetime_;} - local_date const& as_local_date (const std::nothrow_t&) const& noexcept {return this->local_date_;} - local_time const& as_local_time (const std::nothrow_t&) const& noexcept {return this->local_time_;} - array_type const& as_array (const std::nothrow_t&) const& noexcept {return this->array_.value();} - table_type const& as_table (const std::nothrow_t&) const& noexcept {return this->table_.value();} - - boolean & as_boolean (const std::nothrow_t&) & noexcept {return this->boolean_;} - integer & as_integer (const std::nothrow_t&) & noexcept {return this->integer_;} - floating & as_floating (const std::nothrow_t&) & noexcept {return this->floating_;} - string & as_string (const std::nothrow_t&) & noexcept {return this->string_;} - offset_datetime& as_offset_datetime(const std::nothrow_t&) & noexcept {return this->offset_datetime_;} - local_datetime & as_local_datetime (const std::nothrow_t&) & noexcept {return this->local_datetime_;} - local_date & as_local_date (const std::nothrow_t&) & noexcept {return this->local_date_;} - local_time & as_local_time (const std::nothrow_t&) & noexcept {return this->local_time_;} - array_type & as_array (const std::nothrow_t&) & noexcept {return this->array_.value();} - table_type & as_table (const std::nothrow_t&) & noexcept {return this->table_.value();} - - boolean && as_boolean (const std::nothrow_t&) && noexcept {return std::move(this->boolean_);} - integer && as_integer (const std::nothrow_t&) && noexcept {return std::move(this->integer_);} - floating && as_floating (const std::nothrow_t&) && noexcept {return std::move(this->floating_);} - string && as_string (const std::nothrow_t&) && noexcept {return std::move(this->string_);} - offset_datetime&& as_offset_datetime(const std::nothrow_t&) && noexcept {return std::move(this->offset_datetime_);} - local_datetime && as_local_datetime (const std::nothrow_t&) && noexcept {return std::move(this->local_datetime_);} - local_date && as_local_date (const std::nothrow_t&) && noexcept {return std::move(this->local_date_);} - local_time && as_local_time (const std::nothrow_t&) && noexcept {return std::move(this->local_time_);} - array_type && as_array (const std::nothrow_t&) && noexcept {return std::move(this->array_.value());} - table_type && as_table (const std::nothrow_t&) && noexcept {return std::move(this->table_.value());} - - // ======================================================================== - // throw version - // ------------------------------------------------------------------------ - // const reference {{{ - - boolean const& as_boolean() const& - { - if(this->type_ != value_t::boolean) - { - detail::throw_bad_cast( - "toml::value::as_boolean(): ", this->type_, *this); - } - return this->boolean_; - } - integer const& as_integer() const& - { - if(this->type_ != value_t::integer) - { - detail::throw_bad_cast( - "toml::value::as_integer(): ", this->type_, *this); - } - return this->integer_; - } - floating const& as_floating() const& - { - if(this->type_ != value_t::floating) - { - detail::throw_bad_cast( - "toml::value::as_floating(): ", this->type_, *this); - } - return this->floating_; - } - string const& as_string() const& - { - if(this->type_ != value_t::string) - { - detail::throw_bad_cast( - "toml::value::as_string(): ", this->type_, *this); - } - return this->string_; - } - offset_datetime const& as_offset_datetime() const& - { - if(this->type_ != value_t::offset_datetime) - { - detail::throw_bad_cast( - "toml::value::as_offset_datetime(): ", this->type_, *this); - } - return this->offset_datetime_; - } - local_datetime const& as_local_datetime() const& - { - if(this->type_ != value_t::local_datetime) - { - detail::throw_bad_cast( - "toml::value::as_local_datetime(): ", this->type_, *this); - } - return this->local_datetime_; - } - local_date const& as_local_date() const& - { - if(this->type_ != value_t::local_date) - { - detail::throw_bad_cast( - "toml::value::as_local_date(): ", this->type_, *this); - } - return this->local_date_; - } - local_time const& as_local_time() const& - { - if(this->type_ != value_t::local_time) - { - detail::throw_bad_cast( - "toml::value::as_local_time(): ", this->type_, *this); - } - return this->local_time_; - } - array_type const& as_array() const& - { - if(this->type_ != value_t::array) - { - detail::throw_bad_cast( - "toml::value::as_array(): ", this->type_, *this); - } - return this->array_.value(); - } - table_type const& as_table() const& - { - if(this->type_ != value_t::table) - { - detail::throw_bad_cast( - "toml::value::as_table(): ", this->type_, *this); - } - return this->table_.value(); - } - // }}} - // ------------------------------------------------------------------------ - // nonconst reference {{{ - - boolean & as_boolean() & - { - if(this->type_ != value_t::boolean) - { - detail::throw_bad_cast( - "toml::value::as_boolean(): ", this->type_, *this); - } - return this->boolean_; - } - integer & as_integer() & - { - if(this->type_ != value_t::integer) - { - detail::throw_bad_cast( - "toml::value::as_integer(): ", this->type_, *this); - } - return this->integer_; - } - floating & as_floating() & - { - if(this->type_ != value_t::floating) - { - detail::throw_bad_cast( - "toml::value::as_floating(): ", this->type_, *this); - } - return this->floating_; - } - string & as_string() & - { - if(this->type_ != value_t::string) - { - detail::throw_bad_cast( - "toml::value::as_string(): ", this->type_, *this); - } - return this->string_; - } - offset_datetime & as_offset_datetime() & - { - if(this->type_ != value_t::offset_datetime) - { - detail::throw_bad_cast( - "toml::value::as_offset_datetime(): ", this->type_, *this); - } - return this->offset_datetime_; - } - local_datetime & as_local_datetime() & - { - if(this->type_ != value_t::local_datetime) - { - detail::throw_bad_cast( - "toml::value::as_local_datetime(): ", this->type_, *this); - } - return this->local_datetime_; - } - local_date & as_local_date() & - { - if(this->type_ != value_t::local_date) - { - detail::throw_bad_cast( - "toml::value::as_local_date(): ", this->type_, *this); - } - return this->local_date_; - } - local_time & as_local_time() & - { - if(this->type_ != value_t::local_time) - { - detail::throw_bad_cast( - "toml::value::as_local_time(): ", this->type_, *this); - } - return this->local_time_; - } - array_type & as_array() & - { - if(this->type_ != value_t::array) - { - detail::throw_bad_cast( - "toml::value::as_array(): ", this->type_, *this); - } - return this->array_.value(); - } - table_type & as_table() & - { - if(this->type_ != value_t::table) - { - detail::throw_bad_cast( - "toml::value::as_table(): ", this->type_, *this); - } - return this->table_.value(); - } - - // }}} - // ------------------------------------------------------------------------ - // rvalue reference {{{ - - boolean && as_boolean() && - { - if(this->type_ != value_t::boolean) - { - detail::throw_bad_cast( - "toml::value::as_boolean(): ", this->type_, *this); - } - return std::move(this->boolean_); - } - integer && as_integer() && - { - if(this->type_ != value_t::integer) - { - detail::throw_bad_cast( - "toml::value::as_integer(): ", this->type_, *this); - } - return std::move(this->integer_); - } - floating && as_floating() && - { - if(this->type_ != value_t::floating) - { - detail::throw_bad_cast( - "toml::value::as_floating(): ", this->type_, *this); - } - return std::move(this->floating_); - } - string && as_string() && - { - if(this->type_ != value_t::string) - { - detail::throw_bad_cast( - "toml::value::as_string(): ", this->type_, *this); - } - return std::move(this->string_); - } - offset_datetime && as_offset_datetime() && - { - if(this->type_ != value_t::offset_datetime) - { - detail::throw_bad_cast( - "toml::value::as_offset_datetime(): ", this->type_, *this); - } - return std::move(this->offset_datetime_); - } - local_datetime && as_local_datetime() && - { - if(this->type_ != value_t::local_datetime) - { - detail::throw_bad_cast( - "toml::value::as_local_datetime(): ", this->type_, *this); - } - return std::move(this->local_datetime_); - } - local_date && as_local_date() && - { - if(this->type_ != value_t::local_date) - { - detail::throw_bad_cast( - "toml::value::as_local_date(): ", this->type_, *this); - } - return std::move(this->local_date_); - } - local_time && as_local_time() && - { - if(this->type_ != value_t::local_time) - { - detail::throw_bad_cast( - "toml::value::as_local_time(): ", this->type_, *this); - } - return std::move(this->local_time_); - } - array_type && as_array() && - { - if(this->type_ != value_t::array) - { - detail::throw_bad_cast( - "toml::value::as_array(): ", this->type_, *this); - } - return std::move(this->array_.value()); - } - table_type && as_table() && - { - if(this->type_ != value_t::table) - { - detail::throw_bad_cast( - "toml::value::as_table(): ", this->type_, *this); - } - return std::move(this->table_.value()); - } - // }}} - - // accessors ============================================================= - // - // may throw type_error or out_of_range - // - value_type& at(const key& k) - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::at(key): ", this->type_, *this); - } - if(this->as_table(std::nothrow).count(k) == 0) - { - detail::throw_key_not_found_error(*this, k); - } - return this->as_table(std::nothrow).at(k); - } - value_type const& at(const key& k) const - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::at(key): ", this->type_, *this); - } - if(this->as_table(std::nothrow).count(k) == 0) - { - detail::throw_key_not_found_error(*this, k); - } - return this->as_table(std::nothrow).at(k); - } - value_type& operator[](const key& k) - { - if(this->is_uninitialized()) - { - *this = table_type{}; - } - else if(!this->is_table()) // initialized, but not a table - { - detail::throw_bad_cast( - "toml::value::operator[](key): ", this->type_, *this); - } - return this->as_table(std::nothrow)[k]; - } - - value_type& at(const std::size_t idx) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::at(idx): ", this->type_, *this); - } - if(this->as_array(std::nothrow).size() <= idx) - { - throw std::out_of_range(detail::format_underline( - "toml::value::at(idx): no element corresponding to the index", { - {this->location(), concat_to_string("the length is ", - this->as_array(std::nothrow).size(), - ", and the specified index is ", idx)} - })); - } - return this->as_array().at(idx); - } - value_type const& at(const std::size_t idx) const - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::at(idx): ", this->type_, *this); - } - if(this->as_array(std::nothrow).size() <= idx) - { - throw std::out_of_range(detail::format_underline( - "toml::value::at(idx): no element corresponding to the index", { - {this->location(), concat_to_string("the length is ", - this->as_array(std::nothrow).size(), - ", and the specified index is ", idx)} - })); - } - return this->as_array(std::nothrow).at(idx); - } - - value_type& operator[](const std::size_t idx) noexcept - { - // no check... - return this->as_array(std::nothrow)[idx]; - } - value_type const& operator[](const std::size_t idx) const noexcept - { - // no check... - return this->as_array(std::nothrow)[idx]; - } - - void push_back(const value_type& x) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::push_back(value): ", this->type_, *this); - } - this->as_array(std::nothrow).push_back(x); - return; - } - void push_back(value_type&& x) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::push_back(value): ", this->type_, *this); - } - this->as_array(std::nothrow).push_back(std::move(x)); - return; - } - - template - value_type& emplace_back(Ts&& ... args) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::emplace_back(...): ", this->type_, *this); - } - this->as_array(std::nothrow).emplace_back(std::forward(args) ...); - return this->as_array(std::nothrow).back(); - } - - std::size_t size() const - { - switch(this->type_) - { - case value_t::array: - { - return this->as_array(std::nothrow).size(); - } - case value_t::table: - { - return this->as_table(std::nothrow).size(); - } - case value_t::string: - { - return this->as_string(std::nothrow).str.size(); - } - default: - { - throw type_error(detail::format_underline( - "toml::value::size(): bad_cast to container types", { - {this->location(), - concat_to_string("the actual type is ", this->type_)} - }), this->location()); - } - } - } - - std::size_t count(const key_type& k) const - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::count(key): ", this->type_, *this); - } - return this->as_table(std::nothrow).count(k); - } - - bool contains(const key_type& k) const - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::contains(key): ", this->type_, *this); - } - return (this->as_table(std::nothrow).count(k) != 0); - } - - source_location location() const - { - return source_location(this->region_info_.get()); - } - - comment_type const& comments() const noexcept {return this->comments_;} - comment_type& comments() noexcept {return this->comments_;} - - private: - - void cleanup() noexcept - { - switch(this->type_) - { - case value_t::string : {string_.~string(); return;} - case value_t::array : {array_.~array_storage(); return;} - case value_t::table : {table_.~table_storage(); return;} - default : return; - } - } - - // for error messages - template - friend region_base const* detail::get_region(const Value& v); - - template - friend void detail::change_region(Value& v, detail::region reg); - - private: - - using array_storage = detail::storage; - using table_storage = detail::storage; - - value_t type_; - union - { - boolean boolean_; - integer integer_; - floating floating_; - string string_; - offset_datetime offset_datetime_; - local_datetime local_datetime_; - local_date local_date_; - local_time local_time_; - array_storage array_; - table_storage table_; - }; - std::shared_ptr region_info_; - comment_type comments_; -}; - -// default toml::value and default array/table. -// TOML11_DEFAULT_COMMENT_STRATEGY is defined in comments.hpp -using value = basic_value; -using array = typename value::array_type; -using table = typename value::table_type; - -template class T, template class A> -inline bool -operator==(const basic_value& lhs, const basic_value& rhs) -{ - if(lhs.type() != rhs.type()) {return false;} - if(lhs.comments() != rhs.comments()) {return false;} - - switch(lhs.type()) - { - case value_t::boolean : - { - return lhs.as_boolean() == rhs.as_boolean(); - } - case value_t::integer : - { - return lhs.as_integer() == rhs.as_integer(); - } - case value_t::floating : - { - return lhs.as_floating() == rhs.as_floating(); - } - case value_t::string : - { - return lhs.as_string() == rhs.as_string(); - } - case value_t::offset_datetime: - { - return lhs.as_offset_datetime() == rhs.as_offset_datetime(); - } - case value_t::local_datetime: - { - return lhs.as_local_datetime() == rhs.as_local_datetime(); - } - case value_t::local_date: - { - return lhs.as_local_date() == rhs.as_local_date(); - } - case value_t::local_time: - { - return lhs.as_local_time() == rhs.as_local_time(); - } - case value_t::array : - { - return lhs.as_array() == rhs.as_array(); - } - case value_t::table : - { - return lhs.as_table() == rhs.as_table(); - } - case value_t::empty : {return true; } - default: {return false;} - } -} - -template class T, template class A> -inline bool operator!=(const basic_value& lhs, const basic_value& rhs) -{ - return !(lhs == rhs); -} - -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator<(const basic_value& lhs, const basic_value& rhs) -{ - if(lhs.type() != rhs.type()){return (lhs.type() < rhs.type());} - switch(lhs.type()) - { - case value_t::boolean : - { - return lhs.as_boolean() < rhs.as_boolean() || - (lhs.as_boolean() == rhs.as_boolean() && - lhs.comments() < rhs.comments()); - } - case value_t::integer : - { - return lhs.as_integer() < rhs.as_integer() || - (lhs.as_integer() == rhs.as_integer() && - lhs.comments() < rhs.comments()); - } - case value_t::floating : - { - return lhs.as_floating() < rhs.as_floating() || - (lhs.as_floating() == rhs.as_floating() && - lhs.comments() < rhs.comments()); - } - case value_t::string : - { - return lhs.as_string() < rhs.as_string() || - (lhs.as_string() == rhs.as_string() && - lhs.comments() < rhs.comments()); - } - case value_t::offset_datetime: - { - return lhs.as_offset_datetime() < rhs.as_offset_datetime() || - (lhs.as_offset_datetime() == rhs.as_offset_datetime() && - lhs.comments() < rhs.comments()); - } - case value_t::local_datetime: - { - return lhs.as_local_datetime() < rhs.as_local_datetime() || - (lhs.as_local_datetime() == rhs.as_local_datetime() && - lhs.comments() < rhs.comments()); - } - case value_t::local_date: - { - return lhs.as_local_date() < rhs.as_local_date() || - (lhs.as_local_date() == rhs.as_local_date() && - lhs.comments() < rhs.comments()); - } - case value_t::local_time: - { - return lhs.as_local_time() < rhs.as_local_time() || - (lhs.as_local_time() == rhs.as_local_time() && - lhs.comments() < rhs.comments()); - } - case value_t::array : - { - return lhs.as_array() < rhs.as_array() || - (lhs.as_array() == rhs.as_array() && - lhs.comments() < rhs.comments()); - } - case value_t::table : - { - return lhs.as_table() < rhs.as_table() || - (lhs.as_table() == rhs.as_table() && - lhs.comments() < rhs.comments()); - } - case value_t::empty : - { - return lhs.comments() < rhs.comments(); - } - default: - { - return lhs.comments() < rhs.comments(); - } - } -} - -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator<=(const basic_value& lhs, const basic_value& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator>(const basic_value& lhs, const basic_value& rhs) -{ - return !(lhs <= rhs); -} -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator>=(const basic_value& lhs, const basic_value& rhs) -{ - return !(lhs < rhs); -} - -template class T, template class A> -inline std::string format_error(const std::string& err_msg, - const basic_value& v, const std::string& comment, - std::vector hints = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - return detail::format_underline(err_msg, {{v.location(), comment}}, - std::move(hints), colorize); -} - -template class T, template class A> -inline std::string format_error(const std::string& err_msg, - const toml::basic_value& v1, const std::string& comment1, - const toml::basic_value& v2, const std::string& comment2, - std::vector hints = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - return detail::format_underline(err_msg, { - {v1.location(), comment1}, {v2.location(), comment2} - }, std::move(hints), colorize); -} - -template class T, template class A> -inline std::string format_error(const std::string& err_msg, - const toml::basic_value& v1, const std::string& comment1, - const toml::basic_value& v2, const std::string& comment2, - const toml::basic_value& v3, const std::string& comment3, - std::vector hints = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - return detail::format_underline(err_msg, {{v1.location(), comment1}, - {v2.location(), comment2}, {v3.location(), comment3} - }, std::move(hints), colorize); -} - -template class T, template class A> -detail::return_type_of_t -visit(Visitor&& visitor, const toml::basic_value& v) -{ - switch(v.type()) - { - case value_t::boolean : {return visitor(v.as_boolean ());} - case value_t::integer : {return visitor(v.as_integer ());} - case value_t::floating : {return visitor(v.as_floating ());} - case value_t::string : {return visitor(v.as_string ());} - case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} - case value_t::local_datetime : {return visitor(v.as_local_datetime ());} - case value_t::local_date : {return visitor(v.as_local_date ());} - case value_t::local_time : {return visitor(v.as_local_time ());} - case value_t::array : {return visitor(v.as_array ());} - case value_t::table : {return visitor(v.as_table ());} - case value_t::empty : break; - default: break; - } - throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " - "does not have any valid basic_value.", v, "here")); -} - -template class T, template class A> -detail::return_type_of_t -visit(Visitor&& visitor, toml::basic_value& v) -{ - switch(v.type()) - { - case value_t::boolean : {return visitor(v.as_boolean ());} - case value_t::integer : {return visitor(v.as_integer ());} - case value_t::floating : {return visitor(v.as_floating ());} - case value_t::string : {return visitor(v.as_string ());} - case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} - case value_t::local_datetime : {return visitor(v.as_local_datetime ());} - case value_t::local_date : {return visitor(v.as_local_date ());} - case value_t::local_time : {return visitor(v.as_local_time ());} - case value_t::array : {return visitor(v.as_array ());} - case value_t::table : {return visitor(v.as_table ());} - case value_t::empty : break; - default: break; - } - throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " - "does not have any valid basic_value.", v, "here")); -} - -template class T, template class A> -detail::return_type_of_t -visit(Visitor&& visitor, toml::basic_value&& v) -{ - switch(v.type()) - { - case value_t::boolean : {return visitor(std::move(v.as_boolean ()));} - case value_t::integer : {return visitor(std::move(v.as_integer ()));} - case value_t::floating : {return visitor(std::move(v.as_floating ()));} - case value_t::string : {return visitor(std::move(v.as_string ()));} - case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));} - case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));} - case value_t::local_date : {return visitor(std::move(v.as_local_date ()));} - case value_t::local_time : {return visitor(std::move(v.as_local_time ()));} - case value_t::array : {return visitor(std::move(v.as_array ()));} - case value_t::table : {return visitor(std::move(v.as_table ()));} - case value_t::empty : break; - default: break; - } - throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " - "does not have any valid basic_value.", v, "here")); -} - -}// toml -#endif// TOML11_VALUE diff --git a/src/frontend/qt_sdl/toml/toml/version.hpp b/src/frontend/qt_sdl/toml/toml/version.hpp deleted file mode 100644 index 9cbfa39b..00000000 --- a/src/frontend/qt_sdl/toml/toml/version.hpp +++ /dev/null @@ -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 diff --git a/src/frontend/qt_sdl/toml/toml11/color.hpp b/src/frontend/qt_sdl/toml/toml11/color.hpp new file mode 100644 index 00000000..d40d7f80 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/color.hpp @@ -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 diff --git a/src/frontend/qt_sdl/toml/toml11/comments.hpp b/src/frontend/qt_sdl/toml/toml11/comments.hpp new file mode 100644 index 00000000..4697f4e3 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/comments.hpp @@ -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 diff --git a/src/frontend/qt_sdl/toml/toml11/compat.hpp b/src/frontend/qt_sdl/toml/toml11/compat.hpp new file mode 100644 index 00000000..3308a32d --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/compat.hpp @@ -0,0 +1,751 @@ +#ifndef TOML11_COMPAT_HPP +#define TOML11_COMPAT_HPP + +#include "version.hpp" + +#include +#include +#include +#include +#include + +#include + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE +# if __has_include() +# include +# endif +#endif + +#include + +// ---------------------------------------------------------------------------- + +#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 +std::unique_ptr make_unique(Ts&& ... args) +{ + return std::unique_ptr(new T(std::forward(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 +std::reverse_iterator make_reverse_iterator(Iterator iter) +{ + return std::reverse_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 +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 +U bit_cast(const T& x) noexcept +{ + static_assert(sizeof(T) == sizeof(U), ""); + static_assert(std::is_default_constructible::value, ""); + + U z; + std::memcpy(reinterpret_cast(std::addressof(z)), + reinterpret_cast(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 +struct remove_cvref +{ + using type = typename std::remove_cv< + typename std::remove_reference::type>::type; +}; + +template +using remove_cvref_t = typename remove_cvref::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 struct conjunction : std::true_type{}; +template struct conjunction : T{}; +template +struct conjunction : + std::conditional(T::value), conjunction, T>::type +{}; + +template struct disjunction : std::false_type{}; +template struct disjunction : T {}; +template +struct disjunction : + std::conditional(T::value), T, disjunction>::type +{}; + +template +struct negation : std::integral_constant(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 struct index_sequence{}; + +template +struct double_index_sequence; + +template +struct double_index_sequence> +{ + using type = index_sequence; +}; +template +struct double_index_sequence> +{ + using type = index_sequence; +}; + +template +struct index_sequence_maker +{ + using type = typename double_index_sequence< + N % 2 == 1, N/2, typename index_sequence_maker::type + >::type; +}; +template<> +struct index_sequence_maker<0> +{ + using type = index_sequence<>; +}; + +template +using make_index_sequence = typename index_sequence_maker::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 +using enable_if_t = typename std::enable_if::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 +using return_type_of_t = std::invoke_result_t; + +#else + +// result_of is deprecated after C++17 +template +using return_type_of_t = typename std::result_of::type; + +#endif // TOML11_HAS_STD_INVOKE_RESULT + +} // cxx +} // toml + +// ---------------------------------------------------------------------------- +// (subset of) source_location + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 202002L +# if __has_include() +# 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() +# 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 +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 +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() +# include +# 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 +std::basic_ostream& +operator<<(std::basic_ostream& 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 +std::basic_ostream& +operator<<(std::basic_ostream& os, const nullopt_t&) +{ + os << "nullopt"; + return os; +} + +template +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::value, std::nullptr_t> = nullptr> + explicit optional(U&& x): has_value_(true), value_(std::forward(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>, std::is_constructible + >::value, std::nullptr_t> = nullptr> + explicit optional(const optional& rhs): has_value_(rhs.has_value_), null_('\0') + { + if(rhs.has_value_) + { + this->assigner(rhs.value_); + } + } + template>, std::is_constructible + >::value, std::nullptr_t> = nullptr> + explicit optional(optional&& rhs): has_value_(rhs.has_value_), null_('\0') + { + if(this->has_value_) + { + this->assigner(std::move(rhs.value_)); + } + } + + template>, std::is_constructible + >::value, std::nullptr_t> = nullptr> + 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; + } + + template>, std::is_constructible + >::value, std::nullptr_t> = nullptr> + 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; + } + ~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 + void assigner(U&& x) + { + const auto tmp = ::new(std::addressof(this->value_)) value_type(std::forward(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 diff --git a/src/frontend/qt_sdl/toml/toml11/context.hpp b/src/frontend/qt_sdl/toml/toml11/context.hpp new file mode 100644 index 00000000..cda038fb --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/context.hpp @@ -0,0 +1,68 @@ +#ifndef TOML11_CONTEXT_HPP +#define TOML11_CONTEXT_HPP + +#include "error_info.hpp" +#include "spec.hpp" + +#include + +namespace toml +{ +namespace detail +{ + +template +class context +{ + public: + + explicit context(const spec& toml_spec) + : toml_spec_(toml_spec), errors_{} + {} + + bool has_error() const noexcept {return !errors_.empty();} + + std::vector 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 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 diff --git a/src/frontend/qt_sdl/toml/toml/macros.hpp b/src/frontend/qt_sdl/toml/toml11/conversion.hpp similarity index 77% rename from src/frontend/qt_sdl/toml/toml/macros.hpp rename to src/frontend/qt_sdl/toml/toml11/conversion.hpp index e8f91aec..819a7658 100644 --- a/src/frontend/qt_sdl/toml/toml/macros.hpp +++ b/src/frontend/qt_sdl/toml/toml11/conversion.hpp @@ -1,5 +1,105 @@ -#ifndef TOML11_MACROS_HPP -#define TOML11_MACROS_HPP +#ifndef TOML11_CONVERSION_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 + +namespace toml +{ +namespace detail +{ + +template +inline constexpr bool is_optional_v = false; + +template +inline constexpr bool is_optional_v> = true; + +template +void find_member_variable_from_value(T& obj, const basic_value& v, const char* var_name) +{ + if constexpr(is_optional_v) + { + if(v.contains(var_name)) + { + obj = toml::find(v, var_name); + } + else + { + obj = std::nullopt; + } + } + else + { + obj = toml::find(v, var_name); + } +} + +template +void assign_member_variable_to_value(const T& obj, basic_value& v, const char* var_name) +{ + if constexpr(is_optional_v) + { + if(obj.has_value()) + { + v[var_name] = obj.value(); + } + } + else + { + v[var_name] = obj; + } +} + +} // detail +} // toml + +#else + +namespace toml +{ +namespace detail +{ + +template +void find_member_variable_from_value(T& obj, const basic_value& v, const char* var_name) +{ + obj = toml::find(v, var_name); +} + +template +void assign_member_variable_to_value(const T& obj, basic_value& 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(v)` and `toml::find(file, "foo");` +// #define TOML11_STRINGIZE_AUX(x) #x #define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) @@ -65,39 +165,20 @@ #define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ 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(file, "foo");` -// #define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ - obj.VAR_NAME = toml::find(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)\ - 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, ...)\ namespace toml { \ template<> \ struct from \ { \ - template class T, \ - template class A> \ - static NAME from_toml(const basic_value& v) \ + template \ + static NAME from_toml(const basic_value& v) \ { \ NAME obj; \ TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ @@ -107,9 +188,10 @@ template<> \ struct into \ { \ - static value into_toml(const NAME& obj) \ + template \ + static basic_value into_toml(const NAME& obj) \ { \ - ::toml::value v = ::toml::table{}; \ + ::toml::basic_value v = typename ::toml::basic_value::table_type{}; \ TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ return v; \ } \ @@ -118,4 +200,4 @@ #endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE -#endif// TOML11_MACROS_HPP +#endif // TOML11_CONVERSION_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/datetime.hpp b/src/frontend/qt_sdl/toml/toml11/datetime.hpp new file mode 100644 index 00000000..08411800 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/datetime.hpp @@ -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 diff --git a/src/frontend/qt_sdl/toml/toml11/error_info.hpp b/src/frontend/qt_sdl/toml/toml11/error_info.hpp new file mode 100644 index 00000000..4575f7ac --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/error_info.hpp @@ -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 diff --git a/src/frontend/qt_sdl/toml/toml11/exception.hpp b/src/frontend/qt_sdl/toml/toml11/exception.hpp new file mode 100644 index 00000000..7149eb3c --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/exception.hpp @@ -0,0 +1,17 @@ +#ifndef TOML11_EXCEPTION_HPP +#define TOML11_EXCEPTION_HPP + +#include + +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 diff --git a/src/frontend/qt_sdl/toml/toml11/find.hpp b/src/frontend/qt_sdl/toml/toml11/find.hpp new file mode 100644 index 00000000..560db2a7 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/find.hpp @@ -0,0 +1,377 @@ +#ifndef TOML11_FIND_HPP +#define TOML11_FIND_HPP + +#include + +#include "get.hpp" +#include "value.hpp" + +#if defined(TOML11_HAS_STRING_VIEW) +#include +#endif + +namespace toml +{ + +// ---------------------------------------------------------------------------- +// find(value, key); + +template +decltype(::toml::get(std::declval const&>())) +find(const basic_value& v, const typename basic_value::key_type& ky) +{ + return ::toml::get(v.at(ky)); +} + +template +decltype(::toml::get(std::declval&>())) +find(basic_value& v, const typename basic_value::key_type& ky) +{ + return ::toml::get(v.at(ky)); +} + +template +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, const typename basic_value::key_type& ky) +{ + return ::toml::get(std::move(v.at(ky))); +} + +// ---------------------------------------------------------------------------- +// find(value, idx) + +template +decltype(::toml::get(std::declval const&>())) +find(const basic_value& v, const std::size_t idx) +{ + return ::toml::get(v.at(idx)); +} +template +decltype(::toml::get(std::declval&>())) +find(basic_value& v, const std::size_t idx) +{ + return ::toml::get(v.at(idx)); +} +template +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, const std::size_t idx) +{ + return ::toml::get(std::move(v.at(idx))); +} + +// ---------------------------------------------------------------------------- +// find(value, key/idx), w/o conversion + +template +cxx::enable_if_t::value, basic_value>& +find(basic_value& v, const typename basic_value::key_type& ky) +{ + return v.at(ky); +} +template +cxx::enable_if_t::value, basic_value> const& +find(basic_value const& v, const typename basic_value::key_type& ky) +{ + return v.at(ky); +} +template +cxx::enable_if_t::value, basic_value> +find(basic_value&& v, const typename basic_value::key_type& ky) +{ + return basic_value(std::move(v.at(ky))); +} + +template +cxx::enable_if_t::value, basic_value>& +find(basic_value& v, const std::size_t idx) +{ + return v.at(idx); +} +template +cxx::enable_if_t::value, basic_value> const& +find(basic_value const& v, const std::size_t idx) +{ + return v.at(idx); +} +template +cxx::enable_if_t::value, basic_value> +find(basic_value&& v, const std::size_t idx) +{ + return basic_value(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 +std::size_t key_cast(const std::size_t& v) noexcept +{ + return v; +} +template +cxx::enable_if_t>::value, std::size_t> +key_cast(const T& v) noexcept +{ + return static_cast(v); +} + +// for string-like (string, string literal, string_view) + +template +typename basic_value::key_type const& +key_cast(const typename basic_value::key_type& v) noexcept +{ + return v; +} +template +typename basic_value::key_type +key_cast(const typename basic_value::key_type::value_type* v) +{ + return typename basic_value::key_type(v); +} +#if defined(TOML11_HAS_STRING_VIEW) +template +typename basic_value::key_type +key_cast(const std::string_view v) +{ + return typename basic_value::key_type(v); +} +#endif // string_view + +} // detail + +// ---------------------------------------------------------------------------- +// find(v, keys...) + +template +cxx::enable_if_t::value, basic_value> const& +find(const basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); +} +template +cxx::enable_if_t::value, basic_value>& +find(basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); +} +template +cxx::enable_if_t::value, basic_value> +find(basic_value&& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(std::move(v.at(detail::key_cast(k1))), detail::key_cast(k2), ks...); +} + +// ---------------------------------------------------------------------------- +// find(v, keys...) + +template +decltype(::toml::get(std::declval&>())) +find(const basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); +} +template +decltype(::toml::get(std::declval&>())) +find(basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); +} +template +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(std::move(v.at(detail::key_cast(k1))), detail::key_cast(k2), ks...); +} + +// =========================================================================== +// find_or(value, key, fallback) + +// --------------------------------------------------------------------------- +// find_or(v, key, other_v) + +template +cxx::enable_if_t::value, basic_value>& +find_or(basic_value& v, const K& k, basic_value& opt) noexcept +{ + try + { + return ::toml::find(v, detail::key_cast(k)); + } + catch(...) + { + return opt; + } +} +template +cxx::enable_if_t::value, basic_value> const& +find_or(const basic_value& v, const K& k, const basic_value& opt) noexcept +{ + try + { + return ::toml::find(v, detail::key_cast(k)); + } + catch(...) + { + return opt; + } +} +template +cxx::enable_if_t::value, basic_value> +find_or(basic_value&& v, const K& k, basic_value&& opt) noexcept +{ + try + { + return ::toml::find(v, detail::key_cast(k)); + } + catch(...) + { + return opt; + } +} + +// --------------------------------------------------------------------------- +// toml types (return type can be a reference) + +template +cxx::enable_if_t>::value, + cxx::remove_cvref_t const&> +find_or(const basic_value& v, const K& k, const T& opt) +{ + try + { + return ::toml::get(v.at(detail::key_cast(k))); + } + catch(...) + { + return opt; + } +} + +template +cxx::enable_if_t>, + detail::is_exact_toml_type> + >::value, cxx::remove_cvref_t&> +find_or(basic_value& v, const K& k, T& opt) +{ + try + { + return ::toml::get(v.at(detail::key_cast(k))); + } + catch(...) + { + return opt; + } +} + +template +cxx::enable_if_t>::value, + cxx::remove_cvref_t> +find_or(basic_value&& v, const K& k, T opt) +{ + try + { + return ::toml::get(std::move(v.at(detail::key_cast(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(), +// we restrict the string type as std::string. +template +cxx::enable_if_t::value, std::string> +find_or(const basic_value& v, const K& k, const char* opt) +{ + try + { + return ::toml::get(v.at(detail::key_cast(k))); + } + catch(...) + { + return std::string(opt); + } +} + +// --------------------------------------------------------------------------- +// other types (requires type conversion and return type cannot be a reference) + +template +cxx::enable_if_t>>, + detail::is_not_toml_type, basic_value>, + cxx::negation, + const typename basic_value::string_type::value_type*>> + >::value, cxx::remove_cvref_t> +find_or(const basic_value& v, const K& ky, T opt) +{ + try + { + return ::toml::get>(v.at(detail::key_cast(ky))); + } + catch(...) + { + return cxx::remove_cvref_t(std::move(opt)); + } +} + +// ---------------------------------------------------------------------------- +// recursive + +namespace detail +{ + +template +auto last_one(Ts&&... args) + -> decltype(std::get(std::forward_as_tuple(std::forward(args)...))) +{ + return std::get(std::forward_as_tuple(std::forward(args)...)); +} + +} // detail + +template +auto find_or(Value&& v, const K1& k1, const K2& k2, K3&& k3, Ks&& ... keys) noexcept + -> cxx::enable_if_t< + detail::is_basic_value>::value, + decltype(find_or(v, k2, std::forward(k3), std::forward(keys)...)) + > +{ + try + { + return find_or(v.at(k1), k2, std::forward(k3), std::forward(keys)...); + } + catch(...) + { + return detail::last_one(k3, keys...); + } +} + +template +T find_or(const basic_value& v, const K1& k1, const K2& k2, const K3& k3, const Ks& ... keys) noexcept +{ + try + { + return find_or(v.at(k1), k2, k3, keys...); + } + catch(...) + { + return static_cast(detail::last_one(k3, keys...)); + } +} + +} // toml +#endif // TOML11_FIND_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/format.hpp b/src/frontend/qt_sdl/toml/toml11/format.hpp new file mode 100644 index 00000000..6662220d --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/format.hpp @@ -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 diff --git a/src/frontend/qt_sdl/toml/toml/from.hpp b/src/frontend/qt_sdl/toml/toml11/from.hpp similarity index 78% rename from src/frontend/qt_sdl/toml/toml/from.hpp rename to src/frontend/qt_sdl/toml/toml11/from.hpp index 10815caf..d2e0e132 100644 --- a/src/frontend/qt_sdl/toml/toml/from.hpp +++ b/src/frontend/qt_sdl/toml/toml11/from.hpp @@ -1,5 +1,3 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. #ifndef TOML11_FROM_HPP #define TOML11_FROM_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/fwd/color_fwd.hpp b/src/frontend/qt_sdl/toml/toml11/fwd/color_fwd.hpp new file mode 100644 index 00000000..ed711c0d --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/fwd/color_fwd.hpp @@ -0,0 +1,88 @@ +#ifndef TOML11_COLOR_FWD_HPP +#define TOML11_COLOR_FWD_HPP + +#include + +#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 diff --git a/src/frontend/qt_sdl/toml/toml/comments.hpp b/src/frontend/qt_sdl/toml/toml11/fwd/comments_fwd.hpp similarity index 86% rename from src/frontend/qt_sdl/toml/toml/comments.hpp rename to src/frontend/qt_sdl/toml/toml11/fwd/comments_fwd.hpp index ec250411..bbc9926b 100644 --- a/src/frontend/qt_sdl/toml/toml/comments.hpp +++ b/src/frontend/qt_sdl/toml/toml11/fwd/comments_fwd.hpp @@ -1,7 +1,10 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_COMMENTS_HPP -#define TOML11_COMMENTS_HPP +#ifndef TOML11_COMMENTS_FWD_HPP +#define TOML11_COMMENTS_FWD_HPP + +// to use __has_builtin +#include "../version.hpp" // IWYU pragma: keep + +#include #include #include #include @@ -9,12 +12,7 @@ #include #include #include - -#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT -# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments -#else -# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments -#endif +#include // This file provides mainly two classes, `preserve_comments` and `discard_comments`. // Those two are a container that have the same interface as `std::vector` @@ -25,16 +23,11 @@ // error whenever you access to the element. namespace toml { -struct discard_comments; // forward decl +class discard_comments; // forward decl -// use it in the following way -// -// const toml::basic_value data = -// toml::parse("example.toml"); -// -// the interface is almost the same as std::vector. -struct preserve_comments +class preserve_comments { + public: // `container_type` is not provided in discard_comments. // do not use this inner-type in a generic code. using container_type = std::vector; @@ -51,6 +44,8 @@ struct preserve_comments using reverse_iterator = container_type::reverse_iterator; using const_reverse_iterator = container_type::const_reverse_iterator; + public: + preserve_comments() = default; ~preserve_comments() = default; preserve_comments(preserve_comments const&) = default; @@ -90,11 +85,9 @@ struct preserve_comments // Related to the issue #97. // - // It is known that `std::vector::insert` and `std::vector::erase` in - // the standard library implementation included in GCC 4.8.5 takes - // `std::vector::iterator` instead of `std::vector::const_iterator`. - // Because of the const-correctness, we cannot convert a `const_iterator` to - // an `iterator`. It causes compilation error in GCC 4.8.5. + // `std::vector::insert` and `std::vector::erase` in the STL implementation + // included in GCC 4.8.5 takes `std::vector::iterator` instead of + // `std::vector::const_iterator`. It causes compilation error in GCC 4.8.5. #if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) # if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 # define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION @@ -233,39 +226,18 @@ struct preserve_comments container_type comments; }; -inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} -inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} -inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} -inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} -inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.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); +bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs); +bool operator< (const preserve_comments& lhs, const preserve_comments& rhs); +bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs); +bool operator> (const preserve_comments& lhs, const preserve_comments& rhs); +bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs); -inline void swap(preserve_comments& lhs, preserve_comments& rhs) -{ - lhs.swap(rhs); - return; -} -inline void swap(preserve_comments& lhs, std::vector& rhs) -{ - lhs.comments.swap(rhs); - return; -} -inline void swap(std::vector& lhs, preserve_comments& rhs) -{ - lhs.swap(rhs.comments); - return; -} +void swap(preserve_comments& lhs, preserve_comments& rhs); +void swap(preserve_comments& lhs, std::vector& rhs); +void swap(std::vector& lhs, preserve_comments& rhs); -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const preserve_comments& com) -{ - for(const auto& c : com) - { - os << '#' << c << '\n'; - } - return os; -} +std::ostream& operator<<(std::ostream& os, const preserve_comments& com); namespace detail { @@ -349,8 +321,9 @@ operator+(const empty_iterator& lhs, typename empty_iterator::differ // efficiency, this is chosen as a default. // // 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 difference_type = std::ptrdiff_t; using value_type = std::string; @@ -363,6 +336,7 @@ struct discard_comments using reverse_iterator = detail::empty_iterator; using const_reverse_iterator = detail::empty_iterator; + public: discard_comments() = default; ~discard_comments() = default; discard_comments(discard_comments const&) = default; @@ -425,14 +399,14 @@ struct discard_comments // empty, so accessing through operator[], front/back, data causes address // error. - reference operator[](const size_type) noexcept {return *data();} - const_reference operator[](const size_type) const noexcept {return *data();} + reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");} + 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.");} const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} - reference front() noexcept {return *data();} - const_reference front() const noexcept {return *data();} - reference back() noexcept {return *data();} - const_reference back() const noexcept {return *data();} + reference front() noexcept {never_call("toml::discard_comment::front");} + const_reference front() const noexcept {never_call("toml::discard_comment::front");} + reference back() noexcept {never_call("toml::discard_comment::back");} + const_reference back() const noexcept {never_call("toml::discard_comment::back");} pointer data() 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 crbegin() 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;} @@ -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;} -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const discard_comments&) -{ - return os; -} +inline std::ostream& operator<<(std::ostream& os, const discard_comments&) {return os;} } // toml11 -#endif// TOML11_COMMENTS_HPP +#endif // TOML11_COMMENTS_FWD_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/fwd/datetime_fwd.hpp b/src/frontend/qt_sdl/toml/toml11/fwd/datetime_fwd.hpp new file mode 100644 index 00000000..44616a17 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/fwd/datetime_fwd.hpp @@ -0,0 +1,261 @@ +#ifndef TOML11_DATETIME_FWD_HPP +#define TOML11_DATETIME_FWD_HPP + +#include +#include +#include + +#include +#include +#include + +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(y)}, + month{static_cast(m)}, + day {static_cast(d)} + {} + + explicit local_date(const std::tm& t) + : year {static_cast(t.tm_year + 1900)}, + month{static_cast(t.tm_mon)}, + day {static_cast(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(h)}, + minute{static_cast(m)}, + second{static_cast(s)}, + millisecond{static_cast(ms)}, + microsecond{static_cast(us)}, + nanosecond {static_cast(ns)} + {} + + explicit local_time(const std::tm& t) + : hour {static_cast(t.tm_hour)}, + minute{static_cast(t.tm_min )}, + second{static_cast(t.tm_sec )}, + millisecond{0}, microsecond{0}, nanosecond{0} + {} + + template + explicit local_time(const std::chrono::duration& t) + { + const auto h = std::chrono::duration_cast(t); + this->hour = static_cast(h.count()); + const auto t2 = t - h; + const auto m = std::chrono::duration_cast(t2); + this->minute = static_cast(m.count()); + const auto t3 = t2 - m; + const auto s = std::chrono::duration_cast(t3); + this->second = static_cast(s.count()); + const auto t4 = t3 - s; + const auto ms = std::chrono::duration_cast(t4); + this->millisecond = static_cast(ms.count()); + const auto t5 = t4 - ms; + const auto us = std::chrono::duration_cast(t5); + this->microsecond = static_cast(us.count()); + const auto t6 = t5 - us; + const auto ns = std::chrono::duration_cast(t6); + this->nanosecond = static_cast(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(h)}, + minute{static_cast(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 diff --git a/src/frontend/qt_sdl/toml/toml11/fwd/error_info_fwd.hpp b/src/frontend/qt_sdl/toml/toml11/fwd/error_info_fwd.hpp new file mode 100644 index 00000000..5b8600cb --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/fwd/error_info_fwd.hpp @@ -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> 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> 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> locations_; + std::string suffix_; // hint or something like that +}; + +// forward decl +template +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 +error_info make_error_info_rec(error_info e, + const basic_value& v, std::string msg, Ts&& ... tail); + +template +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(tail)...); +} + +} // detail + +template +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(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 +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(tail)...)); +} + +std::ostream& operator<<(std::ostream& os, const error_info& e); + +} // toml +#endif // TOML11_ERROR_INFO_FWD_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/fwd/format_fwd.hpp b/src/frontend/qt_sdl/toml/toml11/fwd/format_fwd.hpp new file mode 100644 index 00000000..d478d96f --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/fwd/format_fwd.hpp @@ -0,0 +1,250 @@ +#ifndef TOML11_FORMAT_FWD_HPP +#define TOML11_FORMAT_FWD_HPP + +#include +#include +#include + +#include +#include + +namespace toml +{ + +// toml types with serialization info + +enum class indent_char : std::uint8_t +{ + space, // use space + tab, // use tab + none // no indent +}; + +std::ostream& operator<<(std::ostream& os, const indent_char& c); +std::string to_string(const indent_char c); + +// ---------------------------------------------------------------------------- +// boolean + +struct boolean_format_info +{ + // nothing, for now +}; + +inline bool operator==(const boolean_format_info&, const boolean_format_info&) noexcept +{ + return true; +} +inline bool operator!=(const boolean_format_info&, const boolean_format_info&) noexcept +{ + return false; +} + +// ---------------------------------------------------------------------------- +// integer + +enum class integer_format : std::uint8_t +{ + dec = 0, + bin = 1, + oct = 2, + hex = 3, +}; + +std::ostream& operator<<(std::ostream& os, const integer_format f); +std::string to_string(const integer_format); + +struct integer_format_info +{ + integer_format fmt = integer_format::dec; + bool uppercase = true; // hex with uppercase + std::size_t width = 0; // minimal width (may exceed) + std::size_t spacer = 0; // position of `_` (if 0, no spacer) + std::string suffix = ""; // _suffix (library extension) +}; + +bool operator==(const integer_format_info&, const integer_format_info&) noexcept; +bool operator!=(const integer_format_info&, const integer_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// floating + +enum class floating_format : std::uint8_t +{ + defaultfloat = 0, + fixed = 1, // does not include exponential part + scientific = 2, // always include exponential part + hex = 3 // hexfloat extension +}; + +std::ostream& operator<<(std::ostream& os, const floating_format f); +std::string to_string(const floating_format); + +struct floating_format_info +{ + floating_format fmt = floating_format::defaultfloat; + std::size_t prec = 0; // precision (if 0, use the default) + std::string suffix = ""; // 1.0e+2_suffix (library extension) +}; + +bool operator==(const floating_format_info&, const floating_format_info&) noexcept; +bool operator!=(const floating_format_info&, const floating_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// string + +enum class string_format : std::uint8_t +{ + basic = 0, + literal = 1, + multiline_basic = 2, + multiline_literal = 3 +}; + +std::ostream& operator<<(std::ostream& os, const string_format f); +std::string to_string(const string_format); + +struct string_format_info +{ + string_format fmt = string_format::basic; + bool start_with_newline = false; +}; + +bool operator==(const string_format_info&, const string_format_info&) noexcept; +bool operator!=(const string_format_info&, const string_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// datetime + +enum class datetime_delimiter_kind : std::uint8_t +{ + upper_T = 0, + lower_t = 1, + space = 2, +}; +std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d); +std::string to_string(const datetime_delimiter_kind); + +struct offset_datetime_format_info +{ + datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; + bool has_seconds = true; + std::size_t subsecond_precision = 6; // [us] +}; + +bool operator==(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; +bool operator!=(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; + +struct local_datetime_format_info +{ + datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; + bool has_seconds = true; + std::size_t subsecond_precision = 6; // [us] +}; + +bool operator==(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; +bool operator!=(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; + +struct local_date_format_info +{ + // nothing, for now +}; + +bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept; +bool operator!=(const local_date_format_info&, const local_date_format_info&) noexcept; + +struct local_time_format_info +{ + bool has_seconds = true; + std::size_t subsecond_precision = 6; // [us] +}; + +bool operator==(const local_time_format_info&, const local_time_format_info&) noexcept; +bool operator!=(const local_time_format_info&, const local_time_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// array + +enum class array_format : std::uint8_t +{ + default_format = 0, + oneline = 1, + multiline = 2, + array_of_tables = 3 // [[format.in.this.way]] +}; + +std::ostream& operator<<(std::ostream& os, const array_format f); +std::string to_string(const array_format); + +struct array_format_info +{ + array_format fmt = array_format::default_format; + indent_char indent_type = indent_char::space; + std::int32_t body_indent = 4; // indent in case of multiline + std::int32_t closing_indent = 0; // indent of `]` +}; + +bool operator==(const array_format_info&, const array_format_info&) noexcept; +bool operator!=(const array_format_info&, const array_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// table + +enum class table_format : std::uint8_t +{ + multiline = 0, // [foo] \n bar = "baz" + oneline = 1, // foo = {bar = "baz"} + dotted = 2, // foo.bar = "baz" + multiline_oneline = 3, // foo = { \n bar = "baz" \n } + implicit = 4 // [x] defined by [x.y.z]. skip in serializer. +}; + +std::ostream& operator<<(std::ostream& os, const table_format f); +std::string to_string(const table_format); + +struct table_format_info +{ + table_format fmt = table_format::multiline; + indent_char indent_type = indent_char::space; + std::int32_t body_indent = 0; // indent of values + std::int32_t name_indent = 0; // indent of [table] + std::int32_t closing_indent = 0; // in case of {inline-table} +}; + +bool operator==(const table_format_info&, const table_format_info&) noexcept; +bool operator!=(const table_format_info&, const table_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// wrapper + +namespace detail +{ +template +struct value_with_format +{ + using value_type = T; + using format_type = F; + + value_with_format() = default; + ~value_with_format() = default; + value_with_format(const value_with_format&) = default; + value_with_format(value_with_format&&) = default; + value_with_format& operator=(const value_with_format&) = default; + value_with_format& operator=(value_with_format&&) = default; + + value_with_format(value_type v, format_type f) + : value{std::move(v)}, format{std::move(f)} + {} + + template + value_with_format(value_with_format other) + : value{std::move(other.value)}, format{std::move(other.format)} + {} + + value_type value; + format_type format; +}; +} // detail + +} // namespace toml +#endif // TOML11_FORMAT_FWD_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/fwd/literal_fwd.hpp b/src/frontend/qt_sdl/toml/toml11/fwd/literal_fwd.hpp new file mode 100644 index 00000000..e46612ce --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/fwd/literal_fwd.hpp @@ -0,0 +1,33 @@ +#ifndef TOML11_LITERAL_FWD_HPP +#define TOML11_LITERAL_FWD_HPP + +#include "../location.hpp" +#include "../types.hpp" +#include "../version.hpp" // IWYU pragma: keep for TOML11_HAS_CHAR8_T + +namespace toml +{ + +namespace detail +{ +// implementation +::toml::value literal_internal_impl(location loc); +} // detail + +inline namespace literals +{ +inline namespace toml_literals +{ + +::toml::value operator"" _toml(const char* str, std::size_t len); + +#if defined(TOML11_HAS_CHAR8_T) +// value of u8"" literal has been changed from char to char8_t and char8_t is +// NOT compatible to char +::toml::value operator"" _toml(const char8_t* str, std::size_t len); +#endif + +} // toml_literals +} // literals +} // toml +#endif // TOML11_LITERAL_FWD_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/fwd/location_fwd.hpp b/src/frontend/qt_sdl/toml/toml11/fwd/location_fwd.hpp new file mode 100644 index 00000000..f6dbfdbc --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/fwd/location_fwd.hpp @@ -0,0 +1,145 @@ +#ifndef TOML11_LOCATION_FWD_HPP +#define TOML11_LOCATION_FWD_HPP + +#include "../result.hpp" + +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +class region; // fwd decl + +// +// To represent where we are reading in the parse functions. +// Since it "points" somewhere in the input stream, the length is always 1. +// +class location +{ + public: + + using char_type = unsigned char; // must be unsigned + using container_type = std::vector; + using difference_type = typename container_type::difference_type; // to suppress sign-conversion warning + using source_ptr = std::shared_ptr; + + public: + + location(source_ptr src, std::string src_name) + : source_(std::move(src)), source_name_(std::move(src_name)), + location_(0), line_number_(1) + {} + + location(const location&) = default; + location(location&&) = default; + location& operator=(const location&) = default; + location& operator=(location&&) = default; + ~location() = default; + + void advance(std::size_t n = 1) noexcept; + void retrace(std::size_t n = 1) noexcept; + + bool is_ok() const noexcept { return static_cast(this->source_); } + + bool eof() const noexcept; + char_type current() const; + + char_type peek(); + + std::size_t get_location() const noexcept + { + return this->location_; + } + void set_location(const std::size_t loc) noexcept; + + std::size_t line_number() const noexcept + { + return this->line_number_; + } + std::string get_line() const; + std::size_t column_number() const noexcept; + + source_ptr const& source() const noexcept {return this->source_;} + std::string const& source_name() const noexcept {return this->source_name_;} + + private: + + void advance_line_number(const std::size_t n); + void retrace_line_number(const std::size_t n); + + private: + + friend region; + + private: + + source_ptr source_; + std::string source_name_; + std::size_t location_; // std::vector<>::difference_type is signed + std::size_t line_number_; +}; + +bool operator==(const location& lhs, const location& rhs) noexcept; +bool operator!=(const location& lhs, const location& rhs); + +location prev(const location& loc); +location next(const location& loc); +location make_temporary_location(const std::string& str) noexcept; + +template +result +find_if(const location& first, const location& last, const F& func) noexcept +{ + if(first.source() != last.source()) { return err(); } + if(first.get_location() >= last.get_location()) { return err(); } + + auto loc = first; + while(loc.get_location() != last.get_location()) + { + if(func(loc.current())) + { + return ok(loc); + } + loc.advance(); + } + return err(); +} + +template +result +rfind_if(location first, const location& last, const F& func) +{ + if(first.source() != last.source()) { return err(); } + if(first.get_location() >= last.get_location()) { return err(); } + + auto loc = last; + while(loc.get_location() != first.get_location()) + { + if(func(loc.current())) + { + return ok(loc); + } + loc.retrace(); + } + if(func(first.current())) + { + return ok(first); + } + return err(); +} + +result find(const location& first, const location& last, + const location::char_type val); +result rfind(const location& first, const location& last, + const location::char_type val); + +std::size_t count(const location& first, const location& last, + const location::char_type& c); + +} // detail +} // toml +#endif // TOML11_LOCATION_FWD_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/fwd/region_fwd.hpp b/src/frontend/qt_sdl/toml/toml11/fwd/region_fwd.hpp new file mode 100644 index 00000000..b40317d2 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/fwd/region_fwd.hpp @@ -0,0 +1,104 @@ +#ifndef TOML11_REGION_FWD_HPP +#define TOML11_REGION_FWD_HPP + +#include "../location.hpp" + +#include +#include + +#include + +namespace toml +{ +namespace detail +{ + +// +// To represent where is a toml::value defined, or where does an error occur. +// Stored in toml::value. source_location will be constructed based on this. +// +class region +{ + public: + + using char_type = location::char_type; + using container_type = location::container_type; + using difference_type = location::difference_type; + using source_ptr = location::source_ptr; + + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + + public: + + // a value that is constructed manually does not have input stream info + region() + : source_(nullptr), source_name_(""), length_(0), + first_line_(0), first_column_(0), last_line_(0), last_column_(0) + {} + + // a value defined in [first, last). + // Those source must be the same. Instread, `region` does not make sense. + region(const location& first, const location& last); + + // shorthand of [loc, loc+1) + explicit region(const location& loc); + + ~region() = default; + region(const region&) = default; + region(region&&) = default; + region& operator=(const region&) = default; + region& operator=(region&&) = default; + + bool is_ok() const noexcept { return static_cast(this->source_); } + + operator bool() const noexcept { return this->is_ok(); } + + std::size_t length() const noexcept {return this->length_;} + + std::size_t first_line_number() const noexcept + { + return this->first_line_; + } + std::size_t first_column_number() const noexcept + { + return this->first_column_; + } + std::size_t last_line_number() const noexcept + { + return this->last_line_; + } + std::size_t last_column_number() const noexcept + { + return this->last_column_; + } + + char_type at(std::size_t i) const; + + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + std::string as_string() const; + std::vector as_lines() const; + + source_ptr const& source() const noexcept {return this->source_;} + std::string const& source_name() const noexcept {return this->source_name_;} + + private: + + source_ptr source_; + std::string source_name_; + std::size_t length_; + std::size_t first_; + std::size_t first_line_; + std::size_t first_column_; + std::size_t last_; + std::size_t last_line_; + std::size_t last_column_; +}; + +} // namespace detail +} // namespace toml +#endif // TOML11_REGION_FWD_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/fwd/scanner_fwd.hpp b/src/frontend/qt_sdl/toml/toml11/fwd/scanner_fwd.hpp new file mode 100644 index 00000000..a8867277 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/fwd/scanner_fwd.hpp @@ -0,0 +1,391 @@ +#ifndef TOML11_SCANNER_FWD_HPP +#define TOML11_SCANNER_FWD_HPP + +#include "../region.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +class scanner_base +{ + public: + virtual ~scanner_base() = default; + virtual region scan(location& loc) const = 0; + virtual scanner_base* clone() const = 0; + + // returns expected character or set of characters or literal. + // to show the error location, it changes loc (in `sequence`, especially). + virtual std::string expected_chars(location& loc) const = 0; + virtual std::string name() const = 0; +}; + +// make `scanner*` copyable +struct scanner_storage +{ + template>::value, + std::nullptr_t> = nullptr> + explicit scanner_storage(Scanner&& s) + : scanner_(cxx::make_unique>(std::forward(s))) + {} + ~scanner_storage() = default; + + scanner_storage(const scanner_storage& other); + scanner_storage& operator=(const scanner_storage& other); + scanner_storage(scanner_storage&&) = default; + scanner_storage& operator=(scanner_storage&&) = default; + + bool is_ok() const noexcept {return static_cast(scanner_);} + + region scan(location& loc) const; + + std::string expected_chars(location& loc) const; + + scanner_base& get() const noexcept; + + std::string name() const; + + private: + + std::unique_ptr scanner_; +}; + +// ---------------------------------------------------------------------------- + +class character final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit character(const char_type c) noexcept + : value_(c) + {} + ~character() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + char_type value_; +}; + +// ---------------------------------------------------------------------------- + +class character_either final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit character_either(std::initializer_list cs) noexcept + : chars_(std::move(cs)) + { + assert(! this->chars_.empty()); + } + + template + explicit character_either(const char (&cs)[N]) noexcept + : chars_(N-1, '\0') + { + static_assert(N >= 1, ""); + for(std::size_t i=0; i+1 chars_; +}; + +// ---------------------------------------------------------------------------- + +class character_in_range final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit character_in_range(const char_type from, const char_type to) noexcept + : from_(from), to_(to) + {} + ~character_in_range() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + char_type from_; + char_type to_; +}; + +// ---------------------------------------------------------------------------- + +class literal final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + template + explicit literal(const char (&cs)[N]) noexcept + : value_(cs), size_(N-1) // remove null character at the end + {} + ~literal() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + const char* value_; + std::size_t size_; +}; + +// ---------------------------------------------------------------------------- + +class sequence final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + explicit sequence(Ts&& ... args) + { + push_back_all(std::forward(args)...); + } + sequence(const sequence&) = default; + sequence(sequence&&) = default; + sequence& operator=(const sequence&) = default; + sequence& operator=(sequence&&) = default; + ~sequence() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location& loc) const override; + + scanner_base* clone() const override; + + template + void push_back(Scanner&& other_scanner) + { + this->others_.emplace_back(std::forward(other_scanner)); + } + + std::string name() const override; + + private: + + void push_back_all() + { + return; + } + template + void push_back_all(T&& head, Ts&& ... args) + { + others_.emplace_back(std::forward(head)); + push_back_all(std::forward(args)...); + return; + } + + private: + std::vector others_; +}; + +// ---------------------------------------------------------------------------- + +class either final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + explicit either(Ts&& ... args) + { + push_back_all(std::forward(args)...); + } + either(const either&) = default; + either(either&&) = default; + either& operator=(const either&) = default; + either& operator=(either&&) = default; + ~either() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location& loc) const override; + + scanner_base* clone() const override; + + template + void push_back(Scanner&& other_scanner) + { + this->others_.emplace_back(std::forward(other_scanner)); + } + + std::string name() const override; + + private: + + void push_back_all() + { + return; + } + template + void push_back_all(T&& head, Ts&& ... args) + { + others_.emplace_back(std::forward(head)); + push_back_all(std::forward(args)...); + return; + } + + private: + std::vector others_; +}; + +// ---------------------------------------------------------------------------- + +class repeat_exact final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + repeat_exact(const std::size_t length, Scanner&& other) + : length_(length), other_(std::forward(other)) + {} + repeat_exact(const repeat_exact&) = default; + repeat_exact(repeat_exact&&) = default; + repeat_exact& operator=(const repeat_exact&) = default; + repeat_exact& operator=(repeat_exact&&) = default; + ~repeat_exact() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location& loc) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + std::size_t length_; + scanner_storage other_; +}; + +// ---------------------------------------------------------------------------- + +class repeat_at_least final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + repeat_at_least(const std::size_t length, Scanner&& s) + : length_(length), other_(std::forward(s)) + {} + repeat_at_least(const repeat_at_least&) = default; + repeat_at_least(repeat_at_least&&) = default; + repeat_at_least& operator=(const repeat_at_least&) = default; + repeat_at_least& operator=(repeat_at_least&&) = default; + ~repeat_at_least() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location& loc) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + std::size_t length_; + scanner_storage other_; +}; + +// ---------------------------------------------------------------------------- + +class maybe final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + explicit maybe(Scanner&& s) + : other_(std::forward(s)) + {} + maybe(const maybe&) = default; + maybe(maybe&&) = default; + maybe& operator=(const maybe&) = default; + maybe& operator=(maybe&&) = default; + ~maybe() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + scanner_storage other_; +}; + +} // detail +} // toml +#endif // TOML11_SCANNER_FWD_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/fwd/source_location_fwd.hpp b/src/frontend/qt_sdl/toml/toml11/fwd/source_location_fwd.hpp new file mode 100644 index 00000000..77c7579d --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/fwd/source_location_fwd.hpp @@ -0,0 +1,115 @@ +#ifndef TOML11_SOURCE_LOCATION_FWD_HPP +#define TOML11_SOURCE_LOCATION_FWD_HPP + +#include "../region.hpp" + +#include +#include +#include + +namespace toml +{ + +// A struct to contain location in a toml file. +struct source_location +{ + public: + + explicit source_location(const detail::region& r); + ~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; + + bool is_ok() const noexcept {return this->is_ok_;} + std::size_t length() const noexcept {return this->length_;} + + std::size_t first_line_number() const noexcept {return this->first_line_;} + std::size_t first_column_number() const noexcept {return this->first_column_;} + std::size_t last_line_number() const noexcept {return this->last_line_;} + std::size_t last_column_number() const noexcept {return this->last_column_;} + + std::string const& file_name() const noexcept {return this->file_name_;} + + std::size_t num_lines() const noexcept {return this->line_str_.size();} + + std::string const& first_line() const; + std::string const& last_line() const; + + std::vector const& lines() const noexcept {return line_str_;} + + private: + + bool is_ok_; + std::size_t first_line_; + std::size_t first_column_; + std::size_t last_line_; + std::size_t last_column_; + std::size_t length_; + std::string file_name_; + std::vector line_str_; +}; + +namespace detail +{ + +std::size_t integer_width_base10(std::size_t i) noexcept; + +inline std::size_t line_width() noexcept {return 0;} + +template +std::size_t line_width(const source_location& loc, const std::string& /*msg*/, + const Ts& ... tail) noexcept +{ + return (std::max)( + integer_width_base10(loc.last_line_number()), line_width(tail...)); +} + +std::ostringstream& +format_filename(std::ostringstream& oss, const source_location& loc); + +std::ostringstream& +format_empty_line(std::ostringstream& oss, const std::size_t lnw); + +std::ostringstream& format_line(std::ostringstream& oss, + const std::size_t lnw, const std::size_t linenum, const std::string& line); + +std::ostringstream& format_underline(std::ostringstream& oss, + const std::size_t lnw, const std::size_t col, const std::size_t len, + const std::string& msg); + +std::string format_location_impl(const std::size_t lnw, + const std::string& prev_fname, + const source_location& loc, const std::string& msg); + +inline std::string format_location_rec(const std::size_t, const std::string&) +{ + return ""; +} + +template +std::string format_location_rec(const std::size_t lnw, + const std::string& prev_fname, + const source_location& loc, const std::string& msg, + const Ts& ... tail) +{ + return format_location_impl(lnw, prev_fname, loc, msg) + + format_location_rec(lnw, loc.file_name(), tail...); +} + +} // namespace detail + +// format a location info without title +template +std::string format_location( + const source_location& loc, const std::string& msg, const Ts& ... tail) +{ + const auto lnw = detail::line_width(loc, msg, tail...); + + const std::string f(""); // at the 1st iteration, no prev_filename is given + return detail::format_location_rec(lnw, f, loc, msg, tail...); +} + +} // toml +#endif // TOML11_SOURCE_LOCATION_FWD_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/fwd/syntax_fwd.hpp b/src/frontend/qt_sdl/toml/toml11/fwd/syntax_fwd.hpp new file mode 100644 index 00000000..35608215 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/fwd/syntax_fwd.hpp @@ -0,0 +1,357 @@ +#ifndef TOML11_SYNTAX_FWD_HPP +#define TOML11_SYNTAX_FWD_HPP + +#include "../scanner.hpp" +#include "../spec.hpp" + +namespace toml +{ +namespace detail +{ +namespace syntax +{ + +using char_type = location::char_type; + +// =========================================================================== +// UTF-8 + +// avoid redundant representation and out-of-unicode sequence + +character_in_range utf8_1byte (const spec&); +sequence utf8_2bytes(const spec&); +sequence utf8_3bytes(const spec&); +sequence utf8_4bytes(const spec&); + +class non_ascii final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit non_ascii(const spec& s) noexcept; + ~non_ascii() override = default; + + region scan(location& loc) const override + { + return scanner_.scan(loc); + } + + std::string expected_chars(location&) const override + { + return "non-ascii utf-8 bytes"; + } + + scanner_base* clone() const override + { + return new non_ascii(*this); + } + + std::string name() const override + { + return "non_ascii"; + } + + private: + + either scanner_; +}; + +// =========================================================================== +// Whitespace + +character_either wschar(const spec&); + +repeat_at_least ws(const spec& s); + +// =========================================================================== +// Newline + +either newline(const spec&); + +// =========================================================================== +// Comments + +either allowed_comment_char(const spec& s); + +// XXX Note that it does not take newline +sequence comment(const spec& s); + +// =========================================================================== +// Boolean + +either boolean(const spec&); + +// =========================================================================== +// Integer + +class digit final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit digit(const spec&) noexcept; + ~digit() override = default; + + region scan(location& loc) const override + { + return scanner_.scan(loc); + } + + std::string expected_chars(location&) const override + { + return "digit [0-9]"; + } + + scanner_base* clone() const override + { + return new digit(*this); + } + + std::string name() const override + { + return "digit"; + } + + private: + + character_in_range scanner_; +}; + +class alpha final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit alpha(const spec&) noexcept; + ~alpha() override = default; + + region scan(location& loc) const override + { + return scanner_.scan(loc); + } + + std::string expected_chars(location&) const override + { + return "alpha [a-zA-Z]"; + } + + scanner_base* clone() const override + { + return new alpha(*this); + } + + std::string name() const override + { + return "alpha"; + } + + private: + + either scanner_; +}; + +class hexdig final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit hexdig(const spec& s) noexcept; + ~hexdig() override = default; + + region scan(location& loc) const override + { + return scanner_.scan(loc); + } + + std::string expected_chars(location&) const override + { + return "hex [0-9a-fA-F]"; + } + + scanner_base* clone() const override + { + return new hexdig(*this); + } + + std::string name() const override + { + return "hexdig"; + } + + private: + + either scanner_; +}; + +sequence num_suffix(const spec& s); + +sequence dec_int(const spec& s); +sequence hex_int(const spec& s); +sequence oct_int(const spec&); +sequence bin_int(const spec&); +either integer(const spec& s); + +// =========================================================================== +// Floating + +sequence zero_prefixable_int(const spec& s); +sequence fractional_part(const spec& s); +sequence exponent_part(const spec& s); +sequence hex_floating(const spec& s); +either floating(const spec& s); + +// =========================================================================== +// Datetime + +sequence local_date(const spec& s); +sequence local_time(const spec& s); +either time_offset(const spec& s); +sequence full_time(const spec& s); +character_either time_delim(const spec&); +sequence local_datetime(const spec& s); +sequence offset_datetime(const spec& s); + +// =========================================================================== +// String + +sequence escaped(const spec& s); + +either basic_char(const spec& s); + +sequence basic_string(const spec& s); + +// --------------------------------------------------------------------------- +// multiline string + +sequence escaped_newline(const spec& s); +sequence ml_basic_string(const spec& s); + +// --------------------------------------------------------------------------- +// literal string + +either literal_char(const spec& s); +sequence literal_string(const spec& s); + +sequence ml_literal_string(const spec& s); + +either string(const spec& s); + +// =========================================================================== +// Keys + +// to keep `expected_chars` simple +class non_ascii_key_char final : public scanner_base +{ + public: + + using char_type = location::char_type; + + private: + + using in_range = character_in_range; // make definition short + + public: + + explicit non_ascii_key_char(const spec& s) noexcept; + ~non_ascii_key_char() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override + { + return "bare key non-ASCII script"; + } + + scanner_base* clone() const override + { + return new non_ascii_key_char(*this); + } + + std::string name() const override + { + return "non-ASCII bare key"; + } + + private: + + std::uint32_t read_utf8(location& loc) const; +}; + + +repeat_at_least unquoted_key(const spec& s); + +either quoted_key(const spec& s); + +either simple_key(const spec& s); + +sequence dot_sep(const spec& s); + +sequence dotted_key(const spec& s); + + +class key final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit key(const spec& s) noexcept; + ~key() override = default; + + region scan(location& loc) const override + { + return scanner_.scan(loc); + } + + std::string expected_chars(location&) const override + { + return "basic key([a-zA-Z0-9_-]) or quoted key(\" or ')"; + } + + scanner_base* clone() const override + { + return new key(*this); + } + + std::string name() const override + { + return "key"; + } + + private: + + either scanner_; +}; + +sequence keyval_sep(const spec& s); + +// =========================================================================== +// Table key + +sequence std_table(const spec& s); + +sequence array_table(const spec& s); + +// =========================================================================== +// extension: null + +literal null_value(const spec&); + +} // namespace syntax +} // namespace detail +} // namespace toml +#endif // TOML11_SYNTAX_FWD_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/fwd/value_t_fwd.hpp b/src/frontend/qt_sdl/toml/toml11/fwd/value_t_fwd.hpp new file mode 100644 index 00000000..fddf8434 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/fwd/value_t_fwd.hpp @@ -0,0 +1,117 @@ +#ifndef TOML11_VALUE_T_FWD_HPP +#define TOML11_VALUE_T_FWD_HPP + +#include "../compat.hpp" +#include "../format.hpp" + +#include +#include +#include + +#include + +namespace toml +{ + +// forward decl +template +class basic_value; + +// ---------------------------------------------------------------------------- +// enum representing toml types + +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 +}; + +std::ostream& operator<<(std::ostream& os, value_t t); +std::string to_string(value_t t); + + +// ---------------------------------------------------------------------------- +// meta functions for internal use + +namespace detail +{ + +template +using value_t_constant = std::integral_constant; + +template +struct type_to_enum : value_t_constant {}; + +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; + +template +struct enum_to_type { using type = void; }; + +template struct enum_to_type { using type = typename V::boolean_type ; }; +template struct enum_to_type { using type = typename V::integer_type ; }; +template struct enum_to_type { using type = typename V::floating_type ; }; +template struct enum_to_type { using type = typename V::string_type ; }; +template struct enum_to_type { using type = typename V::offset_datetime_type; }; +template struct enum_to_type { using type = typename V::local_datetime_type ; }; +template struct enum_to_type { using type = typename V::local_date_type ; }; +template struct enum_to_type { using type = typename V::local_time_type ; }; +template struct enum_to_type { using type = typename V::array_type ; }; +template struct enum_to_type { using type = typename V::table_type ; }; + +template +using enum_to_type_t = typename enum_to_type::type; + +template +struct enum_to_fmt_type { using type = void; }; + +template<> struct enum_to_fmt_type { using type = boolean_format_info ; }; +template<> struct enum_to_fmt_type { using type = integer_format_info ; }; +template<> struct enum_to_fmt_type { using type = floating_format_info ; }; +template<> struct enum_to_fmt_type { using type = string_format_info ; }; +template<> struct enum_to_fmt_type { using type = offset_datetime_format_info; }; +template<> struct enum_to_fmt_type { using type = local_datetime_format_info ; }; +template<> struct enum_to_fmt_type { using type = local_date_format_info ; }; +template<> struct enum_to_fmt_type { using type = local_time_format_info ; }; +template<> struct enum_to_fmt_type { using type = array_format_info ; }; +template<> struct enum_to_fmt_type { using type = table_format_info ; }; + +template +using enum_to_fmt_type_t = typename enum_to_fmt_type::type; + +template +struct is_exact_toml_type0 : cxx::disjunction< + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same + >{}; +template struct is_exact_toml_type: is_exact_toml_type0, V> {}; +template struct is_not_toml_type : cxx::negation> {}; + +} // namespace detail +} // namespace toml +#endif // TOML11_VALUE_T_FWD_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/get.hpp b/src/frontend/qt_sdl/toml/toml11/get.hpp new file mode 100644 index 00000000..3805d09f --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/get.hpp @@ -0,0 +1,632 @@ +#ifndef TOML11_GET_HPP +#define TOML11_GET_HPP + +#include + +#include "from.hpp" +#include "types.hpp" +#include "value.hpp" + +#if defined(TOML11_HAS_STRING_VIEW) +#include +#endif // string_view + +namespace toml +{ + +// ============================================================================ +// T is toml::value; identity transformation. + +template +cxx::enable_if_t>::value, T>& +get(basic_value& v) +{ + return v; +} + +template +cxx::enable_if_t>::value, T> const& +get(const basic_value& v) +{ + return v; +} + +template +cxx::enable_if_t>::value, T> +get(basic_value&& v) +{ + return basic_value(std::move(v)); +} + +// ============================================================================ +// exact toml::* type + +template +cxx::enable_if_t>::value, T> & +get(basic_value& v) +{ + constexpr auto ty = detail::type_to_enum>::value; + return detail::getter::get(v); +} + +template +cxx::enable_if_t>::value, T> const& +get(const basic_value& v) +{ + constexpr auto ty = detail::type_to_enum>::value; + return detail::getter::get(v); +} + +template +cxx::enable_if_t>::value, T> +get(basic_value&& v) +{ + constexpr auto ty = detail::type_to_enum>::value; + return detail::getter::get(std::move(v)); +} + +// ============================================================================ +// T is toml::basic_value + +template +cxx::enable_if_t, + cxx::negation>> + >::value, T> +get(basic_value v) +{ + return T(std::move(v)); +} + +// ============================================================================ +// integer convertible from toml::value::integer_type + +template +cxx::enable_if_t, + cxx::negation>, + detail::is_not_toml_type>, + cxx::negation>, + cxx::negation> + >::value, T> +get(const basic_value& v) +{ + return static_cast(v.as_integer()); +} + +// ============================================================================ +// floating point convertible from toml::value::floating_type + +template +cxx::enable_if_t, + detail::is_not_toml_type>, + cxx::negation>, + cxx::negation> + >::value, T> +get(const basic_value& v) +{ + return static_cast(v.as_floating()); +} + +// ============================================================================ +// std::string with different char/trait/allocator + +template +cxx::enable_if_t>, + detail::is_1byte_std_basic_string + >::value, T> +get(const basic_value& v) +{ + return detail::string_conv>(v.as_string()); +} + +// ============================================================================ +// std::string_view + +#if defined(TOML11_HAS_STRING_VIEW) + +template +cxx::enable_if_t::string_type>::value, T> +get(const basic_value& v) +{ + return T(v.as_string()); +} + +#endif // string_view + +// ============================================================================ +// std::chrono::duration from toml::local_time + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + return std::chrono::duration_cast( + std::chrono::nanoseconds(v.as_local_time())); +} + +// ============================================================================ +// std::chrono::system_clock::time_point from toml::datetime variants + +template +cxx::enable_if_t< + std::is_same::value, T> +get(const basic_value& v) +{ + switch(v.type()) + { + case value_t::local_date: + { + return std::chrono::system_clock::time_point(v.as_local_date()); + } + case value_t::local_datetime: + { + return std::chrono::system_clock::time_point(v.as_local_datetime()); + } + case value_t::offset_datetime: + { + return std::chrono::system_clock::time_point(v.as_offset_datetime()); + } + default: + { + const auto loc = v.location(); + throw type_error(format_error("toml::get: " + "bad_cast to std::chrono::system_clock::time_point", loc, + "the actual type is " + to_string(v.type())), loc); + } + } +} + +// ============================================================================ +// forward declaration to use this recursively. ignore this and go ahead. + +// array-like (w/ push_back) +template +cxx::enable_if_t, // T is a container + detail::has_push_back_method, // .push_back() works + detail::is_not_toml_type>, // but not toml::array + cxx::negation>, // but not std::basic_string +#if defined(TOML11_HAS_STRING_VIEW) + cxx::negation>, // but not std::basic_string_view +#endif + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value&); + +// std::array +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// std::forward_list +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// std::pair +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// std::tuple +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// std::map (key is convertible from toml::value::key_type) +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + std::is_convertible::key_type, + typename T::key_type>, // keys are convertible + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v); + +// std::map (key is not convertible from toml::value::key_type, but +// is a std::basic_string) +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + cxx::negation::key_type, + typename T::key_type>>, // keys are NOT convertible + detail::is_1byte_std_basic_string, // is std::basic_string + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v); + +// toml::from::from_toml(v) +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// has T.from_toml(v) but no from +template +cxx::enable_if_t, // has T.from_toml() + cxx::negation>, // no toml::from + std::is_default_constructible // T{} works + >::value, T> +get(const basic_value&); + +// T(const toml::value&) and T is not toml::basic_value, +// and it does not have `from` nor `from_toml`. +template +cxx::enable_if_t&>, // has T(const basic_value&) + cxx::negation>, // but not basic_value itself + cxx::negation>, // no .from_toml() + cxx::negation> // no toml::from + >::value, T> +get(const basic_value&); + +// ============================================================================ +// array-like types; most likely STL container, like std::vector, etc. + +template +cxx::enable_if_t, // T is a container + detail::has_push_back_method, // .push_back() works + detail::is_not_toml_type>, // but not toml::array + cxx::negation>, // but not std::basic_string +#if defined(TOML11_HAS_STRING_VIEW) + cxx::negation>, // but not std::basic_string_view +#endif + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v) +{ + using value_type = typename T::value_type; + const auto& a = v.as_array(); + + T container; + detail::try_reserve(container, a.size()); // if T has .reserve(), call it + + for(const auto& elem : a) + { + container.push_back(get(elem)); + } + return container; +} + +// ============================================================================ +// std::array + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + using value_type = typename T::value_type; + const auto& a = v.as_array(); + + T container; + if(a.size() != container.size()) + { + const auto loc = v.location(); + throw std::out_of_range(format_error("toml::get: while converting to an array: " + " array size is " + std::to_string(container.size()) + + " but there are " + std::to_string(a.size()) + " elements in toml array.", + loc, "here")); + } + for(std::size_t i=0; i(a.at(i)); + } + return container; +} + +// ============================================================================ +// std::forward_list + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + using value_type = typename T::value_type; + + T container; + for(const auto& elem : v.as_array()) + { + container.push_front(get(elem)); + } + container.reverse(); + return container; +} + +// ============================================================================ +// std::pair + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + using first_type = typename T::first_type; + using second_type = typename T::second_type; + + const auto& ar = v.as_array(); + if(ar.size() != 2) + { + const auto loc = v.location(); + throw std::out_of_range(format_error("toml::get: while converting std::pair: " + " but there are " + std::to_string(ar.size()) + " > 2 elements in toml array.", + loc, "here")); + } + return std::make_pair(::toml::get(ar.at(0)), + ::toml::get(ar.at(1))); +} + +// ============================================================================ +// std::tuple. + +namespace detail +{ +template +T get_tuple_impl(const Array& a, cxx::index_sequence) +{ + return std::make_tuple( + ::toml::get::type>(a.at(I))...); +} +} // detail + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + const auto& ar = v.as_array(); + if(ar.size() != std::tuple_size::value) + { + const auto loc = v.location(); + throw std::out_of_range(format_error("toml::get: while converting std::tuple: " + " there are " + std::to_string(ar.size()) + " > " + + std::to_string(std::tuple_size::value) + " elements in toml array.", + loc, "here")); + } + return detail::get_tuple_impl(ar, + cxx::make_index_sequence::value>{}); +} + +// ============================================================================ +// map-like types; most likely STL map, like std::map or std::unordered_map. + +// key is convertible from toml::value::key_type +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + std::is_convertible::key_type, + typename T::key_type>, // keys are convertible + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v) +{ + using key_type = typename T::key_type; + using mapped_type = typename T::mapped_type; + static_assert( + std::is_convertible::key_type, key_type>::value, + "toml::get only supports map type of which key_type is " + "convertible from toml::basic_value::key_type."); + + T m; + for(const auto& kv : v.as_table()) + { + m.emplace(key_type(kv.first), get(kv.second)); + } + return m; +} + +// key is NOT convertible from toml::value::key_type but std::basic_string +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + cxx::negation::key_type, + typename T::key_type>>, // keys are NOT convertible + detail::is_1byte_std_basic_string, // is std::basic_string + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v) +{ + using key_type = typename T::key_type; + using mapped_type = typename T::mapped_type; + + T m; + for(const auto& kv : v.as_table()) + { + m.emplace(detail::string_conv(kv.first), get(kv.second)); + } + return m; +} + +// ============================================================================ +// user-defined, but convertible types. + +// toml::from +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + return ::toml::from::from_toml(v); +} + +// has T.from_toml(v) but no from +template +cxx::enable_if_t, // has T.from_toml() + cxx::negation>, // no toml::from + std::is_default_constructible // T{} works + >::value, T> +get(const basic_value& v) +{ + T ud; + ud.from_toml(v); + return ud; +} + +// T(const toml::value&) and T is not toml::basic_value, +// and it does not have `from` nor `from_toml`. +template +cxx::enable_if_t&>, // has T(const basic_value&) + cxx::negation>, // but not basic_value itself + cxx::negation>, // no .from_toml() + cxx::negation> // no toml::from + >::value, T> +get(const basic_value& v) +{ + return T(v); +} + +// ============================================================================ +// get_or(value, fallback) + +template +cxx::enable_if_t::value, basic_value> const& +get_or(const basic_value& v, const basic_value&) +{ + return v; +} + +template +cxx::enable_if_t::value, basic_value>& +get_or(basic_value& v, basic_value&) +{ + return v; +} + +template +cxx::enable_if_t::value, basic_value> +get_or(basic_value&& v, basic_value&&) +{ + return v; +} + +// ---------------------------------------------------------------------------- +// specialization for the exact toml types (return type becomes lvalue ref) + +template +cxx::enable_if_t< + detail::is_exact_toml_type>::value, T> const& +get_or(const basic_value& v, const T& opt) noexcept +{ + try + { + return get>(v); + } + catch(...) + { + return opt; + } +} +template +cxx::enable_if_t>, + detail::is_exact_toml_type> + >::value, T>& +get_or(basic_value& v, T& opt) noexcept +{ + try + { + return get>(v); + } + catch(...) + { + return opt; + } +} +template +cxx::enable_if_t, + basic_value>::value, cxx::remove_cvref_t> +get_or(basic_value&& v, T&& opt) noexcept +{ + try + { + return get>(std::move(v)); + } + catch(...) + { + return cxx::remove_cvref_t(std::forward(opt)); + } +} + +// ---------------------------------------------------------------------------- +// specialization for string literal + +// template +// typename basic_value::string_type +// get_or(const basic_value& v, +// const typename basic_value::string_type::value_type (&opt)[N]) +// { +// try +// { +// return v.as_string(); +// } +// catch(...) +// { +// return typename basic_value::string_type(opt); +// } +// } +// +// The above only matches to the literal, like `get_or(v, "foo");` but not +// ```cpp +// const auto opt = "foo"; +// const auto str = get_or(v, opt); +// ``` +// . And the latter causes an error. +// To match to both `"foo"` and `const auto opt = "foo"`, we take a pointer to +// a character here. + +template +typename basic_value::string_type +get_or(const basic_value& v, + const typename basic_value::string_type::value_type* opt) +{ + try + { + return v.as_string(); + } + catch(...) + { + return typename basic_value::string_type(opt); + } +} + +// ---------------------------------------------------------------------------- +// others (require type conversion and return type cannot be lvalue reference) + +template +cxx::enable_if_t>, + cxx::negation>>, + cxx::negation, typename basic_value::string_type::value_type const*>> + >::value, cxx::remove_cvref_t> +get_or(const basic_value& v, T&& opt) +{ + try + { + return get>(v); + } + catch(...) + { + return cxx::remove_cvref_t(std::forward(opt)); + } +} + +} // toml +#endif // TOML11_GET_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/impl/color_impl.hpp b/src/frontend/qt_sdl/toml/toml11/impl/color_impl.hpp new file mode 100644 index 00000000..4215587f --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/impl/color_impl.hpp @@ -0,0 +1,76 @@ +#ifndef TOML11_COLOR_IMPL_HPP +#define TOML11_COLOR_IMPL_HPP + +#include "../fwd/color_fwd.hpp" +#include "../version.hpp" + +#include + +namespace toml +{ +namespace color +{ +// put ANSI escape sequence to ostream +inline namespace ansi +{ + +TOML11_INLINE std::ostream& reset(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[00m";} + return os; +} +TOML11_INLINE std::ostream& bold(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[01m";} + return os; +} +TOML11_INLINE std::ostream& grey(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[30m";} + return os; +} +TOML11_INLINE std::ostream& gray(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[30m";} + return os; +} +TOML11_INLINE std::ostream& red(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[31m";} + return os; +} +TOML11_INLINE std::ostream& green(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[32m";} + return os; +} +TOML11_INLINE std::ostream& yellow(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[33m";} + return os; +} +TOML11_INLINE std::ostream& blue(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[34m";} + return os; +} +TOML11_INLINE std::ostream& magenta(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[35m";} + return os; +} +TOML11_INLINE std::ostream& cyan (std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[36m";} + return os; +} +TOML11_INLINE std::ostream& white (std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[37m";} + return os; +} + +} // ansi +} // color +} // toml +#endif // TOML11_COLOR_IMPL_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/impl/comments_impl.hpp b/src/frontend/qt_sdl/toml/toml11/impl/comments_impl.hpp new file mode 100644 index 00000000..25023eba --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/impl/comments_impl.hpp @@ -0,0 +1,46 @@ +#ifndef TOML11_COMMENTS_IMPL_HPP +#define TOML11_COMMENTS_IMPL_HPP + +#include "../fwd/comments_fwd.hpp" // IWYU pragma: keep + +namespace toml +{ + +TOML11_INLINE bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} +TOML11_INLINE bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} +TOML11_INLINE bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} +TOML11_INLINE bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} +TOML11_INLINE bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} +TOML11_INLINE bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} + +TOML11_INLINE void swap(preserve_comments& lhs, preserve_comments& rhs) +{ + lhs.swap(rhs); + return; +} +TOML11_INLINE void swap(preserve_comments& lhs, std::vector& rhs) +{ + lhs.comments.swap(rhs); + return; +} +TOML11_INLINE void swap(std::vector& lhs, preserve_comments& rhs) +{ + lhs.swap(rhs.comments); + return; +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const preserve_comments& com) +{ + for(const auto& c : com) + { + if(c.front() != '#') + { + os << '#'; + } + os << c << '\n'; + } + return os; +} + +} // toml11 +#endif // TOML11_COMMENTS_IMPL_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/impl/datetime_impl.hpp b/src/frontend/qt_sdl/toml/toml11/impl/datetime_impl.hpp new file mode 100644 index 00000000..50c04557 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/impl/datetime_impl.hpp @@ -0,0 +1,518 @@ +#ifndef TOML11_DATETIME_IMPL_HPP +#define TOML11_DATETIME_IMPL_HPP + +#include "../fwd/datetime_fwd.hpp" +#include "../version.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +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) +TOML11_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; +} +TOML11_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) +TOML11_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; +} +TOML11_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 +TOML11_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; +} +TOML11_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 + +// ---------------------------------------------------------------------------- + +TOML11_INLINE local_date::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); +} + +TOML11_INLINE local_date::local_date(const std::time_t t) + : local_date{std::chrono::system_clock::from_time_t(t)} +{} + +TOML11_INLINE local_date::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(this->day); + t.tm_mon = static_cast(this->month); + t.tm_year = static_cast(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)); +} + +TOML11_INLINE local_date::operator std::time_t() const +{ + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); +} + +TOML11_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); +} +TOML11_INLINE bool operator!=(const local_date& lhs, const local_date& rhs) +{ + return !(lhs == rhs); +} +TOML11_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); +} +TOML11_INLINE bool operator<=(const local_date& lhs, const local_date& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const local_date& lhs, const local_date& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const local_date& lhs, const local_date& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_date& date) +{ + os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; + os << std::setfill('0') << std::setw(2) << static_cast(date.month) + 1 << '-'; + os << std::setfill('0') << std::setw(2) << static_cast(date.day ) ; + return os; +} + +TOML11_INLINE std::string to_string(const local_date& date) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << date; + return oss.str(); +} + +// ----------------------------------------------------------------------------- + +TOML11_INLINE local_time::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); +} + +TOML11_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); +} +TOML11_INLINE bool operator!=(const local_time& lhs, const local_time& rhs) +{ + return !(lhs == rhs); +} +TOML11_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); +} +TOML11_INLINE bool operator<=(const local_time& lhs, const local_time& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const local_time& lhs, const local_time& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const local_time& lhs, const local_time& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_time& time) +{ + os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.second); + if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) + { + os << '.'; + os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); + if(time.microsecond != 0 || time.nanosecond != 0) + { + os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); + if(time.nanosecond != 0) + { + os << std::setfill('0') << std::setw(3) << static_cast(time.nanosecond); + } + } + } + return os; +} + +TOML11_INLINE std::string to_string(const local_time& time) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << time; + return oss.str(); +} + +// ---------------------------------------------------------------------------- + +TOML11_INLINE time_offset::operator std::chrono::minutes() const +{ + return std::chrono::minutes(this->minute) + + std::chrono::hours(this->hour); +} + +TOML11_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); +} +TOML11_INLINE bool operator!=(const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs == rhs); +} +TOML11_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); +} +TOML11_INLINE bool operator<=(const time_offset& lhs, const time_offset& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const time_offset& offset) +{ + if(offset.hour == 0 && offset.minute == 0) + { + os << 'Z'; + return os; + } + int minute = static_cast(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; +} + +TOML11_INLINE std::string to_string(const time_offset& offset) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << offset; + return oss.str(); +} + +// ----------------------------------------------------------------------------- + +TOML11_INLINE local_datetime::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(<ime)); + this->time.millisecond = static_cast( + std::chrono::duration_cast(t_diff).count()); + this->time.microsecond = static_cast( + std::chrono::duration_cast(t_diff).count()); + this->time.nanosecond = static_cast( + std::chrono::duration_cast(t_diff).count()); +} + +TOML11_INLINE local_datetime::local_datetime(const std::time_t t) + : local_datetime{std::chrono::system_clock::from_time_t(t)} +{} + +TOML11_INLINE local_datetime::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(this->time.second); + t.tm_min = static_cast(this->time.minute); + t.tm_hour = static_cast(this->time.hour); + t.tm_mday = static_cast(this->date.day); + t.tm_mon = static_cast(this->date.month); + t.tm_year = static_cast(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( + std::chrono::milliseconds(this->time.millisecond) + + std::chrono::microseconds(this->time.microsecond) + + std::chrono::nanoseconds (this->time.nanosecond)); + return dt; +} + +TOML11_INLINE local_datetime::operator std::time_t() const +{ + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); +} + +TOML11_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); +} +TOML11_INLINE bool operator!=(const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs == rhs); +} +TOML11_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); +} +TOML11_INLINE bool operator<=(const local_datetime& lhs, const local_datetime& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_datetime& dt) +{ + os << dt.date << 'T' << dt.time; + return os; +} + +TOML11_INLINE std::string to_string(const local_datetime& dt) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << dt; + return oss.str(); +} + +// ----------------------------------------------------------------------------- + + +TOML11_INLINE offset_datetime::offset_datetime(const local_datetime& ld) + : date{ld.date}, time{ld.time}, offset{get_local_offset(nullptr)} + // use the current local timezone offset +{} +TOML11_INLINE offset_datetime::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); +} +TOML11_INLINE offset_datetime::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); +} +TOML11_INLINE offset_datetime::offset_datetime(const std::tm& t) + : offset{0, 0} // assume gmtime +{ + this->date = local_date(t); + this->time = local_time(t); +} + +TOML11_INLINE offset_datetime::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(this->time.second); + t.tm_min = static_cast(this->time.minute); + t.tm_hour = static_cast(this->time.hour); + t.tm_mday = static_cast(this->date.day); + t.tm_mon = static_cast(this->date.month); + t.tm_year = static_cast(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( + 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; +} + +TOML11_INLINE offset_datetime::operator std::time_t() const +{ + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); +} + +TOML11_INLINE time_offset offset_datetime::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 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); +} + +TOML11_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); +} +TOML11_INLINE bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs == rhs); +} +TOML11_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); +} +TOML11_INLINE bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const offset_datetime& dt) +{ + os << dt.date << 'T' << dt.time << dt.offset; + return os; +} + +TOML11_INLINE std::string to_string(const offset_datetime& dt) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << dt; + return oss.str(); +} + +}//toml +#endif // TOML11_DATETIME_IMPL_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/impl/error_info_impl.hpp b/src/frontend/qt_sdl/toml/toml11/impl/error_info_impl.hpp new file mode 100644 index 00000000..b87f4776 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/impl/error_info_impl.hpp @@ -0,0 +1,75 @@ +#ifndef TOML11_ERROR_INFO_IMPL_HPP +#define TOML11_ERROR_INFO_IMPL_HPP + +#include "../fwd/error_info_fwd.hpp" +#include "../fwd/color_fwd.hpp" + +#include + +namespace toml +{ + +TOML11_INLINE std::string format_error(const std::string& errkind, const error_info& err) +{ + std::string errmsg; + if( ! errkind.empty()) + { + errmsg = errkind; + errmsg += ' '; + } + errmsg += err.title(); + errmsg += '\n'; + + const auto lnw = [&err]() { + std::size_t width = 0; + for(const auto& l : err.locations()) + { + width = (std::max)(detail::integer_width_base10(l.first.last_line_number()), width); + } + return width; + }(); + + bool first = true; + std::string prev_fname; + for(const auto& lm : err.locations()) + { + if( ! first) + { + std::ostringstream oss; + oss << detail::make_string(lnw + 1, ' ') + << color::bold << color::blue << " |" << color::reset + << color::bold << " ...\n" << color::reset; + oss << detail::make_string(lnw + 1, ' ') + << color::bold << color::blue << " |\n" << color::reset; + errmsg += oss.str(); + } + + const auto& l = lm.first; + const auto& m = lm.second; + + errmsg += detail::format_location_impl(lnw, prev_fname, l, m); + + prev_fname = l.file_name(); + first = false; + } + + errmsg += err.suffix(); + + return errmsg; +} + +TOML11_INLINE std::string format_error(const error_info& err) +{ + std::ostringstream oss; + oss << color::red << color::bold << "[error]" << color::reset; + return format_error(oss.str(), err); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const error_info& e) +{ + os << format_error(e); + return os; +} + +} // toml +#endif // TOML11_ERROR_INFO_IMPL_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/impl/format_impl.hpp b/src/frontend/qt_sdl/toml/toml11/impl/format_impl.hpp new file mode 100644 index 00000000..c4985faf --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/impl/format_impl.hpp @@ -0,0 +1,297 @@ +#ifndef TOML11_FORMAT_IMPL_HPP +#define TOML11_FORMAT_IMPL_HPP + +#include "../fwd/format_fwd.hpp" +#include "../version.hpp" + +#include +#include + +namespace toml +{ + +// toml types with serialization info + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const indent_char& c) +{ + switch(c) + { + case indent_char::space: {os << "space" ; break;} + case indent_char::tab: {os << "tab" ; break;} + case indent_char::none: {os << "none" ; break;} + default: + { + os << "unknown indent char: " << static_cast(c); + } + } + return os; +} + +TOML11_INLINE std::string to_string(const indent_char c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +// ---------------------------------------------------------------------------- +// boolean + +// ---------------------------------------------------------------------------- +// integer + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const integer_format f) +{ + switch(f) + { + case integer_format::dec: {os << "dec"; break;} + case integer_format::bin: {os << "bin"; break;} + case integer_format::oct: {os << "oct"; break;} + case integer_format::hex: {os << "hex"; break;} + default: + { + os << "unknown integer_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const integer_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + + +TOML11_INLINE bool operator==(const integer_format_info& lhs, const integer_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.uppercase == rhs.uppercase && + lhs.width == rhs.width && + lhs.spacer == rhs.spacer && + lhs.suffix == rhs.suffix ; +} +TOML11_INLINE bool operator!=(const integer_format_info& lhs, const integer_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +// ---------------------------------------------------------------------------- +// floating + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const floating_format f) +{ + switch(f) + { + case floating_format::defaultfloat: {os << "defaultfloat"; break;} + case floating_format::fixed : {os << "fixed" ; break;} + case floating_format::scientific : {os << "scientific" ; break;} + case floating_format::hex : {os << "hex" ; break;} + default: + { + os << "unknown floating_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const floating_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const floating_format_info& lhs, const floating_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.prec == rhs.prec && + lhs.suffix == rhs.suffix ; +} +TOML11_INLINE bool operator!=(const floating_format_info& lhs, const floating_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +// ---------------------------------------------------------------------------- +// string + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const string_format f) +{ + switch(f) + { + case string_format::basic : {os << "basic" ; break;} + case string_format::literal : {os << "literal" ; break;} + case string_format::multiline_basic : {os << "multiline_basic" ; break;} + case string_format::multiline_literal: {os << "multiline_literal"; break;} + default: + { + os << "unknown string_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const string_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const string_format_info& lhs, const string_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.start_with_newline == rhs.start_with_newline ; +} +TOML11_INLINE bool operator!=(const string_format_info& lhs, const string_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} +// ---------------------------------------------------------------------------- +// datetime + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d) +{ + switch(d) + { + case datetime_delimiter_kind::upper_T: { os << "upper_T, "; break; } + case datetime_delimiter_kind::lower_t: { os << "lower_t, "; break; } + case datetime_delimiter_kind::space: { os << "space, "; break; } + default: + { + os << "unknown datetime delimiter: " << static_cast(d); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const datetime_delimiter_kind c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept +{ + return lhs.delimiter == rhs.delimiter && + lhs.has_seconds == rhs.has_seconds && + lhs.subsecond_precision == rhs.subsecond_precision ; +} +TOML11_INLINE bool operator!=(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +TOML11_INLINE bool operator==(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept +{ + return lhs.delimiter == rhs.delimiter && + lhs.has_seconds == rhs.has_seconds && + lhs.subsecond_precision == rhs.subsecond_precision ; +} +TOML11_INLINE bool operator!=(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +TOML11_INLINE bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept +{ + return true; +} +TOML11_INLINE bool operator!=(const local_date_format_info& lhs, const local_date_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +TOML11_INLINE bool operator==(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept +{ + return lhs.has_seconds == rhs.has_seconds && + lhs.subsecond_precision == rhs.subsecond_precision ; +} +TOML11_INLINE bool operator!=(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +// ---------------------------------------------------------------------------- +// array + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const array_format f) +{ + switch(f) + { + case array_format::default_format : {os << "default_format" ; break;} + case array_format::oneline : {os << "oneline" ; break;} + case array_format::multiline : {os << "multiline" ; break;} + case array_format::array_of_tables: {os << "array_of_tables"; break;} + default: + { + os << "unknown array_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const array_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const array_format_info& lhs, const array_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.indent_type == rhs.indent_type && + lhs.body_indent == rhs.body_indent && + lhs.closing_indent == rhs.closing_indent ; +} +TOML11_INLINE bool operator!=(const array_format_info& lhs, const array_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +// ---------------------------------------------------------------------------- +// table + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const table_format f) +{ + switch(f) + { + case table_format::multiline : {os << "multiline" ; break;} + case table_format::oneline : {os << "oneline" ; break;} + case table_format::dotted : {os << "dotted" ; break;} + case table_format::multiline_oneline: {os << "multiline_oneline"; break;} + case table_format::implicit : {os << "implicit" ; break;} + default: + { + os << "unknown table_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const table_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const table_format_info& lhs, const table_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.indent_type == rhs.indent_type && + lhs.body_indent == rhs.body_indent && + lhs.name_indent == rhs.name_indent && + lhs.closing_indent == rhs.closing_indent ; +} +TOML11_INLINE bool operator!=(const table_format_info& lhs, const table_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +} // namespace toml +#endif // TOML11_FORMAT_IMPL_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/impl/literal_impl.hpp b/src/frontend/qt_sdl/toml/toml11/impl/literal_impl.hpp new file mode 100644 index 00000000..e8298c21 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/impl/literal_impl.hpp @@ -0,0 +1,174 @@ +#ifndef TOML11_LITERAL_IMPL_HPP +#define TOML11_LITERAL_IMPL_HPP + +#include "../fwd/literal_fwd.hpp" +#include "../parser.hpp" +#include "../syntax.hpp" + +namespace toml +{ + +namespace detail +{ +// implementation +TOML11_INLINE ::toml::value literal_internal_impl(location loc) +{ + const auto s = ::toml::spec::default_version(); + context ctx(s); + + const auto front = loc; + + // ------------------------------------------------------------------------ + // check if it is a raw value. + + // skip empty lines and comment lines + auto sp = skip_multiline_spacer(loc, ctx); + if(loc.eof()) + { + ::toml::value val; + if(sp.has_value()) + { + for(std::size_t i=0; i(str), + reinterpret_cast(str + len), + c.begin()); + if( ! c.empty() && c.back()) + { + c.push_back('\n'); // to make it easy to parse comment, we add newline + } + + return literal_internal_impl(::toml::detail::location( + std::make_shared(std::move(c)), + "TOML literal encoded in a C++ code")); +} + +#if defined(__cpp_char8_t) +# if __cpp_char8_t >= 201811L +# define TOML11_HAS_CHAR8_T 1 +# endif +#endif + +#if defined(TOML11_HAS_CHAR8_T) +// value of u8"" literal has been changed from char to char8_t and char8_t is +// NOT compatible to char +TOML11_INLINE ::toml::value +operator"" _toml(const char8_t* str, std::size_t len) +{ + if(len == 0) + { + return ::toml::value{}; + } + + ::toml::detail::location::container_type c(len); + std::copy(reinterpret_cast(str), + reinterpret_cast(str + len), + c.begin()); + if( ! c.empty() && c.back()) + { + c.push_back('\n'); // to make it easy to parse comment, we add newline + } + + return literal_internal_impl(::toml::detail::location( + std::make_shared(std::move(c)), + "TOML literal encoded in a C++ code")); +} +#endif + +} // toml_literals +} // literals +} // toml +#endif // TOML11_LITERAL_IMPL_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/impl/location_impl.hpp b/src/frontend/qt_sdl/toml/toml11/impl/location_impl.hpp new file mode 100644 index 00000000..19c418ab --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/impl/location_impl.hpp @@ -0,0 +1,226 @@ +#ifndef TOML11_LOCATION_IMPL_HPP +#define TOML11_LOCATION_IMPL_HPP + +#include "../fwd/location_fwd.hpp" +#include "../utility.hpp" +#include "../version.hpp" + +namespace toml +{ +namespace detail +{ + +TOML11_INLINE void location::advance(std::size_t n) noexcept +{ + assert(this->is_ok()); + if(this->location_ + n < this->source_->size()) + { + this->advance_line_number(n); + this->location_ += n; + } + else + { + this->advance_line_number(this->source_->size() - this->location_); + this->location_ = this->source_->size(); + } +} +TOML11_INLINE void location::retrace(std::size_t n) noexcept +{ + assert(this->is_ok()); + if(this->location_ < n) + { + this->location_ = 0; + this->line_number_ = 1; + } + else + { + this->retrace_line_number(n); + this->location_ -= n; + } +} + +TOML11_INLINE bool location::eof() const noexcept +{ + assert(this->is_ok()); + return this->location_ >= this->source_->size(); +} +TOML11_INLINE location::char_type location::current() const +{ + assert(this->is_ok()); + if(this->eof()) {return '\0';} + + assert(this->location_ < this->source_->size()); + return this->source_->at(this->location_); +} + +TOML11_INLINE location::char_type location::peek() +{ + assert(this->is_ok()); + if(this->location_ >= this->source_->size()) + { + return '\0'; + } + else + { + return this->source_->at(this->location_ + 1); + } +} + +TOML11_INLINE void location::set_location(const std::size_t loc) noexcept +{ + if(this->location_ == loc) + { + return ; + } + + if(loc == 0) + { + this->line_number_ = 1; + } + else if(this->location_ < loc) + { + const auto d = loc - this->location_; + this->advance_line_number(d); + } + else + { + const auto d = this->location_ - loc; + this->retrace_line_number(d); + } + this->location_ = loc; +} + +TOML11_INLINE std::string location::get_line() const +{ + assert(this->is_ok()); + const auto iter = std::next(this->source_->cbegin(), static_cast(this->location_)); + const auto riter = cxx::make_reverse_iterator(iter); + + const auto prev = std::find(riter, this->source_->crend(), char_type('\n')); + const auto next = std::find(iter, this->source_->cend(), char_type('\n')); + + return make_string(std::next(prev.base()), next); +} +TOML11_INLINE std::size_t location::column_number() const noexcept +{ + assert(this->is_ok()); + const auto iter = std::next(this->source_->cbegin(), static_cast(this->location_)); + const auto riter = cxx::make_reverse_iterator(iter); + const auto prev = std::find(riter, this->source_->crend(), char_type('\n')); + + assert(prev.base() <= iter); + return static_cast(std::distance(prev.base(), iter) + 1); // 1-origin +} + + +TOML11_INLINE void location::advance_line_number(const std::size_t n) +{ + assert(this->is_ok()); + assert(this->location_ + n <= this->source_->size()); + + const auto iter = this->source_->cbegin(); + this->line_number_ += static_cast(std::count( + std::next(iter, static_cast(this->location_)), + std::next(iter, static_cast(this->location_ + n)), + char_type('\n'))); + + return; +} +TOML11_INLINE void location::retrace_line_number(const std::size_t n) +{ + assert(this->is_ok()); + assert(n <= this->location_); // loc - n >= 0 + + const auto iter = this->source_->cbegin(); + const auto dline_num = static_cast(std::count( + std::next(iter, static_cast(this->location_ - n)), + std::next(iter, static_cast(this->location_)), + char_type('\n'))); + + if(this->line_number_ <= dline_num) + { + this->line_number_ = 1; + } + else + { + this->line_number_ -= dline_num; + } + return; +} + +TOML11_INLINE bool operator==(const location& lhs, const location& rhs) noexcept +{ + if( ! lhs.is_ok() || ! rhs.is_ok()) + { + return (!lhs.is_ok()) && (!rhs.is_ok()); + } + return lhs.source() == rhs.source() && + lhs.source_name() == rhs.source_name() && + lhs.get_location() == rhs.get_location(); +} +TOML11_INLINE bool operator!=(const location& lhs, const location& rhs) +{ + return !(lhs == rhs); +} + +TOML11_INLINE location prev(const location& loc) +{ + location p(loc); + p.retrace(1); + return p; +} +TOML11_INLINE location next(const location& loc) +{ + location p(loc); + p.advance(1); + return p; +} + +TOML11_INLINE location make_temporary_location(const std::string& str) noexcept +{ + location::container_type cont(str.size()); + std::transform(str.begin(), str.end(), cont.begin(), + [](const std::string::value_type& c) { + return cxx::bit_cast(c); + }); + return location(std::make_shared( + std::move(cont)), "internal temporary"); +} + +TOML11_INLINE result +find(const location& first, const location& last, const location::char_type val) +{ + return find_if(first, last, [val](const location::char_type c) { + return c == val; + }); +} +TOML11_INLINE result +rfind(const location& first, const location& last, const location::char_type val) +{ + return rfind_if(first, last, [val](const location::char_type c) { + return c == val; + }); +} + +TOML11_INLINE std::size_t +count(const location& first, const location& last, const location::char_type& c) +{ + if(first.source() != last.source()) { return 0; } + if(first.get_location() >= last.get_location()) { return 0; } + + auto loc = first; + std::size_t num = 0; + while(loc.get_location() != last.get_location()) + { + if(loc.current() == c) + { + num += 1; + } + loc.advance(); + } + return num; +} + +} // detail +} // toml +#endif // TOML11_LOCATION_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/impl/region_impl.hpp b/src/frontend/qt_sdl/toml/toml11/impl/region_impl.hpp new file mode 100644 index 00000000..08147e25 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/impl/region_impl.hpp @@ -0,0 +1,188 @@ +#ifndef TOML11_REGION_IMPL_HPP +#define TOML11_REGION_IMPL_HPP + +#include "../fwd/region_fwd.hpp" +#include "../utility.hpp" + +#include +#include +#include +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +// a value defined in [first, last). +// Those source must be the same. Instread, `region` does not make sense. +TOML11_INLINE region::region(const location& first, const location& last) + : source_(first.source()), source_name_(first.source_name()), + length_(last.get_location() - first.get_location()), + first_(first.get_location()), + first_line_(first.line_number()), + first_column_(first.column_number()), + last_(last.get_location()), + last_line_(last.line_number()), + last_column_(last.column_number()) +{ + assert(first.source() == last.source()); + assert(first.source_name() == last.source_name()); +} + + // shorthand of [loc, loc+1) +TOML11_INLINE region::region(const location& loc) + : source_(loc.source()), source_name_(loc.source_name()), length_(0), + first_line_(0), first_column_(0), last_line_(0), last_column_(0) +{ + // if the file ends with LF, the resulting region points no char. + if(loc.eof()) + { + if(loc.get_location() == 0) + { + this->length_ = 0; + this->first_ = 0; + this->first_line_ = 0; + this->first_column_ = 0; + this->last_ = 0; + this->last_line_ = 0; + this->last_column_ = 0; + } + else + { + const auto first = prev(loc); + this->first_ = first.get_location(); + this->first_line_ = first.line_number(); + this->first_column_ = first.column_number(); + this->last_ = loc.get_location(); + this->last_line_ = loc.line_number(); + this->last_column_ = loc.column_number(); + this->length_ = 1; + } + } + else + { + this->first_ = loc.get_location(); + this->first_line_ = loc.line_number(); + this->first_column_ = loc.column_number(); + this->last_ = loc.get_location() + 1; + this->last_line_ = loc.line_number(); + this->last_column_ = loc.column_number() + 1; + this->length_ = 1; + } +} + +TOML11_INLINE region::char_type region::at(std::size_t i) const +{ + if(this->last_ <= this->first_ + i) + { + throw std::out_of_range("range::at: index " + std::to_string(i) + + " exceeds length " + std::to_string(this->length_)); + } + const auto iter = std::next(this->source_->cbegin(), + static_cast(this->first_ + i)); + return *iter; +} + +TOML11_INLINE region::const_iterator region::begin() const noexcept +{ + return std::next(this->source_->cbegin(), + static_cast(this->first_)); +} +TOML11_INLINE region::const_iterator region::end() const noexcept +{ + return std::next(this->source_->cbegin(), + static_cast(this->last_)); +} +TOML11_INLINE region::const_iterator region::cbegin() const noexcept +{ + return std::next(this->source_->cbegin(), + static_cast(this->first_)); +} +TOML11_INLINE region::const_iterator region::cend() const noexcept +{ + return std::next(this->source_->cbegin(), + static_cast(this->last_)); +} + +TOML11_INLINE std::string region::as_string() const +{ + if(this->is_ok()) + { + const auto begin = std::next(this->source_->cbegin(), static_cast(this->first_)); + const auto end = std::next(this->source_->cbegin(), static_cast(this->last_ )); + return ::toml::detail::make_string(begin, end); + } + else + { + return std::string(""); + } +} + +TOML11_INLINE std::vector region::as_lines() const +{ + assert(this->is_ok()); + if(this->length_ == 0) + { + return std::vector{""}; + } + + // Consider the following toml file + // ``` + // array = [ + // ] # comment + // ``` + // and the region represnets + // ``` + // [ + // ] + // ``` + // but we want to show the following. + // ``` + // array = [ + // ] # comment + // ``` + // So we need to find LFs before `begin` and after `end`. + // + // But, if region ends with LF, it should not include the next line. + // ``` + // a = 42 + // ^^^- with the last LF + // ``` + // So we start from `end-1` when looking for LF. + + const auto begin_idx = static_cast(this->first_); + const auto end_idx = static_cast(this->last_) - 1; + + // length_ != 0, so begin < end. then begin <= end-1 + assert(begin_idx <= end_idx); + + const auto begin = std::next(this->source_->cbegin(), begin_idx); + const auto end = std::next(this->source_->cbegin(), end_idx); + + const auto line_begin = std::find(cxx::make_reverse_iterator(begin), this->source_->crend(), char_type('\n')).base(); + const auto line_end = std::find(end, this->source_->cend(), char_type('\n')); + + const auto reg_lines = make_string(line_begin, line_end); + + if(reg_lines == "") // the region is an empty line that only contains LF + { + return std::vector{""}; + } + + std::istringstream iss(reg_lines); + + std::vector lines; + std::string line; + while(std::getline(iss, line)) + { + lines.push_back(line); + } + return lines; +} + +} // namespace detail +} // namespace toml +#endif // TOML11_REGION_IMPL_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/impl/scanner_impl.hpp b/src/frontend/qt_sdl/toml/toml11/impl/scanner_impl.hpp new file mode 100644 index 00000000..d97d3178 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/impl/scanner_impl.hpp @@ -0,0 +1,473 @@ +#ifndef TOML11_SCANNER_IMPL_HPP +#define TOML11_SCANNER_IMPL_HPP + +#include "../fwd/scanner_fwd.hpp" +#include "../utility.hpp" + +namespace toml +{ +namespace detail +{ + +TOML11_INLINE scanner_storage::scanner_storage(const scanner_storage& other) + : scanner_(nullptr) +{ + if(other.is_ok()) + { + scanner_.reset(other.get().clone()); + } +} +TOML11_INLINE scanner_storage& scanner_storage::operator=(const scanner_storage& other) +{ + if(this == std::addressof(other)) {return *this;} + if(other.is_ok()) + { + scanner_.reset(other.get().clone()); + } + return *this; +} + +TOML11_INLINE region scanner_storage::scan(location& loc) const +{ + assert(this->is_ok()); + return this->scanner_->scan(loc); +} + +TOML11_INLINE std::string scanner_storage::expected_chars(location& loc) const +{ + assert(this->is_ok()); + return this->scanner_->expected_chars(loc); +} + +TOML11_INLINE scanner_base& scanner_storage::get() const noexcept +{ + assert(this->is_ok()); + return *scanner_; +} + +TOML11_INLINE std::string scanner_storage::name() const +{ + assert(this->is_ok()); + return this->scanner_->name(); +} + +// ---------------------------------------------------------------------------- + +TOML11_INLINE region character::scan(location& loc) const +{ + if(loc.eof()) {return region{};} + + if(loc.current() == this->value_) + { + const auto first = loc; + loc.advance(1); + return region(first, loc); + } + return region{}; +} + +TOML11_INLINE std::string character::expected_chars(location&) const +{ + return show_char(value_); +} + +TOML11_INLINE scanner_base* character::clone() const +{ + return new character(*this); +} + +TOML11_INLINE std::string character::name() const +{ + return "character{" + show_char(value_) + "}"; +} + +// ---------------------------------------------------------------------------- + +TOML11_INLINE region character_either::scan(location& loc) const +{ + if(loc.eof()) {return region{};} + + for(const auto c : this->chars_) + { + if(loc.current() == c) + { + const auto first = loc; + loc.advance(1); + return region(first, loc); + } + } + return region{}; +} + +TOML11_INLINE std::string character_either::expected_chars(location&) const +{ + assert( ! chars_.empty()); + + std::string expected; + if(chars_.size() == 1) + { + expected += show_char(chars_.at(0)); + } + else if(chars_.size() == 2) + { + expected += show_char(chars_.at(0)) + " or " + show_char(chars_.at(1)); + } + else + { + for(std::size_t i=0; ichars_) + { + n += show_char(c); + n += ", "; + } + if( ! this->chars_.empty()) + { + n.pop_back(); + n.pop_back(); + } + n += "}"; + return n; +} + +// ---------------------------------------------------------------------------- +// character_in_range + +TOML11_INLINE region character_in_range::scan(location& loc) const +{ + if(loc.eof()) {return region{};} + + const auto curr = loc.current(); + if(this->from_ <= curr && curr <= this->to_) + { + const auto first = loc; + loc.advance(1); + return region(first, loc); + } + return region{}; +} + +TOML11_INLINE std::string character_in_range::expected_chars(location&) const +{ + std::string expected("from `"); + expected += show_char(from_); + expected += "` to `"; + expected += show_char(to_); + expected += "`"; + return expected; +} + +TOML11_INLINE scanner_base* character_in_range::clone() const +{ + return new character_in_range(*this); +} + +TOML11_INLINE std::string character_in_range::name() const +{ + return "character_in_range{" + show_char(from_) + "," + show_char(to_) + "}"; +} + +// ---------------------------------------------------------------------------- +// literal + +TOML11_INLINE region literal::scan(location& loc) const +{ + const auto first = loc; + for(std::size_t i=0; iothers_.empty()) + { + n.pop_back(); + n.pop_back(); + } + n += "}"; + return n; +} + +// ---------------------------------------------------------------------------- +// either + +TOML11_INLINE region either::scan(location& loc) const +{ + for(const auto& other : others_) + { + const auto reg = other.scan(loc); + if(reg.is_ok()) + { + return reg; + } + } + return region{}; +} + +TOML11_INLINE std::string either::expected_chars(location& loc) const +{ + assert( ! others_.empty()); + + std::string expected = others_.at(0).expected_chars(loc); + if(others_.size() == 2) + { + expected += " or "; + expected += others_.at(1).expected_chars(loc); + } + else + { + for(std::size_t i=1; iothers_.empty()) + { + n.pop_back(); + n.pop_back(); + } + n += "}"; + return n; +} + +// ---------------------------------------------------------------------------- +// repeat_exact + +TOML11_INLINE region repeat_exact::scan(location& loc) const +{ + const auto first = loc; + for(std::size_t i=0; i +#include +#include +#include + +#include + +namespace toml +{ + +TOML11_INLINE source_location::source_location(const detail::region& r) + : is_ok_(false), + first_line_(1), + first_column_(1), + last_line_(1), + last_column_(1), + length_(0), + file_name_("unknown file") +{ + if(r.is_ok()) + { + this->is_ok_ = true; + this->file_name_ = r.source_name(); + this->first_line_ = r.first_line_number(); + this->first_column_ = r.first_column_number(); + this->last_line_ = r.last_line_number(); + this->last_column_ = r.last_column_number(); + this->length_ = r.length(); + this->line_str_ = r.as_lines(); + } +} + +TOML11_INLINE std::string const& source_location::first_line() const +{ + if(this->line_str_.size() == 0) + { + throw std::out_of_range("toml::source_location::first_line: `lines` is empty"); + } + return this->line_str_.front(); +} +TOML11_INLINE std::string const& source_location::last_line() const +{ + if(this->line_str_.size() == 0) + { + throw std::out_of_range("toml::source_location::first_line: `lines` is empty"); + } + return this->line_str_.back(); +} + +namespace detail +{ + +TOML11_INLINE std::size_t integer_width_base10(std::size_t i) noexcept +{ + std::size_t width = 0; + while(i != 0) + { + i /= 10; + width += 1; + } + return width; +} + +TOML11_INLINE std::ostringstream& +format_filename(std::ostringstream& oss, const source_location& loc) +{ + // --> example.toml + oss << color::bold << color::blue << " --> " << color::reset + << color::bold << loc.file_name() << '\n' << color::reset; + return oss; +} + +TOML11_INLINE std::ostringstream& format_empty_line(std::ostringstream& oss, + const std::size_t lnw) +{ + // | + oss << detail::make_string(lnw + 1, ' ') + << color::bold << color::blue << " |\n" << color::reset; + return oss; +} + +TOML11_INLINE std::ostringstream& format_line(std::ostringstream& oss, + const std::size_t lnw, const std::size_t linenum, const std::string& line) +{ + // 10 | key = "value" + oss << ' ' << color::bold << color::blue + << std::setw(static_cast(lnw)) + << std::right << linenum << " | " << color::reset; + for(const char c : line) + { + if(std::isgraph(c) || c == ' ') + { + oss << c; + } + else + { + oss << show_char(c); + } + } + oss << '\n'; + return oss; +} +TOML11_INLINE std::ostringstream& format_underline(std::ostringstream& oss, + const std::size_t lnw, const std::size_t col, const std::size_t len, + const std::string& msg) +{ + // | ^^^^^^^-- this part + oss << make_string(lnw + 1, ' ') + << color::bold << color::blue << " | " << color::reset; + + oss << make_string(col-1 /*1-origin*/, ' ') + << color::bold << color::red + << make_string(len, '^') << "-- " + << color::reset << msg << '\n'; + + return oss; +} + +TOML11_INLINE std::string format_location_impl(const std::size_t lnw, + const std::string& prev_fname, + const source_location& loc, const std::string& msg) +{ + std::ostringstream oss; + + if(loc.file_name() != prev_fname) + { + format_filename(oss, loc); + if( ! loc.lines().empty()) + { + format_empty_line(oss, lnw); + } + } + + if(loc.lines().size() == 1) + { + // when column points LF, it exceeds the size of the first line. + std::size_t underline_limit = 1; + if(loc.first_line().size() < loc.first_column_number()) + { + underline_limit = 1; + } + else + { + underline_limit = loc.first_line().size() - loc.first_column_number() + 1; + } + const auto underline_len = (std::min)(underline_limit, loc.length()); + + format_line(oss, lnw, loc.first_line_number(), loc.first_line()); + format_underline(oss, lnw, loc.first_column_number(), underline_len, msg); + } + else if(loc.lines().size() == 2) + { + const auto first_underline_len = + loc.first_line().size() - loc.first_column_number() + 1; + format_line(oss, lnw, loc.first_line_number(), loc.first_line()); + format_underline(oss, lnw, loc.first_column_number(), + first_underline_len, ""); + + format_line(oss, lnw, loc.last_line_number(), loc.last_line()); + format_underline(oss, lnw, 1, loc.last_column_number(), msg); + } + else if(loc.lines().size() > 2) + { + const auto first_underline_len = + loc.first_line().size() - loc.first_column_number() + 1; + format_line(oss, lnw, loc.first_line_number(), loc.first_line()); + format_underline(oss, lnw, loc.first_column_number(), + first_underline_len, "and"); + + if(loc.lines().size() == 3) + { + format_line(oss, lnw, loc.first_line_number()+1, loc.lines().at(1)); + format_underline(oss, lnw, 1, loc.lines().at(1).size(), "and"); + } + else + { + format_line(oss, lnw, loc.first_line_number()+1, " ..."); + format_empty_line(oss, lnw); + } + format_line(oss, lnw, loc.last_line_number(), loc.last_line()); + format_underline(oss, lnw, 1, loc.last_column_number(), msg); + } + // if loc is empty, do nothing. + return oss.str(); +} + +} // namespace detail +} // toml +#endif // TOML11_SOURCE_LOCATION_IMPL_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/impl/syntax_impl.hpp b/src/frontend/qt_sdl/toml/toml11/impl/syntax_impl.hpp new file mode 100644 index 00000000..7dd3b81f --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/impl/syntax_impl.hpp @@ -0,0 +1,732 @@ +#ifndef TOML11_SYNTAX_IMPL_HPP +#define TOML11_SYNTAX_IMPL_HPP + +#include "../fwd/syntax_fwd.hpp" +#include "../scanner.hpp" +#include "../spec.hpp" + +namespace toml +{ +namespace detail +{ +namespace syntax +{ + +using char_type = location::char_type; + +// =========================================================================== +// UTF-8 + +// avoid redundant representation and out-of-unicode sequence + +TOML11_INLINE character_in_range utf8_1byte(const spec&) +{ + return character_in_range(0x00, 0x7F); +} + +TOML11_INLINE sequence utf8_2bytes(const spec&) +{ + return sequence(character_in_range(0xC2, 0xDF), + character_in_range(0x80, 0xBF)); +} + +TOML11_INLINE sequence utf8_3bytes(const spec&) +{ + return sequence(/*1~2 bytes = */either( + sequence(character (0xE0), character_in_range(0xA0, 0xBF)), + sequence(character_in_range(0xE1, 0xEC), character_in_range(0x80, 0xBF)), + sequence(character (0xED), character_in_range(0x80, 0x9F)), + sequence(character_in_range(0xEE, 0xEF), character_in_range(0x80, 0xBF)) + ), /*3rd byte = */ character_in_range(0x80, 0xBF)); +} + +TOML11_INLINE sequence utf8_4bytes(const spec&) +{ + return sequence(/*1~2 bytes = */either( + sequence(character (0xF0), character_in_range(0x90, 0xBF)), + sequence(character_in_range(0xF1, 0xF3), character_in_range(0x80, 0xBF)), + sequence(character (0xF4), character_in_range(0x80, 0x8F)) + ), character_in_range(0x80, 0xBF), character_in_range(0x80, 0xBF)); +} + +TOML11_INLINE non_ascii::non_ascii(const spec& s) noexcept + : scanner_(utf8_2bytes(s), utf8_3bytes(s), utf8_4bytes(s)) +{} + + +// =========================================================================== +// Whitespace + +TOML11_INLINE character_either wschar(const spec&) +{ + return character_either{char_type(' '), char_type('\t')}; +} + +TOML11_INLINE repeat_at_least ws(const spec& s) +{ + return repeat_at_least(0, wschar(s)); +} + +// =========================================================================== +// Newline + +TOML11_INLINE either newline(const spec&) +{ + return either(character(char_type('\n')), literal("\r\n")); +} + +// =========================================================================== +// Comments + +TOML11_INLINE either allowed_comment_char(const spec& s) +{ + if(s.v1_1_0_allow_control_characters_in_comments) + { + return either( + character_in_range(0x01, 0x09), + character_in_range(0x0E, 0x7F), + non_ascii(s) + ); + } + else + { + return either( + character(0x09), + character_in_range(0x20, 0x7E), + non_ascii(s) + ); + } +} + +// XXX Note that it does not take newline +TOML11_INLINE sequence comment(const spec& s) +{ + return sequence(character(char_type('#')), + repeat_at_least(0, allowed_comment_char(s))); +} + +// =========================================================================== +// Boolean + +TOML11_INLINE either boolean(const spec&) +{ + return either(literal("true"), literal("false")); +} + +// =========================================================================== +// Integer + +TOML11_INLINE digit::digit(const spec&) noexcept + : scanner_(char_type('0'), char_type('9')) +{} + +TOML11_INLINE alpha::alpha(const spec&) noexcept + : scanner_( + character_in_range(char_type('a'), char_type('z')), + character_in_range(char_type('A'), char_type('Z')) + ) +{} + +TOML11_INLINE hexdig::hexdig(const spec& s) noexcept + : scanner_( + digit(s), + character_in_range(char_type('a'), char_type('f')), + character_in_range(char_type('A'), char_type('F')) + ) +{} + +// non-digit-graph = ([a-zA-Z]|unicode mb char) +// graph = ([a-zA-Z0-9]|unicode mb char) +// suffix = _ non-digit-graph (graph | _graph) +TOML11_INLINE sequence num_suffix(const spec& s) +{ + const auto non_digit_graph = [&s]() { + return either( + alpha(s), + non_ascii(s) + ); + }; + const auto graph = [&s]() { + return either( + alpha(s), + digit(s), + non_ascii(s) + ); + }; + + return sequence( + character(char_type('_')), + non_digit_graph(), + repeat_at_least(0, + either( + sequence(character(char_type('_')), graph()), + graph() + ) + ) + ); +} + +TOML11_INLINE sequence dec_int(const spec& s) +{ + const auto digit19 = []() { + return character_in_range(char_type('1'), char_type('9')); + }; + return sequence( + maybe(character_either{char_type('-'), char_type('+')}), + either( + sequence( + digit19(), + repeat_at_least(1, + either( + digit(s), + sequence(character(char_type('_')), digit(s)) + ) + ) + ), + digit(s) + ) + ); +} + +TOML11_INLINE sequence hex_int(const spec& s) +{ + return sequence( + literal("0x"), + hexdig(s), + repeat_at_least(0, + either( + hexdig(s), + sequence(character(char_type('_')), hexdig(s)) + ) + ) + ); +} + +TOML11_INLINE sequence oct_int(const spec&) +{ + const auto digit07 = []() { + return character_in_range(char_type('0'), char_type('7')); + }; + return sequence( + literal("0o"), + digit07(), + repeat_at_least(0, + either( + digit07(), + sequence(character(char_type('_')), digit07()) + ) + ) + ); +} + +TOML11_INLINE sequence bin_int(const spec&) +{ + const auto digit01 = []() { + return character_either{char_type('0'), char_type('1')}; + }; + return sequence( + literal("0b"), + digit01(), + repeat_at_least(0, + either( + digit01(), + sequence(character(char_type('_')), digit01()) + ) + ) + ); +} + +TOML11_INLINE either integer(const spec& s) +{ + return either( + hex_int(s), + oct_int(s), + bin_int(s), + dec_int(s) + ); +} + + +// =========================================================================== +// Floating + +TOML11_INLINE sequence zero_prefixable_int(const spec& s) +{ + return sequence( + digit(s), + repeat_at_least(0, + either( + digit(s), + sequence(character('_'), digit(s)) + ) + ) + ); +} + +TOML11_INLINE sequence fractional_part(const spec& s) +{ + return sequence( + character('.'), + zero_prefixable_int(s) + ); +} + +TOML11_INLINE sequence exponent_part(const spec& s) +{ + return sequence( + character_either{char_type('e'), char_type('E')}, + maybe(character_either{char_type('+'), char_type('-')}), + zero_prefixable_int(s) + ); +} + +TOML11_INLINE sequence hex_floating(const spec& s) +{ + // C99 hexfloat (%a) + // [+-]? 0x ( [0-9a-fA-F]*\.[0-9a-fA-F]+ | [0-9a-fA-F]+\.? ) [pP] [+-]? [0-9]+ + + // - 0x(int).(frac)p[+-](int) + // - 0x(int).p[+-](int) + // - 0x.(frac)p[+-](int) + // - 0x(int)p[+-](int) + + return sequence( + maybe(character_either{char_type('+'), char_type('-')}), + character('0'), + character_either{char_type('x'), char_type('X')}, + either( + sequence( + repeat_at_least(0, hexdig(s)), + character('.'), + repeat_at_least(1, hexdig(s)) + ), + sequence( + repeat_at_least(1, hexdig(s)), + maybe(character('.')) + ) + ), + character_either{char_type('p'), char_type('P')}, + maybe(character_either{char_type('+'), char_type('-')}), + repeat_at_least(1, character_in_range('0', '9')) + ); +} + +TOML11_INLINE either floating(const spec& s) +{ + return either( + sequence( + dec_int(s), + either( + exponent_part(s), + sequence(fractional_part(s), maybe(exponent_part(s))) + ) + ), + sequence( + maybe(character_either{char_type('-'), char_type('+')}), + either(literal("inf"), literal("nan")) + ) + ); +} + +// =========================================================================== +// Datetime + +TOML11_INLINE sequence local_date(const spec& s) +{ + return sequence( + repeat_exact(4, digit(s)), + character('-'), + repeat_exact(2, digit(s)), + character('-'), + repeat_exact(2, digit(s)) + ); +} +TOML11_INLINE sequence local_time(const spec& s) +{ + auto time = sequence( + repeat_exact(2, digit(s)), + character(':'), + repeat_exact(2, digit(s)) + ); + + if(s.v1_1_0_make_seconds_optional) + { + time.push_back(maybe(sequence( + character(':'), + repeat_exact(2, digit(s)), + maybe(sequence(character('.'), repeat_at_least(1, digit(s)))) + ))); + } + else + { + time.push_back(character(':')); + time.push_back(repeat_exact(2, digit(s))); + time.push_back( + maybe(sequence(character('.'), repeat_at_least(1, digit(s)))) + ); + } + + return time; +} +TOML11_INLINE either time_offset(const spec& s) +{ + return either( + character_either{'Z', 'z'}, + sequence(character_either{'+', '-'}, + repeat_exact(2, digit(s)), + character(':'), + repeat_exact(2, digit(s)) + ) + ); +} +TOML11_INLINE sequence full_time(const spec& s) +{ + return sequence(local_time(s), time_offset(s)); +} +TOML11_INLINE character_either time_delim(const spec&) +{ + return character_either{'T', 't', ' '}; +} +TOML11_INLINE sequence local_datetime(const spec& s) +{ + return sequence(local_date(s), time_delim(s), local_time(s)); +} +TOML11_INLINE sequence offset_datetime(const spec& s) +{ + return sequence(local_date(s), time_delim(s), full_time(s)); +} + +// =========================================================================== +// String + +TOML11_INLINE sequence escaped(const spec& s) +{ + character_either escape_char{ + '\"','\\', 'b', 'f', 'n', 'r', 't' + }; + if(s.v1_1_0_add_escape_sequence_e) + { + escape_char.push_back(char_type('e')); + } + + either escape_seq( + std::move(escape_char), + sequence(character('u'), repeat_exact(4, hexdig(s))), + sequence(character('U'), repeat_exact(8, hexdig(s))) + ); + + if(s.v1_1_0_add_escape_sequence_x) + { + escape_seq.push_back( + sequence(character('x'), repeat_exact(2, hexdig(s))) + ); + } + + return sequence( + character('\\'), + std::move(escape_seq) + ); +} + +TOML11_INLINE either basic_char(const spec& s) +{ + const auto basic_unescaped = [&s]() { + return either( + wschar(s), + character(0x21), // 22 is " + character_in_range(0x23, 0x5B), // 5C is backslash + character_in_range(0x5D, 0x7E), // 7F is DEL + non_ascii(s) + ); + }; + return either(basic_unescaped(), escaped(s)); +} + +TOML11_INLINE sequence basic_string(const spec& s) +{ + return sequence( + character('"'), + repeat_at_least(0, basic_char(s)), + character('"') + ); +} + +// --------------------------------------------------------------------------- +// multiline string + +TOML11_INLINE sequence escaped_newline(const spec& s) +{ + return sequence( + character('\\'), ws(s), newline(s), + repeat_at_least(0, either(wschar(s), newline(s))) + ); +} + +TOML11_INLINE sequence ml_basic_string(const spec& s) +{ + const auto mlb_content = [&s]() { + return either(basic_char(s), newline(s), escaped_newline(s)); + }; + const auto mlb_quotes = []() { + return either(literal("\"\""), character('\"')); + }; + + return sequence( + literal("\"\"\""), + maybe(newline(s)), + repeat_at_least(0, mlb_content()), + repeat_at_least(0, + sequence( + mlb_quotes(), + repeat_at_least(1, mlb_content()) + ) + ), + // XXX """ and mlb_quotes are intentionally reordered to avoid + // unexpected match of mlb_quotes + literal("\"\"\""), + maybe(mlb_quotes()) + ); +} + +// --------------------------------------------------------------------------- +// literal string + +TOML11_INLINE either literal_char(const spec& s) +{ + return either( + character (0x09), + character_in_range(0x20, 0x26), + character_in_range(0x28, 0x7E), + non_ascii(s) + ); +} + +TOML11_INLINE sequence literal_string(const spec& s) +{ + return sequence( + character('\''), + repeat_at_least(0, literal_char(s)), + character('\'') + ); +} + +TOML11_INLINE sequence ml_literal_string(const spec& s) +{ + const auto mll_quotes = []() { + return either(literal("''"), character('\'')); + }; + const auto mll_content = [&s]() { + return either(literal_char(s), newline(s)); + }; + + return sequence( + literal("'''"), + maybe(newline(s)), + repeat_at_least(0, mll_content()), + repeat_at_least(0, sequence( + mll_quotes(), + repeat_at_least(1, mll_content()) + ) + ), + literal("'''"), + maybe(mll_quotes()) + // XXX ''' and mll_quotes are intentionally reordered to avoid + // unexpected match of mll_quotes + ); +} + +TOML11_INLINE either string(const spec& s) +{ + return either( + ml_basic_string(s), + ml_literal_string(s), + basic_string(s), + literal_string(s) + ); +} + +// =========================================================================== +// Keys + +// to keep `expected_chars` simple +TOML11_INLINE non_ascii_key_char::non_ascii_key_char(const spec& s) noexcept +{ + assert(s.v1_1_0_allow_non_english_in_bare_keys); + (void)s; // for NDEBUG +} + +TOML11_INLINE std::uint32_t non_ascii_key_char::read_utf8(location& loc) const +{ + // U+0000 ... U+0079 ; 0xxx_xxxx + // U+0080 ... U+07FF ; 110y_yyyx 10xx_xxxx; + // U+0800 ... U+FFFF ; 1110_yyyy 10yx_xxxx 10xx_xxxx + // U+010000 ... U+10FFFF; 1111_0yyy 10yy_xxxx 10xx_xxxx 10xx_xxxx + + const unsigned char b1 = loc.current(); loc.advance(1); + if(b1 < 0x80) + { + return static_cast(b1); + } + else if((b1 >> 5) == 6) // 0b110 == 6 + { + const auto b2 = loc.current(); loc.advance(1); + + const std::uint32_t c1 = b1 & ((1 << 5) - 1); + const std::uint32_t c2 = b2 & ((1 << 6) - 1); + const std::uint32_t codep = (c1 << 6) + c2; + + if(codep < 0x80) + { + return 0xFFFFFFFF; + } + return codep; + } + else if((b1 >> 4) == 14) // 0b1110 == 14 + { + const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} + const auto b3 = loc.current(); loc.advance(1); + + const std::uint32_t c1 = b1 & ((1 << 4) - 1); + const std::uint32_t c2 = b2 & ((1 << 6) - 1); + const std::uint32_t c3 = b3 & ((1 << 6) - 1); + + const std::uint32_t codep = (c1 << 12) + (c2 << 6) + c3; + if(codep < 0x800) + { + return 0xFFFFFFFF; + } + return codep; + } + else if((b1 >> 3) == 30) // 0b11110 == 30 + { + const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} + const auto b3 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} + const auto b4 = loc.current(); loc.advance(1); + + const std::uint32_t c1 = b1 & ((1 << 3) - 1); + const std::uint32_t c2 = b2 & ((1 << 6) - 1); + const std::uint32_t c3 = b3 & ((1 << 6) - 1); + const std::uint32_t c4 = b4 & ((1 << 6) - 1); + const std::uint32_t codep = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4; + + if(codep < 0x10000) + { + return 0xFFFFFFFF; + } + return codep; + } + else // not a Unicode codepoint in UTF-8 + { + return 0xFFFFFFFF; + } +} + +TOML11_INLINE region non_ascii_key_char::scan(location& loc) const +{ + if(loc.eof()) {return region{};} + + const auto first = loc; + + const auto cp = read_utf8(loc); + + if(cp == 0xFFFFFFFF) + { + return region{}; + } + + // ALPHA / DIGIT / %x2D / %x5F ; a-z A-Z 0-9 - _ + // / %xB2 / %xB3 / %xB9 / %xBC-BE ; superscript digits, fractions + // / %xC0-D6 / %xD8-F6 / %xF8-37D ; non-symbol chars in Latin block + // / %x37F-1FFF ; exclude GREEK QUESTION MARK, which is basically a semi-colon + // / %x200C-200D / %x203F-2040 ; from General Punctuation Block, include the two tie symbols and ZWNJ, ZWJ + // / %x2070-218F / %x2460-24FF ; include super-/subscripts, letterlike/numberlike forms, enclosed alphanumerics + // / %x2C00-2FEF / %x3001-D7FF ; skip arrows, math, box drawing etc, skip 2FF0-3000 ideographic up/down markers and spaces + // / %xF900-FDCF / %xFDF0-FFFD ; skip D800-DFFF surrogate block, E000-F8FF Private Use area, FDD0-FDEF intended for process-internal use (unicode) + // / %x10000-EFFFF ; all chars outside BMP range, excluding Private Use planes (F0000-10FFFF) + + if(cp == 0xB2 || cp == 0xB3 || cp == 0xB9 || (0xBC <= cp && cp <= 0xBE) || + (0xC0 <= cp && cp <= 0xD6 ) || (0xD8 <= cp && cp <= 0xF6) || (0xF8 <= cp && cp <= 0x37D) || + (0x37F <= cp && cp <= 0x1FFF) || + (0x200C <= cp && cp <= 0x200D) || (0x203F <= cp && cp <= 0x2040) || + (0x2070 <= cp && cp <= 0x218F) || (0x2460 <= cp && cp <= 0x24FF) || + (0x2C00 <= cp && cp <= 0x2FEF) || (0x3001 <= cp && cp <= 0xD7FF) || + (0xF900 <= cp && cp <= 0xFDCF) || (0xFDF0 <= cp && cp <= 0xFFFD) || + (0x10000 <= cp && cp <= 0xEFFFF) ) + { + return region(first, loc); + } + loc = first; + return region{}; +} + +TOML11_INLINE repeat_at_least unquoted_key(const spec& s) +{ + auto keychar = either( + alpha(s), digit(s), character{0x2D}, character{0x5F} + ); + + if(s.v1_1_0_allow_non_english_in_bare_keys) + { + keychar.push_back(non_ascii_key_char(s)); + } + + return repeat_at_least(1, std::move(keychar)); +} + +TOML11_INLINE either quoted_key(const spec& s) +{ + return either(basic_string(s), literal_string(s)); +} + +TOML11_INLINE either simple_key(const spec& s) +{ + return either(unquoted_key(s), quoted_key(s)); +} + +TOML11_INLINE sequence dot_sep(const spec& s) +{ + return sequence(ws(s), character('.'), ws(s)); +} + +TOML11_INLINE sequence dotted_key(const spec& s) +{ + return sequence( + simple_key(s), + repeat_at_least(1, sequence(dot_sep(s), simple_key(s))) + ); +} + +TOML11_INLINE key::key(const spec& s) noexcept + : scanner_(dotted_key(s), simple_key(s)) +{} + +TOML11_INLINE sequence keyval_sep(const spec& s) +{ + return sequence(ws(s), character('='), ws(s)); +} + +// =========================================================================== +// Table key + +TOML11_INLINE sequence std_table(const spec& s) +{ + return sequence(character('['), ws(s), key(s), ws(s), character(']')); +} + +TOML11_INLINE sequence array_table(const spec& s) +{ + return sequence(literal("[["), ws(s), key(s), ws(s), literal("]]")); +} + +// =========================================================================== +// extension: null + +TOML11_INLINE literal null_value(const spec&) +{ + return literal("null"); +} + +} // namespace syntax +} // namespace detail +} // namespace toml +#endif // TOML11_SYNTAX_IMPL_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/impl/value_t_impl.hpp b/src/frontend/qt_sdl/toml/toml11/impl/value_t_impl.hpp new file mode 100644 index 00000000..784dc8b2 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/impl/value_t_impl.hpp @@ -0,0 +1,40 @@ +#ifndef TOML11_VALUE_T_IMPL_HPP +#define TOML11_VALUE_T_IMPL_HPP + +#include "../fwd/value_t_fwd.hpp" + +#include +#include +#include + +namespace toml +{ + +TOML11_INLINE std::ostream& operator<<(std::ostream& 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; + } +} + +TOML11_INLINE std::string to_string(value_t t) +{ + std::ostringstream oss; + oss << t; + return oss.str(); +} + +} // namespace toml +#endif // TOML11_VALUE_T_IMPL_HPP diff --git a/src/frontend/qt_sdl/toml/toml/into.hpp b/src/frontend/qt_sdl/toml/toml11/into.hpp similarity index 79% rename from src/frontend/qt_sdl/toml/toml/into.hpp rename to src/frontend/qt_sdl/toml/toml11/into.hpp index 74495560..86a0020e 100644 --- a/src/frontend/qt_sdl/toml/toml/into.hpp +++ b/src/frontend/qt_sdl/toml/toml11/into.hpp @@ -1,5 +1,3 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. #ifndef TOML11_INTO_HPP #define TOML11_INTO_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/literal.hpp b/src/frontend/qt_sdl/toml/toml11/literal.hpp new file mode 100644 index 00000000..e30e7a83 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/literal.hpp @@ -0,0 +1,10 @@ +#ifndef TOML11_LITERAL_HPP +#define TOML11_LITERAL_HPP + +#include "fwd/literal_fwd.hpp" // IWYU pragma: export + +#if ! defined(TOML11_COMPILE_SOURCES) +#include "impl/literal_impl.hpp" // IWYU pragma: export +#endif + +#endif // TOML11_LITERAL_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/location.hpp b/src/frontend/qt_sdl/toml/toml11/location.hpp new file mode 100644 index 00000000..fd232744 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/location.hpp @@ -0,0 +1,10 @@ +#ifndef TOML11_LOCATION_HPP +#define TOML11_LOCATION_HPP + +#include "fwd/location_fwd.hpp" // IWYU pragma: export + +#if ! defined(TOML11_COMPILE_SOURCES) +#include "impl/location_impl.hpp" // IWYU pragma: export +#endif + +#endif // TOML11_LOCATION_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/ordered_map.hpp b/src/frontend/qt_sdl/toml/toml11/ordered_map.hpp new file mode 100644 index 00000000..b9cd3042 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/ordered_map.hpp @@ -0,0 +1,265 @@ +#ifndef TOML11_ORDERED_MAP_HPP +#define TOML11_ORDERED_MAP_HPP + +#include +#include +#include +#include + +namespace toml +{ + +namespace detail +{ +template +struct ordered_map_ebo_container +{ + Cmp cmp_; // empty base optimization for empty Cmp type +}; +} // detail + +template, + typename Allocator = std::allocator>> +class ordered_map : detail::ordered_map_ebo_container +{ + public: + using key_type = Key; + using mapped_type = Val; + using value_type = std::pair; + + using key_compare = Cmp; + using allocator_type = Allocator; + + using container_type = std::vector; + using reference = typename container_type::reference; + using pointer = typename container_type::pointer; + using const_reference = typename container_type::const_reference; + using const_pointer = typename container_type::const_pointer; + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + using size_type = typename container_type::size_type; + using difference_type = typename container_type::difference_type; + + private: + + using ebo_base = detail::ordered_map_ebo_container; + + public: + + ordered_map() = default; + ~ordered_map() = default; + ordered_map(const ordered_map&) = default; + ordered_map(ordered_map&&) = default; + ordered_map& operator=(const ordered_map&) = default; + ordered_map& operator=(ordered_map&&) = default; + + ordered_map(const ordered_map& other, const Allocator& alloc) + : container_(other.container_, alloc) + {} + ordered_map(ordered_map&& other, const Allocator& alloc) + : container_(std::move(other.container_), alloc) + {} + + explicit ordered_map(const Cmp& cmp, const Allocator& alloc = Allocator()) + : ebo_base{cmp}, container_(alloc) + {} + explicit ordered_map(const Allocator& alloc) + : container_(alloc) + {} + + template + ordered_map(InputIterator first, InputIterator last, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()) + : ebo_base{cmp}, container_(first, last, alloc) + {} + template + ordered_map(InputIterator first, InputIterator last, const Allocator& alloc) + : container_(first, last, alloc) + {} + + ordered_map(std::initializer_list v, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()) + : ebo_base{cmp}, container_(std::move(v), alloc) + {} + ordered_map(std::initializer_list v, const Allocator& alloc) + : container_(std::move(v), alloc) + {} + ordered_map& operator=(std::initializer_list v) + { + this->container_ = std::move(v); + return *this; + } + + iterator begin() noexcept {return container_.begin();} + iterator end() noexcept {return container_.end();} + const_iterator begin() const noexcept {return container_.begin();} + const_iterator end() const noexcept {return container_.end();} + const_iterator cbegin() const noexcept {return container_.cbegin();} + const_iterator cend() const noexcept {return container_.cend();} + + bool empty() const noexcept {return container_.empty();} + std::size_t size() const noexcept {return container_.size();} + std::size_t max_size() const noexcept {return container_.max_size();} + + void clear() {container_.clear();} + + void push_back(const value_type& v) + { + if(this->contains(v.first)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.push_back(v); + } + void push_back(value_type&& v) + { + if(this->contains(v.first)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.push_back(std::move(v)); + } + void emplace_back(key_type k, mapped_type v) + { + if(this->contains(k)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.emplace_back(std::move(k), std::move(v)); + } + void pop_back() {container_.pop_back();} + + void insert(value_type kv) + { + if(this->contains(kv.first)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.push_back(std::move(kv)); + } + void emplace(key_type k, mapped_type v) + { + if(this->contains(k)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.emplace_back(std::move(k), std::move(v)); + } + + std::size_t count(const key_type& key) const + { + if(this->find(key) != this->end()) + { + return 1; + } + else + { + return 0; + } + } + bool contains(const key_type& key) const + { + return this->find(key) != this->end(); + } + iterator find(const key_type& key) noexcept + { + return std::find_if(this->begin(), this->end(), + [&key, this](const value_type& v) {return this->cmp_(v.first, key);}); + } + const_iterator find(const key_type& key) const noexcept + { + return std::find_if(this->begin(), this->end(), + [&key, this](const value_type& v) {return this->cmp_(v.first, key);}); + } + + mapped_type& at(const key_type& k) + { + const auto iter = this->find(k); + if(iter == this->end()) + { + throw std::out_of_range("ordered_map: no such element"); + } + return iter->second; + } + mapped_type const& at(const key_type& k) const + { + const auto iter = this->find(k); + if(iter == this->end()) + { + throw std::out_of_range("ordered_map: no such element"); + } + return iter->second; + } + + mapped_type& operator[](const key_type& k) + { + const auto iter = this->find(k); + if(iter == this->end()) + { + this->container_.emplace_back(k, mapped_type{}); + return this->container_.back().second; + } + return iter->second; + } + + mapped_type const& operator[](const key_type& k) const + { + const auto iter = this->find(k); + if(iter == this->end()) + { + throw std::out_of_range("ordered_map: no such element"); + } + return iter->second; + } + + key_compare key_comp() const {return this->cmp_;} + + void swap(ordered_map& other) + { + container_.swap(other.container_); + } + + private: + + container_type container_; +}; + +template +bool operator==(const ordered_map& lhs, const ordered_map& rhs) +{ + return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} +template +bool operator!=(const ordered_map& lhs, const ordered_map& rhs) +{ + return !(lhs == rhs); +} +template +bool operator<(const ordered_map& lhs, const ordered_map& rhs) +{ + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} +template +bool operator>(const ordered_map& lhs, const ordered_map& rhs) +{ + return rhs < lhs; +} +template +bool operator<=(const ordered_map& lhs, const ordered_map& rhs) +{ + return !(lhs > rhs); +} +template +bool operator>=(const ordered_map& lhs, const ordered_map& rhs) +{ + return !(lhs < rhs); +} + +template +void swap(ordered_map& lhs, ordered_map& rhs) +{ + lhs.swap(rhs); + return; +} + + +} // toml +#endif // TOML11_ORDERED_MAP_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/parser.hpp b/src/frontend/qt_sdl/toml/toml11/parser.hpp new file mode 100644 index 00000000..6d147311 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/parser.hpp @@ -0,0 +1,3829 @@ +#ifndef TOML11_PARSER_HPP +#define TOML11_PARSER_HPP + +#include "context.hpp" +#include "datetime.hpp" +#include "error_info.hpp" +#include "region.hpp" +#include "result.hpp" +#include "scanner.hpp" +#include "skip.hpp" +#include "syntax.hpp" +#include "value.hpp" + +#include +#include + +#include +#include + +#if defined(TOML11_HAS_FILESYSTEM) && TOML11_HAS_FILESYSTEM +#include +#endif + +namespace toml +{ + +struct syntax_error final : public ::toml::exception +{ + public: + syntax_error(std::string what_arg, std::vector err) + : what_(std::move(what_arg)), err_(std::move(err)) + {} + ~syntax_error() noexcept override = default; + + const char* what() const noexcept override {return what_.c_str();} + + std::vector const& errors() const noexcept + { + return err_; + } + + private: + std::string what_; + std::vector err_; +}; + +struct file_io_error final : public ::toml::exception +{ + public: + + file_io_error(const std::string& msg, const std::string& fname) + : errno_(cxx::make_nullopt()), + what_(msg + " \"" + fname + "\"") + {} + file_io_error(int errnum, const std::string& msg, const std::string& fname) + : errno_(errnum), + what_(msg + " \"" + fname + "\": errno=" + std::to_string(errnum)) + {} + ~file_io_error() noexcept override = default; + + const char* what() const noexcept override {return what_.c_str();} + + bool has_errno() const noexcept {return errno_.has_value();} + int get_errno() const noexcept {return errno_.value_or(0);} + + private: + + cxx::optional errno_; + std::string what_; +}; + +namespace detail +{ + +/* ============================================================================ + * __ ___ _ __ _ __ ___ _ _ + * / _/ _ \ ' \| ' \/ _ \ ' \ + * \__\___/_|_|_|_|_|_\___/_||_| + */ + +template +error_info make_syntax_error(std::string title, + const S& scanner, location loc, std::string suffix = "") +{ + auto msg = std::string("expected ") + scanner.expected_chars(loc); + auto src = source_location(region(loc)); + return make_error_info( + std::move(title), std::move(src), std::move(msg), std::move(suffix)); +} + + +/* ============================================================================ + * _ + * __ ___ _ __ _ __ ___ _ _| |_ + * / _/ _ \ ' \| ' \/ -_) ' \ _| + * \__\___/_|_|_|_|_|_\___|_||_\__| + */ + +template +result, error_info> +parse_comment_line(location& loc, context& ctx) +{ + const auto& spec = ctx.toml_spec(); + const auto first = loc; + + skip_whitespace(loc, ctx); + + const auto com_reg = syntax::comment(spec).scan(loc); + if(com_reg.is_ok()) + { + // once comment started, newline must follow (or reach EOF). + if( ! loc.eof() && ! syntax::newline(spec).scan(loc).is_ok()) + { + while( ! loc.eof()) // skip until newline to continue parsing + { + loc.advance(); + if(loc.current() == '\n') { /*skip LF*/ loc.advance(); break; } + } + return err(make_error_info("toml::parse_comment_line: " + "newline (LF / CRLF) or EOF is expected", + source_location(region(loc)), "but got this", + "Hint: most of the control characters are not allowed in comments")); + } + return ok(cxx::optional(com_reg.as_string())); + } + else + { + loc = first; // rollback whitespace to parse indent + return ok(cxx::optional(cxx::make_nullopt())); + } +} + +/* ============================================================================ + * ___ _ + * | _ ) ___ ___| |___ __ _ _ _ + * | _ \/ _ \/ _ \ / -_) _` | ' \ + * |___/\___/\___/_\___\__,_|_||_| + */ + +template +result, error_info> +parse_boolean(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::boolean(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_boolean: " + "invalid boolean: boolean must be `true` or `false`, in lowercase. " + "string must be surrounded by `\"`", syntax::boolean(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + const auto str = reg.as_string(); + const auto val = [&str]() { + if(str == "true") + { + return true; + } + else + { + assert(str == "false"); + return false; + } + }(); + + // ---------------------------------------------------------------------- + // no format info for boolean + boolean_format_info fmt; + + return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); +} + +/* ============================================================================ + * ___ _ + * |_ _|_ _| |_ ___ __ _ ___ _ _ + * | || ' \ _/ -_) _` / -_) '_| + * |___|_||_\__\___\__, \___|_| + * |___/ + */ + +template +result, error_info> +parse_bin_integer(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + auto reg = syntax::bin_int(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_bin_integer: " + "invalid integer: bin_int must be like: 0b0101, 0b1111_0000", + syntax::bin_int(spec), loc)); + } + + auto str = reg.as_string(); + + integer_format_info fmt; + fmt.fmt = integer_format::bin; + fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); + + const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); + if(first_underscore != str.rend()) + { + fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); + } + + // skip prefix `0b` and zeros and underscores at the MSB + str.erase(str.begin(), std::find(std::next(str.begin(), 2), str.end(), '1')); + + // remove all `_` before calling TC::parse_int + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + // 0b0000_0000 becomes empty. + if(str.empty()) { str = "0"; } + + const auto val = TC::parse_int(str, source_location(region(loc)), 2); + if(val.is_ok()) + { + return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); + } + else + { + loc = first; + return err(val.as_err()); + } +} + +// ---------------------------------------------------------------------------- + +template +result, error_info> +parse_oct_integer(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + auto reg = syntax::oct_int(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_oct_integer: " + "invalid integer: oct_int must be like: 0o775, 0o04_44", + syntax::oct_int(spec), loc)); + } + + auto str = reg.as_string(); + + integer_format_info fmt; + fmt.fmt = integer_format::oct; + fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); + + const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); + if(first_underscore != str.rend()) + { + fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); + } + + // skip prefix `0o` and zeros and underscores at the MSB + str.erase(str.begin(), std::find_if( + std::next(str.begin(), 2), str.end(), [](const char c) { + return c != '0' && c != '_'; + })); + + // remove all `_` before calling TC::parse_int + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + // 0o0000_0000 becomes empty. + if(str.empty()) { str = "0"; } + + const auto val = TC::parse_int(str, source_location(region(loc)), 8); + if(val.is_ok()) + { + return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); + } + else + { + loc = first; + return err(val.as_err()); + } +} + +template +result, error_info> +parse_hex_integer(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + auto reg = syntax::hex_int(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_hex_integer: " + "invalid integer: hex_int must be like: 0xC0FFEE, 0xdead_beef", + syntax::hex_int(spec), loc)); + } + + auto str = reg.as_string(); + + integer_format_info fmt; + fmt.fmt = integer_format::hex; + fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); + + const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); + if(first_underscore != str.rend()) + { + fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); + } + + // skip prefix `0x` and zeros and underscores at the MSB + str.erase(str.begin(), std::find_if( + std::next(str.begin(), 2), str.end(), [](const char c) { + return c != '0' && c != '_'; + })); + + // remove all `_` before calling TC::parse_int + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + // 0x0000_0000 becomes empty. + if(str.empty()) { str = "0"; } + + // prefix zero and _ is removed. check if it uses upper/lower case. + // if both upper and lower case letters are found, set upper=true. + const auto lower_not_found = std::find_if(str.begin(), str.end(), + [](const char c) { return std::islower(static_cast(c)) != 0; }) == str.end(); + const auto upper_found = std::find_if(str.begin(), str.end(), + [](const char c) { return std::isupper(static_cast(c)) != 0; }) != str.end(); + fmt.uppercase = lower_not_found || upper_found; + + const auto val = TC::parse_int(str, source_location(region(loc)), 16); + if(val.is_ok()) + { + return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); + } + else + { + loc = first; + return err(val.as_err()); + } +} + +template +result, error_info> +parse_dec_integer(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::dec_int(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_dec_integer: " + "invalid integer: dec_int must be like: 42, 123_456_789", + syntax::dec_int(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + auto str = reg.as_string(); + + integer_format_info fmt; + fmt.fmt = integer_format::dec; + fmt.width = str.size() - static_cast(std::count(str.begin(), str.end(), '_')); + + const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); + if(first_underscore != str.rend()) + { + fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); + } + + // remove all `_` before calling TC::parse_int + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + auto src = source_location(region(loc)); + const auto val = TC::parse_int(str, src, 10); + if(val.is_err()) + { + loc = first; + return err(val.as_err()); + } + + // ---------------------------------------------------------------------- + // parse suffix (extension) + + if(spec.ext_num_suffix && loc.current() == '_') + { + const auto sfx_reg = syntax::num_suffix(spec).scan(loc); + if( ! sfx_reg.is_ok()) + { + loc = first; + return err(make_error_info("toml::parse_dec_integer: " + "invalid suffix: should be `_ non-digit-graph (graph | _graph)`", + source_location(region(loc)), "here")); + } + auto sfx = sfx_reg.as_string(); + assert( ! sfx.empty() && sfx.front() == '_'); + sfx.erase(sfx.begin()); // remove the first `_` + + fmt.suffix = sfx; + } + + return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); +} + +template +result, error_info> +parse_integer(location& loc, const context& ctx) +{ + const auto first = loc; + + if( ! loc.eof() && (loc.current() == '+' || loc.current() == '-')) + { + // skip +/- to diagnose +0xDEADBEEF or -0b0011 (invalid). + // without this, +0xDEAD_BEEF will be parsed as a decimal int and + // unexpected "xDEAD_BEEF" will appear after integer "+0". + loc.advance(); + } + + if( ! loc.eof() && loc.current() == '0') + { + loc.advance(); + if(loc.eof()) + { + // `[+-]?0`. parse as an decimal integer. + loc = first; + return parse_dec_integer(loc, ctx); + } + + const auto prefix = loc.current(); + auto prefix_src = source_location(region(loc)); + + loc = first; + + if(prefix == 'b') {return parse_bin_integer(loc, ctx);} + if(prefix == 'o') {return parse_oct_integer(loc, ctx);} + if(prefix == 'x') {return parse_hex_integer(loc, ctx);} + + if(std::isdigit(prefix)) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_integer: " + "leading zero in an decimal integer is not allowed", + std::move(src), "leading zero")); + } + } + + loc = first; + return parse_dec_integer(loc, ctx); +} + +/* ============================================================================ + * ___ _ _ _ + * | __| |___ __ _| |_(_)_ _ __ _ + * | _|| / _ \/ _` | _| | ' \/ _` | + * |_| |_\___/\__,_|\__|_|_||_\__, | + * |___/ + */ + +template +result, error_info> +parse_floating(location& loc, const context& ctx) +{ + using floating_type = typename basic_value::floating_type; + + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + // ---------------------------------------------------------------------- + // check syntax + bool is_hex = false; + std::string str; + region reg; + if(spec.ext_hex_float && sequence(character('0'), character('x')).scan(loc).is_ok()) + { + loc = first; + is_hex = true; + + reg = syntax::hex_floating(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_floating: " + "invalid hex floating: float must be like: 0xABCp-3f", + syntax::floating(spec), loc)); + } + str = reg.as_string(); + } + else + { + reg = syntax::floating(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_floating: " + "invalid floating: float must be like: -3.14159_26535, 6.022e+23, " + "inf, or nan (lowercase).", syntax::floating(spec), loc)); + } + str = reg.as_string(); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + floating_format_info fmt; + + if(is_hex) + { + fmt.fmt = floating_format::hex; + } + else + { + // since we already checked that the string conforms the TOML standard. + if(std::find(str.begin(), str.end(), 'e') != str.end() || + std::find(str.begin(), str.end(), 'E') != str.end()) + { + fmt.fmt = floating_format::scientific; // use exponent part + } + else + { + fmt.fmt = floating_format::fixed; // do not use exponent part + } + } + + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + floating_type val{0}; + + if(str == "inf" || str == "+inf") + { + TOML11_CONSTEXPR_IF(std::numeric_limits::has_infinity) + { + val = std::numeric_limits::infinity(); + } + else + { + return err(make_error_info("toml::parse_floating: inf value found" + " but the current environment does not support inf. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard.", + source_location(region(loc)), + "floating_type: inf is not supported")); + } + } + else if(str == "-inf") + { + TOML11_CONSTEXPR_IF(std::numeric_limits::has_infinity) + { + val = -std::numeric_limits::infinity(); + } + else + { + return err(make_error_info("toml::parse_floating: inf value found" + " but the current environment does not support inf. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard.", + source_location(region(loc)), + "floating_type: inf is not supported")); + } + } + else if(str == "nan" || str == "+nan") + { + TOML11_CONSTEXPR_IF(std::numeric_limits::has_quiet_NaN) + { + val = std::numeric_limits::quiet_NaN(); + } + else TOML11_CONSTEXPR_IF(std::numeric_limits::has_signaling_NaN) + { + val = std::numeric_limits::signaling_NaN(); + } + else + { + return err(make_error_info("toml::parse_floating: NaN value found" + " but the current environment does not support NaN. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard.", + source_location(region(loc)), + "floating_type: NaN is not supported")); + } + } + else if(str == "-nan") + { + using std::copysign; + TOML11_CONSTEXPR_IF(std::numeric_limits::has_quiet_NaN) + { + val = copysign(std::numeric_limits::quiet_NaN(), floating_type(-1)); + } + else TOML11_CONSTEXPR_IF(std::numeric_limits::has_signaling_NaN) + { + val = copysign(std::numeric_limits::signaling_NaN(), floating_type(-1)); + } + else + { + return err(make_error_info("toml::parse_floating: NaN value found" + " but the current environment does not support NaN. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard.", + source_location(region(loc)), + "floating_type: NaN is not supported")); + } + } + else + { + // set precision + const auto has_sign = ! str.empty() && (str.front() == '+' || str.front() == '-'); + const auto decpoint = std::find(str.begin(), str.end(), '.'); + const auto exponent = std::find_if(str.begin(), str.end(), + [](const char c) { return c == 'e' || c == 'E'; }); + if(decpoint != str.end() && exponent != str.end()) + { + assert(decpoint < exponent); + } + + if(fmt.fmt == floating_format::scientific) + { + // total width + fmt.prec = static_cast(std::distance(str.begin(), exponent)); + if(has_sign) + { + fmt.prec -= 1; + } + if(decpoint != str.end()) + { + fmt.prec -= 1; + } + } + else if(fmt.fmt == floating_format::hex) + { + fmt.prec = std::numeric_limits::max_digits10; + } + else + { + // width after decimal point + fmt.prec = static_cast(std::distance(std::next(decpoint), exponent)); + } + + auto src = source_location(region(loc)); + const auto res = TC::parse_float(str, src, is_hex); + if(res.is_ok()) + { + val = res.as_ok(); + } + else + { + return err(res.as_err()); + } + } + + // ---------------------------------------------------------------------- + // parse suffix (extension) + + if(spec.ext_num_suffix && loc.current() == '_') + { + const auto sfx_reg = syntax::num_suffix(spec).scan(loc); + if( ! sfx_reg.is_ok()) + { + auto src = source_location(region(loc)); + loc = first; + return err(make_error_info("toml::parse_floating: " + "invalid suffix: should be `_ non-digit-graph (graph | _graph)`", + std::move(src), "here")); + } + auto sfx = sfx_reg.as_string(); + assert( ! sfx.empty() && sfx.front() == '_'); + sfx.erase(sfx.begin()); // remove the first `_` + + fmt.suffix = sfx; + } + + return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); +} + +/* ============================================================================ + * ___ _ _ _ + * | \ __ _| |_ ___| |_(_)_ __ ___ + * | |) / _` | _/ -_) _| | ' \/ -_) + * |___/\__,_|\__\___|\__|_|_|_|_\___| + */ + +// all the offset_datetime, local_datetime, local_date parses date part. +template +result, error_info> +parse_local_date_only(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + local_date_format_info fmt; + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::local_date(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_local_date: " + "invalid date: date must be like: 1234-05-06, yyyy-mm-dd.", + syntax::local_date(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + const auto str = reg.as_string(); + + // 0123456789 + // yyyy-mm-dd + const auto year_r = from_string(str.substr(0, 4)); + const auto month_r = from_string(str.substr(5, 2)); + const auto day_r = from_string(str.substr(8, 2)); + + if(year_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_date: " + "failed to read year `" + str.substr(0, 4) + "`", + std::move(src), "here")); + } + if(month_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_date: " + "failed to read month `" + str.substr(5, 2) + "`", + std::move(src), "here")); + } + if(day_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_date: " + "failed to read day `" + str.substr(8, 2) + "`", + std::move(src), "here")); + } + + const auto year = year_r.unwrap(); + const auto month = month_r.unwrap(); + const auto day = day_r.unwrap(); + + { + // We briefly check whether the input date is valid or not. + // Actually, because of the historical reasons, there are several + // edge cases, such as 1582/10/5-1582/10/14 (only in several countries). + // But here, we do not care about it. + // It makes the code complicated and there is only low probability + // that such a specific date is needed in practice. If someone need to + // validate date accurately, that means that the one need a specialized + // library for their purpose in another layer. + + const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); + const auto max_day = [month, is_leap]() { + if(month == 2) + { + return is_leap ? 29 : 28; + } + if(month == 4 || month == 6 || month == 9 || month == 11) + { + return 30; + } + return 31; + }(); + + if((month < 1 || 12 < month) || (day < 1 || max_day < day)) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_date: invalid date.", + std::move(src), "month must be 01-12, day must be any of " + "01-28,29,30,31 depending on the month/year.")); + } + } + + return ok(std::make_tuple( + local_date(year, static_cast(month - 1), day), + std::move(fmt), std::move(reg) + )); +} + +template +result, error_info> +parse_local_date(location& loc, const context& ctx) +{ + auto val_fmt_reg = parse_local_date_only(loc, ctx); + if(val_fmt_reg.is_err()) + { + return err(val_fmt_reg.unwrap_err()); + } + + auto val = std::move(std::get<0>(val_fmt_reg.unwrap())); + auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap())); + auto reg = std::move(std::get<2>(val_fmt_reg.unwrap())); + + return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); +} + +// all the offset_datetime, local_datetime, local_time parses date part. +template +result, error_info> +parse_local_time_only(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + local_time_format_info fmt; + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::local_time(spec).scan(loc); + if( ! reg.is_ok()) + { + if(spec.v1_1_0_make_seconds_optional) + { + return err(make_syntax_error("toml::parse_local_time: " + "invalid time: time must be HH:MM(:SS.sss) (seconds are optional)", + syntax::local_time(spec), loc)); + } + else + { + return err(make_syntax_error("toml::parse_local_time: " + "invalid time: time must be HH:MM:SS(.sss) (subseconds are optional)", + syntax::local_time(spec), loc)); + } + } + + // ---------------------------------------------------------------------- + // it matches. gen value + const auto str = reg.as_string(); + + // at least we have HH:MM. + // 01234 + // HH:MM + const auto hour_r = from_string(str.substr(0, 2)); + const auto minute_r = from_string(str.substr(3, 2)); + + if(hour_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read hour `" + str.substr(0, 2) + "`", + std::move(src), "here")); + } + if(minute_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read minute `" + str.substr(3, 2) + "`", + std::move(src), "here")); + } + + const auto hour = hour_r.unwrap(); + const auto minute = minute_r.unwrap(); + + if((hour < 0 || 24 <= hour) || (minute < 0 || 60 <= minute)) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: invalid time.", + std::move(src), "hour must be 00-23, minute must be 00-59.")); + } + + // ----------------------------------------------------------------------- + // we have hour and minute. + // Since toml v1.1.0, second and subsecond part becomes optional. + // Check the version and return if second does not exist. + + if(str.size() == 5 && spec.v1_1_0_make_seconds_optional) + { + fmt.has_seconds = false; + fmt.subsecond_precision = 0; + return ok(std::make_tuple(local_time(hour, minute, 0), std::move(fmt), std::move(reg))); + } + assert(str.at(5) == ':'); + + // we have at least `:SS` part. `.subseconds` are optional. + + // 0 1 + // 012345678901234 + // HH:MM:SS.subsec + const auto sec_r = from_string(str.substr(6, 2)); + if(sec_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read second `" + str.substr(6, 2) + "`", + std::move(src), "here")); + } + const auto sec = sec_r.unwrap(); + + if(sec < 0 || 60 < sec) // :60 is allowed + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: invalid time.", + std::move(src), "second must be 00-60.")); + } + + if(str.size() == 8) + { + fmt.has_seconds = true; + fmt.subsecond_precision = 0; + return ok(std::make_tuple(local_time(hour, minute, sec), std::move(fmt), std::move(reg))); + } + + assert(str.at(8) == '.'); + + auto secfrac = str.substr(9, str.size() - 9); + + fmt.has_seconds = true; + fmt.subsecond_precision = secfrac.size(); + + while(secfrac.size() < 9) + { + secfrac += '0'; + } + assert(9 <= secfrac.size()); + const auto ms_r = from_string(secfrac.substr(0, 3)); + const auto us_r = from_string(secfrac.substr(3, 3)); + const auto ns_r = from_string(secfrac.substr(6, 3)); + + if(ms_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read milliseconds `" + secfrac.substr(0, 3) + "`", + std::move(src), "here")); + } + if(us_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read microseconds`" + str.substr(3, 3) + "`", + std::move(src), "here")); + } + if(ns_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read nanoseconds`" + str.substr(6, 3) + "`", + std::move(src), "here")); + } + const auto ms = ms_r.unwrap(); + const auto us = us_r.unwrap(); + const auto ns = ns_r.unwrap(); + + return ok(std::make_tuple(local_time(hour, minute, sec, ms, us, ns), std::move(fmt), std::move(reg))); +} + +template +result, error_info> +parse_local_time(location& loc, const context& ctx) +{ + const auto first = loc; + + auto val_fmt_reg = parse_local_time_only(loc, ctx); + if(val_fmt_reg.is_err()) + { + return err(val_fmt_reg.unwrap_err()); + } + + auto val = std::move(std::get<0>(val_fmt_reg.unwrap())); + auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap())); + auto reg = std::move(std::get<2>(val_fmt_reg.unwrap())); + + return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); +} + +template +result, error_info> +parse_local_datetime(location& loc, const context& ctx) +{ + using char_type = location::char_type; + + const auto first = loc; + + local_datetime_format_info fmt; + + // ---------------------------------------------------------------------- + + auto date_fmt_reg = parse_local_date_only(loc, ctx); + if(date_fmt_reg.is_err()) + { + return err(date_fmt_reg.unwrap_err()); + } + + if(loc.current() == char_type('T')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::upper_T; + } + else if(loc.current() == char_type('t')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::lower_t; + } + else if(loc.current() == char_type(' ')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::space; + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_local_datetime: " + "expect date-time delimiter `T`, `t` or ` `(space).", + std::move(src), "here")); + } + + auto time_fmt_reg = parse_local_time_only(loc, ctx); + if(time_fmt_reg.is_err()) + { + return err(time_fmt_reg.unwrap_err()); + } + + fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds; + fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision; + + // ---------------------------------------------------------------------- + + region reg(first, loc); + local_datetime val(std::get<0>(date_fmt_reg.unwrap()), + std::get<0>(time_fmt_reg.unwrap())); + + return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); +} + +template +result, error_info> +parse_offset_datetime(location& loc, const context& ctx) +{ + using char_type = location::char_type; + + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + offset_datetime_format_info fmt; + + // ---------------------------------------------------------------------- + // date part + + auto date_fmt_reg = parse_local_date_only(loc, ctx); + if(date_fmt_reg.is_err()) + { + return err(date_fmt_reg.unwrap_err()); + } + + // ---------------------------------------------------------------------- + // delimiter + + if(loc.current() == char_type('T')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::upper_T; + } + else if(loc.current() == char_type('t')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::lower_t; + } + else if(loc.current() == char_type(' ')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::space; + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_offset_datetime: " + "expect date-time delimiter `T` or ` `(space).", std::move(src), "here" + )); + } + + // ---------------------------------------------------------------------- + // time part + + auto time_fmt_reg = parse_local_time_only(loc, ctx); + if(time_fmt_reg.is_err()) + { + return err(time_fmt_reg.unwrap_err()); + } + + fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds; + fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision; + + // ---------------------------------------------------------------------- + // offset part + + const auto ofs_reg = syntax::time_offset(spec).scan(loc); + if( ! ofs_reg.is_ok()) + { + return err(make_syntax_error("toml::parse_offset_datetime: " + "invalid offset: offset must be like: Z, +01:00, or -10:00.", + syntax::time_offset(spec), loc)); + } + + const auto ofs_str = ofs_reg.as_string(); + + time_offset offset(0, 0); + + assert(ofs_str.size() != 0); + + if(ofs_str.at(0) == char_type('+') || ofs_str.at(0) == char_type('-')) + { + const auto hour_r = from_string(ofs_str.substr(1, 2)); + const auto minute_r = from_string(ofs_str.substr(4, 2)); + if(hour_r.is_err()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_offset_datetime: " + "Failed to read offset hour part", std::move(src), "here")); + } + if(minute_r.is_err()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_offset_datetime: " + "Failed to read offset minute part", std::move(src), "here")); + } + const auto hour = hour_r.unwrap(); + const auto minute = minute_r.unwrap(); + + if(ofs_str.at(0) == '+') + { + offset = time_offset(hour, minute); + } + else + { + offset = time_offset(-hour, -minute); + } + } + else + { + assert(ofs_str.at(0) == char_type('Z') || ofs_str.at(0) == char_type('z')); + } + + if (offset.hour < -24 || 24 < offset.hour || + offset.minute < -60 || 60 < offset.minute) + { + return err(make_error_info("toml::parse_offset_datetime: " + "too large offset: |hour| <= 24, |minute| <= 60", + source_location(region(first, loc)), "here")); + } + + + // ---------------------------------------------------------------------- + + region reg(first, loc); + offset_datetime val(local_datetime(std::get<0>(date_fmt_reg.unwrap()), + std::get<0>(time_fmt_reg.unwrap())), + offset); + + return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); +} + +/* ============================================================================ + * ___ _ _ + * / __| |_ _ _(_)_ _ __ _ + * \__ \ _| '_| | ' \/ _` | + * |___/\__|_| |_|_||_\__, | + * |___/ + */ + +template +result::string_type, error_info> +parse_utf8_codepoint(const region& reg) +{ + using string_type = typename basic_value::string_type; + using char_type = typename string_type::value_type; + + // assert(reg.as_lines().size() == 1); // XXX heavy check + + const auto str = reg.as_string(); + assert( ! str.empty()); + assert(str.front() == 'u' || str.front() == 'U' || str.front() == 'x'); + + std::uint_least32_t codepoint; + std::istringstream iss(str.substr(1)); + iss >> std::hex >> codepoint; + + const auto to_char = [](const std::uint_least32_t i) noexcept -> char_type { + const auto uc = static_cast(i & 0xFF); + return cxx::bit_cast(uc); + }; + + string_type character; + if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. + { + character += static_cast(codepoint); + } + else if(codepoint < 0x800) //U+0080 ... U+07FF + { + // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 + character += to_char(0xC0|(codepoint >> 6 )); + character += to_char(0x80|(codepoint & 0x3F)); + } + else if(codepoint < 0x10000) // U+0800...U+FFFF + { + if(0xD800 <= codepoint && codepoint <= 0xDFFF) + { + auto src = source_location(reg); + return err(make_error_info("toml::parse_utf8_codepoint: " + "[0xD800, 0xDFFF] is not a valid UTF-8", + std::move(src), "here")); + } + assert(codepoint < 0xD800 || 0xDFFF < codepoint); + // 1110yyyy 10yxxxxx 10xxxxxx + character += to_char(0xE0| (codepoint >> 12)); + character += to_char(0x80|((codepoint >> 6 ) & 0x3F)); + character += to_char(0x80|((codepoint ) & 0x3F)); + } + else if(codepoint < 0x110000) // U+010000 ... U+10FFFF + { + // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx + character += to_char(0xF0| (codepoint >> 18)); + character += to_char(0x80|((codepoint >> 12) & 0x3F)); + character += to_char(0x80|((codepoint >> 6 ) & 0x3F)); + character += to_char(0x80|((codepoint ) & 0x3F)); + } + else // out of UTF-8 region + { + auto src = source_location(reg); + return err(make_error_info("toml::parse_utf8_codepoint: " + "input codepoint is too large.", + std::move(src), "must be in range [0x00, 0x10FFFF]")); + } + return ok(character); +} + +template +result::string_type, error_info> +parse_escape_sequence(location& loc, const context& ctx) +{ + using string_type = typename basic_value::string_type; + using char_type = typename string_type::value_type; + + const auto& spec = ctx.toml_spec(); + + assert( ! loc.eof()); + assert(loc.current() == '\\'); + loc.advance(); // consume the first backslash + + string_type retval; + + if (loc.current() == '\\') { retval += char_type('\\'); loc.advance(); } + else if(loc.current() == '"') { retval += char_type('\"'); loc.advance(); } + else if(loc.current() == 'b') { retval += char_type('\b'); loc.advance(); } + else if(loc.current() == 'f') { retval += char_type('\f'); loc.advance(); } + else if(loc.current() == 'n') { retval += char_type('\n'); loc.advance(); } + else if(loc.current() == 'r') { retval += char_type('\r'); loc.advance(); } + else if(loc.current() == 't') { retval += char_type('\t'); loc.advance(); } + else if(spec.v1_1_0_add_escape_sequence_e && loc.current() == 'e') + { + retval += char_type('\x1b'); + loc.advance(); + } + else if(spec.v1_1_0_add_escape_sequence_x && loc.current() == 'x') + { + auto scanner = sequence(character('x'), repeat_exact(2, syntax::hexdig(spec))); + const auto reg = scanner.scan(loc); + if( ! reg.is_ok()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_escape_sequence: " + "invalid token found in UTF-8 codepoint \\xhh", + std::move(src), "here")); + } + const auto utf8 = parse_utf8_codepoint(reg); + if(utf8.is_err()) + { + return err(utf8.as_err()); + } + retval += utf8.unwrap(); + } + else if(loc.current() == 'u') + { + auto scanner = sequence(character('u'), repeat_exact(4, syntax::hexdig(spec))); + const auto reg = scanner.scan(loc); + if( ! reg.is_ok()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_escape_sequence: " + "invalid token found in UTF-8 codepoint \\uhhhh", + std::move(src), "here")); + } + const auto utf8 = parse_utf8_codepoint(reg); + if(utf8.is_err()) + { + return err(utf8.as_err()); + } + retval += utf8.unwrap(); + } + else if(loc.current() == 'U') + { + auto scanner = sequence(character('U'), repeat_exact(8, syntax::hexdig(spec))); + const auto reg = scanner.scan(loc); + if( ! reg.is_ok()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_escape_sequence: " + "invalid token found in UTF-8 codepoint \\Uhhhhhhhh", + std::move(src), "here")); + } + const auto utf8 = parse_utf8_codepoint(reg); + if(utf8.is_err()) + { + return err(utf8.as_err()); + } + retval += utf8.unwrap(); + } + else + { + auto src = source_location(region(loc)); + std::string escape_seqs = "allowed escape seqs: \\\\, \\\", \\b, \\f, \\n, \\r, \\t"; + if(spec.v1_1_0_add_escape_sequence_e) + { + escape_seqs += ", \\e"; + } + if(spec.v1_1_0_add_escape_sequence_x) + { + escape_seqs += ", \\xhh"; + } + escape_seqs += ", \\uhhhh, or \\Uhhhhhhhh"; + + return err(make_error_info("toml::parse_escape_sequence: " + "unknown escape sequence.", std::move(src), escape_seqs)); + } + return ok(retval); +} + +template +result, error_info> +parse_ml_basic_string(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + string_format_info fmt; + fmt.fmt = string_format::multiline_basic; + + auto reg = syntax::ml_basic_string(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_ml_basic_string: " + "invalid string format", + syntax::ml_basic_string(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + auto str = reg.as_string(); + + // we already checked that it starts with """ and ends with """. + assert(str.substr(0, 3) == "\"\"\""); + str.erase(0, 3); + + assert(str.size() >= 3); + assert(str.substr(str.size()-3, 3) == "\"\"\""); + str.erase(str.size()-3, 3); + + // the first newline just after """ is trimmed + if(str.size() >= 1 && str.at(0) == '\n') + { + str.erase(0, 1); + fmt.start_with_newline = true; + } + else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n') + { + str.erase(0, 2); + fmt.start_with_newline = true; + } + + using string_type = typename basic_value::string_type; + string_type val; + { + auto iter = str.cbegin(); + while(iter != str.cend()) + { + if(*iter == '\\') // remove whitespaces around escaped-newline + { + // we assume that the string is not too long to copy + auto loc2 = make_temporary_location(make_string(iter, str.cend())); + if(syntax::escaped_newline(spec).scan(loc2).is_ok()) + { + std::advance(iter, loc2.get_location()); // skip escaped newline and indent + // now iter points non-WS char + assert(iter == str.end() || (*iter != ' ' && *iter != '\t')); + } + else // normal escape seq. + { + auto esc = parse_escape_sequence(loc2, ctx); + + // syntax does not check its value. the unicode codepoint may be + // invalid, e.g. out-of-bound, [0xD800, 0xDFFF] + if(esc.is_err()) + { + return err(esc.unwrap_err()); + } + + val += esc.unwrap(); + std::advance(iter, loc2.get_location()); + } + } + else // we already checked the syntax. we don't need to check it again. + { + val += static_cast(*iter); + ++iter; + } + } + } + + return ok(basic_value( + std::move(val), std::move(fmt), {}, std::move(reg) + )); +} + +template +result::string_type, region>, error_info> +parse_basic_string_only(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto reg = syntax::basic_string(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_basic_string: " + "invalid string format", + syntax::basic_string(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + auto str = reg.as_string(); + + assert(str.back() == '\"'); + str.pop_back(); + assert(str.at(0) == '\"'); + str.erase(0, 1); + + using string_type = typename basic_value::string_type; + using char_type = typename string_type::value_type; + string_type val; + + { + auto iter = str.begin(); + while(iter != str.end()) + { + if(*iter == '\\') + { + auto loc2 = make_temporary_location(make_string(iter, str.end())); + + auto esc = parse_escape_sequence(loc2, ctx); + + // syntax does not check its value. the unicode codepoint may be + // invalid, e.g. out-of-bound, [0xD800, 0xDFFF] + if(esc.is_err()) + { + return err(esc.unwrap_err()); + } + + val += esc.unwrap(); + std::advance(iter, loc2.get_location()); + } + else + { + val += char_type(*iter); // we already checked the syntax. + ++iter; + } + } + } + return ok(std::make_pair(val, reg)); +} + +template +result, error_info> +parse_basic_string(location& loc, const context& ctx) +{ + const auto first = loc; + + string_format_info fmt; + fmt.fmt = string_format::basic; + + auto val_res = parse_basic_string_only(loc, ctx); + if(val_res.is_err()) + { + return err(std::move(val_res.unwrap_err())); + } + auto val = std::move(val_res.unwrap().first ); + auto reg = std::move(val_res.unwrap().second); + + return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); +} + +template +result, error_info> +parse_ml_literal_string(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + string_format_info fmt; + fmt.fmt = string_format::multiline_literal; + + auto reg = syntax::ml_literal_string(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_ml_literal_string: " + "invalid string format", + syntax::ml_literal_string(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + auto str = reg.as_string(); + + assert(str.substr(0, 3) == "'''"); + assert(str.substr(str.size()-3, 3) == "'''"); + str.erase(0, 3); + str.erase(str.size()-3, 3); + + // the first newline just after """ is trimmed + if(str.size() >= 1 && str.at(0) == '\n') + { + str.erase(0, 1); + fmt.start_with_newline = true; + } + else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n') + { + str.erase(0, 2); + fmt.start_with_newline = true; + } + + using string_type = typename basic_value::string_type; + string_type val(str.begin(), str.end()); + + return ok(basic_value( + std::move(val), std::move(fmt), {}, std::move(reg) + )); +} + +template +result::string_type, region>, error_info> +parse_literal_string_only(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto reg = syntax::literal_string(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_literal_string: " + "invalid string format", + syntax::literal_string(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + auto str = reg.as_string(); + + assert(str.back() == '\''); + str.pop_back(); + assert(str.at(0) == '\''); + str.erase(0, 1); + + using string_type = typename basic_value::string_type; + string_type val(str.begin(), str.end()); + + return ok(std::make_pair(std::move(val), std::move(reg))); +} + +template +result, error_info> +parse_literal_string(location& loc, const context& ctx) +{ + const auto first = loc; + + string_format_info fmt; + fmt.fmt = string_format::literal; + + auto val_res = parse_literal_string_only(loc, ctx); + if(val_res.is_err()) + { + return err(std::move(val_res.unwrap_err())); + } + auto val = std::move(val_res.unwrap().first ); + auto reg = std::move(val_res.unwrap().second); + + return ok(basic_value( + std::move(val), std::move(fmt), {}, std::move(reg) + )); +} + +template +result, error_info> +parse_string(location& loc, const context& ctx) +{ + const auto first = loc; + + if( ! loc.eof() && loc.current() == '"') + { + if(literal("\"\"\"").scan(loc).is_ok()) + { + loc = first; + return parse_ml_basic_string(loc, ctx); + } + else + { + loc = first; + return parse_basic_string(loc, ctx); + } + } + else if( ! loc.eof() && loc.current() == '\'') + { + if(literal("'''").scan(loc).is_ok()) + { + loc = first; + return parse_ml_literal_string(loc, ctx); + } + else + { + loc = first; + return parse_literal_string(loc, ctx); + } + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_string: " + "not a string", std::move(src), "here")); + } +} + +template +result, error_info> +parse_null(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + if( ! spec.ext_null_value) + { + return err(make_error_info("toml::parse_null: " + "invalid spec: spec.ext_null_value must be true.", + source_location(region(loc)), "here")); + } + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::null_value(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_null: " + "invalid null: null must be lowercase. ", + syntax::null_value(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + // ---------------------------------------------------------------------- + // no format info for boolean + + return ok(basic_value(detail::none_t{}, std::move(reg))); +} + +/* ============================================================================ + * _ __ + * | |/ /___ _ _ + * | ' +result::key_type, error_info> +parse_simple_key(location& loc, const context& ctx) +{ + using key_type = typename basic_value::key_type; + const auto& spec = ctx.toml_spec(); + + if(loc.current() == '\"') + { + auto str_res = parse_basic_string_only(loc, ctx); + if(str_res.is_ok()) + { + return ok(std::move(str_res.unwrap().first)); + } + else + { + return err(std::move(str_res.unwrap_err())); + } + } + else if(loc.current() == '\'') + { + auto str_res = parse_literal_string_only(loc, ctx); + if(str_res.is_ok()) + { + return ok(std::move(str_res.unwrap().first)); + } + else + { + return err(std::move(str_res.unwrap_err())); + } + } + + // bare key. + + if(const auto bare = syntax::unquoted_key(spec).scan(loc)) + { + return ok(string_conv(bare.as_string())); + } + else + { + std::string postfix; + if(spec.v1_1_0_allow_non_english_in_bare_keys) + { + postfix = "Hint: Not all Unicode characters are allowed as bare key.\n"; + } + else + { + postfix = "Hint: non-ASCII scripts are allowed in toml v1.1.0, but not in v1.0.0.\n"; + } + return err(make_syntax_error("toml::parse_simple_key: " + "invalid key: key must be \"quoted\", 'quoted-literal', or bare key.", + syntax::unquoted_key(spec), loc, postfix)); + } +} + +// dotted key become vector of keys +template +result::key_type>, region>, error_info> +parse_key(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + using key_type = typename basic_value::key_type; + std::vector keys; + while( ! loc.eof()) + { + auto key = parse_simple_key(loc, ctx); + if( ! key.is_ok()) + { + return err(key.unwrap_err()); + } + keys.push_back(std::move(key.unwrap())); + + auto reg = syntax::dot_sep(spec).scan(loc); + if( ! reg.is_ok()) + { + break; + } + } + if(keys.empty()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_key: expected a new key, " + "but got nothing", std::move(src), "reached EOF")); + } + + return ok(std::make_pair(std::move(keys), region(first, loc))); +} + +// ============================================================================ + +// forward-decl to implement parse_array and parse_table +template +result, error_info> +parse_value(location&, context& ctx); + +template +result::key_type>, region>, + basic_value + >, error_info> +parse_key_value_pair(location& loc, context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto key_res = parse_key(loc, ctx); + if(key_res.is_err()) + { + loc = first; + return err(key_res.unwrap_err()); + } + + if( ! syntax::keyval_sep(spec).scan(loc).is_ok()) + { + auto e = make_syntax_error("toml::parse_key_value_pair: " + "invalid key value separator `=`", syntax::keyval_sep(spec), loc); + loc = first; + return err(std::move(e)); + } + + auto v_res = parse_value(loc, ctx); + if(v_res.is_err()) + { + // loc = first; + return err(v_res.unwrap_err()); + } + return ok(std::make_pair(std::move(key_res.unwrap()), std::move(v_res.unwrap()))); +} + +/* ============================================================================ + * __ _ _ _ _ _ __ _ _ _ + * / _` | '_| '_/ _` | || | + * \__,_|_| |_| \__,_|\_, | + * |__/ + */ + +// array(and multiline inline table with `{` and `}`) has the following format. +// `[` +// (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,` +// (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,` +// ... +// (ws|newline|comment-line)? (value) (ws|newline|comment-line)? (`,`)? +// (ws|newline|comment-line)? `]` +// it skips (ws|newline|comment-line) and returns the token. +template +struct multiline_spacer +{ + using comment_type = typename TC::comment_type; + bool newline_found; + indent_char indent_type; + std::int32_t indent; + comment_type comments; +}; +template +std::ostream& operator<<(std::ostream& os, const multiline_spacer& sp) +{ + os << "{newline=" << sp.newline_found << ", "; + os << "indent_type=" << sp.indent_type << ", "; + os << "indent=" << sp.indent << ", "; + os << "comments=" << sp.comments.size() << "}"; + return os; +} + +template +cxx::optional> +skip_multiline_spacer(location& loc, context& ctx, const bool newline_found = false) +{ + const auto& spec = ctx.toml_spec(); + + multiline_spacer spacer; + spacer.newline_found = newline_found; + spacer.indent_type = indent_char::none; + spacer.indent = 0; + spacer.comments.clear(); + + bool spacer_found = false; + while( ! loc.eof()) + { + if(auto comm = sequence(syntax::comment(spec), syntax::newline(spec)).scan(loc)) + { + spacer.newline_found = true; + auto comment = comm.as_string(); + if( ! comment.empty() && comment.back() == '\n') + { + comment.pop_back(); + if (!comment.empty() && comment.back() == '\r') + { + comment.pop_back(); + } + } + + spacer.comments.push_back(std::move(comment)); + spacer.indent_type = indent_char::none; + spacer.indent = 0; + spacer_found = true; + } + else if(auto nl = syntax::newline(spec).scan(loc)) + { + spacer.newline_found = true; + spacer.comments.clear(); + spacer.indent_type = indent_char::none; + spacer.indent = 0; + spacer_found = true; + } + else if(auto sp = repeat_at_least(1, character(cxx::bit_cast(' '))).scan(loc)) + { + spacer.indent_type = indent_char::space; + spacer.indent = static_cast(sp.length()); + spacer_found = true; + } + else if(auto tabs = repeat_at_least(1, character(cxx::bit_cast('\t'))).scan(loc)) + { + spacer.indent_type = indent_char::tab; + spacer.indent = static_cast(tabs.length()); + spacer_found = true; + } + else + { + break; // done + } + } + if( ! spacer_found) + { + return cxx::make_nullopt(); + } + return spacer; +} + +// not an [[array.of.tables]]. It parses ["this", "type"] +template +result, error_info> +parse_array(location& loc, context& ctx) +{ + const auto num_errors = ctx.errors().size(); + + const auto first = loc; + + if(loc.eof() || loc.current() != '[') + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_array: " + "The next token is not an array", std::move(src), "here")); + } + loc.advance(); + + typename basic_value::array_type val; + + array_format_info fmt; + fmt.fmt = array_format::oneline; + fmt.indent_type = indent_char::none; + + auto spacer = skip_multiline_spacer(loc, ctx); + if(spacer.has_value() && spacer.value().newline_found) + { + fmt.fmt = array_format::multiline; + } + + bool comma_found = true; + while( ! loc.eof()) + { + if(loc.current() == location::char_type(']')) + { + if(spacer.has_value() && spacer.value().newline_found && + spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.closing_indent = spacer.value().indent; + } + break; + } + + if( ! comma_found) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_array: " + "expected value-separator `,` or closing `]`", + std::move(src), "here")); + } + + if(spacer.has_value() && spacer.value().newline_found && + spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.body_indent = spacer.value().indent; + } + + if(auto elem_res = parse_value(loc, ctx)) + { + auto elem = std::move(elem_res.unwrap()); + + if(spacer.has_value()) // copy previous comments to value + { + elem.comments() = std::move(spacer.value().comments); + } + + // parse spaces between a value and a comma + // array = [ + // 42 , # the answer + // ^^^^ + // 3.14 # pi + // , 2.71 ^^^^ + // ^^ + spacer = skip_multiline_spacer(loc, ctx); + if(spacer.has_value()) + { + for(std::size_t i=0; i( + std::move(val), std::move(fmt), {}, region(first, loc) + )); +} + +/* ============================================================================ + * _ _ _ _ _ _ + * (_)_ _ | (_)_ _ ___ | |_ __ _| |__| |___ + * | | ' \| | | ' \/ -_) | _/ _` | '_ \ / -_) + * |_|_||_|_|_|_||_\___| \__\__,_|_.__/_\___| + */ + +// ---------------------------------------------------------------------------- +// insert_value is the most complicated part of the toml spec. +// +// To parse a toml file correctly, we sometimes need to check an exising value +// is appendable or not. +// +// For example while parsing an inline array of tables, +// +// ```toml +// aot = [ +// {a = "foo"}, +// {a = "bar", b = "baz"}, +// ] +// ``` +// +// this `aot` is appendable until parser reaches to `]`. After that, it becomes +// non-appendable. +// +// On the other hand, a normal array of tables, such as +// +// ```toml +// [[aot]] +// a = "foo" +// +// [[aot]] +// a = "bar" +// b = "baz" +// ``` +// This `[[aot]]` is appendable until the parser reaches to the EOF. +// +// +// It becomes a bit more difficult in case of dotted keys. +// In TOML, it is allowed to append a key-value pair to a table that is +// *implicitly* defined by a subtable definitino. +// +// ```toml +// [x.y.z] +// w = 123 +// +// [x] +// a = "foo" # OK. x is defined implicitly by `[x.y.z]`. +// ``` +// +// But if the table is defined by a dotted keys, it is not appendable. +// +// ```toml +// [x] +// y.z.w = 123 +// +// [x.y] +// # ERROR. x.y is already defined by a dotted table in the previous table. +// ``` +// +// Also, reopening a table using dotted keys is invalid. +// +// ```toml +// [x.y.z] +// w = 123 +// +// [x] +// y.z.v = 42 # ERROR. [x.y.z] is already defined. +// ``` +// +// +// ```toml +// [a] +// b.c = "foo" +// b.d = "bar" +// ``` +// +// +// ```toml +// a.b = "foo" +// [a] +// c = "bar" # ERROR +// ``` +// +// In summary, +// - a table must be defined only once. +// - assignment to an exising table is possible only when: +// - defining a subtable [x.y] to an existing table [x]. +// - defining supertable [x] explicitly after [x.y]. +// - adding dotted keys in the same table. + +enum class inserting_value_kind : std::uint8_t +{ + std_table, // insert [standard.table] + array_table, // insert [[array.of.tables]] + dotted_keys // insert a.b.c = "this" +}; + +template +result*, error_info> +insert_value(const inserting_value_kind kind, + typename basic_value::table_type* current_table_ptr, + const std::vector::key_type>& keys, region key_reg, + basic_value val) +{ + using value_type = basic_value; + using array_type = typename basic_value::array_type; + using table_type = typename basic_value::table_type; + + auto key_loc = source_location(key_reg); + + assert( ! keys.empty()); + + // dotted key can insert to dotted key tables defined at the same level. + // dotted key can NOT reopen a table even if it is implcitly-defined one. + // + // [x.y.z] # define x and x.y implicitly. + // a = 42 + // + // [x] # reopening implcitly defined table + // r.s.t = 3.14 # VALID r and r.s are new tables. + // r.s.u = 2.71 # VALID r and r.s are dotted-key tables. valid. + // + // y.z.b = "foo" # INVALID x.y.z are multiline table, not a dotted key. + // y.c = "bar" # INVALID x.y is implicit multiline table, not a dotted key. + + // a table cannot reopen dotted-key tables. + // + // [t1] + // t2.t3.v = 0 + // [t1.t2] # INVALID t1.t2 is defined as a dotted-key table. + + for(std::size_t i=0; i{}, key_reg)); + + assert(current_table.at(key).is_table()); + current_table_ptr = std::addressof(current_table.at(key).as_table()); + } + else if (found->second.is_table()) + { + const auto fmt = found->second.as_table_fmt().fmt; + if(fmt == table_format::oneline || fmt == table_format::multiline_oneline) + { + // foo = {bar = "baz"} or foo = { \n bar = "baz" \n } + return err(make_error_info("toml::insert_value: " + "failed to insert a value: inline table is immutable", + key_loc, "inserting this", + found->second.location(), "to this table")); + } + // dotted key cannot reopen a table. + if(kind ==inserting_value_kind::dotted_keys && fmt != table_format::dotted) + { + return err(make_error_info("toml::insert_value: " + "reopening a table using dotted keys", + key_loc, "dotted key cannot reopen a table", + found->second.location(), "this table is already closed")); + } + assert(found->second.is_table()); + current_table_ptr = std::addressof(found->second.as_table()); + } + else if(found->second.is_array_of_tables()) + { + // aot = [{this = "type", of = "aot"}] # cannot be reopened + if(found->second.as_array_fmt().fmt != array_format::array_of_tables) + { + return err(make_error_info("toml::insert_value:" + "inline array of tables are immutable", + key_loc, "inserting this", + found->second.location(), "inline array of tables")); + } + // appending to [[aot]] + + if(kind == inserting_value_kind::dotted_keys) + { + // [[array.of.tables]] + // [array.of] # reopening supertable is okay + // tables.x = "foo" # appending `x` to the first table + return err(make_error_info("toml::insert_value:" + "dotted key cannot reopen an array-of-tables", + key_loc, "inserting this", + found->second.location(), "to this array-of-tables.")); + } + + // insert_value_by_dotkeys::std_table + // [[array.of.tables]] + // [array.of.tables.subtable] # appending to the last aot + // + // insert_value_by_dotkeys::array_table + // [[array.of.tables]] + // [[array.of.tables.subtable]] # appending to the last aot + auto& current_array_table = found->second.as_array().back(); + + assert(current_array_table.is_table()); + current_table_ptr = std::addressof(current_array_table.as_table()); + } + else + { + return err(make_error_info("toml::insert_value: " + "failed to insert a value, value already exists", + key_loc, "while inserting this", + found->second.location(), "non-table value already exists")); + } + } + else // this is the last key. insert a new value. + { + switch(kind) + { + case inserting_value_kind::dotted_keys: + { + if(current_table.find(key) != current_table.end()) + { + return err(make_error_info("toml::insert_value: " + "failed to insert a value, value already exists", + key_loc, "inserting this", + current_table.at(key).location(), "but value already exists")); + } + current_table.emplace(key, std::move(val)); + return ok(std::addressof(current_table.at(key))); + } + case inserting_value_kind::std_table: + { + // defining a new table or reopening supertable + auto found = current_table.find(key); + if(found == current_table.end()) // define a new aot + { + current_table.emplace(key, std::move(val)); + return ok(std::addressof(current_table.at(key))); + } + else // the table is already defined, reopen it + { + // assigning a [std.table]. it must be an implicit table. + auto& target = found->second; + if( ! target.is_table() || // could be an array-of-tables + target.as_table_fmt().fmt != table_format::implicit) + { + return err(make_error_info("toml::insert_value: " + "failed to insert a table, table already defined", + key_loc, "inserting this", + target.location(), "this table is explicitly defined")); + } + + // merge table + for(const auto& kv : val.as_table()) + { + if(target.contains(kv.first)) + { + // [x.y.z] + // w = "foo" + // [x] + // y = "bar" + return err(make_error_info("toml::insert_value: " + "failed to insert a table, table keys conflict to each other", + key_loc, "inserting this table", + kv.second.location(), "having this value", + target.at(kv.first).location(), "already defined here")); + } + else + { + target[kv.first] = kv.second; + } + } + // change implicit -> explicit + target.as_table_fmt().fmt = table_format::multiline; + // change definition region + change_region_of_value(target, val); + + return ok(std::addressof(current_table.at(key))); + } + } + case inserting_value_kind::array_table: + { + auto found = current_table.find(key); + if(found == current_table.end()) // define a new aot + { + array_format_info fmt; + fmt.fmt = array_format::array_of_tables; + fmt.indent_type = indent_char::none; + + current_table.emplace(key, value_type( + array_type{ std::move(val) }, std::move(fmt), + std::vector{}, std::move(key_reg) + )); + + assert( ! current_table.at(key).as_array().empty()); + return ok(std::addressof(current_table.at(key).as_array().back())); + } + else // the array is already defined, append to it + { + if( ! found->second.is_array_of_tables()) + { + return err(make_error_info("toml::insert_value: " + "failed to insert an array of tables, value already exists", + key_loc, "while inserting this", + found->second.location(), "non-table value already exists")); + } + if(found->second.as_array_fmt().fmt != array_format::array_of_tables) + { + return err(make_error_info("toml::insert_value: " + "failed to insert a table, inline array of tables is immutable", + key_loc, "while inserting this", + found->second.location(), "this is inline array-of-tables")); + } + found->second.as_array().push_back(std::move(val)); + assert( ! current_table.at(key).as_array().empty()); + return ok(std::addressof(current_table.at(key).as_array().back())); + } + } + default: {assert(false);} + } + } + } + return err(make_error_info("toml::insert_key: no keys found", + std::move(key_loc), "here")); +} + +// ---------------------------------------------------------------------------- + +template +result, error_info> +parse_inline_table(location& loc, context& ctx) +{ + using table_type = typename basic_value::table_type; + + const auto num_errors = ctx.errors().size(); + + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + if(loc.eof() || loc.current() != '{') + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_inline_table: " + "The next token is not an inline table", std::move(src), "here")); + } + loc.advance(); + + table_type table; + table_format_info fmt; + fmt.fmt = table_format::oneline; + fmt.indent_type = indent_char::none; + + cxx::optional> spacer(cxx::make_nullopt()); + + if(spec.v1_1_0_allow_newlines_in_inline_tables) + { + spacer = skip_multiline_spacer(loc, ctx); + if(spacer.has_value() && spacer.value().newline_found) + { + fmt.fmt = table_format::multiline_oneline; + } + } + else + { + skip_whitespace(loc, ctx); + } + + bool still_empty = true; + bool comma_found = false; + while( ! loc.eof()) + { + // closing! + if(loc.current() == '}') + { + if(comma_found && ! spec.v1_1_0_allow_trailing_comma_in_inline_tables) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_inline_table: trailing " + "comma is not allowed in TOML-v1.0.0)", std::move(src), "here")); + } + + if(spec.v1_1_0_allow_newlines_in_inline_tables) + { + if(spacer.has_value() && spacer.value().newline_found && + spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.closing_indent = spacer.value().indent; + } + } + break; + } + + // if we already found a value and didn't found `,` nor `}`, error. + if( ! comma_found && ! still_empty) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_inline_table: " + "expected value-separator `,` or closing `}`", + std::move(src), "here")); + } + + // parse indent. + if(spacer.has_value() && spacer.value().newline_found && + spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.body_indent = spacer.value().indent; + } + + still_empty = false; // parsing a value... + if(auto kv_res = parse_key_value_pair(loc, ctx)) + { + auto keys = std::move(kv_res.unwrap().first.first); + auto key_reg = std::move(kv_res.unwrap().first.second); + auto val = std::move(kv_res.unwrap().second); + + auto ins_res = insert_value(inserting_value_kind::dotted_keys, + std::addressof(table), keys, std::move(key_reg), std::move(val)); + if(ins_res.is_err()) + { + ctx.report_error(std::move(ins_res.unwrap_err())); + // we need to skip until the next value (or end of the table) + // because we don't have valid kv pair. + while( ! loc.eof()) + { + const auto c = loc.current(); + if(c == ',' || c == '\n' || c == '}') + { + comma_found = (c == ','); + break; + } + loc.advance(); + } + continue; + } + + // if comment line follows immediately(without newline) after `,`, then + // the comment is for the elem. we need to check if comment follows `,`. + // + // (key) = (val) (ws|newline|comment-line)? `,` (ws)? (comment)? + + if(spec.v1_1_0_allow_newlines_in_inline_tables) + { + if(spacer.has_value()) // copy previous comments to value + { + for(std::size_t i=0; icomments().push_back(spacer.value().comments.at(i)); + } + } + spacer = skip_multiline_spacer(loc, ctx); + if(spacer.has_value()) + { + for(std::size_t i=0; icomments().push_back(spacer.value().comments.at(i)); + } + if(spacer.value().newline_found) + { + fmt.fmt = table_format::multiline_oneline; + if(spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.body_indent = spacer.value().indent; + } + } + } + } + else + { + skip_whitespace(loc, ctx); + } + + comma_found = character(',').scan(loc).is_ok(); + + if(spec.v1_1_0_allow_newlines_in_inline_tables) + { + auto com_res = parse_comment_line(loc, ctx); + if(com_res.is_err()) + { + ctx.report_error(com_res.unwrap_err()); + } + const bool comment_found = com_res.is_ok() && com_res.unwrap().has_value(); + if(comment_found) + { + fmt.fmt = table_format::multiline_oneline; + ins_res.unwrap()->comments().push_back(com_res.unwrap().value()); + } + if(comma_found) + { + spacer = skip_multiline_spacer(loc, ctx, comment_found); + if(spacer.has_value() && spacer.value().newline_found) + { + fmt.fmt = table_format::multiline_oneline; + } + } + } + else + { + skip_whitespace(loc, ctx); + } + } + else + { + ctx.report_error(std::move(kv_res.unwrap_err())); + while( ! loc.eof()) + { + if(loc.current() == '}') + { + break; + } + if( ! spec.v1_1_0_allow_newlines_in_inline_tables && loc.current() == '\n') + { + break; + } + loc.advance(); + } + break; + } + } + + if(loc.current() != '}') + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_inline_table: " + "missing closing bracket `}`", + std::move(src), "expected `}`, reached line end")); + } + else + { + loc.advance(); // skip } + } + + // any error reported from this function + if(num_errors < ctx.errors().size()) + { + assert(ctx.has_error()); // already reported + return err(ctx.pop_last_error()); + } + + basic_value retval( + std::move(table), std::move(fmt), {}, region(first, loc)); + + return ok(std::move(retval)); +} + +/* ============================================================================ + * _ + * __ ____ _| |_ _ ___ + * \ V / _` | | || / -_) + * \_/\__,_|_|\_,_\___| + */ + +template +result +guess_number_type(const location& first, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + location loc = first; + + if(syntax::offset_datetime(spec).scan(loc).is_ok()) + { + return ok(value_t::offset_datetime); + } + loc = first; + + if(syntax::local_datetime(spec).scan(loc).is_ok()) + { + const auto curr = loc.current(); + // if offset_datetime contains bad offset, it syntax::offset_datetime + // fails to scan it. + if(curr == '+' || curr == '-') + { + return err(make_syntax_error("bad offset: must be [+-]HH:MM or Z", + syntax::time_offset(spec), loc, std::string( + "Hint: valid : +09:00, -05:30\n" + "Hint: invalid: +9:00, -5:30\n"))); + } + return ok(value_t::local_datetime); + } + loc = first; + + if(syntax::local_date(spec).scan(loc).is_ok()) + { + // bad time may appear after this. + + if( ! loc.eof()) + { + const auto c = loc.current(); + if(c == 'T' || c == 't') + { + loc.advance(); + + return err(make_syntax_error("bad time: must be HH:MM:SS.subsec", + syntax::local_time(spec), loc, std::string( + "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" + "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); + } + if(c == ' ') + { + // A space is allowed as a delimiter between local time. + // But there is a case where bad time follows a space. + // - invalid: 2019-06-16 7:00:00 + // - valid : 2019-06-16 07:00:00 + loc.advance(); + if( ! loc.eof() && ('0' <= loc.current() && loc.current() <= '9')) + { + return err(make_syntax_error("bad time: must be HH:MM:SS.subsec", + syntax::local_time(spec), loc, std::string( + "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" + "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); + } + } + if('0' <= c && c <= '9') + { + return err(make_syntax_error("bad datetime: missing T or space", + character_either{'T', 't', ' '}, loc, std::string( + "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" + "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); + } + } + return ok(value_t::local_date); + } + loc = first; + + if(syntax::local_time(spec).scan(loc).is_ok()) + { + return ok(value_t::local_time); + } + loc = first; + + if(syntax::floating(spec).scan(loc).is_ok()) + { + if( ! loc.eof() && loc.current() == '_') + { + if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) + { + return ok(value_t::floating); + } + auto src = source_location(region(loc)); + return err(make_error_info( + "bad float: `_` must be surrounded by digits", + std::move(src), "invalid underscore", + "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" + "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")); + } + return ok(value_t::floating); + } + loc = first; + + if(spec.ext_hex_float) + { + if(syntax::hex_floating(spec).scan(loc).is_ok()) + { + if( ! loc.eof() && loc.current() == '_') + { + if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) + { + return ok(value_t::floating); + } + auto src = source_location(region(loc)); + return err(make_error_info( + "bad float: `_` must be surrounded by digits", + std::move(src), "invalid underscore", + "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" + "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")); + } + return ok(value_t::floating); + } + loc = first; + } + + if(auto int_reg = syntax::integer(spec).scan(loc)) + { + if( ! loc.eof()) + { + const auto c = loc.current(); + if(c == '_') + { + if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) + { + return ok(value_t::integer); + } + + if(int_reg.length() <= 2 && (int_reg.as_string() == "0" || + int_reg.as_string() == "-0" || int_reg.as_string() == "+0")) + { + auto src = source_location(region(loc)); + return err(make_error_info( + "bad integer: leading zero is not allowed in decimal int", + std::move(src), "leading zero", + "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n")); + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info( + "bad integer: `_` must be surrounded by digits", + std::move(src), "invalid underscore", + "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n")); + } + } + if('0' <= c && c <= '9') + { + if(loc.current() == '0') + { + loc.retrace(); + return err(make_error_info( + "bad integer: leading zero", + source_location(region(loc)), "leading zero is not allowed", + std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n") + )); + } + else // invalid digits, especially in oct/bin ints. + { + return err(make_error_info( + "bad integer: invalid digit after an integer", + source_location(region(loc)), "this digit is not allowed", + std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n") + )); + } + } + if(c == ':' || c == '-') + { + auto src = source_location(region(loc)); + return err(make_error_info("bad datetime: invalid format", + std::move(src), "here", + std::string("Hint: valid : 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z\n" + "Hint: invalid: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30") + )); + } + if(c == '.' || c == 'e' || c == 'E') + { + auto src = source_location(region(loc)); + return err(make_error_info("bad float: invalid format", + std::move(src), "here", std::string( + "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" + "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n"))); + } + } + return ok(value_t::integer); + } + if( ! loc.eof() && loc.current() == '.') + { + auto src = source_location(region(loc)); + return err(make_error_info("bad float: integer part is required before decimal point", + std::move(src), "missing integer part", std::string( + "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" + "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n") + )); + } + if( ! loc.eof() && loc.current() == '_') + { + auto src = source_location(region(loc)); + return err(make_error_info("bad number: `_` must be surrounded by digits", + std::move(src), "digits required before `_`", std::string( + "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n") + )); + } + + auto src = source_location(region(loc)); + return err(make_error_info("bad format: unknown value appeared", + std::move(src), "here")); +} + +template +result +guess_value_type(const location& loc, const context& ctx) +{ + const auto& sp = ctx.toml_spec(); + location inner(loc); + + switch(loc.current()) + { + case '"' : {return ok(value_t::string); } + case '\'': {return ok(value_t::string); } + case '[' : {return ok(value_t::array); } + case '{' : {return ok(value_t::table); } + case 't' : + { + return ok(value_t::boolean); + } + case 'f' : + { + return ok(value_t::boolean); + } + case 'T' : // invalid boolean. + { + return err(make_syntax_error("toml::parse_value: " + "`true` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::boolean(sp), inner)); + } + case 'F' : + { + return err(make_syntax_error("toml::parse_value: " + "`false` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::boolean(sp), inner)); + } + case 'i' : // inf or string without quotes(syntax error). + { + if(literal("inf").scan(inner).is_ok()) + { + return ok(value_t::floating); + } + else + { + return err(make_syntax_error("toml::parse_value: " + "`inf` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + } + case 'I' : // Inf or string without quotes(syntax error). + { + return err(make_syntax_error("toml::parse_value: " + "`inf` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + case 'n' : // nan or null-extension + { + if(sp.ext_null_value) + { + if(literal("nan").scan(inner).is_ok()) + { + return ok(value_t::floating); + } + else if(literal("null").scan(inner).is_ok()) + { + return ok(value_t::empty); + } + else + { + return err(make_syntax_error("toml::parse_value: " + "Both `nan` and `null` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + } + else // must be nan. + { + if(literal("nan").scan(inner).is_ok()) + { + return ok(value_t::floating); + } + else + { + return err(make_syntax_error("toml::parse_value: " + "`nan` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + } + } + case 'N' : // nan or null-extension + { + if(sp.ext_null_value) + { + return err(make_syntax_error("toml::parse_value: " + "Both `nan` and `null` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + else + { + return err(make_syntax_error("toml::parse_value: " + "`nan` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + } + default : + { + return guess_number_type(loc, ctx); + } + } +} + +template +result, error_info> +parse_value(location& loc, context& ctx) +{ + const auto ty_res = guess_value_type(loc, ctx); + if(ty_res.is_err()) + { + return err(ty_res.unwrap_err()); + } + + switch(ty_res.unwrap()) + { + case value_t::empty: + { + if(ctx.toml_spec().ext_null_value) + { + return parse_null(loc, ctx); + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_value: unknown value appeared", + std::move(src), "here")); + } + } + case value_t::boolean : {return parse_boolean (loc, ctx);} + case value_t::integer : {return parse_integer (loc, ctx);} + case value_t::floating : {return parse_floating (loc, ctx);} + case value_t::string : {return parse_string (loc, ctx);} + case value_t::offset_datetime: {return parse_offset_datetime(loc, ctx);} + case value_t::local_datetime : {return parse_local_datetime (loc, ctx);} + case value_t::local_date : {return parse_local_date (loc, ctx);} + case value_t::local_time : {return parse_local_time (loc, ctx);} + case value_t::array : {return parse_array (loc, ctx);} + case value_t::table : {return parse_inline_table (loc, ctx);} + default: + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_value: unknown value appeared", + std::move(src), "here")); + } + } +} + +/* ============================================================================ + * _____ _ _ + * |_ _|_ _| |__| |___ + * | |/ _` | '_ \ / -_) + * |_|\__,_|_.__/_\___| + */ + +template +result::key_type>, region>, error_info> +parse_table_key(location& loc, context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto reg = syntax::std_table(spec).scan(loc); + if(!reg.is_ok()) + { + return err(make_syntax_error("toml::parse_table_key: invalid table key", + syntax::std_table(spec), loc)); + } + + loc = first; + loc.advance(); // skip [ + skip_whitespace(loc, ctx); + + auto keys_res = parse_key(loc, ctx); + if(keys_res.is_err()) + { + return err(std::move(keys_res.unwrap_err())); + } + + skip_whitespace(loc, ctx); + loc.advance(); // ] + + return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg))); +} + +template +result::key_type>, region>, error_info> +parse_array_table_key(location& loc, context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto reg = syntax::array_table(spec).scan(loc); + if(!reg.is_ok()) + { + return err(make_syntax_error("toml::parse_array_table_key: invalid array-of-tables key", + syntax::array_table(spec), loc)); + } + + loc = first; + loc.advance(); // [ + loc.advance(); // [ + skip_whitespace(loc, ctx); + + auto keys_res = parse_key(loc, ctx); + if(keys_res.is_err()) + { + return err(std::move(keys_res.unwrap_err())); + } + + skip_whitespace(loc, ctx); + loc.advance(); // ] + loc.advance(); // ] + + return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg))); +} + +// called after reading [table.keys] and comments around it. +// Since table may already contain a subtable ([x.y.z] can be defined before [x]), +// the table that is being parsed is passed as an argument. +template +result +parse_table(location& loc, context& ctx, basic_value& table) +{ + assert(table.is_table()); + + const auto num_errors = ctx.errors().size(); + const auto& spec = ctx.toml_spec(); + + // clear indent info + table.as_table_fmt().indent_type = indent_char::none; + + bool newline_found = true; + while( ! loc.eof()) + { + const auto start = loc; + + auto sp = skip_multiline_spacer(loc, ctx, newline_found); + + // if reached to EOF, the table ends here. return. + if(loc.eof()) + { + break; + } + // if next table is comming, return. + if(sequence(syntax::ws(spec), character('[')).scan(loc).is_ok()) + { + loc = start; + break; + } + // otherwise, it should be a key-value pair. + newline_found = newline_found || (sp.has_value() && sp.value().newline_found); + if( ! newline_found) + { + return err(make_error_info("toml::parse_table: " + "newline (LF / CRLF) or EOF is expected", + source_location(region(loc)), "here")); + } + if(sp.has_value() && sp.value().indent_type != indent_char::none) + { + table.as_table_fmt().indent_type = sp.value().indent_type; + table.as_table_fmt().body_indent = sp.value().indent; + } + + newline_found = false; // reset + if(auto kv_res = parse_key_value_pair(loc, ctx)) + { + auto keys = std::move(kv_res.unwrap().first.first); + auto key_reg = std::move(kv_res.unwrap().first.second); + auto val = std::move(kv_res.unwrap().second); + + if(sp.has_value()) + { + for(const auto& com : sp.value().comments) + { + val.comments().push_back(com); + } + } + + if(auto com_res = parse_comment_line(loc, ctx)) + { + if(auto com_opt = com_res.unwrap()) + { + val.comments().push_back(com_opt.value()); + newline_found = true; // comment includes newline at the end + } + } + else + { + ctx.report_error(std::move(com_res.unwrap_err())); + } + + auto ins_res = insert_value(inserting_value_kind::dotted_keys, + std::addressof(table.as_table()), + keys, std::move(key_reg), std::move(val)); + if(ins_res.is_err()) + { + ctx.report_error(std::move(ins_res.unwrap_err())); + } + } + else + { + ctx.report_error(std::move(kv_res.unwrap_err())); + skip_key_value_pair(loc, ctx); + } + } + + if(num_errors < ctx.errors().size()) + { + assert(ctx.has_error()); // already reported + return err(ctx.pop_last_error()); + } + return ok(); +} + +template +result, std::vector> +parse_file(location& loc, context& ctx) +{ + using value_type = basic_value; + using table_type = typename value_type::table_type; + + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + if(loc.eof()) + { + return ok(value_type(table_type(), table_format_info{}, {}, region(loc))); + } + + value_type root(table_type(), table_format_info{}, {}, region(loc)); + root.as_table_fmt().fmt = table_format::multiline; + root.as_table_fmt().indent_type = indent_char::none; + + // parse top comment. + // + // ```toml + // # this is a comment for the top-level table. + // + // key = "the first value" + // ``` + // + // ```toml + // # this is a comment for "the first value". + // key = "the first value" + // ``` + while( ! loc.eof()) + { + if(auto com_res = parse_comment_line(loc, ctx)) + { + if(auto com_opt = com_res.unwrap()) + { + root.comments().push_back(std::move(com_opt.value())); + } + else // no comment found. + { + // if it is not an empty line, clear the root comment. + if( ! sequence(syntax::ws(spec), syntax::newline(spec)).scan(loc).is_ok()) + { + loc = first; + root.comments().clear(); + } + break; + } + } + else + { + ctx.report_error(std::move(com_res.unwrap_err())); + skip_comment_block(loc, ctx); + } + } + + // parse root table + { + const auto res = parse_table(loc, ctx, root); + if(res.is_err()) + { + ctx.report_error(std::move(res.unwrap_err())); + skip_until_next_table(loc, ctx); + } + } + + // parse tables + + while( ! loc.eof()) + { + auto sp = skip_multiline_spacer(loc, ctx, /*newline_found=*/true); + + if(auto key_res = parse_array_table_key(loc, ctx)) + { + auto key = std::move(std::get<0>(key_res.unwrap())); + auto reg = std::move(std::get<1>(key_res.unwrap())); + + std::vector com; + if(sp.has_value()) + { + for(std::size_t i=0; i(table_type()); + auto res = parse_table(loc, ctx, tmp); + if(res.is_err()) + { + ctx.report_error(res.unwrap_err()); + skip_until_next_table(loc, ctx); + } + continue; + } + + auto tab_ptr = inserted.unwrap(); + assert(tab_ptr); + + const auto tab_res = parse_table(loc, ctx, *tab_ptr); + if(tab_res.is_err()) + { + ctx.report_error(tab_res.unwrap_err()); + skip_until_next_table(loc, ctx); + } + + // parse_table first clears `indent_type`. + // to keep header indent info, we must store it later. + if(sp.has_value() && sp.value().indent_type != indent_char::none) + { + tab_ptr->as_table_fmt().indent_type = sp.value().indent_type; + tab_ptr->as_table_fmt().name_indent = sp.value().indent; + } + continue; + } + if(auto key_res = parse_table_key(loc, ctx)) + { + auto key = std::move(std::get<0>(key_res.unwrap())); + auto reg = std::move(std::get<1>(key_res.unwrap())); + + std::vector com; + if(sp.has_value()) + { + for(std::size_t i=0; i(table_type()); + auto res = parse_table(loc, ctx, tmp); + if(res.is_err()) + { + ctx.report_error(res.unwrap_err()); + skip_until_next_table(loc, ctx); + } + continue; + } + + auto tab_ptr = inserted.unwrap(); + assert(tab_ptr); + + const auto tab_res = parse_table(loc, ctx, *tab_ptr); + if(tab_res.is_err()) + { + ctx.report_error(tab_res.unwrap_err()); + skip_until_next_table(loc, ctx); + } + if(sp.has_value() && sp.value().indent_type != indent_char::none) + { + tab_ptr->as_table_fmt().indent_type = sp.value().indent_type; + tab_ptr->as_table_fmt().name_indent = sp.value().indent; + } + continue; + } + + // does not match array_table nor std_table. report an error. + const auto keytop = loc; + const auto maybe_array_of_tables = literal("[[").scan(loc).is_ok(); + loc = keytop; + + if(maybe_array_of_tables) + { + ctx.report_error(make_syntax_error("toml::parse_file: invalid array-table key", + syntax::array_table(spec), loc)); + } + else + { + ctx.report_error(make_syntax_error("toml::parse_file: invalid table key", + syntax::std_table(spec), loc)); + } + skip_until_next_table(loc, ctx); + } + + if( ! ctx.errors().empty()) + { + return err(std::move(ctx.errors())); + } + return ok(std::move(root)); +} + +template +result, std::vector> +parse_impl(std::vector cs, std::string fname, const spec& s) +{ + using value_type = basic_value; + using table_type = typename value_type::table_type; + + // an empty file is a valid toml file. + if(cs.empty()) + { + auto src = std::make_shared>(std::move(cs)); + location loc(std::move(src), std::move(fname)); + return ok(value_type(table_type(), table_format_info{}, std::vector{}, region(loc))); + } + + // to simplify parser, add newline at the end if there is no LF. + // But, if it has raw CR, the file is invalid (in TOML, CR is not a valid + // newline char). if it ends with CR, do not add LF and report it. + if(cs.back() != '\n' && cs.back() != '\r') + { + cs.push_back('\n'); + } + + auto src = std::make_shared>(std::move(cs)); + + location loc(std::move(src), std::move(fname)); + + // skip BOM if found + if(loc.source()->size() >= 3) + { + auto first = loc.get_location(); + + const auto c0 = loc.current(); loc.advance(); + const auto c1 = loc.current(); loc.advance(); + const auto c2 = loc.current(); loc.advance(); + + const auto bom_found = (c0 == 0xEF) && (c1 == 0xBB) && (c2 == 0xBF); + if( ! bom_found) + { + loc.set_location(first); + } + } + + context ctx(s); + + return parse_file(loc, ctx); +} + +} // detail + +// ----------------------------------------------------------------------------- +// parse(byte array) + +template +result, std::vector> +try_parse(std::vector content, std::string filename, + spec s = spec::default_version()) +{ + return detail::parse_impl(std::move(content), std::move(filename), std::move(s)); +} +template +basic_value +parse(std::vector content, std::string filename, + spec s = spec::default_version()) +{ + auto res = try_parse(std::move(content), std::move(filename), std::move(s)); + if(res.is_ok()) + { + return res.unwrap(); + } + else + { + std::string msg; + for(const auto& err : res.unwrap_err()) + { + msg += format_error(err); + } + throw syntax_error(std::move(msg), std::move(res.unwrap_err())); + } +} + +// ----------------------------------------------------------------------------- +// parse(istream) + +template +result, std::vector> +try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()) +{ + const auto beg = is.tellg(); + is.seekg(0, std::ios::end); + const auto end = is.tellg(); + const auto fsize = end - beg; + is.seekg(beg); + + // read whole file as a sequence of char + assert(fsize >= 0); + std::vector letters(static_cast(fsize), '\0'); + is.read(reinterpret_cast(letters.data()), static_cast(fsize)); + + return detail::parse_impl(std::move(letters), std::move(fname), std::move(s)); +} + +template +basic_value parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()) +{ + auto res = try_parse(is, std::move(fname), std::move(s)); + if(res.is_ok()) + { + return res.unwrap(); + } + else + { + std::string msg; + for(const auto& err : res.unwrap_err()) + { + msg += format_error(err); + } + throw syntax_error(std::move(msg), std::move(res.unwrap_err())); + } +} + +// ----------------------------------------------------------------------------- +// parse(filename) + +template +result, std::vector> +try_parse(std::string fname, spec s = spec::default_version()) +{ + std::ifstream ifs(fname, std::ios_base::binary); + if(!ifs.good()) + { + std::vector e; + e.push_back(error_info("toml::parse: Error opening file \"" + fname + "\"", {})); + return err(std::move(e)); + } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + return try_parse(ifs, std::move(fname), std::move(s)); +} + +template +basic_value parse(std::string fname, spec s = spec::default_version()) +{ + std::ifstream ifs(fname, std::ios_base::binary); + if(!ifs.good()) + { + throw file_io_error("toml::parse: error opening file", fname); + } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + return parse(ifs, std::move(fname), std::move(s)); +} + +template +result, std::vector> +try_parse(const char (&fname)[N], spec s = spec::default_version()) +{ + return try_parse(std::string(fname), std::move(s)); +} + +template +basic_value parse(const char (&fname)[N], spec s = spec::default_version()) +{ + return parse(std::string(fname), std::move(s)); +} + +// ---------------------------------------------------------------------------- +// parse_str + +template +result, std::vector> +try_parse_str(std::string content, spec s = spec::default_version(), + cxx::source_location loc = cxx::source_location::current()) +{ + std::istringstream iss(std::move(content)); + std::string name("internal string" + cxx::to_string(loc)); + return try_parse(iss, std::move(name), std::move(s)); +} + +template +basic_value parse_str(std::string content, spec s = spec::default_version(), + cxx::source_location loc = cxx::source_location::current()) +{ + auto res = try_parse_str(std::move(content), std::move(s), std::move(loc)); + if(res.is_ok()) + { + return res.unwrap(); + } + else + { + std::string msg; + for(const auto& err : res.unwrap_err()) + { + msg += format_error(err); + } + throw syntax_error(std::move(msg), std::move(res.unwrap_err())); + } +} + +// ---------------------------------------------------------------------------- +// filesystem + +#if defined(TOML11_HAS_FILESYSTEM) + +template +cxx::enable_if_t::value, + result, std::vector>> +try_parse(const FSPATH& fpath, spec s = spec::default_version()) +{ + std::ifstream ifs(fpath, std::ios_base::binary); + if(!ifs.good()) + { + std::vector e; + e.push_back(error_info("toml::parse: Error opening file \"" + fpath.string() + "\"", {})); + return err(std::move(e)); + } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + return try_parse(ifs, fpath.string(), std::move(s)); +} + +template +cxx::enable_if_t::value, + basic_value> +parse(const FSPATH& fpath, spec s = spec::default_version()) +{ + std::ifstream ifs(fpath, std::ios_base::binary); + if(!ifs.good()) + { + throw file_io_error("toml::parse: error opening file", fpath.string()); + } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + return parse(ifs, fpath.string(), std::move(s)); +} +#endif + +// ----------------------------------------------------------------------------- +// FILE* + +template +result, std::vector> +try_parse(FILE* fp, std::string filename, spec s = spec::default_version()) +{ + const long beg = std::ftell(fp); + if (beg == -1L) + { + return err(std::vector{error_info( + std::string("Failed to access: \"") + filename + + "\", errno = " + std::to_string(errno), {} + )}); + } + + const int res_seekend = std::fseek(fp, 0, SEEK_END); + if (res_seekend != 0) + { + return err(std::vector{error_info( + std::string("Failed to seek: \"") + filename + + "\", errno = " + std::to_string(errno), {} + )}); + } + + const long end = std::ftell(fp); + if (end == -1L) + { + return err(std::vector{error_info( + std::string("Failed to access: \"") + filename + + "\", errno = " + std::to_string(errno), {} + )}); + } + + const auto fsize = end - beg; + + const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET); + if (res_seekbeg != 0) + { + return err(std::vector{error_info( + std::string("Failed to seek: \"") + filename + + "\", errno = " + std::to_string(errno), {} + )}); + + } + + // read whole file as a sequence of char + assert(fsize >= 0); + std::vector letters(static_cast(fsize)); + const auto actual = std::fread(letters.data(), sizeof(char), static_cast(fsize), fp); + if(actual != static_cast(fsize)) + { + return err(std::vector{error_info( + std::string("File size changed: \"") + filename + + std::string("\" make sure that FILE* is in binary mode " + "to avoid LF <-> CRLF conversion"), {} + )}); + } + + return detail::parse_impl(std::move(letters), std::move(filename), std::move(s)); +} + +template +basic_value +parse(FILE* fp, std::string filename, spec s = spec::default_version()) +{ + const long beg = std::ftell(fp); + if (beg == -1L) + { + throw file_io_error(errno, "Failed to access", filename); + } + + const int res_seekend = std::fseek(fp, 0, SEEK_END); + if (res_seekend != 0) + { + throw file_io_error(errno, "Failed to seek", filename); + } + + const long end = std::ftell(fp); + if (end == -1L) + { + throw file_io_error(errno, "Failed to access", filename); + } + + const auto fsize = end - beg; + + const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET); + if (res_seekbeg != 0) + { + throw file_io_error(errno, "Failed to seek", filename); + } + + // read whole file as a sequence of char + assert(fsize >= 0); + std::vector letters(static_cast(fsize)); + const auto actual = std::fread(letters.data(), sizeof(char), static_cast(fsize), fp); + if(actual != static_cast(fsize)) + { + throw file_io_error(errno, "File size changed; make sure that " + "FILE* is in binary mode to avoid LF <-> CRLF conversion", filename); + } + + auto res = detail::parse_impl(std::move(letters), std::move(filename), std::move(s)); + if(res.is_ok()) + { + return res.unwrap(); + } + else + { + std::string msg; + for(const auto& err : res.unwrap_err()) + { + msg += format_error(err); + } + throw syntax_error(std::move(msg), std::move(res.unwrap_err())); + } +} + +} // namespace toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +struct type_config; +struct ordered_type_config; + +extern template result, std::vector> try_parse(std::vector, std::string, spec); +extern template result, std::vector> try_parse(std::istream&, std::string, spec); +extern template result, std::vector> try_parse(std::string, spec); +extern template result, std::vector> try_parse(FILE*, std::string, spec); +extern template result, std::vector> try_parse_str(std::string, spec, cxx::source_location); + +extern template basic_value parse(std::vector, std::string, spec); +extern template basic_value parse(std::istream&, std::string, spec); +extern template basic_value parse(std::string, spec); +extern template basic_value parse(FILE*, std::string, spec); +extern template basic_value parse_str(std::string, spec, cxx::source_location); + +extern template result, std::vector> try_parse(std::vector, std::string, spec); +extern template result, std::vector> try_parse(std::istream&, std::string, spec); +extern template result, std::vector> try_parse(std::string, spec); +extern template result, std::vector> try_parse(FILE*, std::string, spec); +extern template result, std::vector> try_parse_str(std::string, spec, cxx::source_location); + +extern template basic_value parse(std::vector, std::string, spec); +extern template basic_value parse(std::istream&, std::string, spec); +extern template basic_value parse(std::string, spec); +extern template basic_value parse(FILE*, std::string, spec); +extern template basic_value parse_str(std::string, spec, cxx::source_location); + +#if defined(TOML11_HAS_FILESYSTEM) +extern template cxx::enable_if_t::value, result, std::vector>> try_parse(const std::filesystem::path&, spec); +extern template cxx::enable_if_t::value, result, std::vector>> try_parse(const std::filesystem::path&, spec); +extern template cxx::enable_if_t::value, basic_value > parse (const std::filesystem::path&, spec); +extern template cxx::enable_if_t::value, basic_value > parse (const std::filesystem::path&, spec); +#endif // filesystem + +} // toml +#endif // TOML11_COMPILE_SOURCES + +#endif // TOML11_PARSER_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/region.hpp b/src/frontend/qt_sdl/toml/toml11/region.hpp new file mode 100644 index 00000000..b48300c0 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/region.hpp @@ -0,0 +1,10 @@ +#ifndef TOML11_REGION_HPP +#define TOML11_REGION_HPP + +#include "fwd/region_fwd.hpp" // IWYU pragma: export + +#if ! defined(TOML11_COMPILE_SOURCES) +#include "impl/region_impl.hpp" // IWYU pragma: export +#endif + +#endif // TOML11_REGION_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/result.hpp b/src/frontend/qt_sdl/toml/toml11/result.hpp new file mode 100644 index 00000000..e847c4b9 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/result.hpp @@ -0,0 +1,486 @@ +#ifndef TOML11_RESULT_HPP +#define TOML11_RESULT_HPP + +#include "compat.hpp" +#include "exception.hpp" + +#include +#include +#include +#include + +#include + +namespace toml +{ + +struct bad_result_access final : public ::toml::exception +{ + public: + explicit bad_result_access(std::string what_arg) + : what_(std::move(what_arg)) + {} + ~bad_result_access() noexcept override = default; + const char* what() const noexcept override {return what_.c_str();} + + private: + std::string what_; +}; + +// ----------------------------------------------------------------------------- + +template +struct success +{ + static_assert( ! std::is_same::value, ""); + + using value_type = T; + + explicit success(value_type v) + noexcept(std::is_nothrow_move_constructible::value) + : value(std::move(v)) + {} + + template, T>::value, + std::nullptr_t> = nullptr> + explicit success(U&& v): value(std::forward(v)) {} + + template + explicit success(success v): value(std::move(v.value)) {} + + ~success() = default; + success(const success&) = default; + success(success&&) = default; + success& operator=(const success&) = default; + success& operator=(success&&) = default; + + value_type& get() noexcept {return value;} + value_type const& get() const noexcept {return value;} + + private: + + value_type value; +}; + +template +struct success> +{ + static_assert( ! std::is_same::value, ""); + + using value_type = T; + + explicit success(std::reference_wrapper v) noexcept + : value(std::move(v)) + {} + + ~success() = default; + success(const success&) = default; + success(success&&) = default; + success& operator=(const success&) = default; + success& operator=(success&&) = default; + + value_type& get() noexcept {return value.get();} + value_type const& get() const noexcept {return value.get();} + + private: + + std::reference_wrapper value; +}; + +template +success::type> ok(T&& v) +{ + return success::type>(std::forward(v)); +} +template +success ok(const char (&literal)[N]) +{ + return success(std::string(literal)); +} + +// ----------------------------------------------------------------------------- + +template +struct failure +{ + using value_type = T; + + explicit failure(value_type v) + noexcept(std::is_nothrow_move_constructible::value) + : value(std::move(v)) + {} + + template, T>::value, + std::nullptr_t> = nullptr> + explicit failure(U&& v): value(std::forward(v)) {} + + template + explicit failure(failure v): value(std::move(v.value)) {} + + ~failure() = default; + failure(const failure&) = default; + failure(failure&&) = default; + failure& operator=(const failure&) = default; + failure& operator=(failure&&) = default; + + value_type& get() noexcept {return value;} + value_type const& get() const noexcept {return value;} + + private: + + value_type value; +}; + +template +struct failure> +{ + using value_type = T; + + explicit failure(std::reference_wrapper v) noexcept + : value(std::move(v)) + {} + + ~failure() = default; + failure(const failure&) = default; + failure(failure&&) = default; + failure& operator=(const failure&) = default; + failure& operator=(failure&&) = default; + + value_type& get() noexcept {return value.get();} + value_type const& get() const noexcept {return value.get();} + + private: + + std::reference_wrapper value; +}; + +template +failure::type> err(T&& v) +{ + return failure::type>(std::forward(v)); +} + +template +failure err(const char (&literal)[N]) +{ + return failure(std::string(literal)); +} + +/* ============================================================================ + * _ _ + * _ _ ___ ____ _| | |_ + * | '_/ -_|_-< || | | _| + * |_| \___/__/\_,_|_|\__| + */ + +template +struct result +{ + using success_type = success; + using failure_type = failure; + using value_type = typename success_type::value_type; + using error_type = typename failure_type::value_type; + + result(success_type s): is_ok_(true), succ_(std::move(s)) {} + result(failure_type f): is_ok_(false), fail_(std::move(f)) {} + + template, value_type>>, + std::is_convertible, value_type> + >::value, std::nullptr_t> = nullptr> + result(success s): is_ok_(true), succ_(std::move(s.value)) {} + + template, error_type>>, + std::is_convertible, error_type> + >::value, std::nullptr_t> = nullptr> + result(failure f): is_ok_(false), fail_(std::move(f.value)) {} + + 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 + result& operator=(success 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 + result& operator=(failure 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.succ_); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_); + 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.succ_)); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_)); + 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.succ_); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_); + 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.succ_)); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_)); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + is_ok_ = other.is_ok(); + return *this; + } + + template, value_type>>, + cxx::negation, error_type>>, + std::is_convertible, value_type>, + std::is_convertible, error_type> + >::value, std::nullptr_t> = nullptr> + 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, value_type>>, + cxx::negation, error_type>>, + std::is_convertible, value_type>, + std::is_convertible, error_type> + >::value, std::nullptr_t> = nullptr> + 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; + } + + bool is_ok() const noexcept {return is_ok_;} + bool is_err() const noexcept {return !is_ok_;} + + explicit operator bool() const noexcept {return is_ok_;} + + value_type& unwrap(cxx::source_location loc = cxx::source_location::current()) + { + if(this->is_err()) + { + throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); + } + return this->succ_.get(); + } + value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const + { + if(this->is_err()) + { + throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); + } + return this->succ_.get(); + } + + value_type& unwrap_or(value_type& opt) noexcept + { + if(this->is_err()) {return opt;} + return this->succ_.get(); + } + value_type const& unwrap_or(value_type const& opt) const noexcept + { + if(this->is_err()) {return opt;} + return this->succ_.get(); + } + + error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current()) + { + if(this->is_ok()) + { + throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); + } + return this->fail_.get(); + } + error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const + { + if(this->is_ok()) + { + throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); + } + return this->fail_.get(); + } + + value_type& as_ok() noexcept + { + assert(this->is_ok()); + return this->succ_.get(); + } + value_type const& as_ok() const noexcept + { + assert(this->is_ok()); + return this->succ_.get(); + } + + error_type& as_err() noexcept + { + assert(this->is_err()); + return this->fail_.get(); + } + error_type const& as_err() const noexcept + { + assert(this->is_err()); + return this->fail_.get(); + } + + private: + + void cleanup() noexcept + { +#if defined(__GNUC__) && ! defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wduplicated-branches" +#endif + + if(this->is_ok_) {this->succ_.~success_type();} + else {this->fail_.~failure_type();} + +#if defined(__GNUC__) && ! defined(__clang__) +#pragma GCC diagnostic pop +#endif + return; + } + + private: + + bool is_ok_; + union + { + success_type succ_; + failure_type fail_; + }; +}; + +// ---------------------------------------------------------------------------- + +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;} +inline std::ostream& operator<<(std::ostream& os, const none_t&) +{ + os << "none"; + return os; +} +} // detail + +inline success ok() noexcept +{ + return success(detail::none_t{}); +} +inline failure err() noexcept +{ + return failure(detail::none_t{}); +} + +} // toml +#endif // TOML11_RESULT_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/scanner.hpp b/src/frontend/qt_sdl/toml/toml11/scanner.hpp new file mode 100644 index 00000000..85812d9a --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/scanner.hpp @@ -0,0 +1,10 @@ +#ifndef TOML11_SCANNER_HPP +#define TOML11_SCANNER_HPP + +#include "fwd/scanner_fwd.hpp" // IWYU pragma: export + +#if ! defined(TOML11_COMPILE_SOURCES) +#include "impl/scanner_impl.hpp" // IWYU pragma: export +#endif + +#endif // TOML11_SCANNER_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/serializer.hpp b/src/frontend/qt_sdl/toml/toml11/serializer.hpp new file mode 100644 index 00000000..a3f148e2 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/serializer.hpp @@ -0,0 +1,1275 @@ +#ifndef TOML11_SERIALIZER_HPP +#define TOML11_SERIALIZER_HPP + +#include "comments.hpp" +#include "error_info.hpp" +#include "exception.hpp" +#include "source_location.hpp" +#include "spec.hpp" +#include "syntax.hpp" +#include "types.hpp" +#include "utility.hpp" +#include "value.hpp" + +#include +#include +#include + +#include +#include + +namespace toml +{ + +struct serialization_error final : public ::toml::exception +{ + public: + explicit serialization_error(std::string what_arg, source_location loc) + : what_(std::move(what_arg)), loc_(std::move(loc)) + {} + ~serialization_error() noexcept override = default; + + const char* what() const noexcept override {return what_.c_str();} + source_location const& location() const noexcept {return loc_;} + + private: + std::string what_; + source_location loc_; +}; + +namespace detail +{ +template +class serializer +{ + public: + + using value_type = basic_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 ; + + using char_type = typename string_type::value_type; + + public: + + explicit serializer(const spec& sp) + : spec_(sp), force_inline_(false), current_indent_(0) + {} + + string_type operator()(const std::vector& ks, const value_type& v) + { + for(const auto& k : ks) + { + this->keys_.push_back(k); + } + return (*this)(v); + } + + string_type operator()(const key_type& k, const value_type& v) + { + this->keys_.push_back(k); + return (*this)(v); + } + + string_type operator()(const value_type& v) + { + switch(v.type()) + { + case value_t::boolean : {return (*this)(v.as_boolean (), v.as_boolean_fmt (), v.location());} + case value_t::integer : {return (*this)(v.as_integer (), v.as_integer_fmt (), v.location());} + case value_t::floating : {return (*this)(v.as_floating (), v.as_floating_fmt (), v.location());} + case value_t::string : {return (*this)(v.as_string (), v.as_string_fmt (), v.location());} + case value_t::offset_datetime: {return (*this)(v.as_offset_datetime(), v.as_offset_datetime_fmt(), v.location());} + case value_t::local_datetime : {return (*this)(v.as_local_datetime (), v.as_local_datetime_fmt (), v.location());} + case value_t::local_date : {return (*this)(v.as_local_date (), v.as_local_date_fmt (), v.location());} + case value_t::local_time : {return (*this)(v.as_local_time (), v.as_local_time_fmt (), v.location());} + case value_t::array : + { + return (*this)(v.as_array(), v.as_array_fmt(), v.comments(), v.location()); + } + case value_t::table : + { + string_type retval; + if(this->keys_.empty()) // it might be the root table. emit comments here. + { + retval += format_comments(v.comments(), v.as_table_fmt().indent_type); + } + if( ! retval.empty()) // we have comment. + { + retval += char_type('\n'); + } + + retval += (*this)(v.as_table(), v.as_table_fmt(), v.comments(), v.location()); + return retval; + } + case value_t::empty: + { + if(this->spec_.ext_null_value) + { + return string_conv("null"); + } + break; + } + default: + { + break; + } + } + throw serialization_error(format_error( + "[error] toml::serializer: toml::basic_value " + "does not have any valid type.", v.location(), "here"), v.location()); + } + + private: + + string_type operator()(const boolean_type& b, const boolean_format_info&, const source_location&) // {{{ + { + if(b) + { + return string_conv("true"); + } + else + { + return string_conv("false"); + } + } // }}} + + string_type operator()(const integer_type i, const integer_format_info& fmt, const source_location& loc) // {{{ + { + std::ostringstream oss; + this->set_locale(oss); + + const auto insert_spacer = [&fmt](std::string s) -> std::string { + if(fmt.spacer == 0) {return s;} + + std::string sign; + if( ! s.empty() && (s.at(0) == '+' || s.at(0) == '-')) + { + sign += s.at(0); + s.erase(s.begin()); + } + + std::string spaced; + std::size_t counter = 0; + for(auto iter = s.rbegin(); iter != s.rend(); ++iter) + { + if(counter != 0 && counter % fmt.spacer == 0) + { + spaced += '_'; + } + spaced += *iter; + counter += 1; + } + if(!spaced.empty() && spaced.back() == '_') {spaced.pop_back();} + + s.clear(); + std::copy(spaced.rbegin(), spaced.rend(), std::back_inserter(s)); + return sign + s; + }; + + std::string retval; + if(fmt.fmt == integer_format::dec) + { + oss << std::setw(static_cast(fmt.width)) << std::dec << i; + retval = insert_spacer(oss.str()); + + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + retval += '_'; + retval += fmt.suffix; + } + } + else + { + if(i < 0) + { + throw serialization_error(format_error("binary, octal, hexadecimal " + "integer does not allow negative value", loc, "here"), loc); + } + switch(fmt.fmt) + { + case integer_format::hex: + { + oss << std::noshowbase + << std::setw(static_cast(fmt.width)) + << std::setfill('0') + << std::hex; + if(fmt.uppercase) + { + oss << std::uppercase; + } + else + { + oss << std::nouppercase; + } + oss << i; + retval = std::string("0x") + insert_spacer(oss.str()); + break; + } + case integer_format::oct: + { + oss << std::setw(static_cast(fmt.width)) << std::setfill('0') << std::oct << i; + retval = std::string("0o") + insert_spacer(oss.str()); + break; + } + case integer_format::bin: + { + integer_type x{i}; + std::string tmp; + std::size_t bits(0); + while(x != 0) + { + if(fmt.spacer != 0) + { + if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';} + } + if(x % 2 == 1) { tmp += '1'; } else { tmp += '0'; } + x >>= 1; + bits += 1; + } + for(; bits < fmt.width; ++bits) + { + if(fmt.spacer != 0) + { + if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';} + } + tmp += '0'; + } + for(auto iter = tmp.rbegin(); iter != tmp.rend(); ++iter) + { + oss << *iter; + } + retval = std::string("0b") + oss.str(); + break; + } + default: + { + throw serialization_error(format_error( + "none of dec, hex, oct, bin: " + to_string(fmt.fmt), + loc, "here"), loc); + } + } + } + return string_conv(retval); + } // }}} + + string_type operator()(const floating_type f, const floating_format_info& fmt, const source_location&) // {{{ + { + using std::isnan; + using std::isinf; + using std::signbit; + + std::ostringstream oss; + this->set_locale(oss); + + if(isnan(f)) + { + if(signbit(f)) + { + oss << '-'; + } + oss << "nan"; + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_'; + oss << fmt.suffix; + } + return string_conv(oss.str()); + } + + if(isinf(f)) + { + if(signbit(f)) + { + oss << '-'; + } + oss << "inf"; + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_'; + oss << fmt.suffix; + } + return string_conv(oss.str()); + } + + switch(fmt.fmt) + { + case floating_format::defaultfloat: + { + if(fmt.prec != 0) + { + oss << std::setprecision(static_cast(fmt.prec)); + } + oss << f; + // since defaultfloat may omit point, we need to add it + std::string s = oss.str(); + if (s.find('.') == std::string::npos && + s.find('e') == std::string::npos && + s.find('E') == std::string::npos ) + { + s += ".0"; + } + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + s += '_'; + s += fmt.suffix; + } + return string_conv(s); + } + case floating_format::fixed: + { + if(fmt.prec != 0) + { + oss << std::setprecision(static_cast(fmt.prec)); + } + oss << std::fixed << f; + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_' << fmt.suffix; + } + return string_conv(oss.str()); + } + case floating_format::scientific: + { + if(fmt.prec != 0) + { + oss << std::setprecision(static_cast(fmt.prec)); + } + oss << std::scientific << f; + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_' << fmt.suffix; + } + return string_conv(oss.str()); + } + case floating_format::hex: + { + if(this->spec_.ext_hex_float) + { + oss << std::hexfloat << f; + // suffix is only for decimal numbers. + return string_conv(oss.str()); + } + else // no hex allowed. output with max precision. + { + oss << std::setprecision(std::numeric_limits::max_digits10) + << std::scientific << f; + // suffix is only for decimal numbers. + return string_conv(oss.str()); + } + } + default: + { + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_' << fmt.suffix; + } + return string_conv(oss.str()); + } + } + } // }}} + + string_type operator()(string_type s, const string_format_info& fmt, const source_location& loc) // {{{ + { + string_type retval; + switch(fmt.fmt) + { + case string_format::basic: + { + retval += char_type('"'); + retval += this->escape_basic_string(s); + retval += char_type('"'); + return retval; + } + case string_format::literal: + { + if(std::find(s.begin(), s.end(), char_type('\n')) != s.end()) + { + throw serialization_error(format_error("toml::serializer: " + "(non-multiline) literal string cannot have a newline", + loc, "here"), loc); + } + retval += char_type('\''); + retval += s; + retval += char_type('\''); + return retval; + } + case string_format::multiline_basic: + { + retval += string_conv("\"\"\""); + if(fmt.start_with_newline) + { + retval += char_type('\n'); + } + + retval += this->escape_ml_basic_string(s); + + retval += string_conv("\"\"\""); + return retval; + } + case string_format::multiline_literal: + { + retval += string_conv("'''"); + if(fmt.start_with_newline) + { + retval += char_type('\n'); + } + retval += s; + retval += string_conv("'''"); + return retval; + } + default: + { + throw serialization_error(format_error( + "[error] toml::serializer::operator()(string): " + "invalid string_format value", loc, "here"), loc); + } + } + } // }}} + + string_type operator()(const local_date_type& d, const local_date_format_info&, const source_location&) // {{{ + { + std::ostringstream oss; + oss << d; + return string_conv(oss.str()); + } // }}} + + string_type operator()(const local_time_type& t, const local_time_format_info& fmt, const source_location&) // {{{ + { + return this->format_local_time(t, fmt.has_seconds, fmt.subsecond_precision); + } // }}} + + string_type operator()(const local_datetime_type& dt, const local_datetime_format_info& fmt, const source_location&) // {{{ + { + std::ostringstream oss; + oss << dt.date; + switch(fmt.delimiter) + { + case datetime_delimiter_kind::upper_T: { oss << 'T'; break; } + case datetime_delimiter_kind::lower_t: { oss << 't'; break; } + case datetime_delimiter_kind::space: { oss << ' '; break; } + default: { oss << 'T'; break; } + } + return string_conv(oss.str()) + + this->format_local_time(dt.time, fmt.has_seconds, fmt.subsecond_precision); + } // }}} + + string_type operator()(const offset_datetime_type& odt, const offset_datetime_format_info& fmt, const source_location&) // {{{ + { + std::ostringstream oss; + oss << odt.date; + switch(fmt.delimiter) + { + case datetime_delimiter_kind::upper_T: { oss << 'T'; break; } + case datetime_delimiter_kind::lower_t: { oss << 't'; break; } + case datetime_delimiter_kind::space: { oss << ' '; break; } + default: { oss << 'T'; break; } + } + oss << string_conv(this->format_local_time(odt.time, fmt.has_seconds, fmt.subsecond_precision)); + oss << odt.offset; + return string_conv(oss.str()); + } // }}} + + string_type operator()(const array_type& a, const array_format_info& fmt, const comment_type& com, const source_location& loc) // {{{ + { + array_format f = fmt.fmt; + if(fmt.fmt == array_format::default_format) + { + // [[in.this.form]], you cannot add a comment to the array itself + // (but you can add a comment to each table). + // To keep comments, we need to avoid multiline array-of-tables + // if array itself has a comment. + if( ! this->keys_.empty() && + ! a.empty() && + com.empty() && + std::all_of(a.begin(), a.end(), [](const value_type& e) {return e.is_table();})) + { + f = array_format::array_of_tables; + } + else + { + f = array_format::oneline; + + // check if it becomes long + std::size_t approx_len = 0; + for(const auto& e : a) + { + // have a comment. cannot be inlined + if( ! e.comments().empty()) + { + f = array_format::multiline; + break; + } + // possibly long types ... + if(e.is_array() || e.is_table() || e.is_offset_datetime() || e.is_local_datetime()) + { + f = array_format::multiline; + break; + } + else if(e.is_boolean()) + { + approx_len += (*this)(e.as_boolean(), e.as_boolean_fmt(), e.location()).size(); + } + else if(e.is_integer()) + { + approx_len += (*this)(e.as_integer(), e.as_integer_fmt(), e.location()).size(); + } + else if(e.is_floating()) + { + approx_len += (*this)(e.as_floating(), e.as_floating_fmt(), e.location()).size(); + } + else if(e.is_string()) + { + if(e.as_string_fmt().fmt == string_format::multiline_basic || + e.as_string_fmt().fmt == string_format::multiline_literal) + { + f = array_format::multiline; + break; + } + approx_len += 2 + (*this)(e.as_string(), e.as_string_fmt(), e.location()).size(); + } + else if(e.is_local_date()) + { + approx_len += 10; // 1234-56-78 + } + else if(e.is_local_time()) + { + approx_len += 15; // 12:34:56.789012 + } + + if(approx_len > 60) // key, ` = `, `[...]` < 80 + { + f = array_format::multiline; + break; + } + approx_len += 2; // `, ` + } + } + } + if(this->force_inline_ && f == array_format::array_of_tables) + { + f = array_format::multiline; + } + if(a.empty() && f == array_format::array_of_tables) + { + f = array_format::oneline; + } + + // -------------------------------------------------------------------- + + if(f == array_format::array_of_tables) + { + if(this->keys_.empty()) + { + throw serialization_error("array of table must have its key. " + "use format(key, v)", loc); + } + string_type retval; + for(const auto& e : a) + { + assert(e.is_table()); + + this->current_indent_ += e.as_table_fmt().name_indent; + retval += this->format_comments(e.comments(), e.as_table_fmt().indent_type); + retval += this->format_indent(e.as_table_fmt().indent_type); + this->current_indent_ -= e.as_table_fmt().name_indent; + + retval += string_conv("[["); + retval += this->format_keys(this->keys_).value(); + retval += string_conv("]]\n"); + + retval += this->format_ml_table(e.as_table(), e.as_table_fmt()); + } + return retval; + } + else if(f == array_format::oneline) + { + // ignore comments. we cannot emit comments + string_type retval; + retval += char_type('['); + for(const auto& e : a) + { + this->force_inline_ = true; + retval += (*this)(e); + retval += string_conv(", "); + } + if( ! a.empty()) + { + retval.pop_back(); // ` ` + retval.pop_back(); // `,` + } + retval += char_type(']'); + this->force_inline_ = false; + return retval; + } + else + { + assert(f == array_format::multiline); + + string_type retval; + retval += string_conv("[\n"); + + for(const auto& e : a) + { + this->current_indent_ += fmt.body_indent; + retval += this->format_comments(e.comments(), fmt.indent_type); + retval += this->format_indent(fmt.indent_type); + this->current_indent_ -= fmt.body_indent; + + this->force_inline_ = true; + retval += (*this)(e); + retval += string_conv(",\n"); + } + this->force_inline_ = false; + + this->current_indent_ += fmt.closing_indent; + retval += this->format_indent(fmt.indent_type); + this->current_indent_ -= fmt.closing_indent; + + retval += char_type(']'); + return retval; + } + } // }}} + + string_type operator()(const table_type& t, const table_format_info& fmt, const comment_type& com, const source_location& loc) // {{{ + { + if(this->force_inline_) + { + if(fmt.fmt == table_format::multiline_oneline) + { + return this->format_ml_inline_table(t, fmt); + } + else + { + return this->format_inline_table(t, fmt); + } + } + else + { + if(fmt.fmt == table_format::multiline) + { + string_type retval; + // comment is emitted inside format_ml_table + if(auto k = this->format_keys(this->keys_)) + { + this->current_indent_ += fmt.name_indent; + retval += this->format_comments(com, fmt.indent_type); + retval += this->format_indent(fmt.indent_type); + this->current_indent_ -= fmt.name_indent; + retval += char_type('['); + retval += k.value(); + retval += string_conv("]\n"); + } + // otherwise, its the root. + + retval += this->format_ml_table(t, fmt); + return retval; + } + else if(fmt.fmt == table_format::oneline) + { + return this->format_inline_table(t, fmt); + } + else if(fmt.fmt == table_format::multiline_oneline) + { + return this->format_ml_inline_table(t, fmt); + } + else if(fmt.fmt == table_format::dotted) + { + std::vector keys; + if(this->keys_.empty()) + { + throw serialization_error(format_error("toml::serializer: " + "dotted table must have its key. use format(key, v)", + loc, "here"), loc); + } + keys.push_back(this->keys_.back()); + + const auto retval = this->format_dotted_table(t, fmt, loc, keys); + keys.pop_back(); + return retval; + } + else + { + assert(fmt.fmt == table_format::implicit); + + string_type retval; + for(const auto& kv : t) + { + const auto& k = kv.first; + const auto& v = kv.second; + + if( ! v.is_table() && ! v.is_array_of_tables()) + { + throw serialization_error(format_error("toml::serializer: " + "an implicit table cannot have non-table value.", + v.location(), "here"), v.location()); + } + if(v.is_table()) + { + if(v.as_table_fmt().fmt != table_format::multiline && + v.as_table_fmt().fmt != table_format::implicit) + { + throw serialization_error(format_error("toml::serializer: " + "an implicit table cannot have non-multiline table", + v.location(), "here"), v.location()); + } + } + else + { + assert(v.is_array()); + for(const auto& e : v.as_array()) + { + if(e.as_table_fmt().fmt != table_format::multiline && + v.as_table_fmt().fmt != table_format::implicit) + { + throw serialization_error(format_error("toml::serializer: " + "an implicit table cannot have non-multiline table", + e.location(), "here"), e.location()); + } + } + } + + keys_.push_back(k); + retval += (*this)(v); + keys_.pop_back(); + } + return retval; + } + } + } // }}} + + private: + + string_type escape_basic_string(const string_type& s) const // {{{ + { + string_type retval; + for(const char_type c : s) + { + switch(c) + { + case char_type('\\'): {retval += string_conv("\\\\"); break;} + case char_type('\"'): {retval += string_conv("\\\""); break;} + case char_type('\b'): {retval += string_conv("\\b" ); break;} + case char_type('\t'): {retval += string_conv("\\t" ); break;} + case char_type('\f'): {retval += string_conv("\\f" ); break;} + case char_type('\n'): {retval += string_conv("\\n" ); break;} + case char_type('\r'): {retval += string_conv("\\r" ); break;} + default : + { + if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e) + { + retval += string_conv("\\e"); + } + else if((char_type(0x00) <= c && c <= char_type(0x08)) || + (char_type(0x0A) <= c && c <= char_type(0x1F)) || + c == char_type(0x7F)) + { + if(spec_.v1_1_0_add_escape_sequence_x) + { + retval += string_conv("\\x"); + } + else + { + retval += string_conv("\\u00"); + } + const auto c1 = c / 16; + const auto c2 = c % 16; + retval += static_cast('0' + c1); + if(c2 < 10) + { + retval += static_cast('0' + c2); + } + else // 10 <= c2 + { + retval += static_cast('A' + (c2 - 10)); + } + } + else + { + retval += c; + } + } + } + } + return retval; + } // }}} + + string_type escape_ml_basic_string(const string_type& s) // {{{ + { + string_type retval; + for(const char_type c : s) + { + switch(c) + { + case char_type('\\'): {retval += string_conv("\\\\"); break;} + case char_type('\b'): {retval += string_conv("\\b" ); break;} + case char_type('\t'): {retval += string_conv("\\t" ); break;} + case char_type('\f'): {retval += string_conv("\\f" ); break;} + case char_type('\n'): {retval += string_conv("\n" ); break;} + case char_type('\r'): {retval += string_conv("\\r" ); break;} + default : + { + if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e) + { + retval += string_conv("\\e"); + } + else if((char_type(0x00) <= c && c <= char_type(0x08)) || + (char_type(0x0A) <= c && c <= char_type(0x1F)) || + c == char_type(0x7F)) + { + if(spec_.v1_1_0_add_escape_sequence_x) + { + retval += string_conv("\\x"); + } + else + { + retval += string_conv("\\u00"); + } + const auto c1 = c / 16; + const auto c2 = c % 16; + retval += static_cast('0' + c1); + if(c2 < 10) + { + retval += static_cast('0' + c2); + } + else // 10 <= c2 + { + retval += static_cast('A' + (c2 - 10)); + } + } + 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(string_conv("\"\"\"")); + while(found_3_quotes != string_type::npos) + { + retval.replace(found_3_quotes, 3, string_conv("\"\"\\\"")); + found_3_quotes = retval.find(string_conv("\"\"\"")); + } + return retval; + } // }}} + + string_type format_local_time(const local_time_type& t, const bool has_seconds, const std::size_t subsec_prec) // {{{ + { + std::ostringstream oss; + oss << std::setfill('0') << std::setw(2) << static_cast(t.hour); + oss << ':'; + oss << std::setfill('0') << std::setw(2) << static_cast(t.minute); + if(has_seconds) + { + oss << ':'; + oss << std::setfill('0') << std::setw(2) << static_cast(t.second); + if(subsec_prec != 0) + { + std::ostringstream subsec; + subsec << std::setfill('0') << std::setw(3) << static_cast(t.millisecond); + subsec << std::setfill('0') << std::setw(3) << static_cast(t.microsecond); + subsec << std::setfill('0') << std::setw(3) << static_cast(t.nanosecond); + std::string subsec_str = subsec.str(); + oss << '.' << subsec_str.substr(0, subsec_prec); + } + } + return string_conv(oss.str()); + } // }}} + + string_type format_ml_table(const table_type& t, const table_format_info& fmt) // {{{ + { + const auto format_later = [](const value_type& v) -> bool { + + const bool is_ml_table = v.is_table() && + v.as_table_fmt().fmt != table_format::oneline && + v.as_table_fmt().fmt != table_format::multiline_oneline && + v.as_table_fmt().fmt != table_format::dotted ; + + const bool is_ml_array_table = v.is_array_of_tables() && + v.as_array_fmt().fmt != array_format::oneline && + v.as_array_fmt().fmt != array_format::multiline; + + return is_ml_table || is_ml_array_table; + }; + + string_type retval; + this->current_indent_ += fmt.body_indent; + for(const auto& kv : t) + { + const auto& key = kv.first; + const auto& val = kv.second; + if(format_later(val)) + { + continue; + } + this->keys_.push_back(key); + + retval += format_comments(val.comments(), fmt.indent_type); + retval += format_indent(fmt.indent_type); + if(val.is_table() && val.as_table_fmt().fmt == table_format::dotted) + { + retval += (*this)(val); + } + else + { + retval += format_key(key); + retval += string_conv(" = "); + retval += (*this)(val); + retval += char_type('\n'); + } + this->keys_.pop_back(); + } + this->current_indent_ -= fmt.body_indent; + + if( ! retval.empty()) + { + retval += char_type('\n'); // for readability, add empty line between tables + } + for(const auto& kv : t) + { + if( ! format_later(kv.second)) + { + continue; + } + // must be a [multiline.table] or [[multiline.array.of.tables]]. + // comments will be generated inside it. + this->keys_.push_back(kv.first); + retval += (*this)(kv.second); + this->keys_.pop_back(); + } + return retval; + } // }}} + + string_type format_inline_table(const table_type& t, const table_format_info&) // {{{ + { + // comments are ignored because we cannot write without newline + string_type retval; + retval += char_type('{'); + for(const auto& kv : t) + { + this->force_inline_ = true; + retval += this->format_key(kv.first); + retval += string_conv(" = "); + retval += (*this)(kv.second); + retval += string_conv(", "); + } + if( ! t.empty()) + { + retval.pop_back(); // ' ' + retval.pop_back(); // ',' + } + retval += char_type('}'); + this->force_inline_ = false; + return retval; + } // }}} + + string_type format_ml_inline_table(const table_type& t, const table_format_info& fmt) // {{{ + { + string_type retval; + retval += string_conv("{\n"); + this->current_indent_ += fmt.body_indent; + for(const auto& kv : t) + { + this->force_inline_ = true; + retval += format_comments(kv.second.comments(), fmt.indent_type); + retval += format_indent(fmt.indent_type); + retval += kv.first; + retval += string_conv(" = "); + + this->force_inline_ = true; + retval += (*this)(kv.second); + + retval += string_conv(",\n"); + } + if( ! t.empty()) + { + retval.pop_back(); // '\n' + retval.pop_back(); // ',' + } + this->current_indent_ -= fmt.body_indent; + this->force_inline_ = false; + + this->current_indent_ += fmt.closing_indent; + retval += format_indent(fmt.indent_type); + this->current_indent_ -= fmt.closing_indent; + + retval += char_type('}'); + return retval; + } // }}} + + string_type format_dotted_table(const table_type& t, const table_format_info& fmt, // {{{ + const source_location&, std::vector& keys) + { + // lets say we have: `{"a": {"b": {"c": {"d": "foo", "e": "bar"} } }` + // and `a` and `b` are `dotted`. + // + // - in case if `c` is `oneline`: + // ```toml + // a.b.c = {d = "foo", e = "bar"} + // ``` + // + // - in case if and `c` is `dotted`: + // ```toml + // a.b.c.d = "foo" + // a.b.c.e = "bar" + // ``` + + string_type retval; + + for(const auto& kv : t) + { + const auto& key = kv.first; + const auto& val = kv.second; + + keys.push_back(key); + + // format recursive dotted table? + if (val.is_table() && + val.as_table_fmt().fmt != table_format::oneline && + val.as_table_fmt().fmt != table_format::multiline_oneline) + { + retval += this->format_dotted_table(val.as_table(), val.as_table_fmt(), val.location(), keys); + } + else // non-table or inline tables. format normally + { + retval += format_comments(val.comments(), fmt.indent_type); + retval += format_indent(fmt.indent_type); + retval += format_keys(keys).value(); + retval += string_conv(" = "); + this->force_inline_ = true; // sub-table must be inlined + retval += (*this)(val); + retval += char_type('\n'); + this->force_inline_ = false; + } + keys.pop_back(); + } + return retval; + } // }}} + + string_type format_key(const key_type& key) // {{{ + { + if(key.empty()) + { + return string_conv("\"\""); + } + + // check the key can be a bare (unquoted) key + auto loc = detail::make_temporary_location(string_conv(key)); + auto reg = detail::syntax::unquoted_key(this->spec_).scan(loc); + if(reg.is_ok() && loc.eof()) + { + return key; + } + + //if it includes special characters, then format it in a "quoted" key. + string_type formatted = string_conv("\""); + for(const char_type c : key) + { + switch(c) + { + case char_type('\\'): {formatted += string_conv("\\\\"); break;} + case char_type('\"'): {formatted += string_conv("\\\""); break;} + case char_type('\b'): {formatted += string_conv("\\b" ); break;} + case char_type('\t'): {formatted += string_conv("\\t" ); break;} + case char_type('\f'): {formatted += string_conv("\\f" ); break;} + case char_type('\n'): {formatted += string_conv("\\n" ); break;} + case char_type('\r'): {formatted += string_conv("\\r" ); break;} + default : + { + // ASCII ctrl char + if( (char_type(0x00) <= c && c <= char_type(0x08)) || + (char_type(0x0A) <= c && c <= char_type(0x1F)) || + c == char_type(0x7F)) + { + if(spec_.v1_1_0_add_escape_sequence_x) + { + formatted += string_conv("\\x"); + } + else + { + formatted += string_conv("\\u00"); + } + const auto c1 = c / 16; + const auto c2 = c % 16; + formatted += static_cast('0' + c1); + if(c2 < 10) + { + formatted += static_cast('0' + c2); + } + else // 10 <= c2 + { + formatted += static_cast('A' + (c2 - 10)); + } + } + else + { + formatted += c; + } + break; + } + } + } + formatted += string_conv("\""); + return formatted; + } // }}} + cxx::optional format_keys(const std::vector& keys) // {{{ + { + if(keys.empty()) + { + return cxx::make_nullopt(); + } + + string_type formatted; + for(const auto& ky : keys) + { + formatted += format_key(ky); + formatted += char_type('.'); + } + formatted.pop_back(); // remove the last dot '.' + return formatted; + } // }}} + + string_type format_comments(const discard_comments&, const indent_char) const // {{{ + { + return string_conv(""); + } // }}} + string_type format_comments(const preserve_comments& comments, const indent_char indent_type) const // {{{ + { + string_type retval; + for(const auto& c : comments) + { + if(c.empty()) {continue;} + retval += format_indent(indent_type); + if(c.front() != '#') {retval += char_type('#');} + retval += string_conv(c); + if(c.back() != '\n') {retval += char_type('\n');} + } + return retval; + } // }}} + + string_type format_indent(const indent_char indent_type) const // {{{ + { + const auto indent = static_cast((std::max)(0, this->current_indent_)); + if(indent_type == indent_char::space) + { + return string_conv(make_string(indent, ' ')); + } + else if(indent_type == indent_char::tab) + { + return string_conv(make_string(indent, '\t')); + } + else + { + return string_type{}; + } + } // }}} + + std::locale set_locale(std::ostream& os) const + { + return os.imbue(std::locale::classic()); + } + + private: + + spec spec_; + bool force_inline_; // table inside an array without fmt specification + std::int32_t current_indent_; + std::vector keys_; +}; +} // detail + +template +typename basic_value::string_type +format(const basic_value& v, const spec s = spec::default_version()) +{ + detail::serializer ser(s); + return ser(v); +} +template +typename basic_value::string_type +format(const typename basic_value::key_type& k, + const basic_value& v, + const spec s = spec::default_version()) +{ + detail::serializer ser(s); + return ser(k, v); +} +template +typename basic_value::string_type +format(const std::vector::key_type>& ks, + const basic_value& v, + const spec s = spec::default_version()) +{ + detail::serializer ser(s); + return ser(ks, v); +} + +template +std::ostream& operator<<(std::ostream& os, const basic_value& v) +{ + os << format(v); + return os; +} + +} // toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +struct type_config; +struct ordered_type_config; + +extern template typename basic_value::string_type +format(const basic_value&, const spec); + +extern template typename basic_value::string_type +format(const typename basic_value::key_type& k, + const basic_value& v, const spec); + +extern template typename basic_value::string_type +format(const std::vector::key_type>& ks, + const basic_value& v, const spec s); + +extern template typename basic_value::string_type +format(const basic_value&, const spec); + +extern template typename basic_value::string_type +format(const typename basic_value::key_type& k, + const basic_value& v, const spec); + +extern template typename basic_value::string_type +format(const std::vector::key_type>& ks, + const basic_value& v, const spec s); + +namespace detail +{ +extern template class serializer<::toml::type_config>; +extern template class serializer<::toml::ordered_type_config>; +} // detail +} // toml +#endif // TOML11_COMPILE_SOURCES + + +#endif // TOML11_SERIALIZER_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/skip.hpp b/src/frontend/qt_sdl/toml/toml11/skip.hpp new file mode 100644 index 00000000..5a3b1b79 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/skip.hpp @@ -0,0 +1,392 @@ +#ifndef TOML11_SKIP_HPP +#define TOML11_SKIP_HPP + +#include "context.hpp" +#include "region.hpp" +#include "scanner.hpp" +#include "syntax.hpp" +#include "types.hpp" + +#include + +namespace toml +{ +namespace detail +{ + +template +bool skip_whitespace(location& loc, const context& ctx) +{ + return syntax::ws(ctx.toml_spec()).scan(loc).is_ok(); +} + +template +bool skip_empty_lines(location& loc, const context& ctx) +{ + return repeat_at_least(1, sequence( + syntax::ws(ctx.toml_spec()), + syntax::newline(ctx.toml_spec()) + )).scan(loc).is_ok(); +} + +// For error recovery. +// +// In case if a comment line contains an invalid character, we need to skip it +// to advance parsing. +template +void skip_comment_block(location& loc, const context& ctx) +{ + while( ! loc.eof()) + { + skip_whitespace(loc, ctx); + if(loc.current() == '#') + { + while( ! loc.eof()) + { + // both CRLF and LF ends with LF. + if(loc.current() == '\n') + { + loc.advance(); + break; + } + } + } + else if(syntax::newline(ctx.toml_spec()).scan(loc).is_ok()) + { + ; // an empty line. skip this also + } + else + { + // the next token is neither a comment nor empty line. + return ; + } + } + return ; +} + +template +void skip_empty_or_comment_lines(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + repeat_at_least(0, sequence( + syntax::ws(spec), + maybe(syntax::comment(spec)), + syntax::newline(spec)) + ).scan(loc); + return ; +} + +// For error recovery. +// +// Sometimes we need to skip a value and find a delimiter, like `,`, `]`, or `}`. +// To find delimiter, we need to skip delimiters in a string. +// Since we are skipping invalid value while error recovery, we don't need +// to check the syntax. Here we just skip string-like region until closing quote +// is found. +template +void skip_string_like(location& loc, const context&) +{ + // if """ is found, skip until the closing """ is found. + if(literal("\"\"\"").scan(loc).is_ok()) + { + while( ! loc.eof()) + { + if(literal("\"\"\"").scan(loc).is_ok()) + { + return; + } + loc.advance(); + } + } + else if(literal("'''").scan(loc).is_ok()) + { + while( ! loc.eof()) + { + if(literal("'''").scan(loc).is_ok()) + { + return; + } + loc.advance(); + } + } + // if " is found, skip until the closing " or newline is found. + else if(loc.current() == '"') + { + while( ! loc.eof()) + { + loc.advance(); + if(loc.current() == '"' || loc.current() == '\n') + { + loc.advance(); + return; + } + } + } + else if(loc.current() == '\'') + { + while( ! loc.eof()) + { + loc.advance(); + if(loc.current() == '\'' || loc.current() == '\n') + { + loc.advance(); + return ; + } + } + } + return; +} + +template +void skip_value(location& loc, const context& ctx); +template +void skip_array_like(location& loc, const context& ctx); +template +void skip_inline_table_like(location& loc, const context& ctx); +template +void skip_key_value_pair(location& loc, const context& ctx); + +template +result +guess_value_type(const location& loc, const context& ctx); + +template +void skip_array_like(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + assert(loc.current() == '['); + loc.advance(); + + while( ! loc.eof()) + { + if(loc.current() == '\"' || loc.current() == '\'') + { + skip_string_like(loc, ctx); + } + else if(loc.current() == '#') + { + skip_comment_block(loc, ctx); + } + else if(loc.current() == '{') + { + skip_inline_table_like(loc, ctx); + } + else if(loc.current() == '[') + { + const auto checkpoint = loc; + if(syntax::std_table(spec).scan(loc).is_ok() || + syntax::array_table(spec).scan(loc).is_ok()) + { + loc = checkpoint; + break; + } + // if it is not a table-definition, then it is an array. + skip_array_like(loc, ctx); + } + else if(loc.current() == '=') + { + // key-value pair cannot be inside the array. + // guessing the error is "missing closing bracket `]`". + // find the previous key just before `=`. + while(loc.get_location() != 0) + { + loc.retrace(); + if(loc.current() == '\n') + { + loc.advance(); + break; + } + } + break; + } + else if(loc.current() == ']') + { + break; // found closing bracket + } + else + { + loc.advance(); + } + } + return ; +} + +template +void skip_inline_table_like(location& loc, const context& ctx) +{ + assert(loc.current() == '{'); + loc.advance(); + + const auto& spec = ctx.toml_spec(); + + while( ! loc.eof()) + { + if(loc.current() == '\n' && ! spec.v1_1_0_allow_newlines_in_inline_tables) + { + break; // missing closing `}`. + } + else if(loc.current() == '\"' || loc.current() == '\'') + { + skip_string_like(loc, ctx); + } + else if(loc.current() == '#') + { + skip_comment_block(loc, ctx); + if( ! spec.v1_1_0_allow_newlines_in_inline_tables) + { + // comment must end with newline. + break; // missing closing `}`. + } + } + else if(loc.current() == '[') + { + const auto checkpoint = loc; + if(syntax::std_table(spec).scan(loc).is_ok() || + syntax::array_table(spec).scan(loc).is_ok()) + { + loc = checkpoint; + break; // missing closing `}`. + } + // if it is not a table-definition, then it is an array. + skip_array_like(loc, ctx); + } + else if(loc.current() == '{') + { + skip_inline_table_like(loc, ctx); + } + else if(loc.current() == '}') + { + // closing brace found. guessing the error is inside the table. + break; + } + else + { + // skip otherwise. + loc.advance(); + } + } + return ; +} + +template +void skip_value(location& loc, const context& ctx) +{ + value_t ty = guess_value_type(loc, ctx).unwrap_or(value_t::empty); + if(ty == value_t::string) + { + skip_string_like(loc, ctx); + } + else if(ty == value_t::array) + { + skip_array_like(loc, ctx); + } + else if(ty == value_t::table) + { + // In case of multiline tables, it may skip key-value pair but not the + // whole table. + skip_inline_table_like(loc, ctx); + } + else // others are an "in-line" values. skip until the next line + { + while( ! loc.eof()) + { + if(loc.current() == '\n') + { + break; + } + else if(loc.current() == ',' || loc.current() == ']' || loc.current() == '}') + { + break; + } + loc.advance(); + } + } + return; +} + +template +void skip_key_value_pair(location& loc, const context& ctx) +{ + while( ! loc.eof()) + { + if(loc.current() == '=') + { + skip_whitespace(loc, ctx); + skip_value(loc, ctx); + return; + } + else if(loc.current() == '\n') + { + // newline is found before finding `=`. assuming "missing `=`". + return; + } + loc.advance(); + } + return ; +} + +template +void skip_until_next_table(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + while( ! loc.eof()) + { + if(loc.current() == '\n') + { + loc.advance(); + const auto line_begin = loc; + + skip_whitespace(loc, ctx); + if(syntax::std_table(spec).scan(loc).is_ok()) + { + loc = line_begin; + return ; + } + if(syntax::array_table(spec).scan(loc).is_ok()) + { + loc = line_begin; + return ; + } + } + loc.advance(); + } +} + +} // namespace detail +} // namespace toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +struct type_config; +struct ordered_type_config; + +namespace detail +{ +extern template bool skip_whitespace (location& loc, const context&); +extern template bool skip_empty_lines (location& loc, const context&); +extern template void skip_comment_block (location& loc, const context&); +extern template void skip_empty_or_comment_lines(location& loc, const context&); +extern template void skip_string_like (location& loc, const context&); +extern template void skip_array_like (location& loc, const context&); +extern template void skip_inline_table_like (location& loc, const context&); +extern template void skip_value (location& loc, const context&); +extern template void skip_key_value_pair (location& loc, const context&); +extern template void skip_until_next_table (location& loc, const context&); + +extern template bool skip_whitespace (location& loc, const context&); +extern template bool skip_empty_lines (location& loc, const context&); +extern template void skip_comment_block (location& loc, const context&); +extern template void skip_empty_or_comment_lines(location& loc, const context&); +extern template void skip_string_like (location& loc, const context&); +extern template void skip_array_like (location& loc, const context&); +extern template void skip_inline_table_like (location& loc, const context&); +extern template void skip_value (location& loc, const context&); +extern template void skip_key_value_pair (location& loc, const context&); +extern template void skip_until_next_table (location& loc, const context&); + +} // detail +} // toml +#endif // TOML11_COMPILE_SOURCES + +#endif // TOML11_SKIP_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/source_location.hpp b/src/frontend/qt_sdl/toml/toml11/source_location.hpp new file mode 100644 index 00000000..02e62851 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/source_location.hpp @@ -0,0 +1,10 @@ +#ifndef TOML11_SOURCE_LOCATION_HPP +#define TOML11_SOURCE_LOCATION_HPP + +#include "fwd/source_location_fwd.hpp" // IWYU pragma: export + +#if ! defined(TOML11_COMPILE_SOURCES) +#include "impl/source_location_impl.hpp" // IWYU pragma: export +#endif + +#endif // TOML11_SOURCE_LOCATION_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/spec.hpp b/src/frontend/qt_sdl/toml/toml11/spec.hpp new file mode 100644 index 00000000..ca0a9962 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/spec.hpp @@ -0,0 +1,121 @@ +#ifndef TOML11_SPEC_HPP +#define TOML11_SPEC_HPP + +#include +#include + +#include + +namespace toml +{ + +struct semantic_version +{ + constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept + : major{mjr}, minor{mnr}, patch{p} + {} + + std::uint32_t major; + std::uint32_t minor; + std::uint32_t patch; +}; + +constexpr inline semantic_version +make_semver(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept +{ + return semantic_version(mjr, mnr, p); +} + +constexpr inline bool +operator==(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return lhs.major == rhs.major && + lhs.minor == rhs.minor && + lhs.patch == rhs.patch; +} +constexpr inline bool +operator!=(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return !(lhs == rhs); +} +constexpr inline bool +operator<(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return lhs.major < rhs.major || + (lhs.major == rhs.major && lhs.minor < rhs.minor) || + (lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch < rhs.patch); +} +constexpr inline bool +operator>(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return rhs < lhs; +} +constexpr inline bool +operator<=(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return !(lhs > rhs); +} +constexpr inline bool +operator>=(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return !(lhs < rhs); +} + +inline std::ostream& operator<<(std::ostream& os, const semantic_version& v) +{ + os << v.major << '.' << v.minor << '.' << v.patch; + return os; +} + +inline std::string to_string(const semantic_version& v) +{ + std::ostringstream oss; + oss << v; + return oss.str(); +} + +struct spec +{ + constexpr static spec default_version() noexcept + { + return spec::v(1, 0, 0); + } + + constexpr static spec v(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept + { + return spec(make_semver(mjr, mnr, p)); + } + + constexpr explicit spec(const semantic_version& semver) noexcept + : version{semver}, + v1_1_0_allow_control_characters_in_comments {semantic_version{1, 1, 0} <= semver}, + v1_1_0_allow_newlines_in_inline_tables {semantic_version{1, 1, 0} <= semver}, + v1_1_0_allow_trailing_comma_in_inline_tables{semantic_version{1, 1, 0} <= semver}, + v1_1_0_allow_non_english_in_bare_keys {semantic_version{1, 1, 0} <= semver}, + v1_1_0_add_escape_sequence_e {semantic_version{1, 1, 0} <= semver}, + v1_1_0_add_escape_sequence_x {semantic_version{1, 1, 0} <= semver}, + v1_1_0_make_seconds_optional {semantic_version{1, 1, 0} <= semver}, + ext_hex_float {false}, + ext_num_suffix{false}, + ext_null_value{false} + {} + + semantic_version version; // toml version + + // diff from v1.0.0 -> v1.1.0 + bool v1_1_0_allow_control_characters_in_comments; + bool v1_1_0_allow_newlines_in_inline_tables; + bool v1_1_0_allow_trailing_comma_in_inline_tables; + bool v1_1_0_allow_non_english_in_bare_keys; + bool v1_1_0_add_escape_sequence_e; + bool v1_1_0_add_escape_sequence_x; + bool v1_1_0_make_seconds_optional; + + // library extensions + bool ext_hex_float; // allow hex float (in C++ style) + bool ext_num_suffix; // allow number suffix (in C++ style) + bool ext_null_value; // allow `null` as a value +}; + +} // namespace toml +#endif // TOML11_SPEC_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/storage.hpp b/src/frontend/qt_sdl/toml/toml11/storage.hpp new file mode 100644 index 00000000..b63e24f6 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/storage.hpp @@ -0,0 +1,49 @@ +#ifndef TOML11_STORAGE_HPP +#define TOML11_STORAGE_HPP + +#include "compat.hpp" + +namespace toml +{ +namespace detail +{ + +// It owns a pointer to T. It does deep-copy when copied. +// This struct is introduced to implement a recursive type. +// +// `toml::value` contains `std::vector` to represent a toml array. +// But, in the definition of `toml::value`, `toml::value` is still incomplete. +// `std::vector` of an incomplete type is not allowed in C++11 (it is allowed +// after C++17). To avoid this, we need to use a pointer to `toml::value`, like +// `std::vector>`. Although `std::unique_ptr` is +// noncopyable, we want to make `toml::value` copyable. `storage` is introduced +// to resolve those problems. +template +struct storage +{ + using value_type = T; + + explicit storage(value_type v): ptr_(cxx::make_unique(std::move(v))) {} + ~storage() = default; + + storage(const storage& rhs): ptr_(cxx::make_unique(*rhs.ptr_)) {} + storage& operator=(const storage& rhs) + { + this->ptr_ = cxx::make_unique(*rhs.ptr_); + return *this; + } + + storage(storage&&) = default; + storage& operator=(storage&&) = default; + + bool is_ok() const noexcept {return static_cast(ptr_);} + + value_type& get() const noexcept {return *ptr_;} + + private: + std::unique_ptr ptr_; +}; + +} // detail +} // toml +#endif // TOML11_STORAGE_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/syntax.hpp b/src/frontend/qt_sdl/toml/toml11/syntax.hpp new file mode 100644 index 00000000..912535de --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/syntax.hpp @@ -0,0 +1,10 @@ +#ifndef TOML11_SYNTAX_HPP +#define TOML11_SYNTAX_HPP + +#include "fwd/syntax_fwd.hpp" // IWYU pragma: export + +#if ! defined(TOML11_COMPILE_SOURCES) +#include "impl/syntax_impl.hpp" // IWYU pragma: export +#endif + +#endif// TOML11_SYNTAX_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/traits.hpp b/src/frontend/qt_sdl/toml/toml11/traits.hpp new file mode 100644 index 00000000..7a8832f2 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/traits.hpp @@ -0,0 +1,240 @@ +#ifndef TOML11_TRAITS_HPP +#define TOML11_TRAITS_HPP + +#include "from.hpp" +#include "into.hpp" +#include "compat.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#if defined(TOML11_HAS_STRING_VIEW) +#include +#endif + +namespace toml +{ +template +class basic_value; + +namespace detail +{ +// --------------------------------------------------------------------------- +// check whether type T is a kind of container/map class + +struct has_iterator_impl +{ + template static std::true_type check(typename T::iterator*); + template static std::false_type check(...); +}; +struct has_value_type_impl +{ + template static std::true_type check(typename T::value_type*); + template static std::false_type check(...); +}; +struct has_key_type_impl +{ + template static std::true_type check(typename T::key_type*); + template static std::false_type check(...); +}; +struct has_mapped_type_impl +{ + template static std::true_type check(typename T::mapped_type*); + template static std::false_type check(...); +}; +struct has_reserve_method_impl +{ + template static std::false_type check(...); + template static std::true_type check( + decltype(std::declval().reserve(std::declval()))*); +}; +struct has_push_back_method_impl +{ + template static std::false_type check(...); + template static std::true_type check( + decltype(std::declval().push_back(std::declval()))*); +}; +struct is_comparable_impl +{ + template static std::false_type check(...); + template static std::true_type check( + decltype(std::declval() < std::declval())*); +}; + +struct has_from_toml_method_impl +{ + template + static std::true_type check( + decltype(std::declval().from_toml(std::declval<::toml::basic_value>()))*); + + template + static std::false_type check(...); +}; +struct has_into_toml_method_impl +{ + template + static std::true_type check(decltype(std::declval().into_toml())*); + template + static std::false_type check(...); +}; + +struct has_template_into_toml_method_impl +{ + template + static std::true_type check(decltype(std::declval().template into_toml())*); + template + static std::false_type check(...); +}; + +struct has_specialized_from_impl +{ + template + static std::false_type check(...); + template)> + static std::true_type check(::toml::from*); +}; +struct has_specialized_into_impl +{ + template + static std::false_type check(...); + template)> + static std::true_type check(::toml::into*); +}; + + +/// 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::type +#endif + +template +struct has_iterator: decltype(has_iterator_impl::check(nullptr)){}; +template +struct has_value_type: decltype(has_value_type_impl::check(nullptr)){}; +template +struct has_key_type: decltype(has_key_type_impl::check(nullptr)){}; +template +struct has_mapped_type: decltype(has_mapped_type_impl::check(nullptr)){}; +template +struct has_reserve_method: decltype(has_reserve_method_impl::check(nullptr)){}; +template +struct has_push_back_method: decltype(has_push_back_method_impl::check(nullptr)){}; +template +struct is_comparable: decltype(is_comparable_impl::check(nullptr)){}; + +template +struct has_from_toml_method: decltype(has_from_toml_method_impl::check(nullptr)){}; + +template +struct has_into_toml_method: decltype(has_into_toml_method_impl::check(nullptr)){}; + +template +struct has_template_into_toml_method: decltype(has_template_into_toml_method_impl::check(nullptr)){}; + +template +struct has_specialized_from: decltype(has_specialized_from_impl::check(nullptr)){}; +template +struct has_specialized_into: decltype(has_specialized_into_impl::check(nullptr)){}; + +#ifdef __INTEL_COMPILER +#undef decltype +#endif + +// --------------------------------------------------------------------------- +// type checkers + +template struct is_std_pair_impl : std::false_type{}; +template +struct is_std_pair_impl> : std::true_type{}; +template +using is_std_pair = is_std_pair_impl>; + +template struct is_std_tuple_impl : std::false_type{}; +template +struct is_std_tuple_impl> : std::true_type{}; +template +using is_std_tuple = is_std_tuple_impl>; + +template struct is_std_array_impl : std::false_type{}; +template +struct is_std_array_impl> : std::true_type{}; +template +using is_std_array = is_std_array_impl>; + +template struct is_std_forward_list_impl : std::false_type{}; +template +struct is_std_forward_list_impl> : std::true_type{}; +template +using is_std_forward_list = is_std_forward_list_impl>; + +template struct is_std_basic_string_impl : std::false_type{}; +template +struct is_std_basic_string_impl> : std::true_type{}; +template +using is_std_basic_string = is_std_basic_string_impl>; + +template struct is_1byte_std_basic_string_impl : std::false_type{}; +template +struct is_1byte_std_basic_string_impl> + : std::integral_constant {}; +template +using is_1byte_std_basic_string = is_std_basic_string_impl>; + +#if defined(TOML11_HAS_STRING_VIEW) +template struct is_std_basic_string_view_impl : std::false_type{}; +template +struct is_std_basic_string_view_impl> : std::true_type{}; +template +using is_std_basic_string_view = is_std_basic_string_view_impl>; + +template +struct is_string_view_of : std::false_type {}; +template +struct is_string_view_of, std::basic_string> : std::true_type {}; +#endif + +template struct is_chrono_duration_impl: std::false_type{}; +template +struct is_chrono_duration_impl>: std::true_type{}; +template +using is_chrono_duration = is_chrono_duration_impl>; + +template +struct is_map_impl : cxx::conjunction< // map satisfies all the following conditions + has_iterator, // has T::iterator + has_value_type, // has T::value_type + has_key_type, // has T::key_type + has_mapped_type // has T::mapped_type + >{}; +template +using is_map = is_map_impl>; + +template +struct is_container_impl : cxx::conjunction< + cxx::negation>, // not a map + cxx::negation>, // not a std::string +#ifdef TOML11_HAS_STRING_VIEW + cxx::negation>, // not a std::string_view +#endif + has_iterator, // has T::iterator + has_value_type // has T::value_type + >{}; +template +using is_container = is_container_impl>; + +template +struct is_basic_value_impl: std::false_type{}; +template +struct is_basic_value_impl<::toml::basic_value>: std::true_type{}; +template +using is_basic_value = is_basic_value_impl>; + +}// detail +}//toml +#endif // TOML11_TRAITS_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/types.hpp b/src/frontend/qt_sdl/toml/toml11/types.hpp new file mode 100644 index 00000000..75f55b2f --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/types.hpp @@ -0,0 +1,397 @@ +#ifndef TOML11_TYPES_HPP +#define TOML11_TYPES_HPP + +#include "comments.hpp" +#include "error_info.hpp" +#include "format.hpp" +#include "ordered_map.hpp" +#include "value.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace toml +{ + +// forward decl +template +class basic_value; + +// when you use a special integer type as toml::value::integer_type, parse must +// be able to read it. So, type_config has static member functions that read the +// integer_type as {dec, hex, oct, bin}-integer. But, in most cases, operator<< +// is enough. To make config easy, we provide the default read functions. +// +// Before this functions is called, syntax is checked and prefix(`0x` etc) and +// spacer(`_`) are removed. + +template +result +read_dec_int(const std::string& str, const source_location src) +{ + constexpr auto max_digits = std::numeric_limits::digits; + assert( ! str.empty()); + + T val{0}; + std::istringstream iss(str); + iss >> val; + if(iss.fail()) + { + return err(make_error_info("toml::parse_dec_integer: " + "too large integer: current max digits = 2^" + std::to_string(max_digits), + std::move(src), "must be < 2^" + std::to_string(max_digits))); + } + return ok(val); +} + +template +result +read_hex_int(const std::string& str, const source_location src) +{ + constexpr auto max_digits = std::numeric_limits::digits; + assert( ! str.empty()); + + T val{0}; + std::istringstream iss(str); + iss >> std::hex >> val; + if(iss.fail()) + { + return err(make_error_info("toml::parse_hex_integer: " + "too large integer: current max value = 2^" + std::to_string(max_digits), + std::move(src), "must be < 2^" + std::to_string(max_digits))); + } + return ok(val); +} + +template +result +read_oct_int(const std::string& str, const source_location src) +{ + constexpr auto max_digits = std::numeric_limits::digits; + assert( ! str.empty()); + + T val{0}; + std::istringstream iss(str); + iss >> std::oct >> val; + if(iss.fail()) + { + return err(make_error_info("toml::parse_oct_integer: " + "too large integer: current max value = 2^" + std::to_string(max_digits), + std::move(src), "must be < 2^" + std::to_string(max_digits))); + } + return ok(val); +} + +template +result +read_bin_int(const std::string& str, const source_location src) +{ + constexpr auto is_bounded = std::numeric_limits::is_bounded; + constexpr auto max_digits = std::numeric_limits::digits; + const auto max_value = (std::numeric_limits::max)(); + + T val{0}; + T base{1}; + for(auto i = str.rbegin(); i != str.rend(); ++i) + { + const auto c = *i; + if(c == '1') + { + val += base; + // prevent `base` from overflow + if(is_bounded && max_value / 2 < base && std::next(i) != str.rend()) + { + base = 0; + } + else + { + base *= 2; + } + } + else + { + assert(c == '0'); + + if(is_bounded && max_value / 2 < base && std::next(i) != str.rend()) + { + base = 0; + } + else + { + base *= 2; + } + } + } + if(base == 0) + { + return err(make_error_info("toml::parse_bin_integer: " + "too large integer: current max value = 2^" + std::to_string(max_digits), + std::move(src), "must be < 2^" + std::to_string(max_digits))); + } + return ok(val); +} + +template +result +read_int(const std::string& str, const source_location src, const std::uint8_t base) +{ + assert(base == 10 || base == 16 || base == 8 || base == 2); + switch(base) + { + case 2: { return read_bin_int(str, src); } + case 8: { return read_oct_int(str, src); } + case 16: { return read_hex_int(str, src); } + default: + { + assert(base == 10); + return read_dec_int(str, src); + } + } +} + +inline result +read_hex_float(const std::string& str, const source_location src, float val) +{ +#if defined(_MSC_VER) && ! defined(__clang__) + const auto res = ::sscanf_s(str.c_str(), "%a", std::addressof(val)); +#else + const auto res = std::sscanf(str.c_str(), "%a", std::addressof(val)); +#endif + if(res != 1) + { + return err(make_error_info("toml::parse_floating: " + "failed to read hexadecimal floating point value ", + std::move(src), "here")); + } + return ok(val); +} +inline result +read_hex_float(const std::string& str, const source_location src, double val) +{ +#if defined(_MSC_VER) && ! defined(__clang__) + const auto res = ::sscanf_s(str.c_str(), "%la", std::addressof(val)); +#else + const auto res = std::sscanf(str.c_str(), "%la", std::addressof(val)); +#endif + if(res != 1) + { + return err(make_error_info("toml::parse_floating: " + "failed to read hexadecimal floating point value ", + std::move(src), "here")); + } + return ok(val); +} +template +cxx::enable_if_t, double>>, + cxx::negation, float>> + >::value, result> +read_hex_float(const std::string&, const source_location src, T) +{ + return err(make_error_info("toml::parse_floating: failed to read " + "floating point value because of unknown type in type_config", + std::move(src), "here")); +} + +template +result +read_dec_float(const std::string& str, const source_location src) +{ + T val; + std::istringstream iss(str); + iss >> val; + if(iss.fail()) + { + return err(make_error_info("toml::parse_floating: " + "failed to read floating point value from stream", + std::move(src), "here")); + } + return ok(val); +} + +template +result +read_float(const std::string& str, const source_location src, const bool is_hex) +{ + if(is_hex) + { + return read_hex_float(str, src, T{}); + } + else + { + return read_dec_float(str, src); + } +} + +struct type_config +{ + using comment_type = preserve_comments; + + using boolean_type = bool; + using integer_type = std::int64_t; + using floating_type = double; + using string_type = std::string; + + template + using array_type = std::vector; + template + using table_type = std::unordered_map; + + static result + parse_int(const std::string& str, const source_location src, const std::uint8_t base) + { + return read_int(str, src, base); + } + static result + parse_float(const std::string& str, const source_location src, const bool is_hex) + { + return read_float(str, src, is_hex); + } +}; + +using value = basic_value; +using table = typename value::table_type; +using array = typename value::array_type; + +struct ordered_type_config +{ + using comment_type = preserve_comments; + + using boolean_type = bool; + using integer_type = std::int64_t; + using floating_type = double; + using string_type = std::string; + + template + using array_type = std::vector; + template + using table_type = ordered_map; + + static result + parse_int(const std::string& str, const source_location src, const std::uint8_t base) + { + return read_int(str, src, base); + } + static result + parse_float(const std::string& str, const source_location src, const bool is_hex) + { + return read_float(str, src, is_hex); + } +}; + +using ordered_value = basic_value; +using ordered_table = typename ordered_value::table_type; +using ordered_array = typename ordered_value::array_type; + +// ---------------------------------------------------------------------------- +// meta functions for internal use + +namespace detail +{ + +// ---------------------------------------------------------------------------- +// check if type T has all the needed member types + +struct has_comment_type_impl +{ + template static std::true_type check(typename T::comment_type*); + template static std::false_type check(...); +}; +template +using has_comment_type = decltype(has_comment_type_impl::check(nullptr)); + +struct has_integer_type_impl +{ + template static std::true_type check(typename T::integer_type*); + template static std::false_type check(...); +}; +template +using has_integer_type = decltype(has_integer_type_impl::check(nullptr)); + +struct has_floating_type_impl +{ + template static std::true_type check(typename T::floating_type*); + template static std::false_type check(...); +}; +template +using has_floating_type = decltype(has_floating_type_impl::check(nullptr)); + +struct has_string_type_impl +{ + template static std::true_type check(typename T::string_type*); + template static std::false_type check(...); +}; +template +using has_string_type = decltype(has_string_type_impl::check(nullptr)); + +struct has_array_type_impl +{ + template static std::true_type check(typename T::template array_type*); + template static std::false_type check(...); +}; +template +using has_array_type = decltype(has_array_type_impl::check(nullptr)); + +struct has_table_type_impl +{ + template static std::true_type check(typename T::template table_type*); + template static std::false_type check(...); +}; +template +using has_table_type = decltype(has_table_type_impl::check(nullptr)); + +struct has_parse_int_impl +{ + template static std::true_type check(decltype(std::declval().parse_int( + std::declval(), + std::declval(), + std::declval() + ))*); + template static std::false_type check(...); +}; +template +using has_parse_int = decltype(has_parse_int_impl::check(nullptr)); + +struct has_parse_float_impl +{ + template static std::true_type check(decltype(std::declval().parse_float( + std::declval(), + std::declval(), + std::declval() + ))*); + template static std::false_type check(...); +}; +template +using has_parse_float = decltype(has_parse_float_impl::check(nullptr)); + +template +using is_type_config = cxx::conjunction< + has_comment_type, + has_integer_type, + has_floating_type, + has_string_type, + has_array_type, + has_table_type, + has_parse_int, + has_parse_float + >; + +} // namespace detail +} // namespace toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +extern template class basic_value; +extern template class basic_value; +} // toml +#endif // TOML11_COMPILE_SOURCES + +#endif // TOML11_TYPES_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/utility.hpp b/src/frontend/qt_sdl/toml/toml11/utility.hpp new file mode 100644 index 00000000..9e30ccb7 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/utility.hpp @@ -0,0 +1,170 @@ +#ifndef TOML11_UTILITY_HPP +#define TOML11_UTILITY_HPP + +#include "result.hpp" +#include "traits.hpp" + +#include +#include + +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +// to output character in an error message. +inline std::string show_char(const int c) +{ + using char_type = unsigned char; + if(std::isgraph(c)) + { + return std::string(1, static_cast(c)); + } + else + { + std::array buf; + buf.fill('\0'); + const auto r = std::snprintf(buf.data(), buf.size(), "0x%02x", c & 0xFF); + assert(r == static_cast(buf.size()) - 1); + (void) r; // Unused variable warning + auto in_hex = std::string(buf.data()); + switch(c) + { + case char_type('\0'): {in_hex += "(NUL)"; break;} + case char_type(' ') : {in_hex += "(SPACE)"; break;} + case char_type('\n'): {in_hex += "(LINE FEED)"; break;} + case char_type('\r'): {in_hex += "(CARRIAGE RETURN)"; break;} + case char_type('\t'): {in_hex += "(TAB)"; break;} + case char_type('\v'): {in_hex += "(VERTICAL TAB)"; break;} + case char_type('\f'): {in_hex += "(FORM FEED)"; break;} + case char_type('\x1B'): {in_hex += "(ESCAPE)"; break;} + default: break; + } + return in_hex; + } +} + +// --------------------------------------------------------------------------- + +template +void try_reserve_impl(Container& container, std::size_t N, std::true_type) +{ + container.reserve(N); + return; +} +template +void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept +{ + return; +} + +template +void try_reserve(Container& container, std::size_t N) +{ + try_reserve_impl(container, N, has_reserve_method{}); + return; +} + +// --------------------------------------------------------------------------- + +template +result from_string(const std::string& str) +{ + T v; + std::istringstream iss(str); + iss >> v; + if(iss.fail()) + { + return err(); + } + return ok(v); +} + +// --------------------------------------------------------------------------- + +// helper function to avoid std::string(0, 'c') or std::string(iter, iter) +template +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); +} + +// --------------------------------------------------------------------------- + +template +struct string_conv_impl +{ + static_assert(sizeof(Char) == sizeof(char), ""); + static_assert(sizeof(Char2) == sizeof(char), ""); + + static std::basic_string invoke(std::basic_string s) + { + std::basic_string retval; + std::transform(s.begin(), s.end(), std::back_inserter(retval), + [](const Char2 c) {return static_cast(c);}); + return retval; + } + template + static std::basic_string invoke(const Char2 (&s)[N]) + { + std::basic_string retval; + // "string literal" has null-char at the end. to skip it, we use prev. + std::transform(std::begin(s), std::prev(std::end(s)), std::back_inserter(retval), + [](const Char2 c) {return static_cast(c);}); + return retval; + } +}; + +template +struct string_conv_impl +{ + static_assert(sizeof(Char) == sizeof(char), ""); + + static std::basic_string invoke(std::basic_string s) + { + return s; + } + template + static std::basic_string invoke(const Char (&s)[N]) + { + return std::basic_string(s); + } +}; + +template +cxx::enable_if_t::value, S> +string_conv(std::basic_string s) +{ + using C = typename S::value_type; + using T = typename S::traits_type; + using A = typename S::allocator_type; + return string_conv_impl::invoke(std::move(s)); +} +template +cxx::enable_if_t::value, S> +string_conv(const char (&s)[N]) +{ + using C = typename S::value_type; + using T = typename S::traits_type; + using A = typename S::allocator_type; + using C2 = char; + using T2 = std::char_traits; + using A2 = std::allocator; + + return string_conv_impl::template invoke(s); +} + +} // namespace detail +} // namespace toml +#endif // TOML11_UTILITY_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/value.hpp b/src/frontend/qt_sdl/toml/toml11/value.hpp new file mode 100644 index 00000000..d945fa01 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/value.hpp @@ -0,0 +1,2257 @@ +#ifndef TOML11_VALUE_HPP +#define TOML11_VALUE_HPP + +#include "color.hpp" +#include "datetime.hpp" +#include "exception.hpp" +#include "error_info.hpp" +#include "format.hpp" +#include "region.hpp" +#include "source_location.hpp" +#include "storage.hpp" +#include "traits.hpp" +#include "value_t.hpp" +#include "version.hpp" // IWYU pragma: keep < TOML11_HAS_STRING_VIEW + +#ifdef TOML11_HAS_STRING_VIEW +#include +#endif + +#include + +namespace toml +{ +template +class basic_value; + +struct type_error final : public ::toml::exception +{ + public: + type_error(std::string what_arg, source_location loc) + : what_(std::move(what_arg)), loc_(std::move(loc)) + {} + ~type_error() noexcept override = default; + + const char* what() const noexcept override {return what_.c_str();} + + source_location const& location() const noexcept {return loc_;} + + private: + std::string what_; + source_location loc_; +}; + +// only for internal use +namespace detail +{ +template +error_info make_type_error(const basic_value&, const std::string&, const value_t); + +template +error_info make_not_found_error(const basic_value&, const std::string&, const typename basic_value::key_type&); + +template +void change_region_of_value(basic_value&, const basic_value&); + +template +struct getter; +} // detail + +template +class basic_value +{ + public: + + using config_type = TypeConfig; + using key_type = typename config_type::string_type; + using value_type = basic_value; + using boolean_type = typename config_type::boolean_type; + using integer_type = typename config_type::integer_type; + using floating_type = typename config_type::floating_type; + using string_type = typename config_type::string_type; + using local_time_type = ::toml::local_time; + using local_date_type = ::toml::local_date; + using local_datetime_type = ::toml::local_datetime; + using offset_datetime_type = ::toml::offset_datetime; + using array_type = typename config_type::template array_type; + using table_type = typename config_type::template table_type; + using comment_type = typename config_type::comment_type; + using char_type = typename string_type::value_type; + + private: + + using region_type = detail::region; + + public: + + basic_value() noexcept + : type_(value_t::empty), empty_('\0'), region_{}, comments_{} + {} + ~basic_value() noexcept {this->cleanup();} + + // copy/move constructor/assigner ===================================== {{{ + + basic_value(const basic_value& v) + : type_(v.type_), region_(v.region_), comments_(v.comments_) + { + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::integer : assigner(integer_ , v.integer_ ); break; + case value_t::floating : assigner(floating_ , v.floating_ ); break; + case value_t::string : assigner(string_ , v.string_ ); break; + case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; + case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; + case value_t::array : assigner(array_ , v.array_ ); break; + case value_t::table : assigner(table_ , v.table_ ); break; + default : assigner(empty_ , '\0' ); break; + } + } + basic_value(basic_value&& v) + : type_(v.type()), region_(std::move(v.region_)), + comments_(std::move(v.comments_)) + { + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::string : assigner(string_ , std::move(v.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::array : assigner(array_ , std::move(v.array_ )); break; + case value_t::table : assigner(table_ , std::move(v.table_ )); break; + default : assigner(empty_ , '\0' ); break; + } + } + + basic_value& operator=(const basic_value& v) + { + if(this == std::addressof(v)) {return *this;} + + this->cleanup(); + this->type_ = v.type_; + this->region_ = v.region_; + this->comments_ = v.comments_; + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::integer : assigner(integer_ , v.integer_ ); break; + case value_t::floating : assigner(floating_ , v.floating_ ); break; + case value_t::string : assigner(string_ , v.string_ ); break; + case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; + case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; + case value_t::array : assigner(array_ , v.array_ ); break; + case value_t::table : assigner(table_ , v.table_ ); break; + default : assigner(empty_ , '\0' ); break; + } + return *this; + } + basic_value& operator=(basic_value&& v) + { + if(this == std::addressof(v)) {return *this;} + + this->cleanup(); + this->type_ = v.type_; + this->region_ = std::move(v.region_); + this->comments_ = std::move(v.comments_); + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::string : assigner(string_ , std::move(v.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::array : assigner(array_ , std::move(v.array_ )); break; + case value_t::table : assigner(table_ , std::move(v.table_ )); break; + default : assigner(empty_ , '\0' ); break; + } + return *this; + } + // }}} + + // constructor to overwrite commnets ================================== {{{ + + basic_value(basic_value v, std::vector com) + : type_(v.type()), region_(std::move(v.region_)), + comments_(std::move(com)) + { + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::string : assigner(string_ , std::move(v.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::array : assigner(array_ , std::move(v.array_ )); break; + case value_t::table : assigner(table_ , std::move(v.table_ )); break; + default : assigner(empty_ , '\0' ); break; + } + } + // }}} + + // conversion between different basic_values ========================== {{{ + + template + basic_value(basic_value other) + : type_(other.type_), + region_(std::move(other.region_)), + comments_(std::move(other.comments_)) + { + switch(other.type_) + { + // use auto-convert in constructor + case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; + case value_t::string : assigner(string_ , std::move(other.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; + + // may have different container type + case value_t::array : + { + array_type tmp( + std::make_move_iterator(other.array_.value.get().begin()), + std::make_move_iterator(other.array_.value.get().end())); + assigner(array_, array_storage( + detail::storage(std::move(tmp)), + other.array_.format + )); + break; + } + case value_t::table : + { + table_type tmp( + std::make_move_iterator(other.table_.value.get().begin()), + std::make_move_iterator(other.table_.value.get().end())); + assigner(table_, table_storage( + detail::storage(std::move(tmp)), + other.table_.format + )); + break; + } + default: break; + } + } + + template + basic_value(basic_value other, std::vector com) + : type_(other.type_), + region_(std::move(other.region_)), + comments_(std::move(com)) + { + switch(other.type_) + { + // use auto-convert in constructor + case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; + case value_t::string : assigner(string_ , std::move(other.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; + + // may have different container type + case value_t::array : + { + array_type tmp( + std::make_move_iterator(other.array_.value.get().begin()), + std::make_move_iterator(other.array_.value.get().end())); + assigner(array_, array_storage( + detail::storage(std::move(tmp)), + other.array_.format + )); + break; + } + case value_t::table : + { + table_type tmp( + std::make_move_iterator(other.table_.value.get().begin()), + std::make_move_iterator(other.table_.value.get().end())); + assigner(table_, table_storage( + detail::storage(std::move(tmp)), + other.table_.format + )); + break; + } + default: break; + } + } + template + basic_value& operator=(basic_value other) + { + this->cleanup(); + this->region_ = other.region_; + this->comments_ = comment_type(other.comments_); + this->type_ = other.type_; + switch(other.type_) + { + // use auto-convert in constructor + case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; + case value_t::string : assigner(string_ , std::move(other.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; + + // may have different container type + case value_t::array : + { + array_type tmp( + std::make_move_iterator(other.array_.value.get().begin()), + std::make_move_iterator(other.array_.value.get().end())); + assigner(array_, array_storage( + detail::storage(std::move(tmp)), + other.array_.format + )); + break; + } + case value_t::table : + { + table_type tmp( + std::make_move_iterator(other.table_.value.get().begin()), + std::make_move_iterator(other.table_.value.get().end())); + assigner(table_, table_storage( + detail::storage(std::move(tmp)), + other.table_.format + )); + break; + } + default: break; + } + return *this; + } + // }}} + + // constructor (boolean) ============================================== {{{ + + basic_value(boolean_type x) + : basic_value(x, boolean_format_info{}, std::vector{}, region_type{}) + {} + basic_value(boolean_type x, boolean_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(boolean_type x, std::vector com) + : basic_value(x, boolean_format_info{}, std::move(com), region_type{}) + {} + basic_value(boolean_type x, boolean_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(boolean_type x, boolean_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::boolean), boolean_(boolean_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(boolean_type x) + { + boolean_format_info fmt; + if(this->is_boolean()) + { + fmt = this->as_boolean_fmt(); + } + this->cleanup(); + this->type_ = value_t::boolean; + this->region_ = region_type{}; + assigner(this->boolean_, boolean_storage(x, fmt)); + return *this; + } + + // }}} + + // constructor (integer) ============================================== {{{ + + basic_value(integer_type x) + : basic_value(std::move(x), integer_format_info{}, std::vector{}, region_type{}) + {} + basic_value(integer_type x, integer_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(integer_type x, std::vector com) + : basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{}) + {} + basic_value(integer_type x, integer_format_info fmt, std::vector com) + : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) + {} + basic_value(integer_type x, integer_format_info fmt, std::vector com, region_type reg) + : type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(integer_type x) + { + integer_format_info fmt; + if(this->is_integer()) + { + fmt = this->as_integer_fmt(); + } + this->cleanup(); + this->type_ = value_t::integer; + this->region_ = region_type{}; + assigner(this->integer_, integer_storage(std::move(x), std::move(fmt))); + return *this; + } + + private: + + template + using enable_if_integer_like_t = cxx::enable_if_t, boolean_type>>, + cxx::negation, integer_type>>, + std::is_integral> + >::value, std::nullptr_t>; + + public: + + template = nullptr> + basic_value(T x) + : basic_value(std::move(x), integer_format_info{}, std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, integer_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, std::vector com) + : basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, integer_format_info fmt, std::vector com) + : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, integer_format_info fmt, std::vector com, region_type reg) + : type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + template = nullptr> + basic_value& operator=(T x) + { + integer_format_info fmt; + if(this->is_integer()) + { + fmt = this->as_integer_fmt(); + } + this->cleanup(); + this->type_ = value_t::integer; + this->region_ = region_type{}; + assigner(this->integer_, integer_storage(x, std::move(fmt))); + return *this; + } + + // }}} + + // constructor (floating) ============================================= {{{ + + basic_value(floating_type x) + : basic_value(std::move(x), floating_format_info{}, std::vector{}, region_type{}) + {} + basic_value(floating_type x, floating_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(floating_type x, std::vector com) + : basic_value(std::move(x), floating_format_info{}, std::move(com), region_type{}) + {} + basic_value(floating_type x, floating_format_info fmt, std::vector com) + : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) + {} + basic_value(floating_type x, floating_format_info fmt, std::vector com, region_type reg) + : type_(value_t::floating), floating_(floating_storage(std::move(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(floating_type x) + { + floating_format_info fmt; + if(this->is_floating()) + { + fmt = this->as_floating_fmt(); + } + this->cleanup(); + this->type_ = value_t::floating; + this->region_ = region_type{}; + assigner(this->floating_, floating_storage(std::move(x), std::move(fmt))); + return *this; + } + + private: + + template + using enable_if_floating_like_t = cxx::enable_if_t, floating_type>>, + std::is_floating_point> + >::value, std::nullptr_t>; + + public: + + template = nullptr> + basic_value(T x) + : basic_value(x, floating_format_info{}, std::vector{}, region_type{}) + {} + + template = nullptr> + basic_value(T x, floating_format_info fmt) + : basic_value(x, std::move(fmt), std::vector{}, region_type{}) + {} + + template = nullptr> + basic_value(T x, std::vector com) + : basic_value(x, floating_format_info{}, std::move(com), region_type{}) + {} + + template = nullptr> + basic_value(T x, floating_format_info fmt, std::vector com) + : basic_value(x, std::move(fmt), std::move(com), region_type{}) + {} + + template = nullptr> + basic_value(T x, floating_format_info fmt, std::vector com, region_type reg) + : type_(value_t::floating), floating_(floating_storage(x, std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + + template = nullptr> + basic_value& operator=(T x) + { + floating_format_info fmt; + if(this->is_floating()) + { + fmt = this->as_floating_fmt(); + } + this->cleanup(); + this->type_ = value_t::floating; + this->region_ = region_type{}; + assigner(this->floating_, floating_storage(x, std::move(fmt))); + return *this; + } + + // }}} + + // constructor (string) =============================================== {{{ + + basic_value(string_type x) + : basic_value(std::move(x), string_format_info{}, std::vector{}, region_type{}) + {} + basic_value(string_type x, string_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(string_type x, std::vector com) + : basic_value(std::move(x), string_format_info{}, std::move(com), region_type{}) + {} + basic_value(string_type x, string_format_info fmt, std::vector com) + : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) + {} + basic_value(string_type x, string_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::string), string_(string_storage(std::move(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(string_type x) + { + string_format_info fmt; + if(this->is_string()) + { + fmt = this->as_string_fmt(); + } + this->cleanup(); + this->type_ = value_t::string; + this->region_ = region_type{}; + assigner(this->string_, string_storage(x, std::move(fmt))); + return *this; + } + + // "string literal" + + basic_value(const typename string_type::value_type* x) + : basic_value(x, string_format_info{}, std::vector{}, region_type{}) + {} + basic_value(const typename string_type::value_type* x, string_format_info fmt) + : basic_value(x, std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(const typename string_type::value_type* x, std::vector com) + : basic_value(x, string_format_info{}, std::move(com), region_type{}) + {} + basic_value(const typename string_type::value_type* x, string_format_info fmt, std::vector com) + : basic_value(x, std::move(fmt), std::move(com), region_type{}) + {} + basic_value(const typename string_type::value_type* x, string_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(const typename string_type::value_type* x) + { + string_format_info fmt; + if(this->is_string()) + { + fmt = this->as_string_fmt(); + } + this->cleanup(); + this->type_ = value_t::string; + this->region_ = region_type{}; + assigner(this->string_, string_storage(string_type(x), std::move(fmt))); + return *this; + } + +#if defined(TOML11_HAS_STRING_VIEW) + using string_view_type = std::basic_string_view< + typename string_type::value_type, typename string_type::traits_type>; + + basic_value(string_view_type x) + : basic_value(x, string_format_info{}, std::vector{}, region_type{}) + {} + basic_value(string_view_type x, string_format_info fmt) + : basic_value(x, std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(string_view_type x, std::vector com) + : basic_value(x, string_format_info{}, std::move(com), region_type{}) + {} + basic_value(string_view_type x, string_format_info fmt, std::vector com) + : basic_value(x, std::move(fmt), std::move(com), region_type{}) + {} + basic_value(string_view_type x, string_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(string_view_type x) + { + string_format_info fmt; + if(this->is_string()) + { + fmt = this->as_string_fmt(); + } + this->cleanup(); + this->type_ = value_t::string; + this->region_ = region_type{}; + assigner(this->string_, string_storage(string_type(x), std::move(fmt))); + return *this; + } + +#endif // TOML11_HAS_STRING_VIEW + + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x) + : basic_value(x, string_format_info{}, std::vector{}, region_type{}) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x, string_format_info fmt) + : basic_value(x, std::move(fmt), std::vector{}, region_type{}) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x, std::vector com) + : basic_value(x, string_format_info{}, std::move(com), region_type{}) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x, string_format_info fmt, std::vector com) + : basic_value(x, std::move(fmt), std::move(com), region_type{}) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x, string_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::string), + string_(string_storage(detail::string_conv(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value& operator=(const T& x) + { + string_format_info fmt; + if(this->is_string()) + { + fmt = this->as_string_fmt(); + } + this->cleanup(); + this->type_ = value_t::string; + this->region_ = region_type{}; + assigner(this->string_, string_storage(detail::string_conv(x), std::move(fmt))); + return *this; + } + + // }}} + + // constructor (local_date) =========================================== {{{ + + basic_value(local_date_type x) + : basic_value(x, local_date_format_info{}, std::vector{}, region_type{}) + {} + basic_value(local_date_type x, local_date_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(local_date_type x, std::vector com) + : basic_value(x, local_date_format_info{}, std::move(com), region_type{}) + {} + basic_value(local_date_type x, local_date_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(local_date_type x, local_date_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::local_date), local_date_(local_date_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(local_date_type x) + { + local_date_format_info fmt; + if(this->is_local_date()) + { + fmt = this->as_local_date_fmt(); + } + this->cleanup(); + this->type_ = value_t::local_date; + this->region_ = region_type{}; + assigner(this->local_date_, local_date_storage(x, fmt)); + return *this; + } + + // }}} + + // constructor (local_time) =========================================== {{{ + + basic_value(local_time_type x) + : basic_value(x, local_time_format_info{}, std::vector{}, region_type{}) + {} + basic_value(local_time_type x, local_time_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(local_time_type x, std::vector com) + : basic_value(x, local_time_format_info{}, std::move(com), region_type{}) + {} + basic_value(local_time_type x, local_time_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(local_time_type x, local_time_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::local_time), local_time_(local_time_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(local_time_type x) + { + local_time_format_info fmt; + if(this->is_local_time()) + { + fmt = this->as_local_time_fmt(); + } + this->cleanup(); + this->type_ = value_t::local_time; + this->region_ = region_type{}; + assigner(this->local_time_, local_time_storage(x, fmt)); + return *this; + } + + template + basic_value(const std::chrono::duration& x) + : basic_value(local_time_type(x), local_time_format_info{}, std::vector{}, region_type{}) + {} + template + basic_value(const std::chrono::duration& x, local_time_format_info fmt) + : basic_value(local_time_type(x), std::move(fmt), std::vector{}, region_type{}) + {} + template + basic_value(const std::chrono::duration& x, std::vector com) + : basic_value(local_time_type(x), local_time_format_info{}, std::move(com), region_type{}) + {} + template + basic_value(const std::chrono::duration& x, local_time_format_info fmt, std::vector com) + : basic_value(local_time_type(x), std::move(fmt), std::move(com), region_type{}) + {} + template + basic_value(const std::chrono::duration& x, + local_time_format_info fmt, + std::vector com, region_type reg) + : basic_value(local_time_type(x), std::move(fmt), std::move(com), std::move(reg)) + {} + template + basic_value& operator=(const std::chrono::duration& x) + { + local_time_format_info fmt; + if(this->is_local_time()) + { + fmt = this->as_local_time_fmt(); + } + this->cleanup(); + this->type_ = value_t::local_time; + this->region_ = region_type{}; + assigner(this->local_time_, local_time_storage(local_time_type(x), std::move(fmt))); + return *this; + } + + // }}} + + // constructor (local_datetime) =========================================== {{{ + + basic_value(local_datetime_type x) + : basic_value(x, local_datetime_format_info{}, std::vector{}, region_type{}) + {} + basic_value(local_datetime_type x, local_datetime_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(local_datetime_type x, std::vector com) + : basic_value(x, local_datetime_format_info{}, std::move(com), region_type{}) + {} + basic_value(local_datetime_type x, local_datetime_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(local_datetime_type x, local_datetime_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::local_datetime), local_datetime_(local_datetime_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(local_datetime_type x) + { + local_datetime_format_info fmt; + if(this->is_local_datetime()) + { + fmt = this->as_local_datetime_fmt(); + } + this->cleanup(); + this->type_ = value_t::local_datetime; + this->region_ = region_type{}; + assigner(this->local_datetime_, local_datetime_storage(x, fmt)); + return *this; + } + + // }}} + + // constructor (offset_datetime) =========================================== {{{ + + basic_value(offset_datetime_type x) + : basic_value(x, offset_datetime_format_info{}, std::vector{}, region_type{}) + {} + basic_value(offset_datetime_type x, offset_datetime_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(offset_datetime_type x, std::vector com) + : basic_value(x, offset_datetime_format_info{}, std::move(com), region_type{}) + {} + basic_value(offset_datetime_type x, offset_datetime_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(offset_datetime_type x, offset_datetime_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::offset_datetime), offset_datetime_(offset_datetime_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(offset_datetime_type x) + { + offset_datetime_format_info fmt; + if(this->is_offset_datetime()) + { + fmt = this->as_offset_datetime_fmt(); + } + this->cleanup(); + this->type_ = value_t::offset_datetime; + this->region_ = region_type{}; + assigner(this->offset_datetime_, offset_datetime_storage(x, fmt)); + return *this; + } + + // system_clock::time_point + + basic_value(std::chrono::system_clock::time_point x) + : basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::vector{}, region_type{}) + {} + basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt) + : basic_value(offset_datetime_type(x), fmt, std::vector{}, region_type{}) + {} + basic_value(std::chrono::system_clock::time_point x, std::vector com) + : basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::move(com), region_type{}) + {} + basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, std::vector com) + : basic_value(offset_datetime_type(x), fmt, std::move(com), region_type{}) + {} + basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, + std::vector com, region_type reg) + : basic_value(offset_datetime_type(x), std::move(fmt), std::move(com), std::move(reg)) + {} + basic_value& operator=(std::chrono::system_clock::time_point x) + { + offset_datetime_format_info fmt; + if(this->is_offset_datetime()) + { + fmt = this->as_offset_datetime_fmt(); + } + this->cleanup(); + this->type_ = value_t::offset_datetime; + this->region_ = region_type{}; + assigner(this->offset_datetime_, offset_datetime_storage(offset_datetime_type(x), fmt)); + return *this; + } + + // }}} + + // constructor (array) ================================================ {{{ + + basic_value(array_type x) + : basic_value(std::move(x), array_format_info{}, std::vector{}, region_type{}) + {} + basic_value(array_type x, array_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(array_type x, std::vector com) + : basic_value(std::move(x), array_format_info{}, std::move(com), region_type{}) + {} + basic_value(array_type x, array_format_info fmt, std::vector com) + : basic_value(std::move(x), fmt, std::move(com), region_type{}) + {} + basic_value(array_type x, array_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::array), array_(array_storage( + detail::storage(std::move(x)), std::move(fmt) + )), region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(array_type x) + { + array_format_info fmt; + if(this->is_array()) + { + fmt = this->as_array_fmt(); + } + this->cleanup(); + this->type_ = value_t::array; + this->region_ = region_type{}; + assigner(this->array_, array_storage( + detail::storage(std::move(x)), std::move(fmt))); + return *this; + } + + private: + + template + using enable_if_array_like_t = cxx::enable_if_t, + cxx::negation>, + cxx::negation>, +#if defined(TOML11_HAS_STRING_VIEW) + cxx::negation>, +#endif + cxx::negation>, + cxx::negation> + >::value, std::nullptr_t>; + + public: + + template = nullptr> + basic_value(T x) + : basic_value(std::move(x), array_format_info{}, std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, array_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, std::vector com) + : basic_value(std::move(x), array_format_info{}, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, array_format_info fmt, std::vector com) + : basic_value(std::move(x), fmt, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, array_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::array), array_(array_storage( + detail::storage(array_type( + std::make_move_iterator(x.begin()), + std::make_move_iterator(x.end())) + ), std::move(fmt) + )), region_(std::move(reg)), comments_(std::move(com)) + {} + template = nullptr> + basic_value& operator=(T x) + { + array_format_info fmt; + if(this->is_array()) + { + fmt = this->as_array_fmt(); + } + this->cleanup(); + this->type_ = value_t::array; + this->region_ = region_type{}; + + array_type a(std::make_move_iterator(x.begin()), + std::make_move_iterator(x.end())); + assigner(this->array_, array_storage( + detail::storage(std::move(a)), std::move(fmt))); + return *this; + } + + // }}} + + // constructor (table) ================================================ {{{ + + basic_value(table_type x) + : basic_value(std::move(x), table_format_info{}, std::vector{}, region_type{}) + {} + basic_value(table_type x, table_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(table_type x, std::vector com) + : basic_value(std::move(x), table_format_info{}, std::move(com), region_type{}) + {} + basic_value(table_type x, table_format_info fmt, std::vector com) + : basic_value(std::move(x), fmt, std::move(com), region_type{}) + {} + basic_value(table_type x, table_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::table), table_(table_storage( + detail::storage(std::move(x)), std::move(fmt) + )), region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(table_type x) + { + table_format_info fmt; + if(this->is_table()) + { + fmt = this->as_table_fmt(); + } + this->cleanup(); + this->type_ = value_t::table; + this->region_ = region_type{}; + assigner(this->table_, table_storage( + detail::storage(std::move(x)), std::move(fmt))); + return *this; + } + + // table-like + + private: + + template + using enable_if_table_like_t = cxx::enable_if_t>, + detail::is_map, + cxx::negation>, + cxx::negation> + >::value, std::nullptr_t>; + + public: + + template = nullptr> + basic_value(T x) + : basic_value(std::move(x), table_format_info{}, std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, table_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, std::vector com) + : basic_value(std::move(x), table_format_info{}, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, table_format_info fmt, std::vector com) + : basic_value(std::move(x), fmt, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, table_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::table), table_(table_storage( + detail::storage(table_type( + std::make_move_iterator(x.begin()), + std::make_move_iterator(x.end()) + )), std::move(fmt) + )), region_(std::move(reg)), comments_(std::move(com)) + {} + template = nullptr> + basic_value& operator=(T x) + { + table_format_info fmt; + if(this->is_table()) + { + fmt = this->as_table_fmt(); + } + this->cleanup(); + this->type_ = value_t::table; + this->region_ = region_type{}; + + table_type t(std::make_move_iterator(x.begin()), + std::make_move_iterator(x.end())); + assigner(this->table_, table_storage( + detail::storage(std::move(t)), std::move(fmt))); + return *this; + } + + // }}} + + // constructor (user_defined) ========================================= {{{ + + template::value, std::nullptr_t> = nullptr> + basic_value(const T& ud) + : basic_value( + into>::template into_toml(ud)) + {} + template::value, std::nullptr_t> = nullptr> + basic_value(const T& ud, std::vector com) + : basic_value( + into>::template into_toml(ud), + std::move(com)) + {} + template::value, std::nullptr_t> = nullptr> + basic_value& operator=(const T& ud) + { + *this = into>::template into_toml(ud); + return *this; + } + + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value(const T& ud): basic_value(ud.into_toml()) {} + + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value(const T& ud, std::vector com) + : basic_value(ud.into_toml(), std::move(com)) + {} + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value& operator=(const T& ud) + { + *this = ud.into_toml(); + return *this; + } + + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value(const T& ud): basic_value(ud.template into_toml()) {} + + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value(const T& ud, std::vector com) + : basic_value(ud.template into_toml(), std::move(com)) + {} + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value& operator=(const T& ud) + { + *this = ud.template into_toml(); + return *this; + } + // }}} + + // empty value with region info ======================================= {{{ + + // mainly for `null` extension + basic_value(detail::none_t, region_type reg) noexcept + : type_(value_t::empty), empty_('\0'), region_(std::move(reg)), comments_{} + {} + + // }}} + + // type checking ====================================================== {{{ + + template, value_type>::value, + std::nullptr_t> = nullptr> + bool is() const noexcept + { + return detail::type_to_enum::value == this->type_; + } + bool is(value_t t) const noexcept {return t == this->type_;} + + bool is_empty() const noexcept {return this->is(value_t::empty );} + bool is_boolean() const noexcept {return this->is(value_t::boolean );} + bool is_integer() const noexcept {return this->is(value_t::integer );} + bool is_floating() const noexcept {return this->is(value_t::floating );} + bool is_string() const noexcept {return this->is(value_t::string );} + bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);} + bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );} + bool is_local_date() const noexcept {return this->is(value_t::local_date );} + bool is_local_time() const noexcept {return this->is(value_t::local_time );} + bool is_array() const noexcept {return this->is(value_t::array );} + bool is_table() const noexcept {return this->is(value_t::table );} + + bool is_array_of_tables() const noexcept + { + if( ! this->is_array()) {return false;} + const auto& a = this->as_array(std::nothrow); // already checked. + + // when you define [[array.of.tables]], at least one empty table will be + // assigned. In case of array of inline tables, `array_of_tables = []`, + // there is no reason to consider this as an array of *tables*. + // So empty array is not an array-of-tables. + if(a.empty()) {return false;} + + // since toml v1.0.0 allows array of heterogeneous types, we need to + // check all the elements. if any of the elements is not a table, it + // is a heterogeneous array and cannot be expressed by `[[aot]]` form. + for(const auto& e : a) + { + if( ! e.is_table()) {return false;} + } + return true; + } + + value_t type() const noexcept {return type_;} + + // }}} + + // as_xxx (noexcept) version ========================================== {{{ + + template + detail::enum_to_type_t> const& + as(const std::nothrow_t&) const noexcept + { + return detail::getter::get_nothrow(*this); + } + template + detail::enum_to_type_t>& + as(const std::nothrow_t&) noexcept + { + return detail::getter::get_nothrow(*this); + } + + boolean_type const& as_boolean (const std::nothrow_t&) const noexcept {return this->boolean_.value;} + integer_type const& as_integer (const std::nothrow_t&) const noexcept {return this->integer_.value;} + floating_type const& as_floating (const std::nothrow_t&) const noexcept {return this->floating_.value;} + string_type const& as_string (const std::nothrow_t&) const noexcept {return this->string_.value;} + offset_datetime_type const& as_offset_datetime(const std::nothrow_t&) const noexcept {return this->offset_datetime_.value;} + local_datetime_type const& as_local_datetime (const std::nothrow_t&) const noexcept {return this->local_datetime_.value;} + local_date_type const& as_local_date (const std::nothrow_t&) const noexcept {return this->local_date_.value;} + local_time_type const& as_local_time (const std::nothrow_t&) const noexcept {return this->local_time_.value;} + array_type const& as_array (const std::nothrow_t&) const noexcept {return this->array_.value.get();} + table_type const& as_table (const std::nothrow_t&) const noexcept {return this->table_.value.get();} + + boolean_type & as_boolean (const std::nothrow_t&) noexcept {return this->boolean_.value;} + integer_type & as_integer (const std::nothrow_t&) noexcept {return this->integer_.value;} + floating_type & as_floating (const std::nothrow_t&) noexcept {return this->floating_.value;} + string_type & as_string (const std::nothrow_t&) noexcept {return this->string_.value;} + offset_datetime_type& as_offset_datetime(const std::nothrow_t&) noexcept {return this->offset_datetime_.value;} + local_datetime_type & as_local_datetime (const std::nothrow_t&) noexcept {return this->local_datetime_.value;} + local_date_type & as_local_date (const std::nothrow_t&) noexcept {return this->local_date_.value;} + local_time_type & as_local_time (const std::nothrow_t&) noexcept {return this->local_time_.value;} + array_type & as_array (const std::nothrow_t&) noexcept {return this->array_.value.get();} + table_type & as_table (const std::nothrow_t&) noexcept {return this->table_.value.get();} + + // }}} + + // as_xxx (throw) ===================================================== {{{ + + template + detail::enum_to_type_t> const& as() const + { + return detail::getter::get(*this); + } + template + detail::enum_to_type_t>& as() + { + return detail::getter::get(*this); + } + + boolean_type const& as_boolean() const + { + if(this->type_ != value_t::boolean) + { + this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean); + } + return this->boolean_.value; + } + integer_type const& as_integer() const + { + if(this->type_ != value_t::integer) + { + this->throw_bad_cast("toml::value::as_integer()", value_t::integer); + } + return this->integer_.value; + } + floating_type const& as_floating() const + { + if(this->type_ != value_t::floating) + { + this->throw_bad_cast("toml::value::as_floating()", value_t::floating); + } + return this->floating_.value; + } + string_type const& as_string() const + { + if(this->type_ != value_t::string) + { + this->throw_bad_cast("toml::value::as_string()", value_t::string); + } + return this->string_.value; + } + offset_datetime_type const& as_offset_datetime() const + { + if(this->type_ != value_t::offset_datetime) + { + this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime); + } + return this->offset_datetime_.value; + } + local_datetime_type const& as_local_datetime() const + { + if(this->type_ != value_t::local_datetime) + { + this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime); + } + return this->local_datetime_.value; + } + local_date_type const& as_local_date() const + { + if(this->type_ != value_t::local_date) + { + this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date); + } + return this->local_date_.value; + } + local_time_type const& as_local_time() const + { + if(this->type_ != value_t::local_time) + { + this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time); + } + return this->local_time_.value; + } + array_type const& as_array() const + { + if(this->type_ != value_t::array) + { + this->throw_bad_cast("toml::value::as_array()", value_t::array); + } + return this->array_.value.get(); + } + table_type const& as_table() const + { + if(this->type_ != value_t::table) + { + this->throw_bad_cast("toml::value::as_table()", value_t::table); + } + return this->table_.value.get(); + } + + // ------------------------------------------------------------------------ + // nonconst reference + + boolean_type& as_boolean() + { + if(this->type_ != value_t::boolean) + { + this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean); + } + return this->boolean_.value; + } + integer_type& as_integer() + { + if(this->type_ != value_t::integer) + { + this->throw_bad_cast("toml::value::as_integer()", value_t::integer); + } + return this->integer_.value; + } + floating_type& as_floating() + { + if(this->type_ != value_t::floating) + { + this->throw_bad_cast("toml::value::as_floating()", value_t::floating); + } + return this->floating_.value; + } + string_type& as_string() + { + if(this->type_ != value_t::string) + { + this->throw_bad_cast("toml::value::as_string()", value_t::string); + } + return this->string_.value; + } + offset_datetime_type& as_offset_datetime() + { + if(this->type_ != value_t::offset_datetime) + { + this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime); + } + return this->offset_datetime_.value; + } + local_datetime_type& as_local_datetime() + { + if(this->type_ != value_t::local_datetime) + { + this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime); + } + return this->local_datetime_.value; + } + local_date_type& as_local_date() + { + if(this->type_ != value_t::local_date) + { + this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date); + } + return this->local_date_.value; + } + local_time_type& as_local_time() + { + if(this->type_ != value_t::local_time) + { + this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time); + } + return this->local_time_.value; + } + array_type& as_array() + { + if(this->type_ != value_t::array) + { + this->throw_bad_cast("toml::value::as_array()", value_t::array); + } + return this->array_.value.get(); + } + table_type& as_table() + { + if(this->type_ != value_t::table) + { + this->throw_bad_cast("toml::value::as_table()", value_t::table); + } + return this->table_.value.get(); + } + + // }}} + + // format accessors (noexcept) ======================================== {{{ + + template + detail::enum_to_fmt_type_t const& + as_fmt(const std::nothrow_t&) const noexcept + { + return detail::getter::get_fmt_nothrow(*this); + } + template + detail::enum_to_fmt_type_t& + as_fmt(const std::nothrow_t&) noexcept + { + return detail::getter::get_fmt_nothrow(*this); + } + + boolean_format_info & as_boolean_fmt (const std::nothrow_t&) noexcept {return this->boolean_.format;} + integer_format_info & as_integer_fmt (const std::nothrow_t&) noexcept {return this->integer_.format;} + floating_format_info & as_floating_fmt (const std::nothrow_t&) noexcept {return this->floating_.format;} + string_format_info & as_string_fmt (const std::nothrow_t&) noexcept {return this->string_.format;} + offset_datetime_format_info& as_offset_datetime_fmt(const std::nothrow_t&) noexcept {return this->offset_datetime_.format;} + local_datetime_format_info & as_local_datetime_fmt (const std::nothrow_t&) noexcept {return this->local_datetime_.format;} + local_date_format_info & as_local_date_fmt (const std::nothrow_t&) noexcept {return this->local_date_.format;} + local_time_format_info & as_local_time_fmt (const std::nothrow_t&) noexcept {return this->local_time_.format;} + array_format_info & as_array_fmt (const std::nothrow_t&) noexcept {return this->array_.format;} + table_format_info & as_table_fmt (const std::nothrow_t&) noexcept {return this->table_.format;} + + boolean_format_info const& as_boolean_fmt (const std::nothrow_t&) const noexcept {return this->boolean_.format;} + integer_format_info const& as_integer_fmt (const std::nothrow_t&) const noexcept {return this->integer_.format;} + floating_format_info const& as_floating_fmt (const std::nothrow_t&) const noexcept {return this->floating_.format;} + string_format_info const& as_string_fmt (const std::nothrow_t&) const noexcept {return this->string_.format;} + offset_datetime_format_info const& as_offset_datetime_fmt(const std::nothrow_t&) const noexcept {return this->offset_datetime_.format;} + local_datetime_format_info const& as_local_datetime_fmt (const std::nothrow_t&) const noexcept {return this->local_datetime_.format;} + local_date_format_info const& as_local_date_fmt (const std::nothrow_t&) const noexcept {return this->local_date_.format;} + local_time_format_info const& as_local_time_fmt (const std::nothrow_t&) const noexcept {return this->local_time_.format;} + array_format_info const& as_array_fmt (const std::nothrow_t&) const noexcept {return this->array_.format;} + table_format_info const& as_table_fmt (const std::nothrow_t&) const noexcept {return this->table_.format;} + + // }}} + + // format accessors (throw) =========================================== {{{ + + template + detail::enum_to_fmt_type_t const& as_fmt() const + { + return detail::getter::get_fmt(*this); + } + template + detail::enum_to_fmt_type_t& as_fmt() + { + return detail::getter::get_fmt(*this); + } + + boolean_format_info const& as_boolean_fmt() const + { + if(this->type_ != value_t::boolean) + { + this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean); + } + return this->boolean_.format; + } + integer_format_info const& as_integer_fmt() const + { + if(this->type_ != value_t::integer) + { + this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer); + } + return this->integer_.format; + } + floating_format_info const& as_floating_fmt() const + { + if(this->type_ != value_t::floating) + { + this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating); + } + return this->floating_.format; + } + string_format_info const& as_string_fmt() const + { + if(this->type_ != value_t::string) + { + this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string); + } + return this->string_.format; + } + offset_datetime_format_info const& as_offset_datetime_fmt() const + { + if(this->type_ != value_t::offset_datetime) + { + this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime); + } + return this->offset_datetime_.format; + } + local_datetime_format_info const& as_local_datetime_fmt() const + { + if(this->type_ != value_t::local_datetime) + { + this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime); + } + return this->local_datetime_.format; + } + local_date_format_info const& as_local_date_fmt() const + { + if(this->type_ != value_t::local_date) + { + this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date); + } + return this->local_date_.format; + } + local_time_format_info const& as_local_time_fmt() const + { + if(this->type_ != value_t::local_time) + { + this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time); + } + return this->local_time_.format; + } + array_format_info const& as_array_fmt() const + { + if(this->type_ != value_t::array) + { + this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array); + } + return this->array_.format; + } + table_format_info const& as_table_fmt() const + { + if(this->type_ != value_t::table) + { + this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table); + } + return this->table_.format; + } + + // ------------------------------------------------------------------------ + // nonconst reference + + boolean_format_info& as_boolean_fmt() + { + if(this->type_ != value_t::boolean) + { + this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean); + } + return this->boolean_.format; + } + integer_format_info& as_integer_fmt() + { + if(this->type_ != value_t::integer) + { + this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer); + } + return this->integer_.format; + } + floating_format_info& as_floating_fmt() + { + if(this->type_ != value_t::floating) + { + this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating); + } + return this->floating_.format; + } + string_format_info& as_string_fmt() + { + if(this->type_ != value_t::string) + { + this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string); + } + return this->string_.format; + } + offset_datetime_format_info& as_offset_datetime_fmt() + { + if(this->type_ != value_t::offset_datetime) + { + this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime); + } + return this->offset_datetime_.format; + } + local_datetime_format_info& as_local_datetime_fmt() + { + if(this->type_ != value_t::local_datetime) + { + this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime); + } + return this->local_datetime_.format; + } + local_date_format_info& as_local_date_fmt() + { + if(this->type_ != value_t::local_date) + { + this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date); + } + return this->local_date_.format; + } + local_time_format_info& as_local_time_fmt() + { + if(this->type_ != value_t::local_time) + { + this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time); + } + return this->local_time_.format; + } + array_format_info& as_array_fmt() + { + if(this->type_ != value_t::array) + { + this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array); + } + return this->array_.format; + } + table_format_info& as_table_fmt() + { + if(this->type_ != value_t::table) + { + this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table); + } + return this->table_.format; + } + // }}} + + // table accessors ==================================================== {{{ + + value_type& at(const key_type& k) + { + if(!this->is_table()) + { + this->throw_bad_cast("toml::value::at(key_type)", value_t::table); + } + auto& table = this->as_table(std::nothrow); + const auto found = table.find(k); + if(found == table.end()) + { + this->throw_key_not_found_error("toml::value::at", k); + } + assert(found->first == k); + return found->second; + } + value_type const& at(const key_type& k) const + { + if(!this->is_table()) + { + this->throw_bad_cast("toml::value::at(key_type)", value_t::table); + } + const auto& table = this->as_table(std::nothrow); + const auto found = table.find(k); + if(found == table.end()) + { + this->throw_key_not_found_error("toml::value::at", k); + } + assert(found->first == k); + return found->second; + } + value_type& operator[](const key_type& k) + { + if(this->is_empty()) + { + (*this) = table_type{}; + } + else if( ! this->is_table()) // initialized, but not a table + { + this->throw_bad_cast("toml::value::operator[](key_type)", value_t::table); + } + return (this->as_table(std::nothrow))[k]; + } + std::size_t count(const key_type& k) const + { + if(!this->is_table()) + { + this->throw_bad_cast("toml::value::count(key_type)", value_t::table); + } + return this->as_table(std::nothrow).count(k); + } + bool contains(const key_type& k) const + { + if(!this->is_table()) + { + this->throw_bad_cast("toml::value::contains(key_type)", value_t::table); + } + const auto& table = this->as_table(std::nothrow); + return table.find(k) != table.end(); + } + // }}} + + // array accessors ==================================================== {{{ + + value_type& at(const std::size_t idx) + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::at(idx)", value_t::array); + } + auto& ar = this->as_array(std::nothrow); + + if(ar.size() <= idx) + { + std::ostringstream oss; + oss << "actual length (" << ar.size() + << ") is shorter than the specified index (" << idx << ")."; + throw std::out_of_range(format_error( + "toml::value::at(idx): no element corresponding to the index", + this->location(), oss.str() + )); + } + return ar.at(idx); + } + value_type const& at(const std::size_t idx) const + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::at(idx)", value_t::array); + } + const auto& ar = this->as_array(std::nothrow); + + if(ar.size() <= idx) + { + std::ostringstream oss; + oss << "actual length (" << ar.size() + << ") is shorter than the specified index (" << idx << ")."; + + throw std::out_of_range(format_error( + "toml::value::at(idx): no element corresponding to the index", + this->location(), oss.str() + )); + } + return ar.at(idx); + } + + value_type& operator[](const std::size_t idx) noexcept + { + // no check... + return this->as_array(std::nothrow)[idx]; + } + value_type const& operator[](const std::size_t idx) const noexcept + { + // no check... + return this->as_array(std::nothrow)[idx]; + } + + void push_back(const value_type& x) + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::push_back(idx)", value_t::array); + } + this->as_array(std::nothrow).push_back(x); + return; + } + void push_back(value_type&& x) + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::push_back(idx)", value_t::array); + } + this->as_array(std::nothrow).push_back(std::move(x)); + return; + } + + template + value_type& emplace_back(Ts&& ... args) + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::emplace_back(idx)", value_t::array); + } + auto& ar = this->as_array(std::nothrow); + ar.emplace_back(std::forward(args) ...); + return ar.back(); + } + + std::size_t size() const + { + switch(this->type_) + { + case value_t::array: + { + return this->as_array(std::nothrow).size(); + } + case value_t::table: + { + return this->as_table(std::nothrow).size(); + } + case value_t::string: + { + return this->as_string(std::nothrow).size(); + } + default: + { + throw type_error(format_error( + "toml::value::size(): bad_cast to container types", + this->location(), + "the actual type is " + to_string(this->type_) + ), this->location()); + } + } + } + + // }}} + + source_location location() const + { + return source_location(this->region_); + } + + comment_type const& comments() const noexcept {return this->comments_;} + comment_type& comments() noexcept {return this->comments_;} + + private: + + // private helper functions =========================================== {{{ + + void cleanup() noexcept + { + switch(this->type_) + { + case value_t::boolean : { boolean_ .~boolean_storage (); break; } + case value_t::integer : { integer_ .~integer_storage (); break; } + case value_t::floating : { floating_ .~floating_storage (); break; } + case value_t::string : { string_ .~string_storage (); break; } + case value_t::offset_datetime : { offset_datetime_.~offset_datetime_storage (); break; } + case value_t::local_datetime : { local_datetime_ .~local_datetime_storage (); break; } + case value_t::local_date : { local_date_ .~local_date_storage (); break; } + case value_t::local_time : { local_time_ .~local_time_storage (); break; } + case value_t::array : { array_ .~array_storage (); break; } + case value_t::table : { table_ .~table_storage (); break; } + default : { break; } + } + this->type_ = value_t::empty; + return; + } + + template + static void assigner(T& dst, U&& v) + { + const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); + assert(tmp == std::addressof(dst)); + (void)tmp; + } + + [[noreturn]] + void throw_bad_cast(const std::string& funcname, const value_t ty) const + { + throw type_error(format_error(detail::make_type_error(*this, funcname, ty)), + this->location()); + } + + [[noreturn]] + void throw_key_not_found_error(const std::string& funcname, const key_type& key) const + { + throw std::out_of_range(format_error( + detail::make_not_found_error(*this, funcname, key))); + } + + template + friend void detail::change_region_of_value(basic_value&, const basic_value&); + + template + friend class basic_value; + + // }}} + + private: + + using boolean_storage = detail::value_with_format; + using integer_storage = detail::value_with_format; + using floating_storage = detail::value_with_format; + using string_storage = detail::value_with_format; + using offset_datetime_storage = detail::value_with_format; + using local_datetime_storage = detail::value_with_format; + using local_date_storage = detail::value_with_format; + using local_time_storage = detail::value_with_format; + using array_storage = detail::value_with_format, array_format_info >; + using table_storage = detail::value_with_format, table_format_info >; + + private: + + value_t type_; + union + { + char empty_; // the smallest type + boolean_storage boolean_; + integer_storage integer_; + floating_storage floating_; + string_storage string_; + offset_datetime_storage offset_datetime_; + local_datetime_storage local_datetime_; + local_date_storage local_date_; + local_time_storage local_time_; + array_storage array_; + table_storage table_; + }; + region_type region_; + comment_type comments_; +}; + +template +bool operator==(const basic_value& lhs, const basic_value& rhs) +{ + if(lhs.type() != rhs.type()) {return false;} + if(lhs.comments() != rhs.comments()) {return false;} + + switch(lhs.type()) + { + case value_t::boolean : + { + return lhs.as_boolean() == rhs.as_boolean(); + } + case value_t::integer : + { + return lhs.as_integer() == rhs.as_integer(); + } + case value_t::floating : + { + return lhs.as_floating() == rhs.as_floating(); + } + case value_t::string : + { + return lhs.as_string() == rhs.as_string(); + } + case value_t::offset_datetime: + { + return lhs.as_offset_datetime() == rhs.as_offset_datetime(); + } + case value_t::local_datetime: + { + return lhs.as_local_datetime() == rhs.as_local_datetime(); + } + case value_t::local_date: + { + return lhs.as_local_date() == rhs.as_local_date(); + } + case value_t::local_time: + { + return lhs.as_local_time() == rhs.as_local_time(); + } + case value_t::array : + { + return lhs.as_array() == rhs.as_array(); + } + case value_t::table : + { + return lhs.as_table() == rhs.as_table(); + } + case value_t::empty : {return true; } + default: {return false;} + } +} + +template +bool operator!=(const basic_value& lhs, const basic_value& rhs) +{ + return !(lhs == rhs); +} + +template +cxx::enable_if_t::array_type>, + detail::is_comparable::table_type> + >::value, bool> +operator<(const basic_value& lhs, const basic_value& rhs) +{ + if(lhs.type() != rhs.type()) + { + return (lhs.type() < rhs.type()); + } + switch(lhs.type()) + { + case value_t::boolean : + { + return lhs.as_boolean() < rhs.as_boolean() || + (lhs.as_boolean() == rhs.as_boolean() && + lhs.comments() < rhs.comments()); + } + case value_t::integer : + { + return lhs.as_integer() < rhs.as_integer() || + (lhs.as_integer() == rhs.as_integer() && + lhs.comments() < rhs.comments()); + } + case value_t::floating : + { + return lhs.as_floating() < rhs.as_floating() || + (lhs.as_floating() == rhs.as_floating() && + lhs.comments() < rhs.comments()); + } + case value_t::string : + { + return lhs.as_string() < rhs.as_string() || + (lhs.as_string() == rhs.as_string() && + lhs.comments() < rhs.comments()); + } + case value_t::offset_datetime: + { + return lhs.as_offset_datetime() < rhs.as_offset_datetime() || + (lhs.as_offset_datetime() == rhs.as_offset_datetime() && + lhs.comments() < rhs.comments()); + } + case value_t::local_datetime: + { + return lhs.as_local_datetime() < rhs.as_local_datetime() || + (lhs.as_local_datetime() == rhs.as_local_datetime() && + lhs.comments() < rhs.comments()); + } + case value_t::local_date: + { + return lhs.as_local_date() < rhs.as_local_date() || + (lhs.as_local_date() == rhs.as_local_date() && + lhs.comments() < rhs.comments()); + } + case value_t::local_time: + { + return lhs.as_local_time() < rhs.as_local_time() || + (lhs.as_local_time() == rhs.as_local_time() && + lhs.comments() < rhs.comments()); + } + case value_t::array : + { + return lhs.as_array() < rhs.as_array() || + (lhs.as_array() == rhs.as_array() && + lhs.comments() < rhs.comments()); + } + case value_t::table : + { + return lhs.as_table() < rhs.as_table() || + (lhs.as_table() == rhs.as_table() && + lhs.comments() < rhs.comments()); + } + case value_t::empty : + { + return lhs.comments() < rhs.comments(); + } + default: + { + return lhs.comments() < rhs.comments(); + } + } +} + +template +cxx::enable_if_t::array_type>, + detail::is_comparable::table_type> + >::value, bool> +operator<=(const basic_value& lhs, const basic_value& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +template +cxx::enable_if_t::array_type>, + detail::is_comparable::table_type> + >::value, bool> +operator>(const basic_value& lhs, const basic_value& rhs) +{ + return !(lhs <= rhs); +} +template +cxx::enable_if_t::array_type>, + detail::is_comparable::table_type> + >::value, bool> +operator>=(const basic_value& lhs, const basic_value& rhs) +{ + return !(lhs < rhs); +} + +// error_info helper +namespace detail +{ +template +error_info make_error_info_rec(error_info e, + const basic_value& v, std::string msg, Ts&& ... tail) +{ + return make_error_info_rec(std::move(e), v.location(), std::move(msg), std::forward(tail)...); +} +} // detail + +template +error_info make_error_info( + std::string title, const basic_value& v, std::string msg, Ts&& ... tail) +{ + return make_error_info(std::move(title), + v.location(), std::move(msg), std::forward(tail)...); +} +template +std::string format_error(std::string title, + const basic_value& v, std::string msg, Ts&& ... tail) +{ + return format_error(std::move(title), + v.location(), std::move(msg), std::forward(tail)...); +} + +namespace detail +{ + +template +error_info make_type_error(const basic_value& v, const std::string& fname, const value_t ty) +{ + return make_error_info(fname + ": bad_cast to " + to_string(ty), + v.location(), "the actual type is " + to_string(v.type())); +} +template +error_info make_not_found_error(const basic_value& v, const std::string& fname, const typename basic_value::key_type& key) +{ + const auto loc = v.location(); + const std::string title = fname + ": key \"" + string_conv(key) + "\" not found"; + + std::vector> locs; + if( ! loc.is_ok()) + { + return error_info(title, locs); + } + + if(loc.first_line_number() == 1 && loc.first_column_number() == 1 && loc.length() == 1) + { + // The top-level table has its region at the 0th character of the file. + // That means that, in the case when a key is not found in the top-level + // table, the error message points to the first character. If the file has + // the first table at the first line, the error message would be like this. + // ```console + // [error] key "a" not found + // --> example.toml + // | + // 1 | [table] + // | ^------ in this table + // ``` + // It actually points to the top-level table at the first character, not + // `[table]`. But it is too confusing. To avoid the confusion, the error + // message should explicitly say "key not found in the top-level table". + locs.emplace_back(v.location(), "at the top-level table"); + } + else + { + locs.emplace_back(v.location(), "in this table"); + } + return error_info(title, locs); +} + +#define TOML11_DETAIL_GENERATE_COMPTIME_GETTER(ty) \ + template \ + struct getter \ + { \ + using value_type = basic_value; \ + using result_type = enum_to_type_t; \ + using format_type = enum_to_fmt_type_t; \ + \ + static result_type& get(value_type& v) \ + { \ + return v.as_ ## ty(); \ + } \ + static result_type const& get(const value_type& v) \ + { \ + return v.as_ ## ty(); \ + } \ + \ + static result_type& get_nothrow(value_type& v) noexcept \ + { \ + return v.as_ ## ty(std::nothrow); \ + } \ + static result_type const& get_nothrow(const value_type& v) noexcept \ + { \ + return v.as_ ## ty(std::nothrow); \ + } \ + \ + static format_type& get_fmt(value_type& v) \ + { \ + return v.as_ ## ty ## _fmt(); \ + } \ + static format_type const& get_fmt(const value_type& v) \ + { \ + return v.as_ ## ty ## _fmt(); \ + } \ + \ + static format_type& get_fmt_nothrow(value_type& v) noexcept \ + { \ + return v.as_ ## ty ## _fmt(std::nothrow); \ + } \ + static format_type const& get_fmt_nothrow(const value_type& v) noexcept \ + { \ + return v.as_ ## ty ## _fmt(std::nothrow); \ + } \ + }; + +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(boolean ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(integer ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(floating ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(string ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(offset_datetime) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_datetime ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_date ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_time ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(array ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(table ) + +#undef TOML11_DETAIL_GENERATE_COMPTIME_GETTER + +template +void change_region_of_value(basic_value& dst, const basic_value& src) +{ + dst.region_ = std::move(src.region_); + return; +} + +} // namespace detail +} // namespace toml +#endif // TOML11_VALUE_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/value_t.hpp b/src/frontend/qt_sdl/toml/toml11/value_t.hpp new file mode 100644 index 00000000..ed0fbe93 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/value_t.hpp @@ -0,0 +1,10 @@ +#ifndef TOML11_VALUE_T_HPP +#define TOML11_VALUE_T_HPP + +#include "fwd/value_t_fwd.hpp" // IWYU pragma: export + +#if ! defined(TOML11_COMPILE_SOURCES) +#include "impl/value_t_impl.hpp" // IWYU pragma: export +#endif + +#endif // TOML11_VALUE_T_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/version.hpp b/src/frontend/qt_sdl/toml/toml11/version.hpp new file mode 100644 index 00000000..f95b3cf5 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/version.hpp @@ -0,0 +1,121 @@ +#ifndef TOML11_VERSION_HPP +#define TOML11_VERSION_HPP + +#define TOML11_VERSION_MAJOR 4 +#define TOML11_VERSION_MINOR 2 +#define TOML11_VERSION_PATCH 0 + +#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. +// +// 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. So we check if _MSVC_LANG is defined before using `__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(_MSVC_LANG) && defined(_MSC_VER) && 190024210 <= _MSC_FULL_VER +# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG +#else +# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus +#endif + +#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L +# error "toml11 requires C++11 or later." +#endif + +#if ! defined(__has_include) +# define __has_include(x) 0 +#endif + +#if ! defined(__has_cpp_attribute) +# define __has_cpp_attribute(x) 0 +#endif + +#if ! defined(__has_builtin) +# define __has_builtin(x) 0 +#endif + +// hard to remember + +#ifndef TOML11_CXX14_VALUE +#define TOML11_CXX14_VALUE 201402L +#endif//TOML11_CXX14_VALUE + +#ifndef TOML11_CXX17_VALUE +#define TOML11_CXX17_VALUE 201703L +#endif//TOML11_CXX17_VALUE + +#ifndef TOML11_CXX20_VALUE +#define TOML11_CXX20_VALUE 202002L +#endif//TOML11_CXX20_VALUE + +#if defined(__cpp_char8_t) +# if __cpp_char8_t >= 201811L +# define TOML11_HAS_CHAR8_T 1 +# endif +#endif + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if __has_include() +# define TOML11_HAS_STRING_VIEW 1 +# endif +#endif + +#ifndef TOML11_DISABLE_STD_FILESYSTEM +# if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if __has_include() +# define TOML11_HAS_FILESYSTEM 1 +# endif +# endif +#endif + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if __has_include() +# define TOML11_HAS_OPTIONAL 1 +# endif +#endif + +#if defined(TOML11_COMPILE_SOURCES) +# define TOML11_INLINE +#else +# define TOML11_INLINE inline +#endif + +namespace toml +{ + +inline const char* license_notice() noexcept +{ + return R"(The MIT License (MIT) + +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.)"; +} + +} // toml +#endif // TOML11_VERSION_HPP diff --git a/src/frontend/qt_sdl/toml/toml11/visit.hpp b/src/frontend/qt_sdl/toml/toml11/visit.hpp new file mode 100644 index 00000000..d97344b3 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml11/visit.hpp @@ -0,0 +1,81 @@ +#ifndef TOML11_VISIT_HPP +#define TOML11_VISIT_HPP + +#include "exception.hpp" +#include "traits.hpp" +#include "value.hpp" + +namespace toml +{ + +template +cxx::return_type_of_t::boolean_type&> +visit(Visitor&& visitor, const basic_value& v) +{ + switch(v.type()) + { + case value_t::boolean : {return visitor(v.as_boolean ());} + case value_t::integer : {return visitor(v.as_integer ());} + case value_t::floating : {return visitor(v.as_floating ());} + case value_t::string : {return visitor(v.as_string ());} + case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} + case value_t::local_datetime : {return visitor(v.as_local_datetime ());} + case value_t::local_date : {return visitor(v.as_local_date ());} + case value_t::local_time : {return visitor(v.as_local_time ());} + case value_t::array : {return visitor(v.as_array ());} + case value_t::table : {return visitor(v.as_table ());} + case value_t::empty : break; + default: break; + } + throw type_error(format_error("[error] toml::visit: toml::basic_value " + "does not have any valid type.", v.location(), "here"), v.location()); +} + +template +cxx::return_type_of_t::boolean_type&> +visit(Visitor&& visitor, basic_value& v) +{ + switch(v.type()) + { + case value_t::boolean : {return visitor(v.as_boolean ());} + case value_t::integer : {return visitor(v.as_integer ());} + case value_t::floating : {return visitor(v.as_floating ());} + case value_t::string : {return visitor(v.as_string ());} + case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} + case value_t::local_datetime : {return visitor(v.as_local_datetime ());} + case value_t::local_date : {return visitor(v.as_local_date ());} + case value_t::local_time : {return visitor(v.as_local_time ());} + case value_t::array : {return visitor(v.as_array ());} + case value_t::table : {return visitor(v.as_table ());} + case value_t::empty : break; + default: break; + } + throw type_error(format_error("[error] toml::visit: toml::basic_value " + "does not have any valid type.", v.location(), "here"), v.location()); +} + +template +cxx::return_type_of_t::boolean_type&&> +visit(Visitor&& visitor, basic_value&& v) +{ + switch(v.type()) + { + case value_t::boolean : {return visitor(std::move(v.as_boolean ()));} + case value_t::integer : {return visitor(std::move(v.as_integer ()));} + case value_t::floating : {return visitor(std::move(v.as_floating ()));} + case value_t::string : {return visitor(std::move(v.as_string ()));} + case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));} + case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));} + case value_t::local_date : {return visitor(std::move(v.as_local_date ()));} + case value_t::local_time : {return visitor(std::move(v.as_local_time ()));} + case value_t::array : {return visitor(std::move(v.as_array ()));} + case value_t::table : {return visitor(std::move(v.as_table ()));} + case value_t::empty : break; + default: break; + } + throw type_error(format_error("[error] toml::visit: toml::basic_value " + "does not have any valid type.", v.location(), "here"), v.location()); +} + +} // toml +#endif // TOML11_VISIT_HPP diff --git a/src/frontend/qt_sdl/toml/toml_fwd.hpp b/src/frontend/qt_sdl/toml/toml_fwd.hpp new file mode 100644 index 00000000..3ba77ff4 --- /dev/null +++ b/src/frontend/qt_sdl/toml/toml_fwd.hpp @@ -0,0 +1,88 @@ +#ifndef TOML11_TOML_FWD_HPP +#define TOML11_TOML_FWD_HPP + +// IWYU pragma: begin_exports +#include "toml11/version.hpp" +#include "toml11/compat.hpp" +#include "toml11/conversion.hpp" +#include "toml11/from.hpp" +#include "toml11/into.hpp" +// IWYU pragma: end_exports + +#include + +#ifdef TOML11_COLORIZE_ERROR_MESSAGE +#define TOML11_ERROR_MESSAGE_COLORIZED true +#else +#define TOML11_ERROR_MESSAGE_COLORIZED false +#endif + +namespace toml +{ +class discard_comments; +class preserve_comments; + +enum class month_t : std::uint8_t; +struct local_date; +struct local_time; +struct time_offset; +struct local_datetime; +struct offset_datetime; + +struct error_info; + +struct exception; + +enum class indent_char : std::uint8_t; +enum class integer_format : std::uint8_t; +enum class floating_format : std::uint8_t; +enum class string_format : std::uint8_t; +enum class datetime_delimiter_kind : std::uint8_t; +enum class array_format : std::uint8_t; +enum class table_format : std::uint8_t; + +struct boolean_format_info; +struct integer_format_info; +struct floating_format_info; +struct string_format_info; +struct offset_datetime_format_info; +struct local_datetime_format_info; +struct local_date_format_info; +struct local_time_format_info; +struct array_format_info; +struct table_format_info; + +template +class ordered_map; + +struct syntax_error; +struct file_io_error; + +struct bad_result_access; +template +struct success; +template +struct failure; +template +struct result; + +struct source_location; + +struct semantic_version; +struct spec; + + +template +class basic_value; +struct type_error; + +struct type_config; +using value = basic_value; + +struct ordered_type_config; +using ordered_value = basic_value; + +enum class value_t : std::uint8_t; + +} // toml +#endif// TOML11_TOML_HPP From bca0457bea10691eb84b4fcd2f3f19f760c3a84f Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 18 Nov 2024 22:56:34 +0100 Subject: [PATCH 034/106] fuck, why did these files get committed --- cmake/toml11Config.cmake__ | 2 -- res/icon/splash.xcf | Bin 246892 -> 0 bytes 2 files changed, 2 deletions(-) delete mode 100644 cmake/toml11Config.cmake__ delete mode 100644 res/icon/splash.xcf diff --git a/cmake/toml11Config.cmake__ b/cmake/toml11Config.cmake__ deleted file mode 100644 index edc73f69..00000000 --- a/cmake/toml11Config.cmake__ +++ /dev/null @@ -1,2 +0,0 @@ -@PACKAGE_INIT@ -include("@PACKAGE_toml11_install_cmake_dir@/toml11Targets.cmake") diff --git a/res/icon/splash.xcf b/res/icon/splash.xcf deleted file mode 100644 index 594ecab02a063d1954d08dbf41b07a10fcf786d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246892 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[C; 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 From b491e99954232fe3cb834797019c9c77d5182c08 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 18 Nov 2024 23:10:21 +0100 Subject: [PATCH 035/106] actually save path settings in instance-local config --- src/frontend/qt_sdl/EmuInstance.cpp | 16 ++++++++-------- src/frontend/qt_sdl/PathSettingsDialog.cpp | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index a1d106b8..c6c63e42 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -686,7 +686,7 @@ std::string EmuInstance::getSavestateName(int slot) { std::string ext = ".ml"; ext += (char)('0'+slot); - return getAssetPath(false, globalCfg.GetString("SavestatePath"), ext); + return getAssetPath(false, localCfg.GetString("SavestatePath"), ext); } bool EmuInstance::savestateExists(int slot) @@ -752,7 +752,7 @@ bool EmuInstance::loadState(const std::string& filename) previousSaveFile = ndsSave->GetPath(); 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(); ndsSave->SetPath(savefile, true); } @@ -803,7 +803,7 @@ bool EmuInstance::saveState(const std::string& filename) if (globalCfg.GetBool("Savestate.RelocSRAM") && ndsSave) { 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(); ndsSave->SetPath(savefile, false); } @@ -839,7 +839,7 @@ void EmuInstance::loadCheats() { 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, ...) cheatFile = std::make_unique(filename); @@ -1405,7 +1405,7 @@ void EmuInstance::reset() if ((cartType != -1) && ndsSave) { 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(); if (oldsave != newsave) ndsSave->SetPath(newsave, false); @@ -1414,7 +1414,7 @@ void EmuInstance::reset() if ((gbaCartType != -1) && gbaSave) { 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(); if (oldsave != newsave) gbaSave->SetPath(newsave, false); @@ -1863,7 +1863,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset) u32 savelen = 0; std::unique_ptr savedata = nullptr; - std::string savname = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav"); + std::string savname = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav"); std::string origsav = savname; savname += instanceFileSuffix(); @@ -2022,7 +2022,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath) u32 savelen = 0; std::unique_ptr savedata = nullptr; - std::string savname = getAssetPath(true, globalCfg.GetString("SaveFilePath"), ".sav"); + std::string savname = getAssetPath(true, localCfg.GetString("SaveFilePath"), ".sav"); std::string origsav = savname; savname += instanceFileSuffix(); diff --git a/src/frontend/qt_sdl/PathSettingsDialog.cpp b/src/frontend/qt_sdl/PathSettingsDialog.cpp index f3a453d1..d0b42b21 100644 --- a/src/frontend/qt_sdl/PathSettingsDialog.cpp +++ b/src/frontend/qt_sdl/PathSettingsDialog.cpp @@ -45,7 +45,7 @@ PathSettingsDialog::PathSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne emuInstance = ((MainWindow*)parent)->getEmuInstance(); - auto& cfg = emuInstance->getGlobalConfig(); + auto& cfg = emuInstance->getLocalConfig(); ui->txtSaveFilePath->setText(cfg.GetQString("SaveFilePath")); ui->txtSavestatePath->setText(cfg.GetQString("SavestatePath")); ui->txtCheatFilePath->setText(cfg.GetQString("CheatFilePath")); @@ -108,7 +108,7 @@ void PathSettingsDialog::done(int r) QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) return; - auto& cfg = emuInstance->getGlobalConfig(); + auto& cfg = emuInstance->getLocalConfig(); cfg.SetQString("SaveFilePath", ui->txtSaveFilePath->text()); cfg.SetQString("SavestatePath", ui->txtSavestatePath->text()); cfg.SetQString("CheatFilePath", ui->txtCheatFilePath->text()); From d68091ee9cb51de4a9cb7b89a59e900084200499 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 18 Nov 2024 23:13:48 +0100 Subject: [PATCH 036/106] fix another oversight --- src/frontend/qt_sdl/InterfaceSettingsDialog.cpp | 8 ++++---- src/frontend/qt_sdl/Window.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp index bd02405e..a2c1bd71 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp @@ -35,9 +35,9 @@ InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(pare 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->setValue(cfg.GetInt("MouseHideSeconds")); + ui->spinMouseHideSeconds->setValue(cfg.GetInt("Mouse.HideSeconds")); ui->cbPauseLostFocus->setChecked(cfg.GetBool("PauseLostFocus")); ui->spinTargetFPS->setValue(cfg.GetDouble("TargetFPS")); ui->spinFFW->setValue(cfg.GetDouble("FastForwardFPS")); @@ -115,8 +115,8 @@ void InterfaceSettingsDialog::done(int r) { auto& cfg = emuInstance->getGlobalConfig(); - cfg.SetBool("MouseHide", ui->cbMouseHide->isChecked()); - cfg.SetInt("MouseHideSeconds", ui->spinMouseHideSeconds->value()); + cfg.SetBool("Mouse.Hide", ui->cbMouseHide->isChecked()); + cfg.SetInt("Mouse.HideSeconds", ui->spinMouseHideSeconds->value()); cfg.SetBool("PauseLostFocus", ui->cbPauseLostFocus->isChecked()); double val = ui->spinTargetFPS->value(); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 596a0f5c..eaeebf27 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1966,8 +1966,8 @@ void MainWindow::onUpdateInterfaceSettings() emuInstance->targetFPS = globalCfg.GetDouble("TargetFPS"); emuInstance->fastForwardFPS = globalCfg.GetDouble("FastForwardFPS"); emuInstance->slowmoFPS = globalCfg.GetDouble("SlowmoFPS"); - panel->setMouseHide(globalCfg.GetBool("MouseHide"), - globalCfg.GetInt("MouseHideSeconds")*1000); + panel->setMouseHide(globalCfg.GetBool("Mouse.Hide"), + globalCfg.GetInt("Mouse.HideSeconds")*1000); } void MainWindow::onInterfaceSettingsFinished(int res) From 259eb4b4080a41dcb4717507f0ff43ce65fab743 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 19 Nov 2024 00:11:46 +0100 Subject: [PATCH 037/106] dsfsdhgf --- src/frontend/qt_sdl/EmuInstance.cpp | 8 +++++--- src/frontend/qt_sdl/Window.cpp | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index c6c63e42..f690bdb8 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -245,6 +245,8 @@ void EmuInstance::deleteWindow(int id, bool close) if (close) win->close(); + if (deleting) return; + if (numWindows == 0) { // if we closed the last window, delete the instance @@ -1456,16 +1458,16 @@ void EmuInstance::reset() bool EmuInstance::bootToMenu() -{printf("bootToMenu 1\n"); +{ // Keep whatever cart is in the console, if any. if (!updateConsole()) // Try to update the console, but keep the existing cart. If that fails... return false; - printf("bootToMenu 2\n"); + // BIOS and firmware files are loaded, patched, and installed in UpdateConsole if (nds->NeedsDirectBoot()) return false; - printf("bootToMenu 3\n"); + initFirmwareSaveManager(); nds->Reset(); setBatteryLevels(); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index eaeebf27..71e4f15a 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -819,6 +819,8 @@ void MainWindow::saveEnabled(bool enabled) void MainWindow::closeEvent(QCloseEvent* event) { + if (!emuInstance) return; + if (windowID == 0) emuInstance->saveEnabledWindows(); else From 317b91533b1547e9cb4e427a468da5d1d576ba39 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 19 Nov 2024 00:33:39 +0100 Subject: [PATCH 038/106] avoid spawning message boxes from the emuthread --- src/frontend/qt_sdl/EmuInstance.cpp | 37 ++++++++++-------- src/frontend/qt_sdl/EmuInstance.h | 8 ++-- src/frontend/qt_sdl/EmuThread.cpp | 28 +++++++++----- src/frontend/qt_sdl/EmuThread.h | 9 +++-- src/frontend/qt_sdl/Window.cpp | 58 ++++++++++++++++++++++------- 5 files changed, 94 insertions(+), 46 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index f690bdb8..309abe66 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -29,7 +29,6 @@ #include #include -#include #include #ifdef ARCHIVE_SUPPORT_ENABLED @@ -1457,16 +1456,22 @@ void EmuInstance::reset() } -bool EmuInstance::bootToMenu() +bool EmuInstance::bootToMenu(QString& errorstr) { // Keep whatever cart is in the console, if any. if (!updateConsole()) + { // Try to update the console, but keep the existing cart. If that fails... + errorstr = "Failed to boot the firmware."; return false; + } // BIOS and firmware files are loaded, patched, and installed in UpdateConsole if (nds->NeedsDirectBoot()) + { + errorstr = "This firmware is not bootable."; return false; + } initFirmwareSaveManager(); nds->Reset(); @@ -1843,7 +1848,7 @@ QString EmuInstance::getSavErrorString(std::string& filepath, bool gba) return QString::fromStdString(err1); } -bool EmuInstance::loadROM(QStringList filepath, bool reset) +bool EmuInstance::loadROM(QStringList filepath, bool reset, QString& errorstr) { unique_ptr filedata = nullptr; u32 filelen; @@ -1852,7 +1857,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset) 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; } @@ -1874,7 +1879,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset) { if (!Platform::CheckFileWritable(origsav)) { - QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(origsav, false)); + errorstr = getSavErrorString(origsav, false); return false; } @@ -1882,7 +1887,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset) } else if (!Platform::CheckFileWritable(savname)) { - QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(savname, false)); + errorstr = getSavErrorString(savname, false); return false; } @@ -1909,7 +1914,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset) if (!cart) { // 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; } @@ -1920,7 +1925,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset) if (!updateConsole()) { - QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM."); + errorstr = "Failed to load the DS ROM."; return false; } @@ -1996,11 +2001,11 @@ QString EmuInstance::cartLabel() } -bool EmuInstance::loadGBAROM(QStringList filepath) +bool EmuInstance::loadGBAROM(QStringList filepath, QString& errorstr) { 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; } @@ -2011,7 +2016,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath) 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; } @@ -2033,7 +2038,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath) { if (!Platform::CheckFileWritable(origsav)) { - QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(origsav, true)); + errorstr = getSavErrorString(origsav, true); return false; } @@ -2041,7 +2046,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath) } else if (!Platform::CheckFileWritable(savname)) { - QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(savname, true)); + errorstr = getSavErrorString(savname, true); return false; } @@ -2061,7 +2066,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath) auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen, this); if (!cart) { - QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM."); + errorstr = "Failed to load the GBA ROM."; return false; } @@ -2080,14 +2085,14 @@ bool EmuInstance::loadGBAROM(QStringList filepath) return true; } -void EmuInstance::loadGBAAddon(int type) +void EmuInstance::loadGBAAddon(int type, QString& errorstr) { if (consoleType == 1) return; auto cart = GBACart::LoadAddon(type, this); if (!cart) { - QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA addon."); + errorstr = "Failed to load the GBA addon."; return; } diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 35943492..48e4e5b9 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -182,7 +182,7 @@ private: std::optional loadSDCard(const std::string& key) noexcept; void setBatteryLevels(); void reset(); - bool bootToMenu(); + bool bootToMenu(QString& errorstr); melonDS::u32 decompressROM(const melonDS::u8* inContent, const melonDS::u32 inSize, std::unique_ptr& outContent); void clearBackupState(); std::pair, std::string> generateDefaultFirmware(); @@ -191,13 +191,13 @@ private: bool loadROMData(const QStringList& filepath, std::unique_ptr& filedata, melonDS::u32& filelen, std::string& basepath, std::string& romname) noexcept; QString getSavErrorString(std::string& filepath, bool gba); - bool loadROM(QStringList filepath, bool reset); + bool loadROM(QStringList filepath, bool reset, QString& errorstr); void ejectCart(); bool cartInserted(); QString cartLabel(); - bool loadGBAROM(QStringList filepath); - void loadGBAAddon(int type); + bool loadGBAROM(QStringList filepath, QString& errorstr); + void loadGBAAddon(int type, QString& errorstr); void ejectGBACart(); bool gbaCartInserted(); QString gbaAddonName(int addon); diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index a7feffce..88e20238 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -577,7 +577,7 @@ void EmuThread::handleMessages() case msg_BootROM: msgResult = 0; - if (!emuInstance->loadROM(msg.param.value(), true)) + if (!emuInstance->loadROM(msg.param.value(), true, msgError)) break; assert(emuInstance->nds != nullptr); @@ -587,7 +587,7 @@ void EmuThread::handleMessages() case msg_BootFirmware: msgResult = 0; - if (!emuInstance->bootToMenu()) + if (!emuInstance->bootToMenu(msgError)) break; assert(emuInstance->nds != nullptr); @@ -597,7 +597,7 @@ void EmuThread::handleMessages() case msg_InsertCart: msgResult = 0; - if (!emuInstance->loadROM(msg.param.value(), false)) + if (!emuInstance->loadROM(msg.param.value(), false, msgError)) break; msgResult = 1; @@ -609,7 +609,7 @@ void EmuThread::handleMessages() case msg_InsertGBACart: msgResult = 0; - if (!emuInstance->loadGBAROM(msg.param.value())) + if (!emuInstance->loadGBAROM(msg.param.value(), msgError)) break; msgResult = 1; @@ -617,7 +617,7 @@ void EmuThread::handleMessages() case msg_InsertGBAAddon: msgResult = 0; - emuInstance->loadGBAAddon(msg.param.value()); + emuInstance->loadGBAAddon(msg.param.value(), msgError); msgResult = 1; break; @@ -753,36 +753,45 @@ bool EmuThread::emuIsActive() return emuActive; } -int EmuThread::bootROM(const QStringList& filename) +int EmuThread::bootROM(const QStringList& filename, QString& errorstr) { sendMessage({.type = msg_BootROM, .param = filename}); waitMessage(); if (!msgResult) + { + errorstr = msgError; return msgResult; + } sendMessage(msg_EmuRun); waitMessage(); + errorstr = ""; return msgResult; } -int EmuThread::bootFirmware() +int EmuThread::bootFirmware(QString& errorstr) { sendMessage(msg_BootFirmware); waitMessage(); if (!msgResult) + { + errorstr = msgError; return msgResult; + } sendMessage(msg_EmuRun); waitMessage(); + errorstr = ""; 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; sendMessage({.type = msgtype, .param = filename}); waitMessage(); + errorstr = msgResult ? "" : msgError; return msgResult; } @@ -792,10 +801,11 @@ void EmuThread::ejectCart(bool gba) waitMessage(); } -int EmuThread::insertGBAAddon(int type) +int EmuThread::insertGBAAddon(int type, QString& errorstr) { sendMessage({.type = msg_InsertGBAAddon, .param = type}); waitMessage(); + errorstr = msgResult ? "" : msgError; return msgResult; } diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index 9fa95f3e..61212553 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -111,11 +111,11 @@ public: void emuFrameStep(); void emuReset(); - int bootROM(const QStringList& filename); - int bootFirmware(); - int insertCart(const QStringList& filename, bool gba); + int bootROM(const QStringList& filename, QString& errorstr); + int bootFirmware(QString& errorstr); + int insertCart(const QStringList& filename, bool gba, QString& errorstr); void ejectCart(bool gba); - int insertGBAAddon(int type); + int insertGBAAddon(int type, QString& errorstr); int saveState(const QString& filename); int loadState(const QString& filename); @@ -179,6 +179,7 @@ private: int emuPauseStack; int msgResult = 0; + QString msgError; QMutex msgMutex; QSemaphore msgSemaphore; diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 71e4f15a..a80b385c 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -986,10 +986,12 @@ void MainWindow::dropEvent(QDropEvent* event) isNdsRom |= ZstdNdsRomByExtension(filename); isGbaRom |= ZstdGbaRomByExtension(filename); + QString errorstr; if (isNdsRom) { - if (!emuThread->bootROM(file)) + if (!emuThread->bootROM(file, errorstr)) { + QMessageBox::critical(this, "melonDS", errorstr); return; } @@ -1002,8 +1004,9 @@ void MainWindow::dropEvent(QDropEvent* event) } else if (isGbaRom) { - if (!emuThread->insertCart(file, true)) + if (!emuThread->insertCart(file, true, errorstr)) { + QMessageBox::critical(this, "melonDS", errorstr); return; } @@ -1071,6 +1074,8 @@ bool MainWindow::verifySetup() bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) { + QString errorstr; + if (file.isEmpty() && gbafile.isEmpty()) return false; @@ -1082,8 +1087,11 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) bool gbaloaded = false; if (!gbafile.isEmpty()) { - if (!emuThread->insertCart(gbafile, true)) + if (!emuThread->insertCart(gbafile, true, errorstr)) + { + QMessageBox::critical(this, "melonDS", errorstr); return false; + } gbaloaded = true; } @@ -1093,13 +1101,19 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) { if (boot) { - if (!emuThread->bootROM(file)) + if (!emuThread->bootROM(file, errorstr)) + { + QMessageBox::critical(this, "melonDS", errorstr); return false; + } } else { - if (!emuThread->insertCart(file, false)) + if (!emuThread->insertCart(file, false, errorstr)) + { + QMessageBox::critical(this, "melonDS", errorstr); return false; + } } recentFileList.removeAll(file.join("|")); @@ -1109,8 +1123,11 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) } else if (boot) { - if (!emuThread->bootFirmware()) + if (!emuThread->bootFirmware(errorstr)) + { + QMessageBox::critical(this, "melonDS", errorstr); return false; + } } updateCartInserted(false); @@ -1308,8 +1325,10 @@ void MainWindow::onOpenFile() if (file.isEmpty()) return; - if (!emuThread->bootROM(file)) + QString errorstr; + if (!emuThread->bootROM(file, errorstr)) { + QMessageBox::critical(this, "melonDS", errorstr); return; } @@ -1419,8 +1438,10 @@ void MainWindow::onClickRecentFile() if (file.isEmpty()) return; - if (!emuThread->bootROM(file)) + QString errorstr; + if (!emuThread->bootROM(file, errorstr)) { + QMessageBox::critical(this, "melonDS", errorstr); return; } @@ -1436,9 +1457,10 @@ void MainWindow::onBootFirmware() if (!verifySetup()) 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; } } @@ -1449,8 +1471,10 @@ void MainWindow::onInsertCart() if (file.isEmpty()) return; - if (!emuThread->insertCart(file, false)) + QString errorstr; + if (!emuThread->insertCart(file, false, errorstr)) { + QMessageBox::critical(this, "melonDS", errorstr); return; } @@ -1469,8 +1493,10 @@ void MainWindow::onInsertGBACart() if (file.isEmpty()) return; - if (!emuThread->insertCart(file, true)) + QString errorstr; + if (!emuThread->insertCart(file, true, errorstr)) { + QMessageBox::critical(this, "melonDS", errorstr); return; } @@ -1482,7 +1508,13 @@ void MainWindow::onInsertGBAAddon() QAction* act = (QAction*)sender(); int type = act->data().toInt(); - emuThread->insertGBAAddon(type); + QString errorstr; + if (!emuThread->insertGBAAddon(type, errorstr)) + { + QMessageBox::critical(this, "melonDS", errorstr); + return; + } + updateCartInserted(true); } From c4f7c1bff7e68231970b2a73f215287c4873fb2d Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 19 Nov 2024 00:34:16 +0100 Subject: [PATCH 039/106] fix compiling without JIT --- src/frontend/qt_sdl/EmuSettingsDialog.cpp | 7 ++++++- src/frontend/qt_sdl/Window.cpp | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index ed5eba10..30b92fd1 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -536,9 +536,14 @@ void EmuSettingsDialog::on_btnDSiSDFolderBrowse_clicked() void EmuSettingsDialog::on_chkEnableJIT_toggled() { bool disabled = !ui->chkEnableJIT->isChecked(); +#ifdef JIT_ENABLED + bool fastmemSupported = ARMJIT_Memory::IsFastMemSupported(); +#else + bool fastmemSupported = false; +#endif ui->chkJITBranchOptimisations->setDisabled(disabled); ui->chkJITLiteralOptimisations->setDisabled(disabled); - ui->chkJITFastMemory->setDisabled(disabled || !ARMJIT_Memory::IsFastMemSupported()); + ui->chkJITFastMemory->setDisabled(disabled || !fastmemSupported); ui->spnJITMaximumBlockSize->setDisabled(disabled); on_cbGdbEnabled_toggled(); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index a80b385c..a156a993 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -857,7 +857,7 @@ void MainWindow::createScreenPanel() // Check that creating the context hasn't failed 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; globalCfg.SetBool("Screen.UseGL", false); From 13096f9fdc7e832697ef2003756450229288e2b0 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 19 Nov 2024 00:50:42 +0100 Subject: [PATCH 040/106] add warnings to the LAN dialogs --- src/frontend/qt_sdl/LANStartClientDialog.ui | 17 +++++++++++----- src/frontend/qt_sdl/LANStartHostDialog.ui | 22 +++++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/frontend/qt_sdl/LANStartClientDialog.ui b/src/frontend/qt_sdl/LANStartClientDialog.ui index eeea25e9..e32714f2 100644 --- a/src/frontend/qt_sdl/LANStartClientDialog.ui +++ b/src/frontend/qt_sdl/LANStartClientDialog.ui @@ -7,7 +7,7 @@ 0 0 547 - 409 + 407 @@ -48,7 +48,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -63,17 +63,24 @@ - QAbstractItemView::NoEditTriggers + QAbstractItemView::EditTrigger::NoEditTriggers + + + + + + + <html><head/><body><p>Warning: LAN requires low network latency to work.</p><p>Do not expect it to work through a VPN or any sort of tunnel.</p></body></html> - Qt::Horizontal + Qt::Orientation::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok diff --git a/src/frontend/qt_sdl/LANStartHostDialog.ui b/src/frontend/qt_sdl/LANStartHostDialog.ui index 0d6cd50c..d9adc3a1 100644 --- a/src/frontend/qt_sdl/LANStartHostDialog.ui +++ b/src/frontend/qt_sdl/LANStartHostDialog.ui @@ -7,7 +7,7 @@ 0 0 389 - 228 + 202 @@ -21,7 +21,7 @@ - QLayout::SetFixedSize + QLayout::SizeConstraint::SetFixedSize @@ -45,15 +45,29 @@ + + + + <html><head/><body><p>Warning: LAN requires low network latency to work.</p><p>Do not expect it to work through a VPN or any sort of tunnel.</p></body></html> + + + + + + + + + + - Qt::Horizontal + Qt::Orientation::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok From 1b3f4664d871c54e2ca9fb6144bfb35eccff9081 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 19 Nov 2024 00:57:53 +0100 Subject: [PATCH 041/106] fix order of ScreenLayoutType --- src/frontend/ScreenLayout.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/ScreenLayout.h b/src/frontend/ScreenLayout.h index 7b69bd40..a813127d 100644 --- a/src/frontend/ScreenLayout.h +++ b/src/frontend/ScreenLayout.h @@ -22,8 +22,8 @@ enum ScreenLayoutType { screenLayout_Natural, // top screen above bottom screen always - screenLayout_Horizontal, screenLayout_Vertical, + screenLayout_Horizontal, screenLayout_Hybrid, screenLayout_MAX, }; From 86c6740b24b6f57c1e5df592575abd2546dfaa28 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 19 Nov 2024 01:07:27 +0100 Subject: [PATCH 042/106] fuck that shit --- src/frontend/qt_sdl/Config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 7d5331fc..0163dc87 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -632,7 +632,7 @@ void Table::SetString(const std::string& path, const std::string& val) void Table::SetDouble(const std::string& path, double val) { toml::value& tval = ResolvePath(path); - tval = val; + tval = toml::value(val, {.prec=10}); } toml::value& Table::ResolvePath(const std::string& path) From d0d010b09db42db0aab24b5ac2ffa1cc3107b1c7 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Wed, 20 Nov 2024 02:55:40 +0100 Subject: [PATCH 043/106] don't use std::map and std::function in scheduler --- src/DSi_Camera.cpp | 8 ++++---- src/DSi_DSP.cpp | 4 ++-- src/DSi_NWifi.cpp | 4 ++-- src/DSi_SD.cpp | 19 +++++++------------ src/GPU.cpp | 17 +++++++++-------- src/NDS.cpp | 31 +++++++++++++++++++------------ src/NDS.h | 14 +++++++++----- src/NDSCart.cpp | 14 ++++++++------ src/RTC.cpp | 4 ++-- src/SPI.cpp | 4 ++-- src/SPU.cpp | 4 ++-- src/Wifi.cpp | 4 ++-- 12 files changed, 68 insertions(+), 59 deletions(-) diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index b1d60d04..8a54bb18 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -40,8 +40,8 @@ const u32 DSi_CamModule::kTransferStart = 60000; DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi) { - DSi.RegisterEventFunc(Event_DSi_CamIRQ, 0, MemberEventFunc(DSi_CamModule, IRQ)); - DSi.RegisterEventFunc(Event_DSi_CamTransfer, 0, MemberEventFunc(DSi_CamModule, TransferScanline)); + DSi.RegisterEventFuncs(Event_DSi_CamIRQ, this, {MakeEventThunk(DSi_CamModule, IRQ)}); + DSi.RegisterEventFuncs(Event_DSi_CamTransfer, this, {MakeEventThunk(DSi_CamModule, TransferScanline)}); Camera0 = DSi.I2C.GetOuterCamera(); Camera1 = DSi.I2C.GetInnerCamera(); @@ -52,8 +52,8 @@ DSi_CamModule::~DSi_CamModule() Camera0 = nullptr; Camera1 = nullptr; - DSi.UnregisterEventFunc(Event_DSi_CamIRQ, 0); - DSi.UnregisterEventFunc(Event_DSi_CamTransfer, 0); + DSi.UnregisterEventFuncs(Event_DSi_CamIRQ); + DSi.UnregisterEventFuncs(Event_DSi_CamTransfer); } void DSi_CamModule::Reset() diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp index 25abd474..24fa08b3 100644 --- a/src/DSi_DSP.cpp +++ b/src/DSi_DSP.cpp @@ -109,7 +109,7 @@ void DSi_DSP::AudioCb(std::array frame) 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(); SCFG_RST = false; @@ -156,7 +156,7 @@ DSi_DSP::~DSi_DSP() //PDATAWriteFifo = NULL; TeakraCore = NULL; - DSi.UnregisterEventFunc(Event_DSi_DSP, 0); + DSi.UnregisterEventFuncs(Event_DSi_DSP); } void DSi_DSP::Reset() diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 9cfb0203..3ec5d3f0 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -134,7 +134,7 @@ DSi_NWifi::DSi_NWifi(melonDS::DSi& dsi, DSi_SDHost* host) : }, 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 EEPROMReady = 0; @@ -144,7 +144,7 @@ DSi_NWifi::~DSi_NWifi() { DSi.CancelEvent(Event_DSi_NWifi); - DSi.UnregisterEventFunc(Event_DSi_NWifi, 0); + DSi.UnregisterEventFuncs(Event_DSi_NWifi); } void DSi_NWifi::Reset() diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index c600bc76..c1d07682 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -64,10 +64,9 @@ enum DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional&& sdcard) noexcept : DSi(dsi), Num(0) { - DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer, - Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX)); - DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer, - Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX)); + DSi.RegisterEventFuncs(Event_DSi_SDMMCTransfer, this, + {MakeEventThunk(DSi_SDHost, FinishTX), + MakeEventThunk(DSi_SDHost, FinishRX)}); Ports[0] = sdcard ? std::make_unique(DSi, this, std::move(*sdcard)) : nullptr; 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 DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1) { - DSi.RegisterEventFunc(Event_DSi_SDIOTransfer , - Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX)); - DSi.RegisterEventFunc(Event_DSi_SDIOTransfer, - Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX)); + DSi.RegisterEventFuncs(Event_DSi_SDIOTransfer, this, + {MakeEventThunk(DSi_SDHost, FinishTX), + MakeEventThunk(DSi_SDHost, FinishRX)}); Ports[0] = std::make_unique(DSi, this); Ports[1] = nullptr; @@ -88,10 +86,7 @@ DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1) DSi_SDHost::~DSi_SDHost() { - DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, - Transfer_TX); - DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, - Transfer_RX); + DSi.UnregisterEventFuncs(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer); // unique_ptr's destructor will clean up the ports } diff --git a/src/GPU.cpp b/src/GPU.cpp index f24d8ab5..585f0bea 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -70,10 +70,13 @@ GPU::GPU(melonDS::NDS& nds, std::unique_ptr&& renderer3d, std::uniqu GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique()), GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique(*this)) { - NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank)); - NDS.RegisterEventFunc(Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline)); - NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame)); - NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO)); + NDS.RegisterEventFuncs(Event_LCD, this, + { + MakeEventThunk(GPU, StartHBlank), + MakeEventThunk(GPU, StartScanline), + MakeEventThunk(GPU, FinishFrame) + }); + NDS.RegisterEventFuncs(Event_DisplayFIFO, this, {MakeEventThunk(GPU, DisplayFIFO)}); InitFramebuffers(); } @@ -82,10 +85,8 @@ GPU::~GPU() noexcept { // All unique_ptr fields are automatically cleaned up - NDS.UnregisterEventFunc(Event_LCD, LCD_StartHBlank); - NDS.UnregisterEventFunc(Event_LCD, LCD_StartScanline); - NDS.UnregisterEventFunc(Event_LCD, LCD_FinishFrame); - NDS.UnregisterEventFunc(Event_DisplayFIFO, 0); + NDS.UnregisterEventFuncs(Event_LCD); + NDS.UnregisterEventFuncs(Event_DisplayFIFO); } void GPU::ResetVRAMCache() noexcept diff --git a/src/NDS.cpp b/src/NDS.cpp index b9370c6b..7cef3bf0 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -122,19 +122,18 @@ NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept : DMA(1, 3, *this), } { - RegisterEventFunc(Event_Div, 0, MemberEventFunc(NDS, DivDone)); - RegisterEventFunc(Event_Sqrt, 0, MemberEventFunc(NDS, SqrtDone)); + RegisterEventFuncs(Event_Div, this, {MakeEventThunk(NDS, DivDone)}); + RegisterEventFuncs(Event_Sqrt, this, {MakeEventThunk(NDS, SqrtDone)}); MainRAM = JIT.Memory.GetMainRAM(); SharedWRAM = JIT.Memory.GetSharedWRAM(); ARM7WRAM = JIT.Memory.GetARM7WRAM(); - } NDS::~NDS() noexcept { - UnregisterEventFunc(Event_Div, 0); - UnregisterEventFunc(Event_Sqrt, 0); + UnregisterEventFuncs(Event_Div); + UnregisterEventFuncs(Event_Sqrt); // The destructor for each component is automatically called by the compiler } @@ -819,7 +818,7 @@ void NDS::RunSystem(u64 timestamp) SchedListMask &= ~(1<& funcs) { 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]; - 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) @@ -1093,7 +1100,7 @@ void NDS::ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param) if (SchedListMask & (1< EventFunc; -#define MemberEventFunc(cls,func) std::bind(&cls::func,this,std::placeholders::_1) +static constexpr u32 MaxEventFunctions = 3; + +typedef void (*EventFunc)(void* that, u32 param); +#define MakeEventThunk(class, func) [](void* that, u32 param) { static_cast(that)->func(param); } + struct SchedEvent { - std::map Funcs; + std::array Funcs; + void* That; u64 Timestamp; u32 FuncID; u32 Param; @@ -401,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) {} void MicInputFrame(s16* data, int samples); - void RegisterEventFunc(u32 id, u32 funcid, EventFunc func); - void UnregisterEventFunc(u32 id, u32 funcid); + void RegisterEventFuncs(u32 id, void* that, const std::initializer_list& funcs); + void UnregisterEventFuncs(u32 id); void ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param); void CancelEvent(u32 id); diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 97de4580..1fa0fbfe 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -1441,9 +1441,12 @@ void CartHomebrew::ROMCommandFinish(const u8* cmd, u8* data, u32 len) NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr&& rom) noexcept : NDS(nds) { - NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData, MemberEventFunc(NDSCartSlot, ROMPrepareData)); - NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_End, MemberEventFunc(NDSCartSlot, ROMEndTransfer)); - NDS.RegisterEventFunc(Event_ROMSPITransfer, 0, MemberEventFunc(NDSCartSlot, SPITransferDone)); + NDS.RegisterEventFuncs(Event_ROMTransfer, this, + { + 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 if (rom) @@ -1452,9 +1455,8 @@ NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr&& rom) n NDSCartSlot::~NDSCartSlot() noexcept { - NDS.UnregisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData); - NDS.UnregisterEventFunc(Event_ROMTransfer, ROMTransfer_End); - NDS.UnregisterEventFunc(Event_ROMSPITransfer, 0); + NDS.UnregisterEventFuncs(Event_ROMTransfer); + NDS.UnregisterEventFuncs(Event_ROMSPITransfer); // Cart is cleaned up automatically because it's a unique_ptr } diff --git a/src/RTC.cpp b/src/RTC.cpp index 24e00de7..c10672fb 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -34,7 +34,7 @@ void WriteDateTime(int num, u8 val); RTC::RTC(melonDS::NDS& nds) : NDS(nds) { - NDS.RegisterEventFunc(Event_RTC, 0, MemberEventFunc(RTC, ClockTimer)); + NDS.RegisterEventFuncs(Event_RTC, this, {MakeEventThunk(RTC, ClockTimer)}); ResetState(); @@ -45,7 +45,7 @@ RTC::RTC(melonDS::NDS& nds) : NDS(nds) RTC::~RTC() { - NDS.UnregisterEventFunc(Event_RTC, 0); + NDS.UnregisterEventFuncs(Event_RTC); } void RTC::Reset() diff --git a/src/SPI.cpp b/src/SPI.cpp index 6ab94c3a..67b63e2a 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -474,7 +474,7 @@ void TSC::Write(u8 val) 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_PowerMan] = new PowerMan(NDS); @@ -495,7 +495,7 @@ SPIHost::~SPIHost() Devices[i] = nullptr; } - NDS.UnregisterEventFunc(Event_SPITransfer, 0); + NDS.UnregisterEventFuncs(Event_SPITransfer); } void SPIHost::Reset() diff --git a/src/SPU.cpp b/src/SPU.cpp index eb30c0a9..5b245890 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -202,7 +202,7 @@ SPU::SPU(melonDS::NDS& nds, AudioBitDepth bitdepth, AudioInterpolation interpola AudioLock(Platform::Mutex_Create()), 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; Degrade10Bit = false; @@ -219,7 +219,7 @@ SPU::~SPU() Platform::Mutex_Free(AudioLock); AudioLock = nullptr; - NDS.UnregisterEventFunc(Event_SPU, 0); + NDS.UnregisterEventFuncs(Event_SPU); } void SPU::Reset() diff --git a/src/Wifi.cpp b/src/Wifi.cpp index 3eaa9fd3..77933dfb 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -91,7 +91,7 @@ bool MACIsBroadcast(const u8* a) 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); } @@ -100,7 +100,7 @@ Wifi::~Wifi() { delete WifiAP; WifiAP = nullptr; - NDS.UnregisterEventFunc(Event_Wifi, 0); + NDS.UnregisterEventFuncs(Event_Wifi); } void Wifi::Reset() From 97a00e31376238a390c69fe724df632cf2447e68 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 21 Nov 2024 00:21:47 +0100 Subject: [PATCH 044/106] BAHAHAHAHAHAHSKSHFOS-#%~/%% --- .github/workflows/build-macos.yml | 1 + .github/workflows/build-ubuntu.yml | 1 + .github/workflows/build-windows.yml | 1 + CMakeLists.txt | 2 +- flake.nix | 2 +- 5 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index f47b3a4a..14209baf 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -13,6 +13,7 @@ env: MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_BUILD_PROVIDER: GitHub Actions + MELONDS_VERSION_SUFFIX: " RC" jobs: build-macos: diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 1104142d..a0b77e64 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -12,6 +12,7 @@ env: MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_BUILD_PROVIDER: GitHub Actions + MELONDS_VERSION_SUFFIX: " RC" jobs: build-x86_64: diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index c3350b4d..077fb152 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -13,6 +13,7 @@ env: MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_BUILD_PROVIDER: GitHub Actions + MELONDS_VERSION_SUFFIX: " RC" jobs: build: diff --git a/CMakeLists.txt b/CMakeLists.txt index 55bf825f..c54b6450 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ if (USE_VCPKG) endif() project(melonDS - VERSION 0.9.5 + VERSION 1.0 DESCRIPTION "DS emulator, sorta" HOMEPAGE_URL "https://melonds.kuribo64.net" LANGUAGES C CXX) diff --git a/flake.nix b/flake.nix index a7768e4e..7d17bb27 100644 --- a/flake.nix +++ b/flake.nix @@ -21,7 +21,7 @@ melonDS = pkgs.stdenv.mkDerivation { pname = "melonDS"; - version = "0.9.5-${shortRevision}"; + version = "1.0-${shortRevision}"; src = ./.; nativeBuildInputs = with pkgs; [ From dc10df0796f4d5e804b5e21ec5978d5de09b4742 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 21 Nov 2024 00:27:30 +0100 Subject: [PATCH 045/106] FUCK THAT SHIT --- src/frontend/qt_sdl/Config.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 0163dc87..73161809 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -632,7 +632,8 @@ void Table::SetString(const std::string& path, const std::string& val) void Table::SetDouble(const std::string& path, double val) { toml::value& tval = ResolvePath(path); - tval = toml::value(val, {.prec=10}); + toml::floating_format_info info = {.prec=10}; + tval = toml::value(val, info); } toml::value& Table::ResolvePath(const std::string& path) From 98ceadd44c93c5d629175140f3777dae18551890 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 21 Nov 2024 00:37:22 +0100 Subject: [PATCH 046/106] really?! --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c54b6450..02a0e420 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ if (USE_VCPKG) endif() project(melonDS - VERSION 1.0 + VERSION 1.0.0 DESCRIPTION "DS emulator, sorta" HOMEPAGE_URL "https://melonds.kuribo64.net" LANGUAGES C CXX) From e3fa6f4224e0d706df3ee262ae41cfb0deadc593 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Thu, 21 Nov 2024 01:10:13 +0100 Subject: [PATCH 047/106] Fix version number in the generated Windows resource --- CMakeLists.txt | 2 +- res/melon.rc.in | 4 ++-- src/frontend/qt_sdl/CMakeLists.txt | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 02a0e420..c54b6450 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ if (USE_VCPKG) endif() project(melonDS - VERSION 1.0.0 + VERSION 1.0 DESCRIPTION "DS emulator, sorta" HOMEPAGE_URL "https://melonds.kuribo64.net" LANGUAGES C CXX) diff --git a/res/melon.rc.in b/res/melon.rc.in index eb437b32..299933cb 100644 --- a/res/melon.rc.in +++ b/res/melon.rc.in @@ -6,8 +6,8 @@ //include version information in .exe, modify these values to match your needs 1 VERSIONINFO -FILEVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0 -PRODUCTVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0 +FILEVERSION ${MELON_RC_VERSION} +PRODUCTVERSION ${MELON_RC_VERSION} FILETYPE VFT_APP { BLOCK "StringFileInfo" diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index fa1fafd8..c2252b21 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -190,6 +190,7 @@ if (WIN32) target_compile_definitions(melonDS PRIVATE WIN32_PORTABLE) endif() + string(REPLACE . , MELON_RC_VERSION ${melonDS_VERSION}) 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_include_directories(melonDS PRIVATE "${CMAKE_BINARY_DIR}/res") From 6a15dbfa12186a99573e64a7c744b035210ce4d2 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Fri, 22 Nov 2024 03:33:35 +0100 Subject: [PATCH 048/106] unmappinged everything --- src/ARMJIT_Memory.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index e1c1da43..ec1ea4eb 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -951,7 +951,6 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept MemoryBase = nullptr; FastMem9Start = nullptr; FastMem7Start = nullptr; - printf("unmappinged everything\n"); } if (MemoryFile) @@ -979,6 +978,8 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept MemoryFile = -1; } + Log(LogLevel::Info, "unmappinged everything\n"); + #if defined(__ANDROID__) if (Libandroid) { From 0db536c063fa7bb9f81abfd2dc9619dfaa033aed Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sat, 23 Nov 2024 12:40:02 +0100 Subject: [PATCH 049/106] Set _WIN32_WINNT to Windows 8 when JIT is enabled (fixes #2209) --- src/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fa8d475c..fef78706 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -102,6 +102,8 @@ if (ENABLE_JIT) dolphin/CommonFuncs.cpp) if (WIN32) + # Required for memory mapping-related functions introduced in Windows 8 + target_compile_definitions(core PRIVATE -D_WIN32_WINNT=_WIN32_WINNT_WIN8) target_link_libraries(core PRIVATE onecore) endif() From 1d6c9023ff3067b9b3b1a3861b738cde4b21a6be Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sat, 23 Nov 2024 12:43:06 +0100 Subject: [PATCH 050/106] get rid of the incorrect CLOCK_MONOTONIC redefinition --- src/net/Net_Slirp.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/net/Net_Slirp.cpp b/src/net/Net_Slirp.cpp index 0386c586..cdd1e553 100644 --- a/src/net/Net_Slirp.cpp +++ b/src/net/Net_Slirp.cpp @@ -54,7 +54,6 @@ const u8 kServerMAC[6] = {0x00, 0xAB, 0x33, 0x28, 0x99, 0x44}; // https://stackoverflow.com/questions/5404277/porting-clock-gettime-to-windows struct timespec { long tv_sec; long tv_nsec; }; -#define CLOCK_MONOTONIC 1312 int clock_gettime(int, struct timespec *spec) { From 730b488fe3208b1de98f12130a1c9797d8456388 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sat, 23 Nov 2024 14:41:25 +0100 Subject: [PATCH 051/106] vcpkg 2024.11.16 & update nixpkgs --- .github/workflows/build-macos.yml | 2 +- .github/workflows/build-windows.yml | 2 +- cmake/ConfigureVcpkg.cmake | 2 +- flake.lock | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 14209baf..634f1b18 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -34,7 +34,7 @@ jobs: - name: Set up vcpkg uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: 10b7a178346f3f0abef60cecd5130e295afd8da4 + vcpkgGitCommitId: b2cb0da531c2f1f740045bfe7c4dac59f0b2b69c - name: Build uses: lukka/run-cmake@v10 with: diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 077fb152..6a5ac754 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -33,7 +33,7 @@ jobs: - name: Set up vcpkg uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: 10b7a178346f3f0abef60cecd5130e295afd8da4 + vcpkgGitCommitId: b2cb0da531c2f1f740045bfe7c4dac59f0b2b69c - name: Configure run: cmake --preset=release-mingw-x86_64 -DMELONDS_EMBED_BUILD_INFO=ON - name: Build diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake index 3fb0786f..d09749ab 100644 --- a/cmake/ConfigureVcpkg.cmake +++ b/cmake/ConfigureVcpkg.cmake @@ -9,7 +9,7 @@ if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}") endif() FetchContent_Declare(vcpkg GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git" - GIT_TAG 2024.10.21 + GIT_TAG 2024.11.16 SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg") FetchContent_MakeAvailable(vcpkg) endif() diff --git a/flake.lock b/flake.lock index d7dd5bc6..5ba6ab84 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1726560853, - "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1730531603, - "narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=", + "lastModified": 1732014248, + "narHash": "sha256-y/MEyuJ5oBWrWAic/14LaIr/u5E0wRVzyYsouYY3W6w=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d", + "rev": "23e89b7da85c3640bbc2173fe04f4bd114342367", "type": "github" }, "original": { From cba838dd52a8d163c59e312d75f451a04cea9b48 Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Wed, 27 Nov 2024 13:15:18 +0000 Subject: [PATCH 052/106] TitleManager: fix handling of title string Truncate the title at the first occurrence of \0, as title strings should be null-terminated. Fixes #2219 (Weird characters on DSi Title Manager on melonDS 1.0RC) --- src/frontend/qt_sdl/TitleManagerDialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp index 30f29d24..fbff261c 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.cpp +++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp @@ -119,6 +119,7 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid) // TODO: make it possible to select other languages? QString title = QString::fromUtf16(banner.EnglishTitle, 128); + title = title.left(title.indexOf('\0')); title.replace("\n", " · "); char gamecode[5]; From 817b409ec893fb0b2b745ee18feced08706419de Mon Sep 17 00:00:00 2001 From: Jakly <102590697+Jaklyy@users.noreply.github.com> Date: Fri, 29 Nov 2024 20:54:54 -0500 Subject: [PATCH 053/106] ah. (#2225) --- src/NDS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NDS.cpp b/src/NDS.cpp index 7cef3bf0..a49d2e17 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -875,7 +875,7 @@ void NDS::RunSystemSleep(u64 timestamp) param = evt.Param; EventFunc func = evt.Funcs[evt.FuncID]; - func(this, param); + func(evt.That, param); } } } From 7d718ada390bc54749fa35389437147e1bbefae1 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Thu, 5 Dec 2024 15:38:44 +0100 Subject: [PATCH 054/106] cmake: set default CMAKE_OSX_DEPLOYMENT_TARGET before project() project() appears to set it to an empty string (the value of nonexistent $ENV{MACOSX_DEPLOYMENT_TARGET}?), causing our attempt to set its default to fail. CMake bug? --- CMakeLists.txt | 3 +-- flake.nix | 6 ++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c54b6450..531ca19f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/DefaultBuildFlags.cmake") +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") option(USE_VCPKG "Use vcpkg for dependency packages" OFF) if (USE_VCPKG) @@ -29,8 +30,6 @@ include(CheckIPOSupported) include(SetupCCache) include(Sanitizers) -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") - set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 17) diff --git a/flake.nix b/flake.nix index 7d17bb27..8dedde8f 100644 --- a/flake.nix +++ b/flake.nix @@ -95,7 +95,13 @@ libtool ninja pkg-config + python3 ]; + + # Undo the SDK setup done by nixpkgs so we can use AppleClang + shellHook = '' + unset DEVELOPER_DIR SDKROOT MACOSX_DEPLOYMENT_TARGET + ''; }; }; } From 72c86ade31e52c14d0161b398b25aae325d86ddb Mon Sep 17 00:00:00 2001 From: Campbell Suter Date: Tue, 24 Dec 2024 12:29:21 +1300 Subject: [PATCH 055/106] Fix gdbstub not activating until the console is reset (#2245) The check for initialising the gdbstub depending on whether the JIT was enabled or not was the wrong way around: previously, it would only enable the gdbstub if the JIT was enabled. The stub started working again if you reset the console, as NDS::SetGdbArgs didn't have any such check and it was called by EmuInstance::updateConsole. --- src/ARM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ARM.cpp b/src/ARM.cpp index 0d93dc45..f75c1e27 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -115,7 +115,7 @@ ARM::ARM(u32 num, bool jit, std::optional gdb, melonDS::NDS& nds) : Num(num), // well uh NDS(nds) { - SetGdbArgs(jit ? gdb : std::nullopt); + SetGdbArgs(jit ? std::nullopt : gdb); } ARM::~ARM() From 66d1091330dd13fdbc99c5b8f18897b06e94b7d2 Mon Sep 17 00:00:00 2001 From: Jakly <102590697+Jaklyy@users.noreply.github.com> Date: Wed, 25 Dec 2024 10:34:30 -0500 Subject: [PATCH 056/106] improve audio handling at non-60 fps targets (#2246) --- src/frontend/qt_sdl/EmuInstanceAudio.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp index a4ac9394..21e573f9 100644 --- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp +++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp @@ -29,7 +29,7 @@ using namespace melonDS; int EmuInstance::audioGetNumSamplesOut(int outlen) { - float f_len_in = (outlen * 32823.6328125) / (float)audioFreq; + float f_len_in = (outlen * 32823.6328125 * (curFPS/60.0)) / (float)audioFreq; f_len_in += audioSampleFrac; int len_in = (int)floor(f_len_in); audioSampleFrac = f_len_in - len_in; @@ -73,6 +73,7 @@ void EmuInstance::audioCallback(void* data, Uint8* stream, int len) // resample incoming audio to match the output sample rate int len_in = inst->audioGetNumSamplesOut(len); + if (len_in > 1024) len_in = 1024; s16 buf_in[1024*2]; int num_in; From be26878b4c1dd902dfd7d14c6bec08d7b2fc098a Mon Sep 17 00:00:00 2001 From: izder456 Date: Wed, 25 Dec 2024 09:14:53 -0600 Subject: [PATCH 057/106] FIX: this should be namespace std:: to preserve compatibilty with non-glibc when building without gdb stub --- src/frontend/qt_sdl/EmuInstance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 309abe66..67791376 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1292,7 +1292,7 @@ bool EmuInstance::updateConsole() noexcept }; auto gdbargs = gdbopt.GetBool("Enabled") ? std::make_optional(_gdbargs) : std::nullopt; #else - optional gdbargs = std::nullopt; + std::optional gdbargs = std::nullopt; #endif NDSArgs ndsargs { From c41951d49c78f0cbb335f8267a8acfac69714df3 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Wed, 25 Dec 2024 16:54:10 +0100 Subject: [PATCH 058/106] Fix almost every warning (#2195) Fix almost every warning as of Clang 19 * is deprecated, we can use QString's UTF-16 conversion instead * remove sem_timedwait implementation as we don't need it anymore * remove a useless shift that has its result discarded * change usages of deprecated sprintf to snprintf --- src/ARMJIT_A64/ARMJIT_Branch.cpp | 4 +- src/ARMJIT_Memory.cpp | 4 +- src/frontend/qt_sdl/CMakeLists.txt | 2 - src/frontend/qt_sdl/CameraManager.cpp | 2 + src/frontend/qt_sdl/DateTimeDialog.h | 2 +- src/frontend/qt_sdl/EmuInstance.cpp | 32 +- src/frontend/qt_sdl/EmuThread.cpp | 8 +- src/frontend/qt_sdl/LANDialog.cpp | 2 + src/frontend/qt_sdl/LANDialog.h | 6 +- src/frontend/qt_sdl/MPSettingsDialog.h | 2 +- src/frontend/qt_sdl/NetplayDialog.cpp | 2 +- src/frontend/qt_sdl/Screen.cpp | 4 +- src/frontend/qt_sdl/TitleManagerDialog.cpp | 4 +- src/frontend/qt_sdl/WifiSettingsDialog.cpp | 12 +- src/frontend/qt_sdl/sem_timedwait.cpp | 488 --------------------- src/frontend/qt_sdl/sem_timedwait.h | 8 - src/net/LAN.cpp | 4 + src/net/Netplay.cpp | 4 + 18 files changed, 50 insertions(+), 540 deletions(-) delete mode 100644 src/frontend/qt_sdl/sem_timedwait.cpp delete mode 100644 src/frontend/qt_sdl/sem_timedwait.h diff --git a/src/ARMJIT_A64/ARMJIT_Branch.cpp b/src/ARMJIT_A64/ARMJIT_Branch.cpp index f9c2e0c5..c83f8161 100644 --- a/src/ARMJIT_A64/ARMJIT_Branch.cpp +++ b/src/ARMJIT_A64/ARMJIT_Branch.cpp @@ -83,7 +83,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles) // doesn't matter if we put garbage in the MSbs there if (addr & 0x2) { - cpu9->CodeRead32(addr-2, true) >> 16; + cpu9->CodeRead32(addr-2, true); cycles += cpu9->CodeCycles; cpu9->CodeRead32(addr+2, false); cycles += CurCPU->CodeCycles; @@ -437,4 +437,4 @@ void Compiler::T_Comp_BL_Merged() Comp_JumpTo(target); } -} \ No newline at end of file +} diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index ec1ea4eb..66838918 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -900,7 +900,7 @@ ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds) } #else char fastmemPidName[snprintf(NULL, 0, "/melondsfastmem%d", getpid()) + 1]; - sprintf(fastmemPidName, "/melondsfastmem%d", getpid()); + snprintf(fastmemPidName, sizeof(fastmemPidName), "/melondsfastmem%d", getpid()); MemoryFile = shm_open(fastmemPidName, O_RDWR | O_CREAT | O_EXCL, 0600); if (MemoryFile == -1) { @@ -1600,4 +1600,4 @@ void* ARMJIT_Memory::GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) co } return NULL; } -} \ No newline at end of file +} diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index c2252b21..b0b6e5ab 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -210,8 +210,6 @@ if (WIN32) endif() if (APPLE) - target_sources(melonDS PRIVATE sem_timedwait.cpp) - # Copy icon into the bundle set(RESOURCE_FILES "${CMAKE_SOURCE_DIR}/res/melon.icns") target_sources(melonDS PUBLIC "${RESOURCE_FILES}") diff --git a/src/frontend/qt_sdl/CameraManager.cpp b/src/frontend/qt_sdl/CameraManager.cpp index 2306da84..687d7b4b 100644 --- a/src/frontend/qt_sdl/CameraManager.cpp +++ b/src/frontend/qt_sdl/CameraManager.cpp @@ -59,6 +59,8 @@ void CameraFrameDumper::present(const QVideoFrame& _frame) case QVideoFrameFormat::Format_NV12: cam->feedFrame_NV12((u8*)frame.bits(0), (u8*)frame.bits(1), frame.width(), frame.height()); break; + default: + break; } frame.unmap(); diff --git a/src/frontend/qt_sdl/DateTimeDialog.h b/src/frontend/qt_sdl/DateTimeDialog.h index 9763f306..5057609f 100644 --- a/src/frontend/qt_sdl/DateTimeDialog.h +++ b/src/frontend/qt_sdl/DateTimeDialog.h @@ -58,7 +58,7 @@ protected: void timerEvent(QTimerEvent* event) override; private slots: - void done(int r); + void done(int r) override; void on_chkChangeTime_clicked(bool checked); void on_chkResetTime_clicked(bool checked); diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 67791376..25ccaa37 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1082,13 +1082,13 @@ std::optional EmuInstance::loadNAND(const std::array, char16_t>{}; + //auto converter = wstring_convert, char16_t>{}; // setting up username - std::u16string username = converter.from_bytes(firmcfg.GetString("Username")); - size_t usernameLength = std::min(username.length(), (size_t) 10); + auto username = firmcfg.GetQString("Username"); + size_t usernameLength = std::min(username.length(), (qsizetype) 10); memset(&settings.Nickname, 0, sizeof(settings.Nickname)); - memcpy(&settings.Nickname, username.data(), usernameLength * sizeof(char16_t)); + memcpy(&settings.Nickname, username.utf16(), usernameLength * sizeof(char16_t)); // setting language settings.Language = static_cast(firmcfg.GetInt("Language")); @@ -1101,10 +1101,10 @@ std::optional EmuInstance::loadNAND(const std::array, char16_t>{}.from_bytes( - orig_username); - size_t usernameLength = std::min(username.length(), (size_t) 10); + size_t usernameLength = std::min(username.length(), (qsizetype) 10); currentData.NameLength = usernameLength; - memcpy(currentData.Nickname, username.data(), usernameLength * sizeof(char16_t)); + memcpy(currentData.Nickname, username.utf16(), usernameLength * sizeof(char16_t)); } auto language = static_cast(firmcfg.GetInt("Language")); @@ -1707,12 +1705,10 @@ void EmuInstance::customizeFirmware(Firmware& firmware, bool overridesettings) n } // setup message - std::string orig_message = firmcfg.GetString("Message"); - if (!orig_message.empty()) + auto message = firmcfg.GetQString("Message"); + if (!message.isEmpty()) { - std::u16string message = std::wstring_convert, char16_t>{}.from_bytes( - orig_message); - size_t messageLength = std::min(message.length(), (size_t) 26); + size_t messageLength = std::min(message.length(), (qsizetype) 26); currentData.MessageLength = messageLength; memcpy(currentData.Message, message.data(), messageLength * sizeof(char16_t)); } diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 88e20238..5a55dfb1 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -428,9 +428,9 @@ void EmuThread::run() double actualfps = (59.8261 * 263.0) / nlines; int inst = emuInstance->instanceID; if (inst == 0) - sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, actualfps); + snprintf(melontitle, sizeof(melontitle), "[%d/%.0f] melonDS " MELONDS_VERSION, fps, actualfps); else - sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1); + snprintf(melontitle, sizeof(melontitle), "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1); changeWindowTitle(melontitle); } } @@ -445,9 +445,9 @@ void EmuThread::run() int inst = emuInstance->instanceID; if (inst == 0) - sprintf(melontitle, "melonDS " MELONDS_VERSION); + snprintf(melontitle, sizeof(melontitle), "melonDS " MELONDS_VERSION); else - sprintf(melontitle, "melonDS (%d)", inst+1); + snprintf(melontitle, sizeof(melontitle), "melonDS (%d)", inst+1); changeWindowTitle(melontitle); SDL_Delay(75); diff --git a/src/frontend/qt_sdl/LANDialog.cpp b/src/frontend/qt_sdl/LANDialog.cpp index bec9c48f..6707cfdc 100644 --- a/src/frontend/qt_sdl/LANDialog.cpp +++ b/src/frontend/qt_sdl/LANDialog.cpp @@ -403,6 +403,8 @@ void LANDialog::doUpdatePlayerList() case LAN::Player_Disconnected: status = "Connection lost"; break; + case LAN::Player_None: + break; } model->item(i, 2)->setText(status); diff --git a/src/frontend/qt_sdl/LANDialog.h b/src/frontend/qt_sdl/LANDialog.h index 03857d79..0a65a6e9 100644 --- a/src/frontend/qt_sdl/LANDialog.h +++ b/src/frontend/qt_sdl/LANDialog.h @@ -48,7 +48,7 @@ public: } private slots: - void done(int r); + void done(int r) override; private: Ui::LANStartHostDialog* ui; @@ -76,7 +76,7 @@ private slots: void onGameSelectionChanged(const QItemSelection& cur, const QItemSelection& prev); void on_tvAvailableGames_doubleClicked(QModelIndex index); void onDirectConnect(); - void done(int r); + void done(int r) override; void doUpdateDiscoveryList(); @@ -105,7 +105,7 @@ protected: private slots: void on_btnLeaveGame_clicked(); - void done(int r); + void done(int r) override; void doUpdatePlayerList(); diff --git a/src/frontend/qt_sdl/MPSettingsDialog.h b/src/frontend/qt_sdl/MPSettingsDialog.h index 0fccb1c9..64b56579 100644 --- a/src/frontend/qt_sdl/MPSettingsDialog.h +++ b/src/frontend/qt_sdl/MPSettingsDialog.h @@ -54,7 +54,7 @@ public: } private slots: - void done(int r); + void done(int r) override; // diff --git a/src/frontend/qt_sdl/NetplayDialog.cpp b/src/frontend/qt_sdl/NetplayDialog.cpp index d7b7cf81..3657eef8 100644 --- a/src/frontend/qt_sdl/NetplayDialog.cpp +++ b/src/frontend/qt_sdl/NetplayDialog.cpp @@ -187,7 +187,7 @@ void NetplayDialog::doUpdatePlayerList(Netplay::Player* players, int num) char ip[32]; u32 addr = player->Address; - sprintf(ip, "%d.%d.%d.%d", addr&0xFF, (addr>>8)&0xFF, (addr>>16)&0xFF, addr>>24); + snprintf(ip, sizeof(ip), "%d.%d.%d.%d", addr&0xFF, (addr>>8)&0xFF, (addr>>16)&0xFF, addr>>24); model->setItem(i, 4, new QStandardItem(ip)); } } diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 10abe1ce..bc63d3ac 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -876,7 +876,7 @@ bool ScreenPanelGL::createContext() if (ourwin->getWindowID() != 0) { if (windowinfo.has_value()) - if (glContext = parentwin->getOGLContext()->CreateSharedContext(*windowinfo)) + if ((glContext = parentwin->getOGLContext()->CreateSharedContext(*windowinfo))) glContext->DoneCurrent(); } else @@ -885,7 +885,7 @@ bool ScreenPanelGL::createContext() GL::Context::Version{GL::Context::Profile::Core, 4, 3}, GL::Context::Version{GL::Context::Profile::Core, 3, 2}}; if (windowinfo.has_value()) - if (glContext = GL::Context::Create(*windowinfo, versionsToTry)) + if ((glContext = GL::Context::Create(*windowinfo, versionsToTry))) glContext->DoneCurrent(); } diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp index fbff261c..844365e7 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.cpp +++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp @@ -126,7 +126,7 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid) *(u32*)&gamecode[0] = *(u32*)&header.GameCode[0]; gamecode[4] = '\0'; char extra[128]; - sprintf(extra, "\n(title ID: %s · %08x/%08x · version %08x)", gamecode, category, titleid, version); + snprintf(extra, sizeof(extra), "\n(title ID: %s · %08x/%08x · version %08x)", gamecode, category, titleid, version); QListWidgetItem* item = new QListWidgetItem(title + QString(extra)); item->setIcon(icon); @@ -482,7 +482,7 @@ void TitleImportDialog::accept() network = new QNetworkAccessManager(this); char url[256]; - sprintf(url, "http://nus.cdn.t.shop.nintendowifi.net/ccs/download/%08x%08x/tmd", titleid[1], titleid[0]); + snprintf(url, sizeof(url), "http://nus.cdn.t.shop.nintendowifi.net/ccs/download/%08x%08x/tmd", titleid[1], titleid[0]); QNetworkRequest req; req.setUrl(QUrl(url)); diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.cpp b/src/frontend/qt_sdl/WifiSettingsDialog.cpp index e0954c83..e041234b 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.cpp +++ b/src/frontend/qt_sdl/WifiSettingsDialog.cpp @@ -152,14 +152,14 @@ void WifiSettingsDialog::on_cbxDirectAdapter_currentIndexChanged(int sel) melonDS::AdapterData* adapter = &adapters[sel]; char tmp[64]; - sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X", - adapter->MAC[0], adapter->MAC[1], adapter->MAC[2], - adapter->MAC[3], adapter->MAC[4], adapter->MAC[5]); + snprintf(tmp, sizeof(tmp), "%02X:%02X:%02X:%02X:%02X:%02X", + adapter->MAC[0], adapter->MAC[1], adapter->MAC[2], + adapter->MAC[3], adapter->MAC[4], adapter->MAC[5]); ui->lblAdapterMAC->setText(QString(tmp)); - sprintf(tmp, "%d.%d.%d.%d", - adapter->IP_v4[0], adapter->IP_v4[1], - adapter->IP_v4[2], adapter->IP_v4[3]); + snprintf(tmp, sizeof(tmp), "%d.%d.%d.%d", + adapter->IP_v4[0], adapter->IP_v4[1], + adapter->IP_v4[2], adapter->IP_v4[3]); ui->lblAdapterIP->setText(QString(tmp)); } diff --git a/src/frontend/qt_sdl/sem_timedwait.cpp b/src/frontend/qt_sdl/sem_timedwait.cpp deleted file mode 100644 index 38b3c16f..00000000 --- a/src/frontend/qt_sdl/sem_timedwait.cpp +++ /dev/null @@ -1,488 +0,0 @@ -/* - * s e m _ t i m e d w a i t - * - * Function: - * Implements a version of sem_timedwait(). - * - * Description: - * Not all systems implement sem_timedwait(), which is a version of - * sem_wait() with a timeout. Mac OS X is one example, at least up to - * and including version 10.6 (Leopard). If such a function is needed, - * this code provides a reasonable implementation, which I think is - * compatible with the standard version, although possibly less - * efficient. It works by creating a thread that interrupts a normal - * sem_wait() call after the specified timeout. - * - * Call: - * - * The Linux man pages say: - * - * #include - * - * int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); - * - * sem_timedwait() is the same as sem_wait(), except that abs_timeout - * specifies a limit on the amount of time that the call should block if - * the decrement cannot be immediately performed. The abs_timeout argument - * points to a structure that specifies an absolute timeout in seconds and - * nanoseconds since the Epoch (00:00:00, 1 January 1970). This structure - * is defined as follows: - * - * struct timespec { - * time_t tv_sec; Seconds - * long tv_nsec; Nanoseconds [0 .. 999999999] - * }; - * - * If the timeout has already expired by the time of the call, and the - * semaphore could not be locked immediately, then sem_timedwait() fails - * with a timeout error (errno set to ETIMEDOUT). - * If the operation can be performed immediately, then sem_timedwait() - * never fails with a timeout error, regardless of the value of abs_timeout. - * Furthermore, the validity of abs_timeout is not checked in this case. - * - * Limitations: - * - * The mechanism used involves sending a SIGUSR2 signal to the thread - * calling sem_timedwait(). The handler for this signal is set to a null - * routine which does nothing, and with any flags for the signal - * (eg SA_RESTART) cleared. Note that this effective disabling of the - * SIGUSR2 signal is a side-effect of using this routine, and means it - * may not be a completely transparent plug-in replacement for a - * 'normal' sig_timedwait() call. Since OS X does not declare the - * sem_timedwait() call in its standard include files, the relevant - * declaration (shown above in the man pages extract) will probably have - * to be added to any code that uses this. - * - * Compiling: - * This compiles and runs cleanly on OS X (10.6) with gcc with the - * -Wall -ansi -pedantic flags. On Linux, using -ansi causes a sweep of - * compiler complaints about the timespec structure, but it compiles - * and works fine with just -Wall -pedantic. (Since Linux provides - * sem_timedwait() anyway, this really isn't needed on Linux.) However, - * since Linux provides sem_timedwait anyway, the sem_timedwait() - * code in this file is only compiled on OS X, and is a null on other - * systems. - * - * Testing: - * This file contains a test program that exercises the sem_timedwait - * code. It is compiled if the pre-processor variable TEST is defined. - * For more details, see the comments for the test routine at the end - * of the file. - * - * Author: Keith Shortridge, AAO. - * - * History: - * 8th Sep 2009. Original version. KS. - * 24th Sep 2009. Added test that the calling thread still exists before - * trying to set the timed-out flag. KS. - * 2nd Oct 2009. No longer restores the original SIGUSR2 signal handler. - * See comments in the body of the code for more details. - * Prototypes for now discontinued internal routines removed. - * 12th Aug 2010. Added the cleanup handler, so that this code no longer - * leaks resources if the calling thread is cancelled. KS. - * 21st Sep 2011. Added copyright notice below. Modified header comments - * to describe the use of SIGUSR2 more accurately in the - * light of the 2/10/09 change above. Now undefs DEBUG - * before defining it, to avoid any possible clash. KS. - * 14th Feb 2012. Tidied out a number of TABs that had got into the - * code. KS. - * 6th May 2013. Copyright notice modified to one based on the MIT licence, - * which is more permissive than the previous notice. KS. - * - * Copyright (c) Australian Astronomical Observatory (AAO), (2013). - * 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. - */ - -#ifdef __APPLE__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sem_timedwait.h" - -/* Some useful definitions - TRUE, FALSE, and DEBUG */ - -#undef TRUE -#define TRUE 1 -#undef FALSE -#define FALSE 0 -#undef DEBUG -#define DEBUG printf - -/* A structure of type timeoutDetails is passed to the thread used to - * implement the timeout. - */ - -typedef struct { - struct timespec delay; /* Specifies the delay, relative to now */ - pthread_t callingThread; /* The thread doing the sem_wait call */ - volatile short *timedOutShort; /* Address of a flag set to indicate that - * the timeout was triggered. */ -} timeoutDetails; - -/* A structure of type cleanupDetails is passed to the thread cleanup - * routine which is called at the end of the routine or if the thread calling - * it is cancelled. - */ - -typedef struct { - pthread_t *threadIdAddr; /* Address of the variable that holds - * the Id of the timeout thread. */ - struct sigaction *sigHandlerAddr; /* Address of the old signal action - * handler. */ - volatile short *timedOutShort; /* Address of a flag set to indicate that - * the timeout was triggered. */ -} cleanupDetails; - -/* Forward declarations of internal routines */ - -static void* timeoutThreadMain (void* passedPtr); -static int triggerSignal (int Signal, pthread_t Thread); -static void ignoreSignal (int Signal); -static void timeoutThreadCleanup (void* passedPtr); - -/* -------------------------------------------------------------------------- */ -/* - * s e m _ t i m e d w a i t - * - * This is the main code for the sem_timedwait() implementation. - */ - -int sem_timedwait ( - sem_t *sem, - const struct timespec *abs_timeout) -{ - int result = 0; /* Code returned by this routine 0 or -1 */ - - /* "Under no circumstances shall the function fail if the semaphore - * can be locked immediately". So we try to get it quickly to see if we - * can avoid all the timeout overheads. - */ - - if (sem_trywait(sem) == 0) { - - /* Yes, got it immediately. */ - - result = 0; - - } else { - - /* No, we've got to do it with a sem_wait() call and a thread to run - * the timeout. First, work out the time from now to the specified - * timeout, which we will pass to the timeout thread in a way that can - * be used to pass to nanosleep(). So we need this in seconds and - * nanoseconds. Along the way, we check for an invalid passed time, - * and for one that's already expired. - */ - - if ((abs_timeout->tv_nsec < 0) || (abs_timeout->tv_nsec > 1000000000)) { - - /* Passed time is invalid */ - - result = -1; - errno = EINVAL; - - } else { - - struct timeval currentTime; /* Time now */ - long secsToWait,nsecsToWait; /* Seconds and nsec to delay */ - gettimeofday (¤tTime,NULL); - secsToWait = abs_timeout->tv_sec - currentTime.tv_sec; - nsecsToWait = (abs_timeout->tv_nsec - (currentTime.tv_usec * 1000)); - while (nsecsToWait < 0) { - nsecsToWait += 1000000000; - secsToWait--; - } - if ((secsToWait < 0) || ((secsToWait == 0) && (nsecsToWait < 0))) { - - /* Time has passed. Report an immediate timeout. */ - - result = -1; - errno = ETIMEDOUT; - - } else { - - /* We're going to have to do a sem_wait() with a timeout thread. - * The thread will wait the specified time, then will issue a - * SIGUSR2 signal that will interrupt the sem_wait() call. - * We pass the thread the id of the current thread, the delay, - * and the address of a flag to set on a timeout, so we can - * distinguish an interrupt caused by the timeout thread from - * one caused by some other signal. - */ - - volatile short timedOut; /* Flag to set on timeout */ - timeoutDetails details; /* All the stuff the thread must know */ - struct sigaction oldSignalAction; /* Current signal setting */ - pthread_t timeoutThread; /* Id of timeout thread */ - cleanupDetails cleaningDetails; /* What the cleanup routine needs */ - int oldCancelState; /* Previous cancellation state */ - int ignoreCancelState; /* Used in call, but ignored */ - int createStatus; /* Status of pthread_create() call */ - - /* If the current thread is cancelled (and CML does do this) - * we don't want to leave our timer thread running - if we've - * started the thread we want to make sure we join it in order - * to release its resources. So we set a cleanup handler to - * do this. We pass it the address of the structure that will - * hold all it needs to know. While we set all this up, - * we prevent ourselves being cancelled, so all this data is - * coherent. - */ - - pthread_setcancelstate (PTHREAD_CANCEL_DISABLE,&oldCancelState); - timeoutThread = (pthread_t) 0; - cleaningDetails.timedOutShort = &timedOut; - cleaningDetails.threadIdAddr = &timeoutThread; - cleaningDetails.sigHandlerAddr = &oldSignalAction; - pthread_cleanup_push (timeoutThreadCleanup,&cleaningDetails); - - /* Set up the details for the thread. Clear the timeout flag, - * record the current SIGUSR2 action settings so we can restore - * them later. - */ - - details.delay.tv_sec = secsToWait; - details.delay.tv_nsec = nsecsToWait; - details.callingThread = pthread_self(); - details.timedOutShort = &timedOut; - timedOut = FALSE; - sigaction (SIGUSR2,NULL,&oldSignalAction); - - /* Start up the timeout thread. Once we've done that, we can - * restore the previous cancellation state. - */ - - createStatus = pthread_create(&timeoutThread,NULL, - timeoutThreadMain, (void*)&details); - pthread_setcancelstate (oldCancelState,&ignoreCancelState); - - if (createStatus < 0) { - - /* Failed to create thread. errno will already be set properly */ - - result = -1; - - } else { - - /* Thread created OK. This is where we wait for the semaphore. - */ - - if (sem_wait(sem) == 0) { - - /* Got the semaphore OK. We return zero, and all's well. */ - - result = 0; - - } else { - - /* If we got a -1 error from sem_wait(), it may be because - * it was interrupted by a timeout, or failed for some - * other reason. We check for the expected timeout - * condition, which is an 'interrupted' status and the - * timeout flag set by the timeout thread. We report that as - * a timeout error. Anything else is some other error and - * errno is already set properly. - */ - - result = -1; - if (errno == EINTR) { - if (timedOut) errno = ETIMEDOUT; - } - } - - } - - /* The cleanup routine - timeoutThreadCleanup() - packages up - * any tidying up that is needed, including joining with the - * timer thread. This will be called if the current thread is - * cancelled, but we need it to happen anyway, so we set the - * execute flag true here as we remove it from the list of - * cleanup routines to be called. So normally, this line amounts - * to calling timeoutThreadCleanup(). - */ - - pthread_cleanup_pop (TRUE); - } - } - } - return (result); -} - -/* -------------------------------------------------------------------------- */ -/* - * t i m e o u t T h r e a d C l e a n u p - * - * This internal routine tidies up at the end of a sem_timedwait() call. - * It is set as a cleanup routine for the current thread (not the timer - * thread) so it is executed even if the thread is cancelled. This is - * important, as we need to tidy up the timeout thread. If we took the - * semaphore (in other words, if we didn't timeout) then the timer thread - * will still be running, sitting in its nanosleep() call, and we need - * to cancel it. If the timer thread did signal a timeout then it will - * now be closing down. In either case, we need to join it (using a call - * to pthread_join()) or its resources will never be released. - * The single argument is a pointer to a cleanupDetails structure that has - * all the routine needs to know. - */ - -static void timeoutThreadCleanup (void* passedPtr) -{ - /* Get what we need from the structure we've been passed. */ - - cleanupDetails *detailsPtr = (cleanupDetails*) passedPtr; - short timedOut = *(detailsPtr->timedOutShort); - pthread_t timeoutThread = *(detailsPtr->threadIdAddr); - - /* If we created the thread, stop it - doesn't matter if it's no longer - * running, pthread_cancel can handle that. We make sure we wait for it - * to complete, because it is this pthread_join() call that releases any - * memory the thread may have allocated. Note that cancelling a thread is - * generally not a good idea, because of the difficulty of cleaning up - * after it, but this is a very simple thread that does nothing but call - * nanosleep(), and that we can cancel quite happily. - */ - - if (!timedOut) pthread_cancel(timeoutThread); - pthread_join(timeoutThread,NULL); - - /* The code originally restored the old action handler, which generally - * was the default handler that caused the task to exit. Just occasionally, - * there seem to be cases where the signal is still queued and ready to - * trigger even though the thread that presumably sent it off just before - * it was cancelled has finished. I had thought that once we'd joined - * that thread, we could be sure of not seeing the signal, but that seems - * not to be the case, and so restoring a handler that will allow the task - * to crash is not a good idea, and so the line below has been commented - * out. - * - * sigaction (SIGUSR2,detailsPtr->sigHandlerAddr,NULL); - */ -} - -/* -------------------------------------------------------------------------- */ -/* - * t i m e o u t T h r e a d M a i n - * - * This internal routine is the main code for the timeout thread. - * The single argument is a pointer to a timeoutDetails structure that has - * all the thread needs to know - thread to signal, delay time, and the - * address of a flag to set if it triggers a timeout. - */ - -static void* timeoutThreadMain (void* passedPtr) -{ - void* Return = (void*) 0; - - /* We grab all the data held in the calling thread right now. In some - * cases, we find that the calling thread has vanished and released - * its memory, including the details structure, by the time the timeout - * expires, and then we get an access violation when we try to set the - * 'timed out' flag. - */ - - timeoutDetails details = *((timeoutDetails*) passedPtr); - struct timespec requestedDelay = details.delay; - - /* We do a nanosleep() for the specified delay, and then trigger a - * timeout. Note that we allow for the case where the nanosleep() is - * interrupted, and restart it for the remaining time. If the - * thread that is doing the sem_wait() call gets the semaphore, it - * will cancel this thread, which is fine as we aren't doing anything - * other than a sleep and a signal. - */ - - for (;;) { - struct timespec remainingDelay; - if (nanosleep (&requestedDelay,&remainingDelay) == 0) { - break; - } else if (errno == EINTR) { - requestedDelay = remainingDelay; - } else { - Return = (void*) errno; - break; - } - } - - /* We've completed the delay without being cancelled, so we now trigger - * the timeout by sending a signal to the calling thread. And that's it, - * although we set the timeout flag first to indicate that it was us - * that interrupted the sem_wait() call. One precaution: before we - * try to set the timed-out flag, make sure the calling thread still - * exists - this may not be the case if things are closing down a bit - * messily. We check this quickly using a zero test signal. - */ - - if (pthread_kill(details.callingThread,0) == 0) { - *(details.timedOutShort) = TRUE; - if (triggerSignal (SIGUSR2,details.callingThread) < 0) { - Return = (void*) errno; - } - } - - return Return; -} - -/* -------------------------------------------------------------------------- */ -/* - * t r i g g e r S i g n a l - * - * This is a general purpose routine that sends a specified signal to - * a specified thread, setting up a signal handler that does nothing, - * and then giving the signal. The only effect will be to interrupt any - * operation that is currently blocking - in this case, we expect this to - * be a sem_wait() call. - */ - -static int triggerSignal (int Signal, pthread_t Thread) -{ - int Result = 0; - struct sigaction SignalDetails; - SignalDetails.sa_handler = ignoreSignal; - SignalDetails.sa_flags = 0; - (void) sigemptyset(&SignalDetails.sa_mask); - if ((Result = sigaction(Signal,&SignalDetails,NULL)) == 0) { - Result = pthread_kill(Thread,Signal); - } - return Result; -} - -/* -------------------------------------------------------------------------- */ -/* - * i g n o r e S i g n a l - * - * And this is the signal handler that does nothing. (It clears its argument, - * but this has no effect and prevents a compiler warning about an unused - * argument.) - */ - -static void ignoreSignal (int Signal) { - Signal = 0; -} - -#endif diff --git a/src/frontend/qt_sdl/sem_timedwait.h b/src/frontend/qt_sdl/sem_timedwait.h deleted file mode 100644 index 42ae201e..00000000 --- a/src/frontend/qt_sdl/sem_timedwait.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __SEM_TIMEDWAIT_H -#define __SEM_TIMEDWAIT_H - -#ifdef __APPLE__ -int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); -#endif - -#endif diff --git a/src/net/LAN.cpp b/src/net/LAN.cpp index ebc66fd8..8cd39584 100644 --- a/src/net/LAN.cpp +++ b/src/net/LAN.cpp @@ -653,6 +653,8 @@ void LAN::ProcessHostEvent(ENetEvent& event) enet_packet_destroy(event.packet); } break; + case ENET_EVENT_TYPE_NONE: + break; } } @@ -777,6 +779,8 @@ void LAN::ProcessClientEvent(ENetEvent& event) enet_packet_destroy(event.packet); } break; + case ENET_EVENT_TYPE_NONE: + break; } } diff --git a/src/net/Netplay.cpp b/src/net/Netplay.cpp index 1dc04282..c89dbf4e 100644 --- a/src/net/Netplay.cpp +++ b/src/net/Netplay.cpp @@ -726,6 +726,8 @@ void ProcessHost() } } break; + case ENET_EVENT_TYPE_NONE: + break; } } } @@ -822,6 +824,8 @@ printf("birf\n"); } } break; + case ENET_EVENT_TYPE_NONE: + break; } } } From 0c5dd28b1c5c1d17a2ce87efbd858059a8f9376f Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Thu, 26 Dec 2024 09:17:46 +0100 Subject: [PATCH 059/106] just case the string length to int to make std::min happy in all cases --- src/frontend/qt_sdl/EmuInstance.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 25ccaa37..75db0018 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1086,7 +1086,7 @@ std::optional EmuInstance::loadNAND(const std::array EmuInstance::loadNAND(const std::array Date: Tue, 14 Jan 2025 12:21:03 -0500 Subject: [PATCH 060/106] fix framelimiter bugs (#2256) --- src/frontend/qt_sdl/EmuThread.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 5a55dfb1..3aabe7e5 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -366,7 +366,7 @@ void EmuThread::run() if (slowmo) emuInstance->curFPS = emuInstance->slowmoFPS; else if (fastforward) emuInstance->curFPS = emuInstance->fastForwardFPS; - else if (!emuInstance->doLimitFPS) emuInstance->curFPS = 1000.0; + else if (!emuInstance->doLimitFPS && !emuInstance->doAudioSync) emuInstance->curFPS = 1000.0; else emuInstance->curFPS = emuInstance->targetFPS; if (emuInstance->audioDSiVolumeSync && emuInstance->nds->ConsoleType == 1) @@ -389,6 +389,7 @@ void EmuThread::run() if (frametimeStep < 0.001) frametimeStep = 0.001; + if (emuInstance->doLimitFPS) { double curtime = SDL_GetPerformanceCounter() * perfCountsSec; @@ -430,7 +431,7 @@ void EmuThread::run() if (inst == 0) snprintf(melontitle, sizeof(melontitle), "[%d/%.0f] melonDS " MELONDS_VERSION, fps, actualfps); else - snprintf(melontitle, sizeof(melontitle), "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1); + snprintf(melontitle, sizeof(melontitle), "[%d/%.0f] melonDS (%d)", fps, actualfps, inst+1); changeWindowTitle(melontitle); } } From 15c3faa26e879bdcff615558ded6dd886681ccae Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 17 Jan 2025 02:52:52 +0100 Subject: [PATCH 061/106] Use GitHub's new arm64 Linux runners for the Ubuntu CI builds --- .github/workflows/build-ubuntu.yml | 68 ++++++++++-------------------- 1 file changed, 22 insertions(+), 46 deletions(-) diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index a0b77e64..98b0bd0b 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - ci/* pull_request: branches: - master @@ -15,16 +16,24 @@ env: MELONDS_VERSION_SUFFIX: " RC" jobs: - build-x86_64: - name: x86_64 - runs-on: ubuntu-22.04 + build: + continue-on-error: true + strategy: + matrix: + arch: + - runner: ubuntu-22.04 + name: x86_64 + - runner: ubuntu-22.04-arm + name: aarch64 + + name: ${{ matrix.arch.name }} + runs-on: ${{ matrix.arch.runner }} steps: - uses: actions/checkout@v4 name: Check out sources - name: Install dependencies run: | - sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list sudo apt update sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev libenet-dev \ qt6-{base,base-private,multimedia}-dev libqt6svg6-dev libarchive-dev libzstd-dev libfuse2 @@ -36,56 +45,23 @@ jobs: DESTDIR=AppDir cmake --install build - uses: actions/upload-artifact@v4 with: - name: melonDS-ubuntu-x86_64 + name: melonDS-ubuntu-${{ matrix.arch.name }} path: AppDir/usr/bin/melonDS - name: Fetch AppImage tools + # linuxdeploy doesn't seem to work on the aarch64 runners? + if: matrix.arch.name != 'aarch64' run: | - wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage - wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage + wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${{ matrix.arch.name }}.AppImage + wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-${{ matrix.arch.name }}.AppImage chmod a+x linuxdeploy-*.AppImage - name: Build the AppImage + if: matrix.arch.name != 'aarch64' env: QMAKE: /usr/lib/qt6/bin/qmake run: | - ./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage + ./linuxdeploy-${{ matrix.arch.name }}.AppImage --appdir AppDir --plugin qt --output appimage - uses: actions/upload-artifact@v4 + if: matrix.arch.name != 'aarch64' with: - name: melonDS-appimage-x86_64 + name: melonDS-appimage-${{ matrix.arch.name }} path: melonDS*.AppImage - - build-aarch64: - name: aarch64 - runs-on: ubuntu-latest - container: ubuntu:22.04 - - steps: - - name: Prepare system - shell: bash - run: | - dpkg --add-architecture arm64 - sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new" - rm /etc/apt/sources.list - mv /etc/apt/sources.list{.new,} - apt update - apt -y full-upgrade - apt -y install git {gcc-12,g++-12}-aarch64-linux-gnu cmake ninja-build extra-cmake-modules \ - {libsdl2,qt6-{base,base-private,multimedia},libqt6svg6,libarchive,libzstd,libenet}-dev:arm64 \ - pkg-config dpkg-dev - - name: Check out source - uses: actions/checkout@v4 - - name: Configure - shell: bash - run: | - cmake -B build -G Ninja \ - -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config \ - -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \ - -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \ - -DMELONDS_EMBED_BUILD_INFO=ON - - name: Build - shell: bash - run: | - cmake --build build - - uses: actions/upload-artifact@v4 - with: - name: melonDS-ubuntu-aarch64 - path: build/melonDS From e8265df4bd385d76aba0e2c5c5938252d8c342b5 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Mon, 10 Feb 2025 20:57:43 +0100 Subject: [PATCH 062/106] vcpkg 2025.01.13, update nixpkgs --- .github/workflows/build-macos.yml | 3 ++- .github/workflows/build-windows.yml | 3 ++- cmake/ConfigureVcpkg.cmake | 2 +- flake.lock | 6 +++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 634f1b18..90328b91 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -10,6 +10,7 @@ on: - master env: + VCPKG_COMMIT: 6f29f12e82a8293156836ad81cc9bf5af41fe836 MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_BUILD_PROVIDER: GitHub Actions @@ -34,7 +35,7 @@ jobs: - name: Set up vcpkg uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: b2cb0da531c2f1f740045bfe7c4dac59f0b2b69c + vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }} - name: Build uses: lukka/run-cmake@v10 with: diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 6a5ac754..1faa8121 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -10,6 +10,7 @@ on: - master env: + VCPKG_COMMIT: 6f29f12e82a8293156836ad81cc9bf5af41fe836 MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_BUILD_PROVIDER: GitHub Actions @@ -33,7 +34,7 @@ jobs: - name: Set up vcpkg uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: b2cb0da531c2f1f740045bfe7c4dac59f0b2b69c + vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }} - name: Configure run: cmake --preset=release-mingw-x86_64 -DMELONDS_EMBED_BUILD_INFO=ON - name: Build diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake index d09749ab..11d9ebda 100644 --- a/cmake/ConfigureVcpkg.cmake +++ b/cmake/ConfigureVcpkg.cmake @@ -9,7 +9,7 @@ if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}") endif() FetchContent_Declare(vcpkg GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git" - GIT_TAG 2024.11.16 + GIT_TAG 2025.01.13 SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg") FetchContent_MakeAvailable(vcpkg) endif() diff --git a/flake.lock b/flake.lock index 5ba6ab84..85be38fa 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1732014248, - "narHash": "sha256-y/MEyuJ5oBWrWAic/14LaIr/u5E0wRVzyYsouYY3W6w=", + "lastModified": 1739020877, + "narHash": "sha256-mIvECo/NNdJJ/bXjNqIh8yeoSjVLAuDuTUzAo7dzs8Y=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "23e89b7da85c3640bbc2173fe04f4bd114342367", + "rev": "a79cfe0ebd24952b580b1cf08cd906354996d547", "type": "github" }, "original": { From 63b468927e1c744e21057ccb4c5677f52502b442 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 21 Feb 2025 08:21:27 +0100 Subject: [PATCH 063/106] ci: enable building of appimages on aarch64 Looks like whatever was causing linuxdeploy to crash got fixed, so we can build them now. --- .github/workflows/build-ubuntu.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 98b0bd0b..9d701524 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -48,20 +48,16 @@ jobs: name: melonDS-ubuntu-${{ matrix.arch.name }} path: AppDir/usr/bin/melonDS - name: Fetch AppImage tools - # linuxdeploy doesn't seem to work on the aarch64 runners? - if: matrix.arch.name != 'aarch64' run: | wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${{ matrix.arch.name }}.AppImage wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-${{ matrix.arch.name }}.AppImage chmod a+x linuxdeploy-*.AppImage - name: Build the AppImage - if: matrix.arch.name != 'aarch64' env: QMAKE: /usr/lib/qt6/bin/qmake run: | ./linuxdeploy-${{ matrix.arch.name }}.AppImage --appdir AppDir --plugin qt --output appimage - uses: actions/upload-artifact@v4 - if: matrix.arch.name != 'aarch64' with: name: melonDS-appimage-${{ matrix.arch.name }} path: melonDS*.AppImage From 0fcf1f6e3a443cb249f85d948ff6e58dc58501d6 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Sun, 9 Mar 2025 13:20:27 -0400 Subject: [PATCH 064/106] Add support for using the solar sensor without requiring a Boktai ROM (#2221) * Add a `GBAHeader` struct * Add extra `GBAAddon` entries for the Boktai carts - Each game in the trilogy has a different effect on Lunar Knights (the only commercial DS game to support the solar sensor) * Copy the logo data from the NDS ROM's header to the Boktai stub's header --- src/GBACart.cpp | 64 ++++++++++++++++++++++++++++- src/GBACart.h | 38 +++++++++++++++++ src/NDS.cpp | 20 +++++++++ src/NDS.h | 6 +++ src/frontend/qt_sdl/EmuInstance.cpp | 6 +++ src/frontend/qt_sdl/Window.cpp | 2 +- 6 files changed, 134 insertions(+), 2 deletions(-) diff --git a/src/GBACart.cpp b/src/GBACart.cpp index c3550207..be0d223b 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -539,6 +539,57 @@ CartGameSolarSensor::CartGameSolarSensor(std::unique_ptr&& rom, u32 len, s { } +std::unique_ptr CreateFakeSolarSensorROM(const char* gamecode, const NDSCart::CartCommon& cart, void* userdata) noexcept +{ + return CreateFakeSolarSensorROM(gamecode, cart.GetHeader().NintendoLogo, userdata); +} + +std::unique_ptr CreateFakeSolarSensorROM(const char* gamecode, const GBACart::CartGame& cart, void* userdata) noexcept +{ + return CreateFakeSolarSensorROM(gamecode, cart.GetHeader().NintendoLogo, userdata); +} + +std::unique_ptr CreateFakeSolarSensorROM(const char* gamecode, const u8* logo, void* userdata) noexcept +{ + if (!gamecode) + return nullptr; + + if (strnlen(gamecode, sizeof(GBAHeader::GameCode)) > sizeof(GBAHeader::GameCode)) + return nullptr; + + bool solarsensor = false; + for (const char* i : SOLAR_SENSOR_GAMECODES) + { + if (strcmp(gamecode, i) == 0) { + solarsensor = true; + break; + } + } + + if (!solarsensor) + return nullptr; + + // just 256 bytes; we don't need a whole ROM! + constexpr size_t FAKE_BOKTAI_ROM_LENGTH = 0x100; + std::unique_ptr rom = std::make_unique(FAKE_BOKTAI_ROM_LENGTH); + + // create a fake ROM + GBAHeader& header = *reinterpret_cast(rom.get()); + memcpy(header.Title, BOKTAI_STUB_TITLE, strnlen(BOKTAI_STUB_TITLE, sizeof(header.Title))); + memcpy(header.GameCode, gamecode, strnlen(gamecode, sizeof(header.GameCode))); + header.FixedValue = 0x96; + if (logo) + { + memcpy(header.NintendoLogo, logo, sizeof(header.NintendoLogo)); + } + else + { + memset(header.NintendoLogo, 0xFF, sizeof(header.NintendoLogo)); + } + + return std::make_unique(std::move(rom), FAKE_BOKTAI_ROM_LENGTH, nullptr, 0, userdata); +} + const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183}; void CartGameSolarSensor::Reset() @@ -843,7 +894,18 @@ std::unique_ptr LoadAddon(int type, void* userdata) case GBAAddon_RumblePak: cart = std::make_unique(userdata); break; - + case GBAAddon_SolarSensorBoktai1: + // US Boktai 1 + cart = CreateFakeSolarSensorROM("U3IE", nullptr, userdata); + break; + case GBAAddon_SolarSensorBoktai2: + // US Boktai 2 + cart = CreateFakeSolarSensorROM("U32E", nullptr, userdata); + break; + case GBAAddon_SolarSensorBoktai3: + // JP Boktai 3 + cart = CreateFakeSolarSensorROM("U33J", nullptr, userdata); + break; default: Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type); return nullptr; diff --git a/src/GBACart.h b/src/GBACart.h index e6639813..c9a7caa9 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -35,6 +35,25 @@ enum CartType RumblePak = 0x202, }; +// See https://problemkaputt.de/gbatek.htm#gbacartridgeheader for details +struct GBAHeader +{ + u32 EntryPoint; + u8 NintendoLogo[156]; // must be valid + char Title[12]; + char GameCode[4]; + char MakerCode[2]; + u8 FixedValue; // must be 0x96 + u8 MainUnitCode; + u8 DeviceType; + u8 Reserved0[7]; + u8 SoftwareVersion; + u8 ComplementCheck; + u8 Reserved1[2]; +}; + +static_assert(sizeof(GBAHeader) == 192, "GBAHeader should be 192 bytes"); + // CartCommon -- base code shared by all cart types class CartCommon { @@ -91,6 +110,8 @@ public: [[nodiscard]] const u8* GetROM() const override { return ROM.get(); } [[nodiscard]] u32 GetROMLength() const override { return ROMLength; } + [[nodiscard]] const GBAHeader& GetHeader() const noexcept { return *reinterpret_cast(ROM.get()); } + [[nodiscard]] GBAHeader& GetHeader() noexcept { return *reinterpret_cast(ROM.get()); } u8* GetSaveMemory() const override; u32 GetSaveMemoryLength() const override; @@ -309,6 +330,23 @@ std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen std::unique_ptr LoadAddon(int type, void* userdata); +/// Creates a solar sensor-enabled GBA cart without needing a real Boktai ROM. +/// This enables the solar sensor to be used in supported games. +/// Will not contain any SRAM. +/// @param gamecode +/// @param logo The Nintendo logo data embedded in the headers for GBA ROMs and NDS ROMs. +/// Required for the cart to be recognized as a valid GBA cart. +/// Overloads that accept cart objects directly exist as well. +/// If not provided, then it will have to be patched with equivalent data +/// from a real ROM (NDS or GBA) before booting the emulator. +/// @param userdata Optional user data to associate with the cart. +/// @return A CartGameSolarSensor if the ROM was created successfully, +/// or nullptr if any argument is wrong (e.g. an incorrect game code). +std::unique_ptr CreateFakeSolarSensorROM(const char* gamecode, const u8* logo, void* userdata = nullptr) noexcept; +std::unique_ptr CreateFakeSolarSensorROM(const char* gamecode, const NDSCart::CartCommon& cart, void* userdata = nullptr) noexcept; +std::unique_ptr CreateFakeSolarSensorROM(const char* gamecode, const GBACart::CartGame& cart, void* userdata = nullptr) noexcept; + +constexpr const char* BOKTAI_STUB_TITLE = "BOKTAI STUB"; } #endif // GBACART_H diff --git a/src/NDS.cpp b/src/NDS.cpp index a49d2e17..2efb3b0a 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -546,6 +546,26 @@ void NDS::Reset() void NDS::Start() { Running = true; + + if (ConsoleType != 0) + return; + + auto* ndscart = NDSCartSlot.GetCart(); + if (!ndscart) + return; + + if (auto* cart = GBACartSlot.GetCart(); cart && cart->Type() == GBACart::CartType::GameSolarSensor) + { // If we have a solar sensor cart inserted... + auto& solarcart = *static_cast(cart); + GBACart::GBAHeader& header = solarcart.GetHeader(); + if (strncmp(header.Title, GBACart::BOKTAI_STUB_TITLE, sizeof(header.Title)) == 0) { + // If this is a stub Boktai cart (so we can use the sensor without a full ROM)... + + // ...then copy the Nintendo logo data from the NDS ROM into the stub GBA ROM. + // Otherwise, the GBA cart won't be recognized. + memcpy(header.NintendoLogo, ndscart->GetHeader().NintendoLogo, sizeof(header.NintendoLogo)); + } + } } static const char* StopReasonName(Platform::StopReason reason) diff --git a/src/NDS.h b/src/NDS.h index 6e486e28..8efbb90c 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -215,6 +215,12 @@ enum { GBAAddon_RAMExpansion = 1, GBAAddon_RumblePak = 2, + // Each game in the GBA Boktai trilogy uses the same solar sensor, + // but Lunar Knights (the only NDS game to use the solar sensor) + // applies slightly different effects depending on the game. + GBAAddon_SolarSensorBoktai1 = 3, + GBAAddon_SolarSensorBoktai2 = 4, + GBAAddon_SolarSensorBoktai3 = 5, }; class SPU; diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 75db0018..eb11ede2 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -2142,6 +2142,12 @@ QString EmuInstance::gbaAddonName(int addon) return "Rumble Pak"; case GBAAddon_RAMExpansion: return "Memory expansion"; + case GBAAddon_SolarSensorBoktai1: + return "Solar Sensor (Boktai 1)"; + case GBAAddon_SolarSensorBoktai2: + return "Solar Sensor (Boktai 2)"; + case GBAAddon_SolarSensorBoktai3: + return "Solar Sensor (Boktai 3)"; } return "???"; diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index a156a993..661a5af1 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -320,7 +320,7 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : QMenu * submenu = menu->addMenu("Insert add-on cart"); QAction *act; - int addons[] = {GBAAddon_RAMExpansion, GBAAddon_RumblePak, -1}; + int addons[] = {GBAAddon_RAMExpansion, GBAAddon_RumblePak, GBAAddon_SolarSensorBoktai1, GBAAddon_SolarSensorBoktai2, GBAAddon_SolarSensorBoktai3, -1}; for (int i = 0; addons[i] != -1; i++) { int addon = addons[i]; From 9ed7e5803e55c5eeb29ec560c8659b38ed331749 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Wed, 9 Apr 2025 16:40:57 +0200 Subject: [PATCH 065/106] ci: upgrade vcpkg to a commit that works for our deps with CMake 4.0 CMake 4.0 dropped support for projects with a minimum required version below 3.5. libarchive, as well as possibly other dependencies, had older versions set so they now fail to build. GitHub Actions and MSYS2 were very quick to update their CMake version and there isn't a tagged vcpkg release with a fix for libarchive yet, so we will use a specific commit for now. --- .github/workflows/build-macos.yml | 4 ++-- .github/workflows/build-windows.yml | 2 +- cmake/ConfigureVcpkg.cmake | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 90328b91..cd25e40c 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -4,13 +4,13 @@ on: push: branches: - master - - ci/vcpkg-update + - ci/* pull_request: branches: - master env: - VCPKG_COMMIT: 6f29f12e82a8293156836ad81cc9bf5af41fe836 + VCPKG_COMMIT: 2ad004460f5db4d3b66f62f5799ff66c265c4b5d MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_BUILD_PROVIDER: GitHub Actions diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 1faa8121..92d45d4d 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -10,7 +10,7 @@ on: - master env: - VCPKG_COMMIT: 6f29f12e82a8293156836ad81cc9bf5af41fe836 + VCPKG_COMMIT: 2ad004460f5db4d3b66f62f5799ff66c265c4b5d MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_BUILD_PROVIDER: GitHub Actions diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake index 11d9ebda..151e2989 100644 --- a/cmake/ConfigureVcpkg.cmake +++ b/cmake/ConfigureVcpkg.cmake @@ -9,7 +9,8 @@ if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}") endif() FetchContent_Declare(vcpkg GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git" - GIT_TAG 2025.01.13 + GIT_TAG 2ad004460f5db4d3b66f62f5799ff66c265c4b5d + EXCLUDE_FROM_ALL SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg") FetchContent_MakeAvailable(vcpkg) endif() From d6d820c013df62e4d0b0f435646e2ff2d6387177 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 11 May 2025 13:10:48 +0200 Subject: [PATCH 066/106] Set SDL_HINT_APP_NAME (#2319) Fixes #2300 --- src/frontend/qt_sdl/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index d940340a..ec038ff2 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -299,6 +299,8 @@ int main(int argc, char** argv) // http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); + SDL_SetHint(SDL_HINT_APP_NAME, "melonDS"); + if (SDL_Init(SDL_INIT_HAPTIC) < 0) { printf("SDL couldn't init rumble\n"); From 0e64a06c84f9b9428f8647c2aafde110c9d917f3 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 15 May 2025 08:36:20 +0200 Subject: [PATCH 067/106] Use standard sysconf Fixes compilation of JIT builds on non-glibc OSes. After some testing in a Fedora 41 VM, __sysconf and sysconf return the same value, and sysconf in glibc appears to just be an alias to __sysconf to begin with --- src/ARMJIT_Memory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index 66838918..c79c2d8f 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -753,7 +753,7 @@ bool ARMJIT_Memory::IsFastMemSupported() PageSize = RegularPageSize; #else - PageSize = __sysconf(_SC_PAGESIZE); + PageSize = sysconf(_SC_PAGESIZE); isSupported = PageSize == RegularPageSize || PageSize == LargePageSize; #endif PageShift = __builtin_ctz(PageSize); From 7baeb26e32d5c6aeaca0a47e72bbb605b2961882 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Mon, 19 May 2025 19:00:48 -0400 Subject: [PATCH 068/106] Fix undefined behavior when indexing into `ARCode::Code` (#2331) - Indexing past the end of a `std::vector`'s length is undefined, even if there's extra capacity - GCC 15 introduced an assert in `vector::operator[]`, so this line caused an abort if melonDS was built with GCC 15 - It was always undefined, but now the STL checks for it --- src/AREngine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AREngine.cpp b/src/AREngine.cpp index bdda5863..0c37b321 100644 --- a/src/AREngine.cpp +++ b/src/AREngine.cpp @@ -58,7 +58,8 @@ void AREngine::RunCheat(const ARCode& arcode) for (;;) { - if (code >= &arcode.Code[arcode.Code.size()]) + if (code > &arcode.Code[arcode.Code.size() - 1]) + // If the instruction pointer is past the end of the cheat code... break; u32 a = *code++; From 528f2495fcde2d5fcff78a181e8455559c742469 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Wed, 21 May 2025 16:16:00 -0400 Subject: [PATCH 069/106] Fix a missing `#include` on Windows builds (#2333) * Fix a GCC 15 build issue on Windows due to a missing `#include` - `` was included implicitly by some other header - The build broke in GCC 15 on MinGW, most likely due to some internal refactoring * Indent these `#include`s the same as the others --- src/ARMJIT_Memory.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ARMJIT_Memory.h b/src/ARMJIT_Memory.h index cac9dc62..a6c68fce 100644 --- a/src/ARMJIT_Memory.h +++ b/src/ARMJIT_Memory.h @@ -29,7 +29,8 @@ # if defined(__SWITCH__) # include # elif defined(_WIN32) -#include +# include +# include # else # include # include From 37ca75acb9533a18fa7d56f541853b4bf6650b2d Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 27 May 2025 00:27:38 +0200 Subject: [PATCH 070/106] add source for DLDI driver --- melonDLDI/Makefile | 15 +++++ melonDLDI/melonDLDI.s | 146 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 melonDLDI/Makefile create mode 100755 melonDLDI/melonDLDI.s diff --git a/melonDLDI/Makefile b/melonDLDI/Makefile new file mode 100644 index 00000000..56b316fb --- /dev/null +++ b/melonDLDI/Makefile @@ -0,0 +1,15 @@ +AS = arm-none-eabi-as +LD = arm-none-eabi-ld +OBJCOPY = arm-none-eabi-objcopy + +BIN = melonDLDI + +all: + $(AS) $(BIN).s -o $(BIN).o + $(LD) $(BIN).o -Ttext 0xBF800000 -e 0xBF800000 -o $(BIN).elf + $(OBJCOPY) -O binary $(BIN).elf $(BIN).bin + xxd -i -n $(BIN) $(BIN).bin $(BIN).h + +clean: + rm -f $(BIN).h $(BIN).bin $(BIN).elf $(BIN).o + diff --git a/melonDLDI/melonDLDI.s b/melonDLDI/melonDLDI.s new file mode 100755 index 00000000..4b044c69 --- /dev/null +++ b/melonDLDI/melonDLDI.s @@ -0,0 +1,146 @@ +.arm +.text + +.align 2 + +_start: +.word 0xBF8DA5ED +.string " Chishm" +.byte 1 +.byte 9 @ size +.byte 0 +.byte 0 +.string "melonDS DLDI driver" +.align 6, 0 +.word _start, melon_end +.word 0, 0 +.word 0, 0 +.word 0, 0 +.ascii "MELN" +.word 0x23 +.word melon_startup +.word melon_isInserted +.word melon_readSectors +.word melon_writeSectors +.word melon_clearStatus +.word melon_shutdown + + +.align 2 + +melon_startup: + mov r0, #1 + bx lr + + +melon_isInserted: + mov r0, #1 + bx lr + + +@ r0=cmd r1=sector r2=out (0=none) +_sendcmd: + mov r12, #0x04000000 + add r12, r12, #0x1A0 + @ init + mov r3, #0x8000 + strh r3, [r12] + @ set cmd + strb r0, [r12, #0x8] + strb r1, [r12, #0xC] + mov r1, r1, lsr #8 + strb r1, [r12, #0xB] + mov r1, r1, lsr #8 + strb r1, [r12, #0xA] + mov r1, r1, lsr #8 + strb r1, [r12, #0x9] + mov r1, r1, lsr #8 + strb r1, [r12, #0xD] + strh r1, [r12, #0xE] + @ send + mov r3, #0xA0000000 + orr r3, r3, r0, lsl #30 + cmp r2, #0 + orrne r3, r3, #0x01000000 @ block size + orr r3, r3, #0x00400000 @ KEY2 + str r3, [r12, #0x4] + mov r3, #0x04100000 + tst r0, #0x01 + bne __send_write + @ receive data +__read_busyloop: + ldr r0, [r12, #0x4] + tst r0, #0x00800000 + ldrne r1, [r3, #0x10] @ load data + cmpne r2, #0 + strne r1, [r2], #4 + tst r0, #0x80000000 + bne __read_busyloop + bx lr + @ send data +__send_write: + mov r1, #0 +__write_busyloop: + ldr r0, [r12, #0x4] + tst r0, #0x00800000 + cmpne r2, #0 + ldrne r1, [r2], #4 + strne r1, [r3, #0x10] @ store data + tst r0, #0x80000000 + bne __write_busyloop + bx lr + + +@ r0=sector r1=numsectors r2=out +melon_readSectors: + tst r2, #0x3 + movne r0, #0 + bxne lr + stmdb sp!, {r3-r6, lr} + mov r4, r0 + mov r5, r1 + mov r6, #0 +_readloop: + mov r0, #0xC0 + add r1, r4, r6 + bl _sendcmd + add r6, r6, #1 + cmp r6, r5 + bcc _readloop + ldmia sp!, {r3-r6, lr} + mov r0, #1 + bx lr + + +@ r0=sector r1=numsectors r2=out +melon_writeSectors: + tst r2, #0x3 + movne r0, #0 + bxne lr + stmdb sp!, {r3-r6, lr} + mov r4, r0 + mov r5, r1 + mov r6, #0 +_writeloop: + mov r0, #0xC1 + add r1, r4, r6 + bl _sendcmd + add r6, r6, #1 + cmp r6, r5 + bcc _writeloop + ldmia sp!, {r3-r6, lr} + mov r0, #1 + bx lr + + +melon_clearStatus: + mov r0, #1 + bx lr + + +melon_shutdown: + mov r0, #1 + bx lr + + +melon_end: From d1eff4acf568ec989fda7aa8c68d3567d59cce43 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 27 May 2025 00:31:37 +0200 Subject: [PATCH 071/106] update copyright headers (about time) --- res/melon.rc.in | 2 +- src/ARCodeFile.cpp | 2 +- src/ARCodeFile.h | 2 +- src/AREngine.cpp | 2 +- src/AREngine.h | 2 +- src/ARM.cpp | 2 +- src/ARM.h | 2 +- src/ARMInterpreter.cpp | 2 +- src/ARMInterpreter.h | 2 +- src/ARMInterpreter_ALU.cpp | 2 +- src/ARMInterpreter_ALU.h | 2 +- src/ARMInterpreter_Branch.cpp | 2 +- src/ARMInterpreter_Branch.h | 2 +- src/ARMInterpreter_LoadStore.cpp | 2 +- src/ARMInterpreter_LoadStore.h | 2 +- src/ARMJIT.cpp | 2 +- src/ARMJIT.h | 2 +- src/ARMJIT_A64/ARMJIT_ALU.cpp | 2 +- src/ARMJIT_A64/ARMJIT_Branch.cpp | 2 +- src/ARMJIT_A64/ARMJIT_Compiler.cpp | 2 +- src/ARMJIT_A64/ARMJIT_Compiler.h | 2 +- src/ARMJIT_A64/ARMJIT_Linkage.S | 2 +- src/ARMJIT_A64/ARMJIT_LoadStore.cpp | 2 +- src/ARMJIT_Compiler.h | 2 +- src/ARMJIT_Global.h | 2 +- src/ARMJIT_Internal.h | 2 +- src/ARMJIT_Memory.cpp | 2 +- src/ARMJIT_Memory.h | 2 +- src/ARMJIT_RegisterCache.h | 2 +- src/ARMJIT_x64/ARMJIT_ALU.cpp | 2 +- src/ARMJIT_x64/ARMJIT_Branch.cpp | 2 +- src/ARMJIT_x64/ARMJIT_Compiler.cpp | 2 +- src/ARMJIT_x64/ARMJIT_Compiler.h | 2 +- src/ARMJIT_x64/ARMJIT_GenOffsets.cpp | 2 +- src/ARMJIT_x64/ARMJIT_Linkage.S | 2 +- src/ARMJIT_x64/ARMJIT_LoadStore.cpp | 2 +- src/ARMJIT_x64/ARMJIT_Offsets.h | 2 +- src/ARM_InstrInfo.cpp | 2 +- src/ARM_InstrInfo.h | 2 +- src/ARM_InstrTable.h | 2 +- src/Args.h | 2 +- src/CP15.cpp | 2 +- src/CRC32.cpp | 2 +- src/CRC32.h | 2 +- src/DMA.cpp | 2 +- src/DMA.h | 2 +- src/DMA_Timings.cpp | 2 +- src/DMA_Timings.h | 2 +- src/DSi.cpp | 2 +- src/DSi.h | 2 +- src/DSi_AES.cpp | 2 +- src/DSi_AES.h | 2 +- src/DSi_Camera.cpp | 2 +- src/DSi_Camera.h | 2 +- src/DSi_I2C.cpp | 2 +- src/DSi_I2C.h | 2 +- src/DSi_NAND.cpp | 2 +- src/DSi_NAND.h | 2 +- src/DSi_NDMA.cpp | 2 +- src/DSi_NDMA.h | 2 +- src/DSi_NWifi.cpp | 2 +- src/DSi_NWifi.h | 2 +- src/DSi_SD.cpp | 2 +- src/DSi_SD.h | 2 +- src/DSi_SPI_TSC.cpp | 2 +- src/DSi_SPI_TSC.h | 2 +- src/DSi_TMD.h | 2 +- src/FATIO.cpp | 2 +- src/FATIO.h | 2 +- src/FATStorage.cpp | 2 +- src/FATStorage.h | 2 +- src/FIFO.h | 2 +- src/GBACart.cpp | 2 +- src/GBACart.h | 2 +- src/GPU.cpp | 2 +- src/GPU.h | 2 +- src/GPU2D.cpp | 2 +- src/GPU2D.h | 2 +- src/GPU2D_Soft.cpp | 2 +- src/GPU2D_Soft.h | 2 +- src/GPU3D.cpp | 2 +- src/GPU3D.h | 2 +- src/GPU3D_Compute.cpp | 2 +- src/GPU3D_Compute.h | 2 +- src/GPU3D_Compute_shaders.h | 2 +- src/GPU3D_OpenGL.cpp | 2 +- src/GPU3D_OpenGL.h | 2 +- src/GPU3D_OpenGL_shaders.h | 2 +- src/GPU3D_Soft.cpp | 2 +- src/GPU3D_Soft.h | 2 +- src/GPU_OpenGL.cpp | 2 +- src/GPU_OpenGL.h | 2 +- src/GPU_OpenGL_shaders.h | 2 +- src/JitBlock.h | 2 +- src/MemConstants.h | 2 +- src/MemRegion.h | 2 +- src/NDS.cpp | 2 +- src/NDS.h | 2 +- src/NDSCart.cpp | 2 +- src/NDSCart.h | 2 +- src/NDSCartR4.cpp | 2 +- src/NDS_Header.h | 2 +- src/NonStupidBitfield.h | 2 +- src/OpenGLSupport.cpp | 2 +- src/OpenGLSupport.h | 2 +- src/Platform.h | 2 +- src/ROMList.cpp | 2 +- src/ROMList.h | 2 +- src/RTC.cpp | 2 +- src/RTC.h | 2 +- src/SPI.cpp | 2 +- src/SPI.h | 2 +- src/SPI_Firmware.cpp | 2 +- src/SPI_Firmware.h | 2 +- src/SPU.cpp | 2 +- src/SPU.h | 2 +- src/Savestate.cpp | 2 +- src/Savestate.h | 2 +- src/TinyVector.h | 2 +- src/Utils.cpp | 2 +- src/Utils.h | 2 +- src/Wifi.cpp | 2 +- src/Wifi.h | 2 +- src/WifiAP.cpp | 2 +- src/WifiAP.h | 2 +- src/frontend/ScreenLayout.cpp | 2 +- src/frontend/ScreenLayout.h | 2 +- src/frontend/mic_blow.h | 2 +- src/frontend/qt_sdl/ArchiveUtil.cpp | 2 +- src/frontend/qt_sdl/ArchiveUtil.h | 2 +- src/frontend/qt_sdl/AudioSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/AudioSettingsDialog.h | 2 +- src/frontend/qt_sdl/CameraManager.cpp | 2 +- src/frontend/qt_sdl/CameraManager.h | 2 +- src/frontend/qt_sdl/CameraSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/CameraSettingsDialog.h | 2 +- src/frontend/qt_sdl/CheatsDialog.cpp | 2 +- src/frontend/qt_sdl/CheatsDialog.h | 2 +- src/frontend/qt_sdl/Config.cpp | 2 +- src/frontend/qt_sdl/Config.h | 2 +- src/frontend/qt_sdl/DateTimeDialog.cpp | 2 +- src/frontend/qt_sdl/DateTimeDialog.h | 2 +- src/frontend/qt_sdl/EmuInstance.cpp | 2 +- src/frontend/qt_sdl/EmuInstance.h | 2 +- src/frontend/qt_sdl/EmuInstanceAudio.cpp | 2 +- src/frontend/qt_sdl/EmuInstanceInput.cpp | 2 +- src/frontend/qt_sdl/EmuSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/EmuSettingsDialog.h | 2 +- src/frontend/qt_sdl/EmuThread.cpp | 2 +- src/frontend/qt_sdl/EmuThread.h | 2 +- src/frontend/qt_sdl/FirmwareSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/FirmwareSettingsDialog.h | 2 +- src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp | 2 +- src/frontend/qt_sdl/InputConfig/InputConfigDialog.h | 2 +- src/frontend/qt_sdl/InputConfig/MapButton.h | 2 +- src/frontend/qt_sdl/InterfaceSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/InterfaceSettingsDialog.h | 2 +- src/frontend/qt_sdl/LANDialog.cpp | 2 +- src/frontend/qt_sdl/LANDialog.h | 2 +- src/frontend/qt_sdl/MPSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/MPSettingsDialog.h | 2 +- src/frontend/qt_sdl/NetplayDialog.cpp | 2 +- src/frontend/qt_sdl/NetplayDialog.h | 2 +- src/frontend/qt_sdl/OSD_shaders.h | 2 +- src/frontend/qt_sdl/PathSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/PathSettingsDialog.h | 2 +- src/frontend/qt_sdl/Platform.cpp | 2 +- src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp | 2 +- src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h | 2 +- src/frontend/qt_sdl/QPathInput.h | 2 +- src/frontend/qt_sdl/RAMInfoDialog.cpp | 2 +- src/frontend/qt_sdl/RAMInfoDialog.h | 2 +- src/frontend/qt_sdl/ROMInfoDialog.cpp | 2 +- src/frontend/qt_sdl/ROMInfoDialog.h | 2 +- src/frontend/qt_sdl/SaveManager.cpp | 2 +- src/frontend/qt_sdl/SaveManager.h | 2 +- src/frontend/qt_sdl/Screen.cpp | 2 +- src/frontend/qt_sdl/Screen.h | 2 +- src/frontend/qt_sdl/TitleManagerDialog.cpp | 2 +- src/frontend/qt_sdl/TitleManagerDialog.h | 2 +- src/frontend/qt_sdl/VideoSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/VideoSettingsDialog.h | 2 +- src/frontend/qt_sdl/WifiSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/WifiSettingsDialog.h | 2 +- src/frontend/qt_sdl/Window.cpp | 2 +- src/frontend/qt_sdl/Window.h | 2 +- src/frontend/qt_sdl/font.h | 2 +- src/frontend/qt_sdl/main.cpp | 2 +- src/frontend/qt_sdl/main.h | 2 +- src/frontend/qt_sdl/main_shaders.h | 2 +- src/melonDLDI.h | 2 +- src/net/LAN.cpp | 2 +- src/net/LAN.h | 2 +- src/net/LocalMP.cpp | 2 +- src/net/LocalMP.h | 2 +- src/net/MPInterface.cpp | 2 +- src/net/MPInterface.h | 2 +- src/net/Net.cpp | 2 +- src/net/Net.h | 2 +- src/net/NetDriver.h | 2 +- src/net/Net_PCap.cpp | 2 +- src/net/Net_PCap.h | 2 +- src/net/Net_Slirp.cpp | 2 +- src/net/Net_Slirp.h | 2 +- src/net/Netplay.cpp | 2 +- src/net/Netplay.h | 2 +- src/net/PacketDispatcher.cpp | 2 +- src/net/PacketDispatcher.h | 2 +- src/types.h | 2 +- src/version.h.in | 2 +- 210 files changed, 210 insertions(+), 210 deletions(-) diff --git a/res/melon.rc.in b/res/melon.rc.in index 299933cb..c96d0f9e 100644 --- a/res/melon.rc.in +++ b/res/melon.rc.in @@ -18,7 +18,7 @@ FILETYPE VFT_APP VALUE "FileVersion", "${melonDS_VERSION}" VALUE "FileDescription", "melonDS emulator" VALUE "InternalName", "SDnolem" - VALUE "LegalCopyright", "2016-2023 melonDS team" + VALUE "LegalCopyright", "2016-2025 melonDS team" VALUE "LegalTrademarks", "" VALUE "OriginalFilename", "melonDS.exe" VALUE "ProductName", "melonDS" diff --git a/src/ARCodeFile.cpp b/src/ARCodeFile.cpp index a98f5e50..deb8bc40 100644 --- a/src/ARCodeFile.cpp +++ b/src/ARCodeFile.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARCodeFile.h b/src/ARCodeFile.h index 04f9e4f4..823871da 100644 --- a/src/ARCodeFile.h +++ b/src/ARCodeFile.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/AREngine.cpp b/src/AREngine.cpp index 0c37b321..8a5b0881 100644 --- a/src/AREngine.cpp +++ b/src/AREngine.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/AREngine.h b/src/AREngine.h index e73fc98e..d9174409 100644 --- a/src/AREngine.h +++ b/src/AREngine.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARM.cpp b/src/ARM.cpp index f75c1e27..827fcb01 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARM.h b/src/ARM.h index f18d7650..96ed66f0 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp index f5bf7713..80b64dea 100644 --- a/src/ARMInterpreter.cpp +++ b/src/ARMInterpreter.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter.h b/src/ARMInterpreter.h index 1066ac69..031c2970 100644 --- a/src/ARMInterpreter.h +++ b/src/ARMInterpreter.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index 167e184e..7680ce54 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter_ALU.h b/src/ARMInterpreter_ALU.h index 58d8165c..446cb194 100644 --- a/src/ARMInterpreter_ALU.h +++ b/src/ARMInterpreter_ALU.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter_Branch.cpp b/src/ARMInterpreter_Branch.cpp index 623be41a..df24f209 100644 --- a/src/ARMInterpreter_Branch.cpp +++ b/src/ARMInterpreter_Branch.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter_Branch.h b/src/ARMInterpreter_Branch.h index e3d16776..8ec47a73 100644 --- a/src/ARMInterpreter_Branch.h +++ b/src/ARMInterpreter_Branch.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp index f7c24312..e03b1e24 100644 --- a/src/ARMInterpreter_LoadStore.cpp +++ b/src/ARMInterpreter_LoadStore.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter_LoadStore.h b/src/ARMInterpreter_LoadStore.h index 62828194..43bf15ba 100644 --- a/src/ARMInterpreter_LoadStore.h +++ b/src/ARMInterpreter_LoadStore.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp index 9582f7c8..39abdd59 100644 --- a/src/ARMJIT.cpp +++ b/src/ARMJIT.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT.h b/src/ARMJIT.h index 309aa8e8..6f347b49 100644 --- a/src/ARMJIT.h +++ b/src/ARMJIT.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_A64/ARMJIT_ALU.cpp b/src/ARMJIT_A64/ARMJIT_ALU.cpp index cb777500..e1448503 100644 --- a/src/ARMJIT_A64/ARMJIT_ALU.cpp +++ b/src/ARMJIT_A64/ARMJIT_ALU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/ARMJIT_A64/ARMJIT_Branch.cpp b/src/ARMJIT_A64/ARMJIT_Branch.cpp index c83f8161..66498457 100644 --- a/src/ARMJIT_A64/ARMJIT_Branch.cpp +++ b/src/ARMJIT_A64/ARMJIT_Branch.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.cpp b/src/ARMJIT_A64/ARMJIT_Compiler.cpp index 7aa71126..e5355f28 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_A64/ARMJIT_Compiler.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h index 44886b13..35e1e1b6 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.h +++ b/src/ARMJIT_A64/ARMJIT_Compiler.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/ARMJIT_A64/ARMJIT_Linkage.S b/src/ARMJIT_A64/ARMJIT_Linkage.S index 9c360ec0..beaec000 100644 --- a/src/ARMJIT_A64/ARMJIT_Linkage.S +++ b/src/ARMJIT_A64/ARMJIT_Linkage.S @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp index 6d2c4276..68afab32 100644 --- a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/ARMJIT_Compiler.h b/src/ARMJIT_Compiler.h index 46cce5b0..5ed04cf5 100644 --- a/src/ARMJIT_Compiler.h +++ b/src/ARMJIT_Compiler.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/ARMJIT_Global.h b/src/ARMJIT_Global.h index 299d71a6..48d3d511 100644 --- a/src/ARMJIT_Global.h +++ b/src/ARMJIT_Global.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_Internal.h b/src/ARMJIT_Internal.h index 12591d30..945e52c0 100644 --- a/src/ARMJIT_Internal.h +++ b/src/ARMJIT_Internal.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index c79c2d8f..748b6710 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_Memory.h b/src/ARMJIT_Memory.h index a6c68fce..960b996f 100644 --- a/src/ARMJIT_Memory.h +++ b/src/ARMJIT_Memory.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_RegisterCache.h b/src/ARMJIT_RegisterCache.h index d2680731..27f485f8 100644 --- a/src/ARMJIT_RegisterCache.h +++ b/src/ARMJIT_RegisterCache.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_ALU.cpp b/src/ARMJIT_x64/ARMJIT_ALU.cpp index 8f7a1b22..24c429e4 100644 --- a/src/ARMJIT_x64/ARMJIT_ALU.cpp +++ b/src/ARMJIT_x64/ARMJIT_ALU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_Branch.cpp b/src/ARMJIT_x64/ARMJIT_Branch.cpp index bd73ae71..1644b6ea 100644 --- a/src/ARMJIT_x64/ARMJIT_Branch.cpp +++ b/src/ARMJIT_x64/ARMJIT_Branch.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.cpp b/src/ARMJIT_x64/ARMJIT_Compiler.cpp index 6de4caf6..b5234430 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_x64/ARMJIT_Compiler.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.h b/src/ARMJIT_x64/ARMJIT_Compiler.h index c714a6ba..bd380b35 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.h +++ b/src/ARMJIT_x64/ARMJIT_Compiler.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp b/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp index e4812c0a..66ee91c8 100644 --- a/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp +++ b/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_Linkage.S b/src/ARMJIT_x64/ARMJIT_Linkage.S index 18596003..838e0515 100644 --- a/src/ARMJIT_x64/ARMJIT_Linkage.S +++ b/src/ARMJIT_x64/ARMJIT_Linkage.S @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp index 71cd0770..7ba6d1b0 100644 --- a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_Offsets.h b/src/ARMJIT_x64/ARMJIT_Offsets.h index 738fc4ea..28fbed24 100644 --- a/src/ARMJIT_x64/ARMJIT_Offsets.h +++ b/src/ARMJIT_x64/ARMJIT_Offsets.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/ARM_InstrInfo.cpp b/src/ARM_InstrInfo.cpp index 58838307..cc1d4b4a 100644 --- a/src/ARM_InstrInfo.cpp +++ b/src/ARM_InstrInfo.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARM_InstrInfo.h b/src/ARM_InstrInfo.h index fe4095b4..7ecc87ac 100644 --- a/src/ARM_InstrInfo.h +++ b/src/ARM_InstrInfo.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ARM_InstrTable.h b/src/ARM_InstrTable.h index 8213c2e0..13d67bff 100644 --- a/src/ARM_InstrTable.h +++ b/src/ARM_InstrTable.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/Args.h b/src/Args.h index 4f4fa1f0..e07d2e0d 100644 --- a/src/Args.h +++ b/src/Args.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/CP15.cpp b/src/CP15.cpp index e924bff3..06ab545e 100644 --- a/src/CP15.cpp +++ b/src/CP15.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/CRC32.cpp b/src/CRC32.cpp index 82fe467f..8417f9ce 100644 --- a/src/CRC32.cpp +++ b/src/CRC32.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/CRC32.h b/src/CRC32.h index 90fb8057..d7daa181 100644 --- a/src/CRC32.h +++ b/src/CRC32.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DMA.cpp b/src/DMA.cpp index 80cd592c..8db1cca2 100644 --- a/src/DMA.cpp +++ b/src/DMA.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DMA.h b/src/DMA.h index 354f4495..7f1f920d 100644 --- a/src/DMA.h +++ b/src/DMA.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DMA_Timings.cpp b/src/DMA_Timings.cpp index a51fedfb..e46da964 100644 --- a/src/DMA_Timings.cpp +++ b/src/DMA_Timings.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DMA_Timings.h b/src/DMA_Timings.h index 38206235..e9a98496 100644 --- a/src/DMA_Timings.h +++ b/src/DMA_Timings.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi.cpp b/src/DSi.cpp index f9115cbf..f6c5b519 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi.h b/src/DSi.h index 23a2460c..e9f32b68 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp index 36fe2892..e53e3ea5 100644 --- a/src/DSi_AES.cpp +++ b/src/DSi_AES.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_AES.h b/src/DSi_AES.h index f3b79868..b0b1c716 100644 --- a/src/DSi_AES.h +++ b/src/DSi_AES.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 8a54bb18..9ceed4ac 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index 604e06ac..5a626f56 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index f28562e9..d841cb68 100644 --- a/src/DSi_I2C.cpp +++ b/src/DSi_I2C.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h index 3102ffeb..93589bcb 100644 --- a/src/DSi_I2C.h +++ b/src/DSi_I2C.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index 13eadd6e..20e9d034 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h index 7af434d9..d4631121 100644 --- a/src/DSi_NAND.h +++ b/src/DSi_NAND.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp index 452ac6e6..3c8d2df0 100644 --- a/src/DSi_NDMA.cpp +++ b/src/DSi_NDMA.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_NDMA.h b/src/DSi_NDMA.h index 9f8e6706..dd566f1f 100644 --- a/src/DSi_NDMA.h +++ b/src/DSi_NDMA.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 3ec5d3f0..269d69ac 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_NWifi.h b/src/DSi_NWifi.h index 4140820b..2b229c17 100644 --- a/src/DSi_NWifi.h +++ b/src/DSi_NWifi.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index c1d07682..08f41727 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_SD.h b/src/DSi_SD.h index 5d376600..6b9b4fd8 100644 --- a/src/DSi_SD.h +++ b/src/DSi_SD.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_SPI_TSC.cpp b/src/DSi_SPI_TSC.cpp index dbb60c10..2ee79c11 100644 --- a/src/DSi_SPI_TSC.cpp +++ b/src/DSi_SPI_TSC.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_SPI_TSC.h b/src/DSi_SPI_TSC.h index f20f7ac1..f7e135c2 100644 --- a/src/DSi_SPI_TSC.h +++ b/src/DSi_SPI_TSC.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/DSi_TMD.h b/src/DSi_TMD.h index 5ea91a6f..e235f5ea 100644 --- a/src/DSi_TMD.h +++ b/src/DSi_TMD.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/FATIO.cpp b/src/FATIO.cpp index aea33ee6..4d92c271 100644 --- a/src/FATIO.cpp +++ b/src/FATIO.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/FATIO.h b/src/FATIO.h index f8184885..d72b967c 100644 --- a/src/FATIO.h +++ b/src/FATIO.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index 200c99d5..c93e90b9 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/FATStorage.h b/src/FATStorage.h index 030b765b..08d856f2 100644 --- a/src/FATStorage.h +++ b/src/FATStorage.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/FIFO.h b/src/FIFO.h index 5fc04832..ccb009b1 100644 --- a/src/FIFO.h +++ b/src/FIFO.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GBACart.cpp b/src/GBACart.cpp index be0d223b..071bf06e 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GBACart.h b/src/GBACart.h index c9a7caa9..86a28edd 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU.cpp b/src/GPU.cpp index 585f0bea..e1c7ddc4 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU.h b/src/GPU.h index 5c373ca8..fd5ad820 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index e76e85c1..a01a5caa 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU2D.h b/src/GPU2D.h index f56167a1..4d6e55de 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU2D_Soft.cpp b/src/GPU2D_Soft.cpp index 01dade2b..2f8afcba 100644 --- a/src/GPU2D_Soft.cpp +++ b/src/GPU2D_Soft.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU2D_Soft.h b/src/GPU2D_Soft.h index d9942f61..4beaf8f9 100644 --- a/src/GPU2D_Soft.h +++ b/src/GPU2D_Soft.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 4a1426aa..9d6be8b8 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU3D.h b/src/GPU3D.h index d10df55f..08c2208c 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU3D_Compute.cpp b/src/GPU3D_Compute.cpp index 346a6a53..6c5ef4c2 100644 --- a/src/GPU3D_Compute.cpp +++ b/src/GPU3D_Compute.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU3D_Compute.h b/src/GPU3D_Compute.h index 751737b7..b9183816 100644 --- a/src/GPU3D_Compute.h +++ b/src/GPU3D_Compute.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU3D_Compute_shaders.h b/src/GPU3D_Compute_shaders.h index 26fb7bde..8b8f6c5a 100644 --- a/src/GPU3D_Compute_shaders.h +++ b/src/GPU3D_Compute_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index 3f85db8c..706c75e2 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU3D_OpenGL.h b/src/GPU3D_OpenGL.h index d69af324..b7805223 100644 --- a/src/GPU3D_OpenGL.h +++ b/src/GPU3D_OpenGL.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU3D_OpenGL_shaders.h b/src/GPU3D_OpenGL_shaders.h index 1bd8296a..ab9985ce 100644 --- a/src/GPU3D_OpenGL_shaders.h +++ b/src/GPU3D_OpenGL_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index a9d0bd64..d54c3a75 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h index 73d02e4f..8d432168 100644 --- a/src/GPU3D_Soft.h +++ b/src/GPU3D_Soft.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU_OpenGL.cpp b/src/GPU_OpenGL.cpp index a58dbedb..888f508e 100644 --- a/src/GPU_OpenGL.cpp +++ b/src/GPU_OpenGL.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU_OpenGL.h b/src/GPU_OpenGL.h index 2e482861..3461ca81 100644 --- a/src/GPU_OpenGL.h +++ b/src/GPU_OpenGL.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/GPU_OpenGL_shaders.h b/src/GPU_OpenGL_shaders.h index 3c463ab8..04af221d 100644 --- a/src/GPU_OpenGL_shaders.h +++ b/src/GPU_OpenGL_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/JitBlock.h b/src/JitBlock.h index 2dc516fd..bf615c1d 100644 --- a/src/JitBlock.h +++ b/src/JitBlock.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/MemConstants.h b/src/MemConstants.h index 3e10cbce..5750e075 100644 --- a/src/MemConstants.h +++ b/src/MemConstants.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/MemRegion.h b/src/MemRegion.h index 0a8212c7..a9a81308 100644 --- a/src/MemRegion.h +++ b/src/MemRegion.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/NDS.cpp b/src/NDS.cpp index 2efb3b0a..1dd7e095 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/NDS.h b/src/NDS.h index 8efbb90c..b44853b5 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 1fa0fbfe..bfc07f93 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/NDSCart.h b/src/NDSCart.h index 3704f659..7e53e62e 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/NDSCartR4.cpp b/src/NDSCartR4.cpp index 46fbeb6a..4ebe128f 100644 --- a/src/NDSCartR4.cpp +++ b/src/NDSCartR4.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/NDS_Header.h b/src/NDS_Header.h index 80d5ced6..a283578b 100644 --- a/src/NDS_Header.h +++ b/src/NDS_Header.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, WaluigiWare64 + Copyright 2016-2025 melonDS team, WaluigiWare64 This file is part of melonDS. diff --git a/src/NonStupidBitfield.h b/src/NonStupidBitfield.h index 7332394b..bfb3f506 100644 --- a/src/NonStupidBitfield.h +++ b/src/NonStupidBitfield.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/OpenGLSupport.cpp b/src/OpenGLSupport.cpp index 2740e157..4cdff2c0 100644 --- a/src/OpenGLSupport.cpp +++ b/src/OpenGLSupport.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/OpenGLSupport.h b/src/OpenGLSupport.h index b426597d..add3130a 100644 --- a/src/OpenGLSupport.h +++ b/src/OpenGLSupport.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/Platform.h b/src/Platform.h index bef66593..8e85c7c7 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ROMList.cpp b/src/ROMList.cpp index a00b0b22..62fd7691 100644 --- a/src/ROMList.cpp +++ b/src/ROMList.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/ROMList.h b/src/ROMList.h index e27ee728..9864953b 100644 --- a/src/ROMList.h +++ b/src/ROMList.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/RTC.cpp b/src/RTC.cpp index c10672fb..306a35ba 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/RTC.h b/src/RTC.h index 7c3671ce..f4ec195b 100644 --- a/src/RTC.h +++ b/src/RTC.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/SPI.cpp b/src/SPI.cpp index 67b63e2a..41866adb 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/SPI.h b/src/SPI.h index 350708ef..147d7e4d 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/SPI_Firmware.cpp b/src/SPI_Firmware.cpp index 472500af..5f134b45 100644 --- a/src/SPI_Firmware.cpp +++ b/src/SPI_Firmware.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/SPI_Firmware.h b/src/SPI_Firmware.h index a5d40eeb..621079a6 100644 --- a/src/SPI_Firmware.h +++ b/src/SPI_Firmware.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/SPU.cpp b/src/SPU.cpp index 5b245890..0f0c286d 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/SPU.h b/src/SPU.h index 19253891..fa93273d 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/Savestate.cpp b/src/Savestate.cpp index c51459d9..61261e6a 100644 --- a/src/Savestate.cpp +++ b/src/Savestate.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/Savestate.h b/src/Savestate.h index dce62844..d100f984 100644 --- a/src/Savestate.h +++ b/src/Savestate.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/TinyVector.h b/src/TinyVector.h index 66da63ec..87b3852a 100644 --- a/src/TinyVector.h +++ b/src/TinyVector.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team, RSDuck + Copyright 2016-2025 melonDS team, RSDuck This file is part of melonDS. diff --git a/src/Utils.cpp b/src/Utils.cpp index bb5848c1..5eba5509 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/Utils.h b/src/Utils.h index eae9193a..41e14c86 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/Wifi.cpp b/src/Wifi.cpp index 77933dfb..a0549283 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/Wifi.h b/src/Wifi.h index 14eddd65..70bfa9b2 100644 --- a/src/Wifi.h +++ b/src/Wifi.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp index 0a239f7d..6001da1b 100644 --- a/src/WifiAP.cpp +++ b/src/WifiAP.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/WifiAP.h b/src/WifiAP.h index 8f0ef608..cc826f5b 100644 --- a/src/WifiAP.h +++ b/src/WifiAP.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/ScreenLayout.cpp b/src/frontend/ScreenLayout.cpp index bb9a60cb..18b504df 100644 --- a/src/frontend/ScreenLayout.cpp +++ b/src/frontend/ScreenLayout.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/ScreenLayout.h b/src/frontend/ScreenLayout.h index a813127d..656490ec 100644 --- a/src/frontend/ScreenLayout.h +++ b/src/frontend/ScreenLayout.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/mic_blow.h b/src/frontend/mic_blow.h index f90d8c8e..22ab0a74 100644 --- a/src/frontend/mic_blow.h +++ b/src/frontend/mic_blow.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/ArchiveUtil.cpp b/src/frontend/qt_sdl/ArchiveUtil.cpp index f4155682..ea7b5f29 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.cpp +++ b/src/frontend/qt_sdl/ArchiveUtil.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/ArchiveUtil.h b/src/frontend/qt_sdl/ArchiveUtil.h index c422a84b..ae623655 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.h +++ b/src/frontend/qt_sdl/ArchiveUtil.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index 5f2aef19..9a13f4ac 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.h b/src/frontend/qt_sdl/AudioSettingsDialog.h index f05cd8dd..946e47fb 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.h +++ b/src/frontend/qt_sdl/AudioSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CameraManager.cpp b/src/frontend/qt_sdl/CameraManager.cpp index 687d7b4b..f080284d 100644 --- a/src/frontend/qt_sdl/CameraManager.cpp +++ b/src/frontend/qt_sdl/CameraManager.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CameraManager.h b/src/frontend/qt_sdl/CameraManager.h index 806a8ba7..91deea7f 100644 --- a/src/frontend/qt_sdl/CameraManager.h +++ b/src/frontend/qt_sdl/CameraManager.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CameraSettingsDialog.cpp b/src/frontend/qt_sdl/CameraSettingsDialog.cpp index 63b7a76e..672cf2a5 100644 --- a/src/frontend/qt_sdl/CameraSettingsDialog.cpp +++ b/src/frontend/qt_sdl/CameraSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CameraSettingsDialog.h b/src/frontend/qt_sdl/CameraSettingsDialog.h index 41caaf63..e9df44c5 100644 --- a/src/frontend/qt_sdl/CameraSettingsDialog.h +++ b/src/frontend/qt_sdl/CameraSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CheatsDialog.cpp b/src/frontend/qt_sdl/CheatsDialog.cpp index 19065a8d..75279a2a 100644 --- a/src/frontend/qt_sdl/CheatsDialog.cpp +++ b/src/frontend/qt_sdl/CheatsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CheatsDialog.h b/src/frontend/qt_sdl/CheatsDialog.h index cafb9684..9f666b11 100644 --- a/src/frontend/qt_sdl/CheatsDialog.h +++ b/src/frontend/qt_sdl/CheatsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 73161809..b81060d3 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index 39278c36..d83463f3 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/DateTimeDialog.cpp b/src/frontend/qt_sdl/DateTimeDialog.cpp index ac98300e..3046fdc2 100644 --- a/src/frontend/qt_sdl/DateTimeDialog.cpp +++ b/src/frontend/qt_sdl/DateTimeDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/DateTimeDialog.h b/src/frontend/qt_sdl/DateTimeDialog.h index 5057609f..3e908cac 100644 --- a/src/frontend/qt_sdl/DateTimeDialog.h +++ b/src/frontend/qt_sdl/DateTimeDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index eb11ede2..deed0f2a 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 48e4e5b9..bbab7010 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp index 21e573f9..e779f190 100644 --- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp +++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/EmuInstanceInput.cpp b/src/frontend/qt_sdl/EmuInstanceInput.cpp index aa1c529f..e85b7f23 100644 --- a/src/frontend/qt_sdl/EmuInstanceInput.cpp +++ b/src/frontend/qt_sdl/EmuInstanceInput.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index 30b92fd1..e8c5cc90 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.h b/src/frontend/qt_sdl/EmuSettingsDialog.h index 58a89b3a..b3b79218 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.h +++ b/src/frontend/qt_sdl/EmuSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 3aabe7e5..9b54ed6e 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h index 61212553..4ca90097 100644 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp index 1f71b5b9..87925642 100644 --- a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp +++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.h b/src/frontend/qt_sdl/FirmwareSettingsDialog.h index 2053ab99..b8e58c84 100644 --- a/src/frontend/qt_sdl/FirmwareSettingsDialog.h +++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp index 4428a515..ade03f32 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h index 3337228f..89483b7f 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/InputConfig/MapButton.h b/src/frontend/qt_sdl/InputConfig/MapButton.h index 14bc3962..38cd02aa 100644 --- a/src/frontend/qt_sdl/InputConfig/MapButton.h +++ b/src/frontend/qt_sdl/InputConfig/MapButton.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp index a2c1bd71..f57187df 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.h b/src/frontend/qt_sdl/InterfaceSettingsDialog.h index c960e560..adeab774 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.h +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/LANDialog.cpp b/src/frontend/qt_sdl/LANDialog.cpp index 6707cfdc..108cc249 100644 --- a/src/frontend/qt_sdl/LANDialog.cpp +++ b/src/frontend/qt_sdl/LANDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/LANDialog.h b/src/frontend/qt_sdl/LANDialog.h index 0a65a6e9..ba0f646f 100644 --- a/src/frontend/qt_sdl/LANDialog.h +++ b/src/frontend/qt_sdl/LANDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/MPSettingsDialog.cpp b/src/frontend/qt_sdl/MPSettingsDialog.cpp index 54c35d15..7e666c0c 100644 --- a/src/frontend/qt_sdl/MPSettingsDialog.cpp +++ b/src/frontend/qt_sdl/MPSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/MPSettingsDialog.h b/src/frontend/qt_sdl/MPSettingsDialog.h index 64b56579..feca95ec 100644 --- a/src/frontend/qt_sdl/MPSettingsDialog.h +++ b/src/frontend/qt_sdl/MPSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/NetplayDialog.cpp b/src/frontend/qt_sdl/NetplayDialog.cpp index 3657eef8..324f3ca4 100644 --- a/src/frontend/qt_sdl/NetplayDialog.cpp +++ b/src/frontend/qt_sdl/NetplayDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/NetplayDialog.h b/src/frontend/qt_sdl/NetplayDialog.h index 1fa0dcf2..6368b92e 100644 --- a/src/frontend/qt_sdl/NetplayDialog.h +++ b/src/frontend/qt_sdl/NetplayDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/OSD_shaders.h b/src/frontend/qt_sdl/OSD_shaders.h index 253fcdc5..ff3b231f 100644 --- a/src/frontend/qt_sdl/OSD_shaders.h +++ b/src/frontend/qt_sdl/OSD_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/PathSettingsDialog.cpp b/src/frontend/qt_sdl/PathSettingsDialog.cpp index d0b42b21..8a4cb94d 100644 --- a/src/frontend/qt_sdl/PathSettingsDialog.cpp +++ b/src/frontend/qt_sdl/PathSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/PathSettingsDialog.h b/src/frontend/qt_sdl/PathSettingsDialog.h index d2dc4970..c92a0660 100644 --- a/src/frontend/qt_sdl/PathSettingsDialog.h +++ b/src/frontend/qt_sdl/PathSettingsDialog.h @@ -1,6 +1,6 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 541b51f2..1466e33f 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp index ddff9314..0216393e 100644 --- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h index 6853aeec..b6800bd9 100644 --- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/QPathInput.h b/src/frontend/qt_sdl/QPathInput.h index a26bf95f..052ccff6 100644 --- a/src/frontend/qt_sdl/QPathInput.h +++ b/src/frontend/qt_sdl/QPathInput.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/RAMInfoDialog.cpp b/src/frontend/qt_sdl/RAMInfoDialog.cpp index cbc954b1..8c3a5fbf 100644 --- a/src/frontend/qt_sdl/RAMInfoDialog.cpp +++ b/src/frontend/qt_sdl/RAMInfoDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/RAMInfoDialog.h b/src/frontend/qt_sdl/RAMInfoDialog.h index 5166e237..9bf2fbe6 100644 --- a/src/frontend/qt_sdl/RAMInfoDialog.h +++ b/src/frontend/qt_sdl/RAMInfoDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/ROMInfoDialog.cpp b/src/frontend/qt_sdl/ROMInfoDialog.cpp index 75255638..5ca720d3 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.cpp +++ b/src/frontend/qt_sdl/ROMInfoDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/ROMInfoDialog.h b/src/frontend/qt_sdl/ROMInfoDialog.h index 7c13ad4d..bc7bc4f4 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.h +++ b/src/frontend/qt_sdl/ROMInfoDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/SaveManager.cpp b/src/frontend/qt_sdl/SaveManager.cpp index ec8e08d3..297b08ec 100644 --- a/src/frontend/qt_sdl/SaveManager.cpp +++ b/src/frontend/qt_sdl/SaveManager.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/SaveManager.h b/src/frontend/qt_sdl/SaveManager.h index 1b0f226a..f9c86110 100644 --- a/src/frontend/qt_sdl/SaveManager.h +++ b/src/frontend/qt_sdl/SaveManager.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index bc63d3ac..8024c9b4 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Screen.h b/src/frontend/qt_sdl/Screen.h index a988815e..a0e1fea7 100644 --- a/src/frontend/qt_sdl/Screen.h +++ b/src/frontend/qt_sdl/Screen.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp index 844365e7..a06f5781 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.cpp +++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/TitleManagerDialog.h b/src/frontend/qt_sdl/TitleManagerDialog.h index 984850b5..ced76256 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.h +++ b/src/frontend/qt_sdl/TitleManagerDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.cpp b/src/frontend/qt_sdl/VideoSettingsDialog.cpp index 619ecda3..b1f778ef 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.cpp +++ b/src/frontend/qt_sdl/VideoSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.h b/src/frontend/qt_sdl/VideoSettingsDialog.h index e7ba5cc7..e4254525 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.h +++ b/src/frontend/qt_sdl/VideoSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.cpp b/src/frontend/qt_sdl/WifiSettingsDialog.cpp index e041234b..2a3f52df 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.cpp +++ b/src/frontend/qt_sdl/WifiSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.h b/src/frontend/qt_sdl/WifiSettingsDialog.h index d78b0f65..262e36dc 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.h +++ b/src/frontend/qt_sdl/WifiSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 661a5af1..c145933f 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 9f652f54..21c65d9e 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/font.h b/src/frontend/qt_sdl/font.h index 6b9e8570..01519491 100644 --- a/src/frontend/qt_sdl/font.h +++ b/src/frontend/qt_sdl/font.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index ec038ff2..9ba3d1b3 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index e0d38963..553b223e 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/main_shaders.h b/src/frontend/qt_sdl/main_shaders.h index b5c4663c..613d9138 100644 --- a/src/frontend/qt_sdl/main_shaders.h +++ b/src/frontend/qt_sdl/main_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/melonDLDI.h b/src/melonDLDI.h index b99b2a30..4f560629 100644 --- a/src/melonDLDI.h +++ b/src/melonDLDI.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/LAN.cpp b/src/net/LAN.cpp index 8cd39584..78b5f50d 100644 --- a/src/net/LAN.cpp +++ b/src/net/LAN.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/LAN.h b/src/net/LAN.h index 87282539..3950e1b5 100644 --- a/src/net/LAN.h +++ b/src/net/LAN.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/LocalMP.cpp b/src/net/LocalMP.cpp index a789964e..2707db78 100644 --- a/src/net/LocalMP.cpp +++ b/src/net/LocalMP.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/LocalMP.h b/src/net/LocalMP.h index 8688d8e1..4e72da76 100644 --- a/src/net/LocalMP.h +++ b/src/net/LocalMP.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/MPInterface.cpp b/src/net/MPInterface.cpp index 39d1915d..7b3b6c77 100644 --- a/src/net/MPInterface.cpp +++ b/src/net/MPInterface.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/MPInterface.h b/src/net/MPInterface.h index eb5bef88..b8c5f50d 100644 --- a/src/net/MPInterface.h +++ b/src/net/MPInterface.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/Net.cpp b/src/net/Net.cpp index 5c169f35..6ea1c037 100644 --- a/src/net/Net.cpp +++ b/src/net/Net.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/Net.h b/src/net/Net.h index 4229468c..293c7dcc 100644 --- a/src/net/Net.h +++ b/src/net/Net.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/NetDriver.h b/src/net/NetDriver.h index 2575fdea..9c416c80 100644 --- a/src/net/NetDriver.h +++ b/src/net/NetDriver.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/Net_PCap.cpp b/src/net/Net_PCap.cpp index e92ad06d..079cc072 100644 --- a/src/net/Net_PCap.cpp +++ b/src/net/Net_PCap.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/Net_PCap.h b/src/net/Net_PCap.h index cd2ab90b..de086f22 100644 --- a/src/net/Net_PCap.h +++ b/src/net/Net_PCap.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/Net_Slirp.cpp b/src/net/Net_Slirp.cpp index cdd1e553..3937c6bc 100644 --- a/src/net/Net_Slirp.cpp +++ b/src/net/Net_Slirp.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/Net_Slirp.h b/src/net/Net_Slirp.h index 5f9b6587..4dadd917 100644 --- a/src/net/Net_Slirp.h +++ b/src/net/Net_Slirp.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/Netplay.cpp b/src/net/Netplay.cpp index c89dbf4e..56756e0e 100644 --- a/src/net/Netplay.cpp +++ b/src/net/Netplay.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/Netplay.h b/src/net/Netplay.h index 1eff54b0..e3f4741e 100644 --- a/src/net/Netplay.h +++ b/src/net/Netplay.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/PacketDispatcher.cpp b/src/net/PacketDispatcher.cpp index c9e0d274..2af45d58 100644 --- a/src/net/PacketDispatcher.cpp +++ b/src/net/PacketDispatcher.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/net/PacketDispatcher.h b/src/net/PacketDispatcher.h index d7f37c9a..10ef8d2d 100644 --- a/src/net/PacketDispatcher.h +++ b/src/net/PacketDispatcher.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/types.h b/src/types.h index e7fccafb..461c84f2 100644 --- a/src/types.h +++ b/src/types.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. diff --git a/src/version.h.in b/src/version.h.in index 9b4cd8ce..8bb2eb74 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,5 +1,5 @@ /* - Copyright 2016-2024 melonDS team + Copyright 2016-2025 melonDS team This file is part of melonDS. From 7117178c2dd56df32b6534ba6a54ad1f8547e693 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 27 May 2025 00:55:12 +0200 Subject: [PATCH 072/106] melonDLDI: add support for unaligned I/O --- melonDLDI/Makefile | 2 +- melonDLDI/melonDLDI.s | 54 ++++++++++++++++++++++++++---------- src/melonDLDI.h | 64 +++++++++++++++++++++++-------------------- 3 files changed, 76 insertions(+), 44 deletions(-) diff --git a/melonDLDI/Makefile b/melonDLDI/Makefile index 56b316fb..7a4eb56a 100644 --- a/melonDLDI/Makefile +++ b/melonDLDI/Makefile @@ -8,7 +8,7 @@ all: $(AS) $(BIN).s -o $(BIN).o $(LD) $(BIN).o -Ttext 0xBF800000 -e 0xBF800000 -o $(BIN).elf $(OBJCOPY) -O binary $(BIN).elf $(BIN).bin - xxd -i -n $(BIN) $(BIN).bin $(BIN).h + xxd -i -n $(BIN) -c 16 $(BIN).bin $(BIN).h clean: rm -f $(BIN).h $(BIN).bin $(BIN).elf $(BIN).o diff --git a/melonDLDI/melonDLDI.s b/melonDLDI/melonDLDI.s index 4b044c69..399ad032 100755 --- a/melonDLDI/melonDLDI.s +++ b/melonDLDI/melonDLDI.s @@ -68,34 +68,63 @@ _sendcmd: tst r0, #0x01 bne __send_write @ receive data + tst r2, #0x3 + bne __read_unal_loop __read_busyloop: ldr r0, [r12, #0x4] + tst r0, #0x80000000 + bxeq lr tst r0, #0x00800000 ldrne r1, [r3, #0x10] @ load data - cmpne r2, #0 strne r1, [r2], #4 - tst r0, #0x80000000 - bne __read_busyloop - bx lr + b __read_busyloop +__read_unal_loop: + ldr r0, [r12, #0x4] + tst r0, #0x80000000 + bxeq lr + tst r0, #0x00800000 + beq __read_unal_loop + ldr r1, [r3, #0x10] @ load data + strb r1, [r2], #1 + mov r1, r1, lsr #8 + strb r1, [r2], #1 + mov r1, r1, lsr #8 + strb r1, [r2], #1 + mov r1, r1, lsr #8 + strb r1, [r2], #1 + b __read_unal_loop @ send data __send_write: mov r1, #0 + tst r2, #0x3 + bne __write_unal_loop __write_busyloop: ldr r0, [r12, #0x4] + tst r0, #0x80000000 + bxeq lr tst r0, #0x00800000 - cmpne r2, #0 ldrne r1, [r2], #4 strne r1, [r3, #0x10] @ store data + b __write_busyloop +__write_unal_loop: + ldr r0, [r12, #0x4] tst r0, #0x80000000 - bne __write_busyloop - bx lr - + bxeq lr + tst r0, #0x00800000 + beq __write_unal_loop + ldrb r1, [r2], #1 + ldrb r0, [r2], #1 + orr r1, r1, r0, lsl #8 + ldrb r0, [r2], #1 + orr r1, r1, r0, lsl #16 + ldrb r0, [r2], #1 + orr r1, r1, r0, lsl #24 + str r1, [r3, #0x10] @ store data + b __write_unal_loop + @ r0=sector r1=numsectors r2=out melon_readSectors: - tst r2, #0x3 - movne r0, #0 - bxne lr stmdb sp!, {r3-r6, lr} mov r4, r0 mov r5, r1 @@ -114,9 +143,6 @@ _readloop: @ r0=sector r1=numsectors r2=out melon_writeSectors: - tst r2, #0x3 - movne r0, #0 - bxne lr stmdb sp!, {r3-r6, lr} mov r4, r0 mov r5, r1 diff --git a/src/melonDLDI.h b/src/melonDLDI.h index 4f560629..271e0713 100644 --- a/src/melonDLDI.h +++ b/src/melonDLDI.h @@ -25,35 +25,41 @@ namespace melonDS { const u8 melonDLDI[] = { - 0xED, 0xA5, 0x8D, 0xBF, 0x20, 0x43, 0x68, 0x69, 0x73, 0x68, 0x6D, 0x00, 0x01, 0x09, 0x00, 0x00, - 0x6D, 0x65, 0x6C, 0x6F, 0x6E, 0x44, 0x53, 0x20, 0x44, 0x4C, 0x44, 0x49, 0x20, 0x64, 0x72, 0x69, - 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0xBF, 0xC4, 0x01, 0x80, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x4D, 0x45, 0x4C, 0x4E, 0x23, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0xBF, 0x88, 0x00, 0x80, 0xBF, - 0x34, 0x01, 0x80, 0xBF, 0x74, 0x01, 0x80, 0xBF, 0xB4, 0x01, 0x80, 0xBF, 0xBC, 0x01, 0x80, 0xBF, - 0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1, - 0x01, 0xC3, 0xA0, 0xE3, 0x1A, 0xCE, 0x8C, 0xE2, 0x02, 0x39, 0xA0, 0xE3, 0xB0, 0x30, 0xCC, 0xE1, - 0x08, 0x00, 0xCC, 0xE5, 0x0C, 0x10, 0xCC, 0xE5, 0x21, 0x14, 0xA0, 0xE1, 0x0B, 0x10, 0xCC, 0xE5, - 0x21, 0x14, 0xA0, 0xE1, 0x0A, 0x10, 0xCC, 0xE5, 0x21, 0x14, 0xA0, 0xE1, 0x09, 0x10, 0xCC, 0xE5, - 0x21, 0x14, 0xA0, 0xE1, 0x0D, 0x10, 0xCC, 0xE5, 0xBE, 0x10, 0xCC, 0xE1, 0x0A, 0x32, 0xA0, 0xE3, - 0x00, 0x3F, 0x83, 0xE1, 0x00, 0x00, 0x52, 0xE3, 0x01, 0x34, 0x83, 0x13, 0x01, 0x35, 0x83, 0xE3, - 0x04, 0x30, 0x8C, 0xE5, 0x41, 0x36, 0xA0, 0xE3, 0x01, 0x00, 0x10, 0xE3, 0x07, 0x00, 0x00, 0x1A, - 0x04, 0x00, 0x9C, 0xE5, 0x02, 0x05, 0x10, 0xE3, 0x10, 0x10, 0x93, 0x15, 0x00, 0x00, 0x52, 0x13, - 0x04, 0x10, 0x82, 0x14, 0x02, 0x01, 0x10, 0xE3, 0xF8, 0xFF, 0xFF, 0x1A, 0x1E, 0xFF, 0x2F, 0xE1, - 0x00, 0x10, 0xA0, 0xE3, 0x04, 0x00, 0x9C, 0xE5, 0x02, 0x05, 0x10, 0xE3, 0x00, 0x00, 0x52, 0x13, - 0x04, 0x10, 0x92, 0x14, 0x10, 0x10, 0x83, 0x15, 0x02, 0x01, 0x10, 0xE3, 0xF8, 0xFF, 0xFF, 0x1A, - 0x1E, 0xFF, 0x2F, 0xE1, 0x03, 0x00, 0x12, 0xE3, 0x00, 0x00, 0xA0, 0x13, 0x1E, 0xFF, 0x2F, 0x11, - 0x78, 0x40, 0x2D, 0xE9, 0x00, 0x40, 0xA0, 0xE1, 0x01, 0x50, 0xA0, 0xE1, 0x00, 0x60, 0xA0, 0xE3, - 0xC0, 0x00, 0xA0, 0xE3, 0x06, 0x10, 0x84, 0xE0, 0xCC, 0xFF, 0xFF, 0xEB, 0x01, 0x60, 0x86, 0xE2, - 0x05, 0x00, 0x56, 0xE1, 0xF9, 0xFF, 0xFF, 0x3A, 0x78, 0x40, 0xBD, 0xE8, 0x01, 0x00, 0xA0, 0xE3, - 0x1E, 0xFF, 0x2F, 0xE1, 0x03, 0x00, 0x12, 0xE3, 0x00, 0x00, 0xA0, 0x13, 0x1E, 0xFF, 0x2F, 0x11, - 0x78, 0x40, 0x2D, 0xE9, 0x00, 0x40, 0xA0, 0xE1, 0x01, 0x50, 0xA0, 0xE1, 0x00, 0x60, 0xA0, 0xE3, - 0xC1, 0x00, 0xA0, 0xE3, 0x06, 0x10, 0x84, 0xE0, 0xBC, 0xFF, 0xFF, 0xEB, 0x01, 0x60, 0x86, 0xE2, - 0x05, 0x00, 0x56, 0xE1, 0xF9, 0xFF, 0xFF, 0x3A, 0x78, 0x40, 0xBD, 0xE8, 0x01, 0x00, 0xA0, 0xE3, - 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x00, 0xA0, 0xE3, - 0x1E, 0xFF, 0x2F, 0xE1 + 0xed, 0xa5, 0x8d, 0xbf, 0x20, 0x43, 0x68, 0x69, 0x73, 0x68, 0x6d, 0x00, 0x01, 0x09, 0x00, 0x00, + 0x6d, 0x65, 0x6c, 0x6f, 0x6e, 0x44, 0x53, 0x20, 0x44, 0x4c, 0x44, 0x49, 0x20, 0x64, 0x72, 0x69, + 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xbf, 0x24, 0x02, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4d, 0x45, 0x4c, 0x4e, 0x23, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0xbf, 0x88, 0x00, 0x80, 0xbf, + 0xac, 0x01, 0x80, 0xbf, 0xe0, 0x01, 0x80, 0xbf, 0x14, 0x02, 0x80, 0xbf, 0x1c, 0x02, 0x80, 0xbf, + 0x01, 0x00, 0xa0, 0xe3, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x00, 0xa0, 0xe3, 0x1e, 0xff, 0x2f, 0xe1, + 0x01, 0xc3, 0xa0, 0xe3, 0x1a, 0xce, 0x8c, 0xe2, 0x02, 0x39, 0xa0, 0xe3, 0xb0, 0x30, 0xcc, 0xe1, + 0x08, 0x00, 0xcc, 0xe5, 0x0c, 0x10, 0xcc, 0xe5, 0x21, 0x14, 0xa0, 0xe1, 0x0b, 0x10, 0xcc, 0xe5, + 0x21, 0x14, 0xa0, 0xe1, 0x0a, 0x10, 0xcc, 0xe5, 0x21, 0x14, 0xa0, 0xe1, 0x09, 0x10, 0xcc, 0xe5, + 0x21, 0x14, 0xa0, 0xe1, 0x0d, 0x10, 0xcc, 0xe5, 0xbe, 0x10, 0xcc, 0xe1, 0x0a, 0x32, 0xa0, 0xe3, + 0x00, 0x3f, 0x83, 0xe1, 0x00, 0x00, 0x52, 0xe3, 0x01, 0x34, 0x83, 0x13, 0x01, 0x35, 0x83, 0xe3, + 0x04, 0x30, 0x8c, 0xe5, 0x41, 0x36, 0xa0, 0xe3, 0x01, 0x00, 0x10, 0xe3, 0x16, 0x00, 0x00, 0x1a, + 0x03, 0x00, 0x12, 0xe3, 0x06, 0x00, 0x00, 0x1a, 0x04, 0x00, 0x9c, 0xe5, 0x02, 0x01, 0x10, 0xe3, + 0x1e, 0xff, 0x2f, 0x01, 0x02, 0x05, 0x10, 0xe3, 0x10, 0x10, 0x93, 0x15, 0x04, 0x10, 0x82, 0x14, + 0xf8, 0xff, 0xff, 0xea, 0x04, 0x00, 0x9c, 0xe5, 0x02, 0x01, 0x10, 0xe3, 0x1e, 0xff, 0x2f, 0x01, + 0x02, 0x05, 0x10, 0xe3, 0xfa, 0xff, 0xff, 0x0a, 0x10, 0x10, 0x93, 0xe5, 0x01, 0x10, 0xc2, 0xe4, + 0x21, 0x14, 0xa0, 0xe1, 0x01, 0x10, 0xc2, 0xe4, 0x21, 0x14, 0xa0, 0xe1, 0x01, 0x10, 0xc2, 0xe4, + 0x21, 0x14, 0xa0, 0xe1, 0x01, 0x10, 0xc2, 0xe4, 0xf1, 0xff, 0xff, 0xea, 0x00, 0x10, 0xa0, 0xe3, + 0x03, 0x00, 0x12, 0xe3, 0x06, 0x00, 0x00, 0x1a, 0x04, 0x00, 0x9c, 0xe5, 0x02, 0x01, 0x10, 0xe3, + 0x1e, 0xff, 0x2f, 0x01, 0x02, 0x05, 0x10, 0xe3, 0x04, 0x10, 0x92, 0x14, 0x10, 0x10, 0x83, 0x15, + 0xf8, 0xff, 0xff, 0xea, 0x04, 0x00, 0x9c, 0xe5, 0x02, 0x01, 0x10, 0xe3, 0x1e, 0xff, 0x2f, 0x01, + 0x02, 0x05, 0x10, 0xe3, 0xfa, 0xff, 0xff, 0x0a, 0x01, 0x10, 0xd2, 0xe4, 0x01, 0x00, 0xd2, 0xe4, + 0x00, 0x14, 0x81, 0xe1, 0x01, 0x00, 0xd2, 0xe4, 0x00, 0x18, 0x81, 0xe1, 0x01, 0x00, 0xd2, 0xe4, + 0x00, 0x1c, 0x81, 0xe1, 0x10, 0x10, 0x83, 0xe5, 0xf1, 0xff, 0xff, 0xea, 0x78, 0x40, 0x2d, 0xe9, + 0x00, 0x40, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1, 0x00, 0x60, 0xa0, 0xe3, 0xc0, 0x00, 0xa0, 0xe3, + 0x06, 0x10, 0x84, 0xe0, 0xb1, 0xff, 0xff, 0xeb, 0x01, 0x60, 0x86, 0xe2, 0x05, 0x00, 0x56, 0xe1, + 0xf9, 0xff, 0xff, 0x3a, 0x78, 0x40, 0xbd, 0xe8, 0x01, 0x00, 0xa0, 0xe3, 0x1e, 0xff, 0x2f, 0xe1, + 0x78, 0x40, 0x2d, 0xe9, 0x00, 0x40, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1, 0x00, 0x60, 0xa0, 0xe3, + 0xc1, 0x00, 0xa0, 0xe3, 0x06, 0x10, 0x84, 0xe0, 0xa4, 0xff, 0xff, 0xeb, 0x01, 0x60, 0x86, 0xe2, + 0x05, 0x00, 0x56, 0xe1, 0xf9, 0xff, 0xff, 0x3a, 0x78, 0x40, 0xbd, 0xe8, 0x01, 0x00, 0xa0, 0xe3, + 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x00, 0xa0, 0xe3, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x00, 0xa0, 0xe3, + 0x1e, 0xff, 0x2f, 0xe1 }; } From 0d294e9373d5edd488e1480bec1b71098263b591 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 14 Jun 2025 23:20:31 +0200 Subject: [PATCH 073/106] fix mic settings not being changed when closing the audio settings dialog --- src/frontend/qt_sdl/Window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index c145933f..e2c54604 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1950,7 +1950,7 @@ void MainWindow::onUpdateAudioSettings() void MainWindow::onAudioSettingsFinished(int res) { - //AudioInOut::UpdateSettings(*emuThread->NDS); + emuInstance->audioUpdateSettings(); } void MainWindow::onOpenMPSettings() From 0b005abedf2bb0623a79308a954927f49ff8a0c7 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sun, 15 Jun 2025 01:15:31 +0200 Subject: [PATCH 074/106] work around building with LTO causing an ICE in gcc 15.1.0 --- cmake/DefaultBuildFlags.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmake/DefaultBuildFlags.cmake b/cmake/DefaultBuildFlags.cmake index 683767b3..ba18f8ed 100644 --- a/cmake/DefaultBuildFlags.cmake +++ b/cmake/DefaultBuildFlags.cmake @@ -7,3 +7,9 @@ endif() string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE_INIT "${CMAKE_C_FLAGS_RELEASE_INIT}") string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE_INIT "${CMAKE_CXX_FLAGS_RELEASE_INIT}") + +# Building with LTO causes an internal compiler error in GCC 15.1.0 +if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.1.1) + set(ENABLE_LTO_RELEASE OFF CACHE BOOL "Enable LTO for release builds" FORCE) + set(ENABLE_LTO OFF CACHE BOOL "Enable LTO" FORCE) +endif() From 79f12de480dd6dc66690bda2aee2c7464f08beba Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 15 Jun 2025 01:42:24 +0200 Subject: [PATCH 075/106] multi-instance: load firmware from correct instance-specific file (load from original file if not found) --- src/frontend/qt_sdl/EmuInstance.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index deed0f2a..f89359fb 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -1013,28 +1013,35 @@ std::optional EmuInstance::loadFirmware(int type) noexcept return generateFirmware(type); } } - //const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath; + string firmwarepath; if (type == 1) firmwarepath = globalCfg.GetString("DSi.FirmwarePath"); else firmwarepath = globalCfg.GetString("DS.FirmwarePath"); - Log(Debug, "SPI firmware: loading from file %s\n", firmwarepath.c_str()); + string fwpath_inst = firmwarepath + instanceFileSuffix(); - FileHandle* file = OpenLocalFile(firmwarepath, Read); + Log(Debug, "Loading firmware from file %s\n", fwpath_inst.c_str()); + FileHandle* file = OpenLocalFile(fwpath_inst, Read); if (!file) { - Log(Error, "SPI firmware: couldn't open firmware file!\n"); - return std::nullopt; + Log(Debug, "Loading firmware from file %s\n", firmwarepath.c_str()); + file = OpenLocalFile(firmwarepath, Read); + if (!file) + { + Log(Error, "Couldn't open firmware file!\n"); + return std::nullopt; + } } + Firmware firmware(file); CloseFile(file); if (!firmware.Buffer()) { - Log(Error, "SPI firmware: couldn't read firmware file!\n"); + Log(Error, "Couldn't read firmware file!\n"); return std::nullopt; } From c65d490351e6e18693e2eb6fcd0a349a7628dec0 Mon Sep 17 00:00:00 2001 From: Jakly <102590697+Jaklyy@users.noreply.github.com> Date: Sun, 15 Jun 2025 13:24:42 -0400 Subject: [PATCH 076/106] small fix to translucency flag assignment (#2301) hardware does not care that the polygon's mode would prevent the texture from rendering translucent pixels --- src/GPU3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 9d6be8b8..1204741d 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -1219,7 +1219,7 @@ void GPU3D::SubmitPolygon() noexcept u32 texfmt = (TexParam >> 26) & 0x7; u32 polyalpha = (CurPolygonAttr >> 16) & 0x1F; - poly->Translucent = ((texfmt == 1 || texfmt == 6) && !(CurPolygonAttr & 0x10)) || (polyalpha > 0 && polyalpha < 31); + poly->Translucent = (texfmt == 1 || texfmt == 6) || (polyalpha > 0 && polyalpha < 31); poly->IsShadowMask = ((CurPolygonAttr & 0x3F000030) == 0x00000030); poly->IsShadow = ((CurPolygonAttr & 0x30) == 0x30) && !poly->IsShadowMask; From d7a4b2e8fe9722e5dfe7eca178ddf19352546850 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Wed, 18 Jun 2025 00:51:47 +0200 Subject: [PATCH 077/106] Don't try to change the core's audio interp setting when the emu instance has no core yet. Fixes #2352 --- src/frontend/qt_sdl/EmuInstanceAudio.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp index e779f190..5e939039 100644 --- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp +++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp @@ -492,9 +492,12 @@ void EmuInstance::audioUpdateSettings() { micClose(); - int audiointerp = globalCfg.GetInt("Audio.Interpolation"); - nds->SPU.SetInterpolation(static_cast(audiointerp)); - setupMicInputData(); + if (nds != nullptr) + { + int audiointerp = globalCfg.GetInt("Audio.Interpolation"); + nds->SPU.SetInterpolation(static_cast(audiointerp)); + setupMicInputData(); + } micOpen(); } From 71edf793fcd2726c4212f7915194c60107dc2809 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 18 Jun 2025 19:32:50 +0200 Subject: [PATCH 078/106] setupMicInputData() ought to be called at all times tho --- src/frontend/qt_sdl/EmuInstanceAudio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp index 5e939039..2c925801 100644 --- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp +++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp @@ -496,9 +496,9 @@ void EmuInstance::audioUpdateSettings() { int audiointerp = globalCfg.GetInt("Audio.Interpolation"); nds->SPU.SetInterpolation(static_cast(audiointerp)); - setupMicInputData(); } + setupMicInputData(); micOpen(); } From 2d04222442451589d19538bdc4dffaf618091898 Mon Sep 17 00:00:00 2001 From: Edoardo Lolletti Date: Sun, 22 Jun 2025 16:29:20 +0200 Subject: [PATCH 079/106] Load Tad key into DSi AES engine (#2252) --- src/DSi_AES.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp index e53e3ea5..9f00744e 100644 --- a/src/DSi_AES.cpp +++ b/src/DSi_AES.cpp @@ -90,6 +90,9 @@ void DSi_AES::Reset() *(u32*)&KeyX[1][8] = (u32)(consoleid >> 32) ^ 0xC80C4B72; *(u32*)&KeyX[1][12] = (u32)consoleid; + // slot 2: For 'Tad' + std::memcpy(KeyX[2], &DSi.ARM9iBIOS[0x8B8C], 0x10); + // slot 3: console-unique eMMC crypto *(u32*)&KeyX[3][0] = (u32)consoleid; *(u32*)&KeyX[3][4] = (u32)consoleid ^ 0x24EE6906; @@ -575,4 +578,4 @@ void DSi_AES::WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask) } } -} \ No newline at end of file +} From fd74181f7d6aafc467c826b81193b2d2f4a8ad61 Mon Sep 17 00:00:00 2001 From: "Adrian \"asie\" Siekierka" Date: Sun, 22 Jun 2025 16:30:01 +0200 Subject: [PATCH 080/106] Slot-2 Motion Pak, Guitar Grip emulation (#2183) * Add DS Motion Pak emulation * Add retail Motion Pak emulation, Guitar Grip emulation * Simplify Motion Pak acceleration conversion formula * Fix Motion Pak emulation axes * Motion Pak: Emulate console laying on a flat table when motion input is not detected * Motion Pak: Add comment * GBACartMotionPak: Update comment --- src/CMakeLists.txt | 1 + src/GBACart.cpp | 30 +++ src/GBACart.h | 60 ++++++ src/GBACartMotionPak.cpp | 196 ++++++++++++++++++ src/NDS.h | 3 + src/Platform.h | 48 +++++ src/frontend/qt_sdl/Config.cpp | 8 + src/frontend/qt_sdl/EmuInstance.cpp | 6 + src/frontend/qt_sdl/EmuInstance.h | 10 + src/frontend/qt_sdl/EmuInstanceInput.cpp | 70 ++++++- .../qt_sdl/InputConfig/InputConfigDialog.h | 8 + src/frontend/qt_sdl/Platform.cpp | 18 ++ src/frontend/qt_sdl/Window.cpp | 14 +- src/frontend/qt_sdl/main.cpp | 4 + 14 files changed, 471 insertions(+), 5 deletions(-) create mode 100644 src/GBACartMotionPak.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fef78706..4fa4d514 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,7 @@ add_library(core STATIC FATStorage.cpp FIFO.h GBACart.cpp + GBACartMotionPak.cpp GPU.cpp GPU2D.cpp GPU2D_Soft.cpp diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 071bf06e..e6217f83 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -775,6 +775,27 @@ void CartRumblePak::ROMWrite(u32 addr, u16 val) } } +CartGuitarGrip::CartGuitarGrip(void* userdata) : + CartCommon(GuitarGrip), + UserData(userdata) +{ +} + +CartGuitarGrip::~CartGuitarGrip() = default; + +u16 CartGuitarGrip::ROMRead(u32 addr) const +{ + return 0xF9FF; +} + +u8 CartGuitarGrip::SRAMRead(u32 addr) +{ + return ~((Platform::Addon_KeyDown(Platform::KeyGuitarGripGreen, UserData) ? 0x40 : 0) + | (Platform::Addon_KeyDown(Platform::KeyGuitarGripRed, UserData) ? 0x20 : 0) + | (Platform::Addon_KeyDown(Platform::KeyGuitarGripYellow, UserData) ? 0x10 : 0) + | (Platform::Addon_KeyDown(Platform::KeyGuitarGripBlue, UserData) ? 0x08 : 0)); +} + GBACartSlot::GBACartSlot(melonDS::NDS& nds, std::unique_ptr&& cart) noexcept : NDS(nds), Cart(std::move(cart)) { } @@ -906,6 +927,15 @@ std::unique_ptr LoadAddon(int type, void* userdata) // JP Boktai 3 cart = CreateFakeSolarSensorROM("U33J", nullptr, userdata); break; + case GBAAddon_MotionPakHomebrew: + cart = std::make_unique(userdata); + break; + case GBAAddon_MotionPakRetail: + cart = std::make_unique(userdata); + break; + case GBAAddon_GuitarGrip: + cart = std::make_unique(userdata); + break; default: Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type); return nullptr; diff --git a/src/GBACart.h b/src/GBACart.h index 86a28edd..4a67719b 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -33,6 +33,9 @@ enum CartType GameSolarSensor = 0x102, RAMExpansion = 0x201, RumblePak = 0x202, + MotionPakHomebrew = 0x203, + MotionPakRetail = 0x204, + GuitarGrip = 0x205, }; // See https://problemkaputt.de/gbatek.htm#gbacartridgeheader for details @@ -232,11 +235,68 @@ private: u16 RumbleState = 0; }; +// CartGuitarGrip -- DS Guitar Grip (used in various NDS games) +class CartGuitarGrip : public CartCommon +{ +public: + CartGuitarGrip(void* userdata); + ~CartGuitarGrip() override; + + u16 ROMRead(u32 addr) const override; + u8 SRAMRead(u32 addr) override; + +private: + void* UserData; +}; + +// CartMotionPakHomebrew -- DS Motion Pak (Homebrew) +class CartMotionPakHomebrew : public CartCommon +{ +public: + CartMotionPakHomebrew(void* userdata); + ~CartMotionPakHomebrew() override; + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + u16 ROMRead(u32 addr) const override; + u8 SRAMRead(u32 addr) override; + +private: + void* UserData; + u16 ShiftVal = 0; +}; + +// CartMotionPakRetail -- DS Motion Pack (Retail) +class CartMotionPakRetail : public CartCommon +{ +public: + CartMotionPakRetail(void* userdata); + ~CartMotionPakRetail() override; + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + u16 ROMRead(u32 addr) const override; + u8 SRAMRead(u32 addr) override; + +private: + void* UserData; + u8 Value; + u8 Step = 16; +}; + // possible inputs for GBA carts that might accept user input enum { Input_SolarSensorDown = 0, Input_SolarSensorUp, + Input_GuitarGripGreen, + Input_GuitarGripRed, + Input_GuitarGripYellow, + Input_GuitarGripBlue, }; class GBACartSlot diff --git a/src/GBACartMotionPak.cpp b/src/GBACartMotionPak.cpp new file mode 100644 index 00000000..c213bb7f --- /dev/null +++ b/src/GBACartMotionPak.cpp @@ -0,0 +1,196 @@ +/* + 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/. +*/ + +#include +#include "NDS.h" +#include "GBACart.h" +#include "Platform.h" +#include +#include "math.h" + +namespace melonDS +{ +using Platform::Log; +using Platform::LogLevel; + +namespace GBACart +{ + +CartMotionPakHomebrew::CartMotionPakHomebrew(void* userdata) : + CartCommon(MotionPakHomebrew), + UserData(userdata) +{ +} + +CartMotionPakHomebrew::~CartMotionPakHomebrew() = default; + +void CartMotionPakHomebrew::Reset() +{ + ShiftVal = 0; +} + +void CartMotionPakHomebrew::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + file->Var16(&ShiftVal); +} + +u16 CartMotionPakHomebrew::ROMRead(u32 addr) const +{ + // CHECKME: Does this apply to the homebrew cart as well? + return 0xFCFF; +} + +static int AccelerationToMotionPak(float accel) +{ + const float GRAVITY_M_S2 = 9.80665f; + + return std::clamp( + (int) ((accel / (5 * GRAVITY_M_S2) + 0.5) * 4096), + 0, 4095 + ); +} + +static int AccelerationToMotionPakRetail(float accel) +{ + const float GRAVITY_M_S2 = 9.80665f; + + return std::clamp( + (int) ((accel / (5 * GRAVITY_M_S2) + 0.5) * 256), + 0, 254 + ); +} + +static int RotationToMotionPak(float rot) +{ + const float DEGREES_PER_RAD = 180 / M_PI; + const float COUNTS_PER_DEG_PER_SEC = 0.825; + const int CENTER = 1680; + + return std::clamp( + (int) ((rot * DEGREES_PER_RAD * COUNTS_PER_DEG_PER_SEC) + CENTER + 0.5), + 0, 4095 + ); +} + +u8 CartMotionPakHomebrew::SRAMRead(u32 addr) +{ + // CHECKME: SRAM address mask + addr &= 0xFFFF; + + switch (addr) + { + case 0: + // Read next byte + break; + case 2: + // Read X acceleration + ShiftVal = AccelerationToMotionPak(Platform::Addon_MotionQuery(Platform::MotionAccelerationX, UserData)) << 4; + // CHECKME: First byte returned when reading acceleration/rotation + return 0; + case 4: + // Read Y acceleration + ShiftVal = AccelerationToMotionPak(Platform::Addon_MotionQuery(Platform::MotionAccelerationY, UserData)) << 4; + return 0; + case 6: + // Read Z acceleration + ShiftVal = AccelerationToMotionPak(Platform::Addon_MotionQuery(Platform::MotionAccelerationZ, UserData)) << 4; + return 0; + case 8: + // Read Z rotation + // CHECKME: This is a guess, compare with real hardware + ShiftVal = RotationToMotionPak(-Platform::Addon_MotionQuery(Platform::MotionRotationZ, UserData)) << 4; + return 0; + case 10: + // Identify cart + ShiftVal = 0xF00F; + return 0; + case 12: + case 14: + case 16: + case 18: + // Read/enable analog inputs + // + // These are not connected by defualt and require do-it-yourself cart + // modification, so there is no reason to emulate them. + ShiftVal = 0; + break; + } + + // Read high byte from the emulated shift register + u8 val = ShiftVal >> 8; + ShiftVal <<= 8; + return val; +} + +CartMotionPakRetail::CartMotionPakRetail(void* userdata) : + CartCommon(MotionPakRetail), + UserData(userdata) +{ +} + +CartMotionPakRetail::~CartMotionPakRetail() = default; + +void CartMotionPakRetail::Reset() +{ + Value = 0; + Step = 16; +} + +void CartMotionPakRetail::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + file->Var8(&Value); + file->Var8(&Step); +} + +u16 CartMotionPakRetail::ROMRead(u32 addr) const +{ + // A9-A8 is pulled low on a real Motion Pack. + return 0xFCFF; +} + +u8 CartMotionPakRetail::SRAMRead(u32 addr) +{ + switch (Step) + { + case 0: // Synchronization - read 0xFF + Value = 0xFF; + break; + case 4: // X acceleration + Value = AccelerationToMotionPakRetail(Platform::Addon_MotionQuery(Platform::MotionAccelerationX, UserData)); + break; + case 8: // Y acceleration + Value = AccelerationToMotionPakRetail(Platform::Addon_MotionQuery(Platform::MotionAccelerationY, UserData)); + break; + case 12: // Z acceleration + Value = AccelerationToMotionPakRetail(Platform::Addon_MotionQuery(Platform::MotionAccelerationZ, UserData)); + break; + case 16: // Synchronization - read 0b00 + Step = 0; + return 0; + } + + int shift = 6 - ((Step & 3) * 2); + Step++; + return (Value >> shift) & 0x03; +} + +} + +} diff --git a/src/NDS.h b/src/NDS.h index b44853b5..e7340c76 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -221,6 +221,9 @@ enum GBAAddon_SolarSensorBoktai1 = 3, GBAAddon_SolarSensorBoktai2 = 4, GBAAddon_SolarSensorBoktai3 = 5, + GBAAddon_MotionPakHomebrew = 6, + GBAAddon_MotionPakRetail = 7, + GBAAddon_GuitarGrip = 8, }; class SPU; diff --git a/src/Platform.h b/src/Platform.h index 8e85c7c7..67d1a5b0 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -322,6 +322,18 @@ void Camera_CaptureFrame(int num, u32* frame, int width, int height, bool yuv, v // interface for addon inputs +enum KeyType +{ + KeyGuitarGripGreen, + KeyGuitarGripRed, + KeyGuitarGripYellow, + KeyGuitarGripBlue, +}; + +// Check if a given key is being pressed. +// @param type The type of the key to check. +bool Addon_KeyDown(KeyType type, void* userdata); + // Called by the DS Rumble Pak emulation to start the necessary // rumble effects on the connected game controller, if available. // @param len The duration of the controller rumble effect in milliseconds. @@ -331,6 +343,42 @@ void Addon_RumbleStart(u32 len, void* userdata); // rumble effects on the connected game controller, if available. void Addon_RumbleStop(void* userdata); +enum MotionQueryType +{ + /** + * @brief X axis acceleration, measured in SI meters per second squared. + * On a DS, the X axis refers to the top screen X-axis (left ... right). + */ + MotionAccelerationX, + /** + * @brief Y axis acceleration, measured in SI meters per second squared. + * On a DS, the Y axis refers to the top screen Y-axis (bottom ... top). + */ + MotionAccelerationY, + /** + * @brief Z axis acceleration, measured in SI meters per second squared. + * On a DS, the Z axis refers to the axis perpendicular to the top screen (farther ... closer). + */ + MotionAccelerationZ, + /** + * @brief X axis rotation, measured in radians per second. + */ + MotionRotationX, + /** + * @brief Y axis rotation, measured in radians per second. + */ + MotionRotationY, + /** + * @brief Z axis rotation, measured in radians per second. + */ + MotionRotationZ, +}; + +// Called by the DS Motion Pak emulation to query the game controller's +// aceelration and rotation, if available. +// @param type The value being queried. +float Addon_MotionQuery(MotionQueryType type, void* userdata); + struct DynamicLibrary; /** diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index b81060d3..86516a91 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -169,6 +169,10 @@ LegacyEntry LegacyFile[] = {"HKKey_PowerButton", 0, "Keyboard.HK_PowerButton", true}, {"HKKey_VolumeUp", 0, "Keyboard.HK_VolumeUp", true}, {"HKKey_VolumeDown", 0, "Keyboard.HK_VolumeDown", true}, + {"HKKey_GuitarGripGreen", 0, "Keyboard.HK_GuitarGripGreen", true}, + {"HKKey_GuitarGripRed", 0, "Keyboard.HK_GuitarGripRed", true}, + {"HKKey_GuitarGripYellow", 0, "Keyboard.HK_GuitarGripYellow", true}, + {"HKKey_GuitarGripBlue", 0, "Keyboard.HK_GuitarGripBlue", true}, {"HKJoy_Lid", 0, "Joystick.HK_Lid", true}, {"HKJoy_Mic", 0, "Joystick.HK_Mic", true}, @@ -185,6 +189,10 @@ LegacyEntry LegacyFile[] = {"HKJoy_PowerButton", 0, "Joystick.HK_PowerButton", true}, {"HKJoy_VolumeUp", 0, "Joystick.HK_VolumeUp", true}, {"HKJoy_VolumeDown", 0, "Joystick.HK_VolumeDown", true}, + {"HKJoy_GuitarGripGreen", 0, "Joystick.HK_GuitarGripGreen", true}, + {"HKJoy_GuitarGripRed", 0, "Joystick.HK_GuitarGripRed", true}, + {"HKJoy_GuitarGripYellow", 0, "Joystick.HK_GuitarGripYellow", true}, + {"HKJoy_GuitarGripBlue", 0, "Joystick.HK_GuitarGripBlue", true}, {"JoystickID", 0, "JoystickID", true}, diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index f89359fb..499987c4 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -2155,6 +2155,12 @@ QString EmuInstance::gbaAddonName(int addon) return "Solar Sensor (Boktai 2)"; case GBAAddon_SolarSensorBoktai3: return "Solar Sensor (Boktai 3)"; + case GBAAddon_MotionPakHomebrew: + return "Motion Pak (Homebrew)"; + case GBAAddon_MotionPakRetail: + return "Motion Pack (Retail)"; + case GBAAddon_GuitarGrip: + return "Guitar Grip"; } return "???"; diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index bbab7010..c83f30a8 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -21,6 +21,7 @@ #include +#include "Platform.h" #include "main.h" #include "NDS.h" #include "EmuThread.h" @@ -50,6 +51,10 @@ enum HK_SlowMo, HK_FastForwardToggle, HK_SlowMoToggle, + HK_GuitarGripGreen, + HK_GuitarGripRed, + HK_GuitarGripYellow, + HK_GuitarGripBlue, HK_MAX }; @@ -143,6 +148,9 @@ public: void inputRumbleStart(melonDS::u32 len_ms); void inputRumbleStop(); + bool inputHotkeyDown(int id) { return hotkeyDown(id); } + float inputMotionQuery(melonDS::Platform::MotionQueryType type); + void setJoystick(int id); int getJoystickID() { return joystickID; } SDL_Joystick* getJoystick() { return joystick; } @@ -332,6 +340,8 @@ private: int joystickID; SDL_Joystick* joystick; SDL_GameController* controller; + bool hasAccelerometer = false; + bool hasGyroscope = false; bool hasRumble = false; bool isRumbling = false; diff --git a/src/frontend/qt_sdl/EmuInstanceInput.cpp b/src/frontend/qt_sdl/EmuInstanceInput.cpp index e85b7f23..ef4637ea 100644 --- a/src/frontend/qt_sdl/EmuInstanceInput.cpp +++ b/src/frontend/qt_sdl/EmuInstanceInput.cpp @@ -19,6 +19,9 @@ #include #include +#include "Platform.h" +#include "SDL_gamecontroller.h" +#include "SDL_sensor.h" #include "main.h" #include "Config.h" @@ -59,7 +62,11 @@ const char* EmuInstance::hotkeyNames[HK_MAX] = "HK_VolumeDown", "HK_SlowMo", "HK_FastForwardToggle", - "HK_SlowMoToggle" + "HK_SlowMoToggle", + "HK_GuitarGripGreen", + "HK_GuitarGripRed", + "HK_GuitarGripYellow", + "HK_GuitarGripBlue" }; @@ -81,6 +88,8 @@ void EmuInstance::inputInit() joystick = nullptr; controller = nullptr; hasRumble = false; + hasAccelerometer = false; + hasGyroscope = false; isRumbling = false; inputLoadConfig(); } @@ -128,6 +137,48 @@ void EmuInstance::inputRumbleStop() } } +float EmuInstance::inputMotionQuery(melonDS::Platform::MotionQueryType type) +{ + float values[3]; + if (type <= melonDS::Platform::MotionAccelerationZ) + { + if (controller && hasAccelerometer) + if (SDL_GameControllerGetSensorData(controller, SDL_SENSOR_ACCEL, values, 3) == 0) + { + // Map values from DS console orientation to SDL controller orientation. + switch (type) + { + case melonDS::Platform::MotionAccelerationX: + return values[0]; + case melonDS::Platform::MotionAccelerationY: + return -values[2]; + case melonDS::Platform::MotionAccelerationZ: + return values[1]; + } + } + } + else if (type <= melonDS::Platform::MotionRotationZ) + { + if (controller && hasGyroscope) + if (SDL_GameControllerGetSensorData(controller, SDL_SENSOR_GYRO, values, 3) == 0) + { + // Map values from DS console orientation to SDL controller orientation. + switch (type) + { + case melonDS::Platform::MotionRotationX: + return values[0]; + case melonDS::Platform::MotionRotationY: + return -values[2]; + case melonDS::Platform::MotionRotationZ: + return values[1]; + } + } + } + if (type == melonDS::Platform::MotionAccelerationZ) + return SDL_STANDARD_GRAVITY; + return 0.0f; +} + void EmuInstance::setJoystick(int id) { @@ -147,6 +198,8 @@ void EmuInstance::openJoystick() controller = nullptr; joystick = nullptr; hasRumble = false; + hasAccelerometer = false; + hasGyroscope = false; return; } @@ -163,9 +216,17 @@ void EmuInstance::openJoystick() if (controller) { if (SDL_GameControllerHasRumble(controller)) - { + { hasRumble = true; - } + } + if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL)) + { + hasAccelerometer = SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE) == 0; + } + if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO)) + { + hasGyroscope = SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE) == 0; + } } } @@ -176,8 +237,9 @@ void EmuInstance::closeJoystick() SDL_GameControllerClose(controller); controller = nullptr; hasRumble = false; + hasAccelerometer = false; + hasGyroscope = false; } - if (joystick) { SDL_JoystickClose(joystick); diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h index 89483b7f..47857f94 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h @@ -32,12 +32,20 @@ static constexpr std::initializer_list hk_addons = { HK_SolarSensorIncrease, HK_SolarSensorDecrease, + HK_GuitarGripGreen, + HK_GuitarGripRed, + HK_GuitarGripYellow, + HK_GuitarGripBlue, }; static constexpr std::initializer_list hk_addons_labels = { "[Boktai] Sunlight + ", "[Boktai] Sunlight - ", + "[Guitar Grip] Green", + "[Guitar Grip] Red", + "[Guitar Grip] Yellow", + "[Guitar Grip] Blue", }; static_assert(hk_addons.size() == hk_addons_labels.size()); diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 1466e33f..0fcb6d55 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -35,6 +35,7 @@ #include "Platform.h" #include "Config.h" +#include "EmuInstance.h" #include "main.h" #include "CameraManager.h" #include "Net.h" @@ -549,6 +550,18 @@ void Camera_CaptureFrame(int num, u32* frame, int width, int height, bool yuv, v return camManager[num]->captureFrame(frame, width, height, yuv); } +static const int hotkeyMap[] = { + HK_GuitarGripGreen, + HK_GuitarGripRed, + HK_GuitarGripYellow, + HK_GuitarGripBlue, +}; + +bool Addon_KeyDown(KeyType type, void* userdata) +{ + return ((EmuInstance*)userdata)->inputHotkeyDown(hotkeyMap[type]); +} + void Addon_RumbleStart(u32 len, void* userdata) { ((EmuInstance*)userdata)->inputRumbleStart(len); @@ -559,6 +572,11 @@ void Addon_RumbleStop(void* userdata) ((EmuInstance*)userdata)->inputRumbleStop(); } +float Addon_MotionQuery(MotionQueryType type, void* userdata) +{ + return ((EmuInstance*)userdata)->inputMotionQuery(type); +} + DynamicLibrary* DynamicLibrary_Load(const char* lib) { return (DynamicLibrary*) SDL_LoadObject(lib); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index e2c54604..9a14dea3 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -16,6 +16,7 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ +#include "NDS.h" #include #include #include @@ -320,7 +321,18 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : QMenu * submenu = menu->addMenu("Insert add-on cart"); QAction *act; - int addons[] = {GBAAddon_RAMExpansion, GBAAddon_RumblePak, GBAAddon_SolarSensorBoktai1, GBAAddon_SolarSensorBoktai2, GBAAddon_SolarSensorBoktai3, -1}; + int addons[] = { + GBAAddon_RAMExpansion, + GBAAddon_RumblePak, + GBAAddon_SolarSensorBoktai1, + GBAAddon_SolarSensorBoktai2, + GBAAddon_SolarSensorBoktai3, + GBAAddon_MotionPakHomebrew, + GBAAddon_MotionPakRetail, + GBAAddon_GuitarGrip, + -1 + }; + for (int i = 0; addons[i] != -1; i++) { int addon = addons[i]; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 9ba3d1b3..65142663 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -309,6 +309,10 @@ int main(int argc, char** argv) { printf("SDL couldn't init joystick\n"); } + if (SDL_Init(SDL_INIT_SENSOR) < 0) + { + printf("SDL couldn't init motion sensors\n"); + } if (SDL_Init(SDL_INIT_AUDIO) < 0) { const char* err = SDL_GetError(); From 83b8f1ae47415887b5aa0cff660fd70f15494e89 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 22 Jun 2025 21:27:27 +0200 Subject: [PATCH 081/106] DSP: fix bug in PDATA read DMA (was reading wrong register) --- src/DSi_DSP.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp index 24fa08b3..20639ea8 100644 --- a/src/DSi_DSP.cpp +++ b/src/DSi_DSP.cpp @@ -323,7 +323,7 @@ void DSi_DSP::PDataDMAFetch() } void DSi_DSP::PDataDMAStart() { - switch ((DSP_PSTS & (3<<2)) >> 2) + switch ((DSP_PCFG & (3<<2)) >> 2) { case 0: PDataDMALen = 1; break; case 1: PDataDMALen = 8; break; From 8e163296d3bc071e90783617e8a7f6e1d44cd949 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 22 Jun 2025 23:58:30 +0200 Subject: [PATCH 082/106] camera: trigger DMA when reaching the end of a frame (fixes issues when the frame height isn't a multiple of the DMA interval) --- src/DSi_Camera.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 9ceed4ac..a5248af3 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -128,6 +128,7 @@ void DSi_CamModule::TransferScanline(u32 line) u32 tmpbuf[512]; int datalen = CurCamera->TransferScanline(tmpbuf, 512); + u32 numscan; // TODO: must be tweaked such that each block has enough time to transfer u32 delay = datalen*4 + 16; @@ -142,12 +143,7 @@ void DSi_CamModule::TransferScanline(u32 line) int ystart = (CropStart >> 16) & 0x1FF; int yend = (CropEnd >> 16) & 0x1FF; if (line < ystart || line > yend) - { - if (!CurCamera->TransferDone()) - DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1); - - return; - } + goto skip_line; int xstart = (CropStart >> 1) & 0x1FF; int xend = (CropEnd >> 1) & 0x1FF; @@ -206,7 +202,7 @@ void DSi_CamModule::TransferScanline(u32 line) memcpy(dstbuf, &tmpbuf[copystart], copylen*sizeof(u32)); } - u32 numscan = Cnt & 0x000F; + numscan = Cnt & 0x000F; if (BufferNumLines >= numscan) { BufferReadPos = 0; // checkme @@ -221,8 +217,21 @@ void DSi_CamModule::TransferScanline(u32 line) BufferNumLines++; } +skip_line: if (CurCamera->TransferDone()) + { + // when the frame is finished, transfer any remaining data if needed + // (if the frame height isn't a multiple of the DMA interval) + if (BufferNumLines > 0) + { + BufferReadPos = 0; + BufferWritePos = 0; + BufferNumLines = 0; + DSi.CheckNDMAs(0, 0x0B); + } + return; + } DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1); } From ab249fc91310e2331d339f484a3958f09c77709b Mon Sep 17 00:00:00 2001 From: Edoardo Lolletti Date: Mon, 23 Jun 2025 08:32:17 +0200 Subject: [PATCH 083/106] Don't return 0 when attempting to read from the GPA GPIO addresses with GPIO disabled (#2266) --- src/GBACart.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/GBACart.cpp b/src/GBACart.cpp index e6217f83..40436391 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -242,8 +242,6 @@ u16 CartGame::ROMRead(u32 addr) const case 0xC8: return GPIO.control; } } - else - return 0; } // CHECKME: does ROM mirror? From 8cd2d972abe58288d491425dd1cbcf699ac3c57a Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 24 Jun 2025 01:03:14 +0200 Subject: [PATCH 084/106] DSP: fix PDATA reads being one off --- src/DSi_DSP.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp index 20639ea8..57ef8204 100644 --- a/src/DSi_DSP.cpp +++ b/src/DSi_DSP.cpp @@ -348,7 +348,7 @@ void DSi_DSP::PDataDMACancel() } u16 DSi_DSP::PDataDMAReadMMIO() { - u16 ret; + u16 ret = 0; // TODO: is this actually 0, or just open bus? if (!PDATAReadFifo.IsEmpty()) ret = PDATAReadFifo.Read(); @@ -362,15 +362,9 @@ u16 DSi_DSP::PDataDMAReadMMIO() for (int i = 0; i < left; ++i) PDataDMAFetch(); - - ret = PDATAReadFifo.Read(); - } - else - { - // ah, crap - ret = 0; // TODO: is this actually 0, or just open bus? } + // TODO only trigger IRQ if enabled!! if (!PDATAReadFifo.IsEmpty() || PDATAReadFifo.IsFull()) DSi.SetIRQ(0, IRQ_DSi_DSP); From fd279bedc58d52e53ce85829e3e31d5d53c7fc1d Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 24 Jun 2025 16:51:33 +0200 Subject: [PATCH 085/106] huh --- src/DSi_NDMA.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp index 3c8d2df0..1cd1bc67 100644 --- a/src/DSi_NDMA.cpp +++ b/src/DSi_NDMA.cpp @@ -132,7 +132,6 @@ void DSi_NDMA::WriteCnt(u32 val) // TODO: unsupported start modes: // * timers (00-03) - // * camera (ARM9 0B) // * microphone (ARM7 0C) // * NDS-wifi?? (ARM7 07, likely not working) From 7b562f71b301e5475d35bf95f06c6fd14b09559c Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 25 Jun 2025 21:52:23 +0200 Subject: [PATCH 086/106] NDMA: fix IRQ in infinite repeat mode --- src/DSi_NDMA.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp index 1cd1bc67..0121b388 100644 --- a/src/DSi_NDMA.cpp +++ b/src/DSi_NDMA.cpp @@ -269,11 +269,18 @@ void DSi_NDMA::Run9() if ((StartMode & 0x1F) == 0x10) // CHECKME { + // no repeat Cnt &= ~(1<<31); if (Cnt & (1<<30)) DSi.SetIRQ(0, IRQ_DSi_NDMA0 + Num); } - else if (!(Cnt & (1<<29))) + else if (Cnt & (1<<29)) { + // repeat infinitely + if (Cnt & (1<<30)) DSi.SetIRQ(0, IRQ_DSi_NDMA0 + Num); + } + else + { + // repeat until total count is reached if (TotalRemCount == 0) { Cnt &= ~(1<<31); @@ -358,11 +365,18 @@ void DSi_NDMA::Run7() if ((StartMode & 0x1F) == 0x10) // CHECKME { + // no repeat Cnt &= ~(1<<31); if (Cnt & (1<<30)) DSi.SetIRQ(1, IRQ_DSi_NDMA0 + Num); } - else if (!(Cnt & (1<<29))) + else if (Cnt & (1<<29)) { + // repeat infinitely + if (Cnt & (1<<30)) DSi.SetIRQ(1, IRQ_DSi_NDMA0 + Num); + } + else + { + // repeat until total count is reached if (TotalRemCount == 0) { Cnt &= ~(1<<31); From 005ef9c9fc0b94518677fe195ea5333836c4bce6 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 26 Jun 2025 23:04:14 +0200 Subject: [PATCH 087/106] camera: fix resolution selection code (could accidentally select a resolution like 640x360) --- src/frontend/qt_sdl/CameraManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/CameraManager.cpp b/src/frontend/qt_sdl/CameraManager.cpp index f080284d..3b133b8b 100644 --- a/src/frontend/qt_sdl/CameraManager.cpp +++ b/src/frontend/qt_sdl/CameraManager.cpp @@ -232,7 +232,7 @@ void CameraManager::init() item.pixelFormat() != QVideoFrameFormat::Format_XRGB8888) continue; - if (item.resolution().width() != 640 && item.resolution().height() != 480) + if (item.resolution().width() != 640 || item.resolution().height() != 480) continue; camDevice->setCameraFormat(item); @@ -282,7 +282,7 @@ void CameraManager::init() item.pixelFormat() != QVideoFrame::Format_RGB32) continue; - if (item.resolution().width() != 640 && item.resolution().height() != 480) + if (item.resolution().width() != 640 || item.resolution().height() != 480) continue; camDevice->setViewfinderSettings(item); From ec2f7ee838e2b0b7f7968ac01b4b683fc6ab7711 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 28 Jun 2025 03:02:00 +0200 Subject: [PATCH 088/106] fix issues with multi-window and OpenGL on Windows --- src/frontend/qt_sdl/EmuInstance.cpp | 9 ++++ src/frontend/qt_sdl/EmuInstance.h | 1 + src/frontend/qt_sdl/EmuThread.cpp | 27 ++++++++++++ src/frontend/qt_sdl/EmuThread.h | 7 +++ src/frontend/qt_sdl/Screen.cpp | 7 +++ src/frontend/qt_sdl/Screen.h | 1 + src/frontend/qt_sdl/Window.cpp | 68 +++++++++++++++++++++-------- src/frontend/qt_sdl/Window.h | 1 + 8 files changed, 102 insertions(+), 19 deletions(-) mode change 100644 => 100755 src/frontend/qt_sdl/EmuInstance.cpp mode change 100644 => 100755 src/frontend/qt_sdl/EmuInstance.h mode change 100644 => 100755 src/frontend/qt_sdl/EmuThread.cpp mode change 100644 => 100755 src/frontend/qt_sdl/EmuThread.h mode change 100644 => 100755 src/frontend/qt_sdl/Screen.cpp mode change 100644 => 100755 src/frontend/qt_sdl/Screen.h mode change 100644 => 100755 src/frontend/qt_sdl/Window.cpp mode change 100644 => 100755 src/frontend/qt_sdl/Window.h diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp old mode 100644 new mode 100755 index 499987c4..d619fd3e --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -408,6 +408,15 @@ void EmuInstance::makeCurrentGL() mainWindow->makeCurrentGL(); } +void EmuInstance::releaseGL() +{ + for (int i = 0; i < kMaxWindows; i++) + { + if (windowList[i]) + windowList[i]->releaseGL(); + } +} + void EmuInstance::drawScreenGL() { for (int i = 0; i < kMaxWindows; i++) diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h old mode 100644 new mode 100755 index c83f30a8..295e9bf6 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -120,6 +120,7 @@ public: void deinitOpenGL(int win); void setVSyncGL(bool vsync); void makeCurrentGL(); + void releaseGL(); void drawScreenGL(); // return: empty string = setup OK, non-empty = error message diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp old mode 100644 new mode 100755 index 9b54ed6e..7737038a --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -485,6 +485,8 @@ void EmuThread::waitAllMessages() void EmuThread::handleMessages() { + bool glborrow = false; + msgMutex.lock(); while (!msgQueue.empty()) { @@ -576,6 +578,11 @@ void EmuThread::handleMessages() useOpenGL = false; break; + case msg_BorrowGL: + emuInstance->releaseGL(); + glborrow = true; + break; + case msg_BootROM: msgResult = 0; if (!emuInstance->loadROM(msg.param.value(), true, msgError)) @@ -667,6 +674,13 @@ void EmuThread::handleMessages() msgSemaphore.release(); } msgMutex.unlock(); + + if (glborrow) + { + glBorrowMutex.lock(); + glBorrowCond.wait(&glBorrowMutex); + glBorrowMutex.unlock(); + } } void EmuThread::changeWindowTitle(char* title) @@ -686,6 +700,19 @@ void EmuThread::deinitContext(int win) waitMessage(); } +void EmuThread::borrowGL() +{ + sendMessage(msg_BorrowGL); + waitMessage(); +} + +void EmuThread::returnGL() +{ + glBorrowMutex.lock(); + glBorrowCond.wakeAll(); + glBorrowMutex.unlock(); +} + void EmuThread::emuRun() { sendMessage(msg_EmuRun); diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h old mode 100644 new mode 100755 index 4ca90097..7a7f0600 --- a/src/frontend/qt_sdl/EmuThread.h +++ b/src/frontend/qt_sdl/EmuThread.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -66,6 +67,7 @@ public: msg_InitGL, msg_DeInitGL, + msg_BorrowGL, msg_BootROM, msg_BootFirmware, @@ -130,12 +132,17 @@ public: void initContext(int win); void deinitContext(int win); + void borrowGL(); + void returnGL(); void updateVideoSettings() { videoSettingsDirty = true; } void updateVideoRenderer() { videoSettingsDirty = true; lastVideoRenderer = -1; } int frontBuffer = 0; QMutex frontBufferLock; + QWaitCondition glBorrowCond; + QMutex glBorrowMutex; + signals: void windowUpdate(); void windowTitleChange(QString title); diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp old mode 100644 new mode 100755 index 8024c9b4..2515b155 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -1055,6 +1055,13 @@ void ScreenPanelGL::makeCurrentGL() glContext->MakeCurrent(); } +void ScreenPanelGL::releaseGL() +{ + if (!glContext) return; + + glContext->DoneCurrent(); +} + void ScreenPanelGL::osdRenderItem(OSDItem* item) { ScreenPanel::osdRenderItem(item); diff --git a/src/frontend/qt_sdl/Screen.h b/src/frontend/qt_sdl/Screen.h old mode 100644 new mode 100755 index a0e1fea7..d7f54797 --- a/src/frontend/qt_sdl/Screen.h +++ b/src/frontend/qt_sdl/Screen.h @@ -189,6 +189,7 @@ public: void initOpenGL(); void deinitOpenGL(); void makeCurrentGL(); + void releaseGL(); void drawScreenGL(); GL::Context* getContext() { return glContext.get(); } diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp old mode 100644 new mode 100755 index 9a14dea3..cf1a27f1 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -864,7 +864,10 @@ void MainWindow::createScreenPanel() ScreenPanelGL* panelGL = new ScreenPanelGL(this); panelGL->show(); - panel = panelGL; + // make sure no GL context is in use by the emu thread + // otherwise we may fail to create a shared context + if (windowID != 0) + emuThread->borrowGL(); // Check that creating the context hasn't failed if (panelGL->createContext() == false) @@ -874,7 +877,15 @@ void MainWindow::createScreenPanel() globalCfg.SetBool("Screen.UseGL", false); globalCfg.SetInt("3D.Renderer", renderer3D_Software); + + delete panelGL; + panelGL = nullptr; } + + if (windowID != 0) + emuThread->returnGL(); + + panel = panelGL; } if (!hasOGL) @@ -924,6 +935,7 @@ void MainWindow::setGLSwapInterval(int intv) if (!hasOGL) return; ScreenPanelGL* glpanel = static_cast(panel); + if (!glpanel) return; return glpanel->setSwapInterval(intv); } @@ -932,14 +944,25 @@ void MainWindow::makeCurrentGL() if (!hasOGL) return; ScreenPanelGL* glpanel = static_cast(panel); + if (!glpanel) return; return glpanel->makeCurrentGL(); } +void MainWindow::releaseGL() +{ + if (!hasOGL) return; + + ScreenPanelGL* glpanel = static_cast(panel); + if (!glpanel) return; + return glpanel->releaseGL(); +} + void MainWindow::drawScreenGL() { if (!hasOGL) return; ScreenPanelGL* glpanel = static_cast(panel); + if (!glpanel) return; return glpanel->drawScreenGL(); } @@ -2263,42 +2286,49 @@ void MainWindow::onUpdateVideoSettings(bool glchange) if (parentwin) return parentwin->onUpdateVideoSettings(glchange); + auto childwins = findChildren(nullptr, Qt::FindDirectChildrenOnly); + bool hadOGL = hasOGL; if (glchange) { emuThread->emuPause(); - if (hadOGL) emuThread->deinitContext(windowID); + if (hadOGL) + { + emuThread->deinitContext(windowID); + for (auto child: childwins) + { + auto thread = child->getEmuInstance()->getEmuThread(); + thread->deinitContext(child->windowID); + } + } createScreenPanel(); + for (auto child: childwins) + { + child->createScreenPanel(); + } } emuThread->updateVideoSettings(); - - if (glchange) - { - if (hasOGL) emuThread->initContext(windowID); - } - - // update any child windows we have - auto childwins = findChildren(nullptr, Qt::FindDirectChildrenOnly); for (auto child: childwins) { // child windows may belong to a different instance // in that case we need to signal their thread appropriately auto thread = child->getEmuInstance()->getEmuThread(); - - if (glchange) - { - if (hadOGL) thread->deinitContext(child->windowID); - child->createScreenPanel(); - } - if (child->getWindowID() == 0) thread->updateVideoSettings(); + } - if (glchange) + if (glchange) + { + if (hasOGL) { - if (hasOGL) thread->initContext(child->windowID); + emuThread->initContext(windowID); + for (auto child: childwins) + { + auto thread = child->getEmuInstance()->getEmuThread(); + thread->initContext(child->windowID); + } } } diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h old mode 100644 new mode 100755 index 21c65d9e..678be7c9 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -124,6 +124,7 @@ public: void deinitOpenGL(); void setGLSwapInterval(int intv); void makeCurrentGL(); + void releaseGL(); void drawScreenGL(); bool preloadROMs(QStringList file, QStringList gbafile, bool boot); From baad893bc0dba47164d4c50f6d70d882485dca79 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 28 Jun 2025 03:48:42 +0200 Subject: [PATCH 089/106] window: move cleanup code to destructor (closeEvent() isn't called for children) --- src/frontend/qt_sdl/Window.cpp | 34 ++++++++++++---------------------- src/frontend/qt_sdl/Window.h | 2 -- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index cf1a27f1..3ba8af1d 100755 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -809,6 +809,18 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : MainWindow::~MainWindow() { + if (windowID == 0) + emuInstance->saveEnabledWindows(); + else + saveEnabled(false); + + QByteArray geom = saveGeometry(); + QByteArray enc = geom.toBase64(QByteArray::Base64Encoding); + windowCfg.SetString("Geometry", enc.toStdString()); + Config::Save(); + + emuInstance->deleteWindow(windowID, false); + if (hasMenu) { delete[] actScreenAspectTop; @@ -829,28 +841,6 @@ void MainWindow::saveEnabled(bool enabled) enabledSaved = true; } -void MainWindow::closeEvent(QCloseEvent* event) -{ - if (!emuInstance) return; - - if (windowID == 0) - emuInstance->saveEnabledWindows(); - else - saveEnabled(false); - - QByteArray geom = saveGeometry(); - QByteArray enc = geom.toBase64(QByteArray::Base64Encoding); - windowCfg.SetString("Geometry", enc.toStdString()); - Config::Save(); - - emuInstance->deleteWindow(windowID, false); - - // emuInstance may be deleted - // prevent use after free from us - emuInstance = nullptr; - QMainWindow::closeEvent(event); -} - void MainWindow::createScreenPanel() { if (panel) delete panel; diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 678be7c9..5d02b4ee 100755 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -242,8 +242,6 @@ private slots: void onScreenEmphasisToggled(); private: - virtual void closeEvent(QCloseEvent* event) override; - QStringList currentROM; QStringList currentGBAROM; QList recentFileList; From 2499ec36c23d8e1e228fb5dd0ec62bf2fa59ac30 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 28 Jun 2025 12:37:53 +0200 Subject: [PATCH 090/106] window: undo last commit (was a trainwreck). explicitly close child windows to avoid GL issues. fix bug with window parenting in second multiplayer instances. --- src/frontend/qt_sdl/EmuInstance.cpp | 2 +- src/frontend/qt_sdl/Window.cpp | 42 ++++++++++++++++++++--------- src/frontend/qt_sdl/Window.h | 2 ++ 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index d619fd3e..13e026d6 100755 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -204,7 +204,7 @@ void EmuInstance::createWindow(int id) if (windowList[id]) return; - MainWindow* win = new MainWindow(id, this, topWindow); + MainWindow* win = new MainWindow(id, this, mainWindow ? mainWindow : topWindow); if (!topWindow) topWindow = win; if (!mainWindow) mainWindow = win; windowList[id] = win; diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 3ba8af1d..965dddcf 100755 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -809,18 +809,6 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : MainWindow::~MainWindow() { - if (windowID == 0) - emuInstance->saveEnabledWindows(); - else - saveEnabled(false); - - QByteArray geom = saveGeometry(); - QByteArray enc = geom.toBase64(QByteArray::Base64Encoding); - windowCfg.SetString("Geometry", enc.toStdString()); - Config::Save(); - - emuInstance->deleteWindow(windowID, false); - if (hasMenu) { delete[] actScreenAspectTop; @@ -841,6 +829,36 @@ void MainWindow::saveEnabled(bool enabled) enabledSaved = true; } +void MainWindow::closeEvent(QCloseEvent* event) +{ + if (emuInstance) + { + if (windowID == 0) + emuInstance->saveEnabledWindows(); + else + saveEnabled(false); + } + + // explicitly close children windows, so the OpenGL contexts get closed properly + auto childwins = findChildren(nullptr, Qt::FindDirectChildrenOnly); + for (auto child : childwins) + child->close(); + + if (!emuInstance) return; + + QByteArray geom = saveGeometry(); + QByteArray enc = geom.toBase64(QByteArray::Base64Encoding); + windowCfg.SetString("Geometry", enc.toStdString()); + Config::Save(); + + emuInstance->deleteWindow(windowID, false); + + // emuInstance may be deleted + // prevent use after free from us + emuInstance = nullptr; + QMainWindow::closeEvent(event); +} + void MainWindow::createScreenPanel() { if (panel) delete panel; diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 5d02b4ee..678be7c9 100755 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -242,6 +242,8 @@ private slots: void onScreenEmphasisToggled(); private: + virtual void closeEvent(QCloseEvent* event) override; + QStringList currentROM; QStringList currentGBAROM; QList recentFileList; From 44b07040638535932f9e206337eae7570bb83d19 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 28 Jun 2025 12:48:22 +0200 Subject: [PATCH 091/106] fix video settings update on second instance secondary windows --- src/frontend/qt_sdl/Window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 965dddcf..8ba08dc5 100755 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -2294,7 +2294,7 @@ void MainWindow::onUpdateVideoSettings(bool glchange) if (parentwin) return parentwin->onUpdateVideoSettings(glchange); - auto childwins = findChildren(nullptr, Qt::FindDirectChildrenOnly); + auto childwins = findChildren(nullptr); bool hadOGL = hasOGL; if (glchange) From 4aaea218c1be7cc076d4f86407e273a36e66c3d7 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 28 Jun 2025 13:06:53 +0200 Subject: [PATCH 092/106] add a way to tell windows apart --- src/frontend/qt_sdl/EmuThread.cpp | 12 ++---------- src/frontend/qt_sdl/Window.cpp | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 7737038a..4b1b76e3 100755 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -427,11 +427,7 @@ void EmuThread::run() winUpdateFreq = 1; double actualfps = (59.8261 * 263.0) / nlines; - int inst = emuInstance->instanceID; - if (inst == 0) - snprintf(melontitle, sizeof(melontitle), "[%d/%.0f] melonDS " MELONDS_VERSION, fps, actualfps); - else - snprintf(melontitle, sizeof(melontitle), "[%d/%.0f] melonDS (%d)", fps, actualfps, inst+1); + snprintf(melontitle, sizeof(melontitle), "[%d/%.0f] melonDS " MELONDS_VERSION, fps, actualfps); changeWindowTitle(melontitle); } } @@ -444,11 +440,7 @@ void EmuThread::run() emit windowUpdate(); - int inst = emuInstance->instanceID; - if (inst == 0) - snprintf(melontitle, sizeof(melontitle), "melonDS " MELONDS_VERSION); - else - snprintf(melontitle, sizeof(melontitle), "melonDS (%d)", inst+1); + snprintf(melontitle, sizeof(melontitle), "melonDS " MELONDS_VERSION); changeWindowTitle(melontitle); SDL_Delay(75); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 8ba08dc5..8e3b2390 100755 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -2180,6 +2180,29 @@ void MainWindow::onChangeAudioSync(bool checked) void MainWindow::onTitleUpdate(QString title) { + if (!emuInstance) return; + + int numinst = numEmuInstances(); + int numwin = emuInstance->getNumWindows(); + if ((numinst > 1) && (numwin > 1)) + { + // add player/window prefix + QString prefix = QString("[p%1:w%2] ").arg(emuInstance->instanceID+1).arg(windowID+1); + title = prefix + title; + } + else if (numinst > 1) + { + // add player prefix + QString prefix = QString("[p%1] ").arg(emuInstance->instanceID+1); + title = prefix + title; + } + else if (numwin > 1) + { + // add window prefix + QString prefix = QString("[w%1] ").arg(windowID+1); + title = prefix + title; + } + setWindowTitle(title); } From f352cf612ad16d494ece5ff49aed91e057b614be Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 28 Jun 2025 22:55:48 +0200 Subject: [PATCH 093/106] call MakeCurrent() before deiniting GL stuff (to avoid deiniting on the wrong context) --- src/frontend/qt_sdl/Screen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 2515b155..17fd4b86 100755 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -1020,6 +1020,8 @@ void ScreenPanelGL::deinitOpenGL() if (!glContext) return; if (!glInited) return; + glContext->MakeCurrent(); + glDeleteTextures(1, &screenTexture); glDeleteVertexArrays(1, &screenVertexArray); From 16d1464d19ed308e4066280090128bd32dcf18ac Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 5 Jul 2025 00:14:11 +0200 Subject: [PATCH 094/106] camera: make timings more realistic (fixes rolling in some games) --- src/DSi_Camera.cpp | 33 ++++++++++++++++++++++++--------- src/DSi_Camera.h | 4 +++- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index a5248af3..38251fd6 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -34,8 +34,11 @@ using Platform::LogLevel; // namely, how long cameras take to process frames // camera IRQ is fired at roughly 15FPS with default config -const u32 DSi_CamModule::kIRQInterval = 1120000; // ~30 FPS -const u32 DSi_CamModule::kTransferStart = 60000; +// camera IRQ marks camera VBlank +// each scanline takes roughly 3173 cycles +const u32 DSi_CamModule::kIRQInterval = 2234248; // ~15 FPS +const u32 DSi_CamModule::kScanlineTime = 3173; +const u32 DSi_CamModule::kTransferStart = DSi_CamModule::kIRQInterval - (DSi_CamModule::kScanlineTime * 480); DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi) @@ -127,11 +130,11 @@ void DSi_CamModule::TransferScanline(u32 line) int maxlen = 512 - BufferWritePos; u32 tmpbuf[512]; - int datalen = CurCamera->TransferScanline(tmpbuf, 512); + int lines_next; + int datalen = CurCamera->TransferScanline(tmpbuf, 512, lines_next); u32 numscan; - // TODO: must be tweaked such that each block has enough time to transfer - u32 delay = datalen*4 + 16; + u32 delay = lines_next * kScanlineTime; int copystart = 0; int copylen = datalen; @@ -438,6 +441,7 @@ void DSi_Camera::Reset() // default state is preview mode (checkme) MCURegs[0x2104] = 3; + InternalY = 0; TransferY = 0; memset(FrameBuffer, 0, (640*480/2)*sizeof(u32)); } @@ -458,6 +462,7 @@ bool DSi_Camera::IsActivated() const void DSi_Camera::StartTransfer() { + InternalY = 0; TransferY = 0; u8 state = MCURegs[0x2104]; @@ -491,9 +496,11 @@ bool DSi_Camera::TransferDone() const return TransferY >= FrameHeight; } -int DSi_Camera::TransferScanline(u32* buffer, int maxlen) +int DSi_Camera::TransferScanline(u32* buffer, int maxlen, int& nlines) { - if (TransferY >= FrameHeight) + nlines = 0; + + if ((TransferY >= FrameHeight) || (InternalY >= 480)) return 0; if (FrameWidth > 640 || FrameHeight > 480 || @@ -509,7 +516,7 @@ int DSi_Camera::TransferScanline(u32* buffer, int maxlen) // TODO: non-YUV pixel formats and all int retlen = FrameWidth >> 1; - int sy = (TransferY * 480) / FrameHeight; + int sy = InternalY; if (FrameReadMode & (1<<1)) sy = 479 - sy; @@ -538,7 +545,15 @@ int DSi_Camera::TransferScanline(u32* buffer, int maxlen) } } - TransferY++; + // determine how many scanlines we're skipping until the next scanline + int oldy = TransferY; + do + { + InternalY++; + TransferY = (InternalY * FrameHeight) / 480; + nlines++; + } + while ((TransferY == oldy) && (InternalY < 480)); return retlen; } diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index 5a626f56..61934bc8 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -44,7 +44,7 @@ public: bool TransferDone() const; // lengths in words - int TransferScanline(u32* buffer, int maxlen); + int TransferScanline(u32* buffer, int maxlen, int& nlines); void Acquire() override; u8 Read(bool last) override; @@ -77,6 +77,7 @@ private: u16 FrameWidth, FrameHeight; u16 FrameReadMode, FrameFormat; + int InternalY; int TransferY; u32 FrameBuffer[640*480/2]; // YUYV framebuffer, two pixels per word }; @@ -124,6 +125,7 @@ private: DSi_Camera* CurCamera; static const u32 kIRQInterval; + static const u32 kScanlineTime; static const u32 kTransferStart; }; From 2cb07bf126fba0d6d8960a1b2e23486809c067d0 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 7 Jul 2025 02:30:01 +0200 Subject: [PATCH 095/106] camera: model FIFO more accurately (avoids weird bugs when DMA fails) --- src/DSi_Camera.cpp | 47 ++++++++++++++++++---------------- src/DSi_Camera.h | 4 +-- src/FIFO.h | 1 + src/frontend/qt_sdl/Window.cpp | 2 +- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 38251fd6..d12f1e34 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -67,9 +67,7 @@ void DSi_CamModule::Reset() CropStart = 0; CropEnd = 0; - memset(DataBuffer, 0, 512*sizeof(u32)); - BufferReadPos = 0; - BufferWritePos = 0; + DataBuffer.Clear(); BufferNumLines = 0; CurCamera = nullptr; @@ -89,6 +87,8 @@ void DSi_CamModule::DoSavestate(Savestate* file) file->Var16(&ModuleCnt); file->Var16(&Cnt); + // TODO: should other stuff be savestated? (pixel buffer, etc) + /*file->VarArray(FrameBuffer, sizeof(FrameBuffer)); file->Var32(&TransferPos); file->Var32(&FrameLength);*/ @@ -113,8 +113,6 @@ void DSi_CamModule::IRQ(u32 param) if (Cnt & (1<<15)) { - BufferReadPos = 0; - BufferWritePos = 0; BufferNumLines = 0; CurCamera = activecam; DSi.ScheduleEvent(Event_DSi_CamTransfer, false, kTransferStart, 0, 0); @@ -126,8 +124,10 @@ void DSi_CamModule::IRQ(u32 param) void DSi_CamModule::TransferScanline(u32 line) { - u32* dstbuf = &DataBuffer[BufferWritePos]; - int maxlen = 512 - BufferWritePos; + if (Cnt & (1<<4)) + return; + + int maxlen = DataBuffer.FreeSpace(); u32 tmpbuf[512]; int lines_next; @@ -195,28 +195,28 @@ void DSi_CamModule::TransferScanline(u32 line) u32 col1 = (r1 >> 3) | ((g1 >> 3) << 5) | ((b1 >> 3) << 10) | 0x8000; u32 col2 = (r2 >> 3) | ((g2 >> 3) << 5) | ((b2 >> 3) << 10) | 0x8000; - dstbuf[i] = col1 | (col2 << 16); + DataBuffer.Write(col1 | (col2 << 16)); } } else { // return raw data - memcpy(dstbuf, &tmpbuf[copystart], copylen*sizeof(u32)); + for (u32 i = 0; i < copylen; i++) + { + u32 val = tmpbuf[copystart + i]; + DataBuffer.Write(val); + } } numscan = Cnt & 0x000F; if (BufferNumLines >= numscan) { - BufferReadPos = 0; // checkme - BufferWritePos = 0; BufferNumLines = 0; DSi.CheckNDMAs(0, 0x0B); } else { - BufferWritePos += copylen; - if (BufferWritePos > 512) BufferWritePos = 512; BufferNumLines++; } @@ -225,10 +225,9 @@ skip_line: { // when the frame is finished, transfer any remaining data if needed // (if the frame height isn't a multiple of the DMA interval) + // (TODO: should it be done earlier?) if (BufferNumLines > 0) { - BufferReadPos = 0; - BufferWritePos = 0; BufferNumLines = 0; DSi.CheckNDMAs(0, 0x0B); } @@ -266,14 +265,19 @@ u32 DSi_CamModule::Read32(u32 addr) { case 0x04004204: { - u32 ret = DataBuffer[BufferReadPos]; + u32 ret; if (Cnt & (1<<15)) { - if (BufferReadPos < 511) - BufferReadPos++; - // CHECKME!!!! - // also presumably we should set bit4 in Cnt if there's no new data to be read + if (DataBuffer.IsEmpty()) + { + ret = 0; + Cnt |= (1<<4); + } + else + ret = DataBuffer.Read(); } + else + ret = DataBuffer.Peek(); return ret; } @@ -339,8 +343,7 @@ void DSi_CamModule::Write16(u32 addr, u16 val) if (val & (1<<5)) { Cnt &= ~(1<<4); - BufferReadPos = 0; - BufferWritePos = 0; + DataBuffer.Clear(); } if ((val & (1<<15)) && !(Cnt & (1<<15))) diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index 61934bc8..bd588c72 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -22,6 +22,7 @@ #include "types.h" #include "Savestate.h" #include "DSi_I2C.h" +#include "FIFO.h" namespace melonDS { @@ -119,8 +120,7 @@ private: u32 CropStart, CropEnd; // pixel data buffer holds a maximum of 512 words, regardless of how long scanlines are - u32 DataBuffer[512]; - u32 BufferReadPos, BufferWritePos; + FIFO DataBuffer; u32 BufferNumLines; DSi_Camera* CurCamera; diff --git a/src/FIFO.h b/src/FIFO.h index ccb009b1..f8749ad3 100644 --- a/src/FIFO.h +++ b/src/FIFO.h @@ -90,6 +90,7 @@ public: } u32 Level() const { return NumOccupied; } + u32 FreeSpace() const { return NumEntries - NumOccupied; } bool IsEmpty() const { return NumOccupied == 0; } bool IsFull() const { return NumOccupied >= NumEntries; } diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 8e3b2390..b5a9b690 100755 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -979,7 +979,7 @@ void MainWindow::keyPressEvent(QKeyEvent* event) if (event->isAutoRepeat()) return; // TODO!! REMOVE ME IN RELEASE BUILDS!! - //if (event->key() == Qt::Key_F11) emuThread->NDS->debug(0); + //if (event->key() == Qt::Key_F11) emuInstance->getNDS()->debug(0); emuInstance->onKeyPress(event); } From 85dcf65231224f34e25b5007b644d8987e33838c Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 7 Jul 2025 02:47:46 +0200 Subject: [PATCH 096/106] DSi: add support for SCFG_EXT bit 13 (32-bit VRAM bus) --- src/DSi.cpp | 25 ++++++++++++++++++++++++- src/DSi.h | 2 ++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/DSi.cpp b/src/DSi.cpp index f6c5b519..8ba72c5c 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -175,6 +175,8 @@ void DSi::Reset() // LCD init flag GPU.DispStat[0] |= (1<<6); GPU.DispStat[1] |= (1<<6); + + UpdateVRAMTimings(); } void DSi::Stop(Platform::StopReason reason) @@ -285,6 +287,8 @@ void DSi::DoSavestateExtra(Savestate* file) I2C.DoSavestate(file); SDMMC.DoSavestate(file); SDIO.DoSavestate(file); + + UpdateVRAMTimings(); } void DSi::SetCartInserted(bool inserted) @@ -666,6 +670,8 @@ void DSi::SetupDirectBoot() ARM9.CP15Write(0x671, 0x02FFC01B); ARM9.CP15Write(0x910, 0x0E00000A); ARM9.CP15Write(0x911, 0x00000020); + + UpdateVRAMTimings(); } void DSi::SoftReset() @@ -717,10 +723,11 @@ void DSi::SoftReset() SCFG_RST = 0; DSP.SetRstLine(false); - // LCD init flag GPU.DispStat[0] |= (1<<6); GPU.DispStat[1] |= (1<<6); + + UpdateVRAMTimings(); } bool DSi::LoadNAND() @@ -1252,6 +1259,20 @@ void DSi::MapNWRAMRange(u32 cpu, u32 num, u32 val) } } +void DSi::UpdateVRAMTimings() +{ + if (SCFG_EXT[0] & (1<<13)) + { + SetARM9RegionTimings(0x06000, 0x07000, Mem9_VRAM, 32, 1, 1); + SetARM7RegionTimings(0x06000, 0x07000, Mem7_VRAM, 32, 1, 1); + } + else + { + SetARM9RegionTimings(0x06000, 0x07000, Mem9_VRAM, 16, 1, 1); + SetARM7RegionTimings(0x06000, 0x07000, Mem7_VRAM, 16, 1, 1); + } +} + void DSi::ApplyNewRAMSize(u32 size) { switch (size) @@ -2565,6 +2586,8 @@ void DSi::ARM9IOWrite32(u32 addr, u32 val) //if (newram != oldram) // NDS::ScheduleEvent(NDS::Event_DSi_RAMSizeChange, false, 512*512*512, ApplyNewRAMSize, newram); Log(LogLevel::Debug, "from %08X, ARM7 %08X, %08X\n", NDS::GetPC(0), NDS::GetPC(1), ARM7.R[1]); + + UpdateVRAMTimings(); } return; diff --git a/src/DSi.h b/src/DSi.h index e9f32b68..e7d65518 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -97,6 +97,8 @@ public: void MapNWRAM_C(u32 num, u8 val); void MapNWRAMRange(u32 cpu, u32 num, u32 val); + void UpdateVRAMTimings(); + u8 ARM9Read8(u32 addr) override; u16 ARM9Read16(u32 addr) override; u32 ARM9Read32(u32 addr) override; From 1f51f27d44bfa29c56944f88f5c914bc8e3db220 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 7 Jul 2025 12:20:11 +0200 Subject: [PATCH 097/106] Revert "camera: model FIFO more accurately (avoids weird bugs when DMA fails)" This reverts commit 2cb07bf126fba0d6d8960a1b2e23486809c067d0. --- src/DSi_Camera.cpp | 47 ++++++++++++++++------------------ src/DSi_Camera.h | 4 +-- src/FIFO.h | 1 - src/frontend/qt_sdl/Window.cpp | 2 +- 4 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index d12f1e34..38251fd6 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -67,7 +67,9 @@ void DSi_CamModule::Reset() CropStart = 0; CropEnd = 0; - DataBuffer.Clear(); + memset(DataBuffer, 0, 512*sizeof(u32)); + BufferReadPos = 0; + BufferWritePos = 0; BufferNumLines = 0; CurCamera = nullptr; @@ -87,8 +89,6 @@ void DSi_CamModule::DoSavestate(Savestate* file) file->Var16(&ModuleCnt); file->Var16(&Cnt); - // TODO: should other stuff be savestated? (pixel buffer, etc) - /*file->VarArray(FrameBuffer, sizeof(FrameBuffer)); file->Var32(&TransferPos); file->Var32(&FrameLength);*/ @@ -113,6 +113,8 @@ void DSi_CamModule::IRQ(u32 param) if (Cnt & (1<<15)) { + BufferReadPos = 0; + BufferWritePos = 0; BufferNumLines = 0; CurCamera = activecam; DSi.ScheduleEvent(Event_DSi_CamTransfer, false, kTransferStart, 0, 0); @@ -124,10 +126,8 @@ void DSi_CamModule::IRQ(u32 param) void DSi_CamModule::TransferScanline(u32 line) { - if (Cnt & (1<<4)) - return; - - int maxlen = DataBuffer.FreeSpace(); + u32* dstbuf = &DataBuffer[BufferWritePos]; + int maxlen = 512 - BufferWritePos; u32 tmpbuf[512]; int lines_next; @@ -195,28 +195,28 @@ void DSi_CamModule::TransferScanline(u32 line) u32 col1 = (r1 >> 3) | ((g1 >> 3) << 5) | ((b1 >> 3) << 10) | 0x8000; u32 col2 = (r2 >> 3) | ((g2 >> 3) << 5) | ((b2 >> 3) << 10) | 0x8000; - DataBuffer.Write(col1 | (col2 << 16)); + dstbuf[i] = col1 | (col2 << 16); } } else { // return raw data - for (u32 i = 0; i < copylen; i++) - { - u32 val = tmpbuf[copystart + i]; - DataBuffer.Write(val); - } + memcpy(dstbuf, &tmpbuf[copystart], copylen*sizeof(u32)); } numscan = Cnt & 0x000F; if (BufferNumLines >= numscan) { + BufferReadPos = 0; // checkme + BufferWritePos = 0; BufferNumLines = 0; DSi.CheckNDMAs(0, 0x0B); } else { + BufferWritePos += copylen; + if (BufferWritePos > 512) BufferWritePos = 512; BufferNumLines++; } @@ -225,9 +225,10 @@ skip_line: { // when the frame is finished, transfer any remaining data if needed // (if the frame height isn't a multiple of the DMA interval) - // (TODO: should it be done earlier?) if (BufferNumLines > 0) { + BufferReadPos = 0; + BufferWritePos = 0; BufferNumLines = 0; DSi.CheckNDMAs(0, 0x0B); } @@ -265,19 +266,14 @@ u32 DSi_CamModule::Read32(u32 addr) { case 0x04004204: { - u32 ret; + u32 ret = DataBuffer[BufferReadPos]; if (Cnt & (1<<15)) { - if (DataBuffer.IsEmpty()) - { - ret = 0; - Cnt |= (1<<4); - } - else - ret = DataBuffer.Read(); + if (BufferReadPos < 511) + BufferReadPos++; + // CHECKME!!!! + // also presumably we should set bit4 in Cnt if there's no new data to be read } - else - ret = DataBuffer.Peek(); return ret; } @@ -343,7 +339,8 @@ void DSi_CamModule::Write16(u32 addr, u16 val) if (val & (1<<5)) { Cnt &= ~(1<<4); - DataBuffer.Clear(); + BufferReadPos = 0; + BufferWritePos = 0; } if ((val & (1<<15)) && !(Cnt & (1<<15))) diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index bd588c72..61934bc8 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -22,7 +22,6 @@ #include "types.h" #include "Savestate.h" #include "DSi_I2C.h" -#include "FIFO.h" namespace melonDS { @@ -120,7 +119,8 @@ private: u32 CropStart, CropEnd; // pixel data buffer holds a maximum of 512 words, regardless of how long scanlines are - FIFO DataBuffer; + u32 DataBuffer[512]; + u32 BufferReadPos, BufferWritePos; u32 BufferNumLines; DSi_Camera* CurCamera; diff --git a/src/FIFO.h b/src/FIFO.h index f8749ad3..ccb009b1 100644 --- a/src/FIFO.h +++ b/src/FIFO.h @@ -90,7 +90,6 @@ public: } u32 Level() const { return NumOccupied; } - u32 FreeSpace() const { return NumEntries - NumOccupied; } bool IsEmpty() const { return NumOccupied == 0; } bool IsFull() const { return NumOccupied >= NumEntries; } diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index b5a9b690..8e3b2390 100755 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -979,7 +979,7 @@ void MainWindow::keyPressEvent(QKeyEvent* event) if (event->isAutoRepeat()) return; // TODO!! REMOVE ME IN RELEASE BUILDS!! - //if (event->key() == Qt::Key_F11) emuInstance->getNDS()->debug(0); + //if (event->key() == Qt::Key_F11) emuThread->NDS->debug(0); emuInstance->onKeyPress(event); } From 85d92026332ebaa65538fc5533be0c6a060ce81a Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 7 Jul 2025 12:21:47 +0200 Subject: [PATCH 098/106] push this back tho... --- src/frontend/qt_sdl/Window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 8e3b2390..b5a9b690 100755 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -979,7 +979,7 @@ void MainWindow::keyPressEvent(QKeyEvent* event) if (event->isAutoRepeat()) return; // TODO!! REMOVE ME IN RELEASE BUILDS!! - //if (event->key() == Qt::Key_F11) emuThread->NDS->debug(0); + //if (event->key() == Qt::Key_F11) emuInstance->getNDS()->debug(0); emuInstance->onKeyPress(event); } From 7499958ad0fed63c38399a3e85caca57b25f48b3 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 7 Jul 2025 14:48:53 +0200 Subject: [PATCH 099/106] camera: revise interface behavior to be more accurate * there are two FIFO buffers (finally fixes Let's Golf) * fix issues with camera start condition/cnt bit15 * add camera interface state to savestate --- src/DSi.cpp | 2 +- src/DSi_Camera.cpp | 136 ++++++++++++++++++++++++++++++--------------- src/DSi_Camera.h | 16 +++++- src/NDS.cpp | 11 ++-- 4 files changed, 112 insertions(+), 53 deletions(-) diff --git a/src/DSi.cpp b/src/DSi.cpp index 8ba72c5c..c9f73c21 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -282,9 +282,9 @@ void DSi::DoSavestateExtra(Savestate* file) NDMAs[i].DoSavestate(file); AES.DoSavestate(file); - CamModule.DoSavestate(file); DSP.DoSavestate(file); I2C.DoSavestate(file); + CamModule.DoSavestate(file); SDMMC.DoSavestate(file); SDIO.DoSavestate(file); diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 38251fd6..2475c44f 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -67,12 +67,15 @@ void DSi_CamModule::Reset() CropStart = 0; CropEnd = 0; - memset(DataBuffer, 0, 512*sizeof(u32)); - BufferReadPos = 0; - BufferWritePos = 0; + Transferring = false; + + memset(PixelBuffer, 0, sizeof(PixelBuffer)); + CurPixelBuffer = 0; BufferNumLines = 0; CurCamera = nullptr; + // TODO: ideally this should be started when a camera is active + // instead of just being a constant thing DSi.ScheduleEvent(Event_DSi_CamIRQ, false, kIRQInterval, 0, 0); } @@ -89,9 +92,30 @@ void DSi_CamModule::DoSavestate(Savestate* file) file->Var16(&ModuleCnt); file->Var16(&Cnt); - /*file->VarArray(FrameBuffer, sizeof(FrameBuffer)); - file->Var32(&TransferPos); - file->Var32(&FrameLength);*/ + file->Var32(&CropStart); + file->Var32(&CropEnd); + + file->Bool32(&Transferring); + + file->VarArray(&PixelBuffer[0].Data, 512); + file->Var32(&PixelBuffer[0].ReadPos); + file->Var32(&PixelBuffer[0].WritePos); + file->VarArray(&PixelBuffer[1].Data, 512); + file->Var32(&PixelBuffer[1].ReadPos); + file->Var32(&PixelBuffer[1].WritePos); + file->Var8(&CurPixelBuffer); + + file->Var32(&BufferNumLines); + + if (!file->Saving) + { + DSi_Camera* activecam = nullptr; + + if (Camera0->IsActivated()) activecam = Camera0; + else if (Camera1->IsActivated()) activecam = Camera1; + + CurCamera = activecam; + } } @@ -111,14 +135,8 @@ void DSi_CamModule::IRQ(u32 param) if (Cnt & (1<<11)) DSi.SetIRQ(0, IRQ_DSi_Camera); - if (Cnt & (1<<15)) - { - BufferReadPos = 0; - BufferWritePos = 0; - BufferNumLines = 0; - CurCamera = activecam; - DSi.ScheduleEvent(Event_DSi_CamTransfer, false, kTransferStart, 0, 0); - } + CurCamera = activecam; + DSi.ScheduleEvent(Event_DSi_CamTransfer, false, kTransferStart, 0, 0); } DSi.ScheduleEvent(Event_DSi_CamIRQ, true, kIRQInterval, 0, 0); @@ -126,8 +144,21 @@ void DSi_CamModule::IRQ(u32 param) void DSi_CamModule::TransferScanline(u32 line) { - u32* dstbuf = &DataBuffer[BufferWritePos]; - int maxlen = 512 - BufferWritePos; + if (line == 0) + { + if (!(Cnt & (1<<15))) + return; + + BufferNumLines = 0; + Transferring = true; + } + + if (Cnt & (1<<4)) + return; + + sPixelBuffer* buffer = &PixelBuffer[CurPixelBuffer]; + u32* dstbuf = &buffer->Data[buffer->WritePos]; + int maxlen = 512 - buffer->WritePos; u32 tmpbuf[512]; int lines_next; @@ -138,6 +169,7 @@ void DSi_CamModule::TransferScanline(u32 line) int copystart = 0; int copylen = datalen; + bool line_last = false; if (Cnt & (1<<14)) { @@ -146,7 +178,10 @@ void DSi_CamModule::TransferScanline(u32 line) int ystart = (CropStart >> 16) & 0x1FF; int yend = (CropEnd >> 16) & 0x1FF; if (line < ystart || line > yend) + { + if (line == yend+1) line_last = true; goto skip_line; + } int xstart = (CropStart >> 1) & 0x1FF; int xend = (CropEnd >> 1) & 0x1FF; @@ -163,7 +198,6 @@ void DSi_CamModule::TransferScanline(u32 line) if (copylen > maxlen) { copylen = maxlen; - Cnt |= (1<<4); } if (Cnt & (1<<13)) @@ -205,40 +239,63 @@ void DSi_CamModule::TransferScanline(u32 line) memcpy(dstbuf, &tmpbuf[copystart], copylen*sizeof(u32)); } + buffer->WritePos += copylen; + if (buffer->WritePos > 512) buffer->WritePos = 512; + numscan = Cnt & 0x000F; if (BufferNumLines >= numscan) { - BufferReadPos = 0; // checkme - BufferWritePos = 0; BufferNumLines = 0; - DSi.CheckNDMAs(0, 0x0B); + SwapPixelBuffers(); } else { - BufferWritePos += copylen; - if (BufferWritePos > 512) BufferWritePos = 512; BufferNumLines++; } skip_line: - if (CurCamera->TransferDone()) + bool done = CurCamera->TransferDone(); + if (done || line_last) { // when the frame is finished, transfer any remaining data if needed // (if the frame height isn't a multiple of the DMA interval) if (BufferNumLines > 0) { - BufferReadPos = 0; - BufferWritePos = 0; BufferNumLines = 0; - DSi.CheckNDMAs(0, 0x0B); + SwapPixelBuffers(); } + } + if (done) + { + Transferring = false; return; } DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1); } +void DSi_CamModule::SwapPixelBuffers() +{ + // pixel buffers are swapped every time a buffer is filled (ie. when the DMA interval is reached) + // the swap fails if the other buffer isn't empty + + sPixelBuffer* otherbuf = &PixelBuffer[CurPixelBuffer ^ 1]; + if (otherbuf->ReadPos < otherbuf->WritePos) + { + // overrun + Cnt |= (1<<4); + Transferring = false; + } + else + { + PixelBuffer[CurPixelBuffer].ReadPos = 0; + otherbuf->WritePos = 0; + CurPixelBuffer ^= 1; + DSi.CheckNDMAs(0, 0x0B); + } +} + u8 DSi_CamModule::Read8(u32 addr) { @@ -253,7 +310,7 @@ u16 DSi_CamModule::Read16(u32 addr) switch (addr) { case 0x04004200: return ModuleCnt; - case 0x04004202: return Cnt; + case 0x04004202: return Cnt | (Transferring ? 0x8000 : 0); } Log(LogLevel::Debug, "unknown DSi cam read16 %08X\n", addr); @@ -266,13 +323,12 @@ u32 DSi_CamModule::Read32(u32 addr) { case 0x04004204: { - u32 ret = DataBuffer[BufferReadPos]; + sPixelBuffer* buffer = &PixelBuffer[CurPixelBuffer ^ 1]; + u32 ret = buffer->Data[buffer->ReadPos]; if (Cnt & (1<<15)) { - if (BufferReadPos < 511) - BufferReadPos++; - // CHECKME!!!! - // also presumably we should set bit4 in Cnt if there's no new data to be read + if (buffer->ReadPos < buffer->WritePos) + buffer->ReadPos++; } return ret; @@ -308,6 +364,7 @@ void DSi_CamModule::Write16(u32 addr, u16 val) // CHECKME Cnt = 0; + Transferring = false; } if ((ModuleCnt & (1<<5)) && !(oldcnt & (1<<5))) @@ -319,12 +376,9 @@ void DSi_CamModule::Write16(u32 addr, u16 val) case 0x04004202: { - // TODO: during a transfer, clearing bit15 does not reflect immediately - // maybe it needs to finish the trasnfer or atleast the current block - // checkme u16 oldmask; - if (Cnt & 0x8000) + if ((Cnt & 0x8000) || Transferring) { val &= 0x8F20; oldmask = 0x601F; @@ -339,14 +393,8 @@ void DSi_CamModule::Write16(u32 addr, u16 val) if (val & (1<<5)) { Cnt &= ~(1<<4); - BufferReadPos = 0; - BufferWritePos = 0; - } - - if ((val & (1<<15)) && !(Cnt & (1<<15))) - { - // start transfer - //DSi::CheckNDMAs(0, 0x0B); + memset(PixelBuffer, 0, sizeof(PixelBuffer)); + CurPixelBuffer = 0; } } return; diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index 61934bc8..c00e4f4c 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -118,15 +118,25 @@ private: u32 CropStart, CropEnd; - // pixel data buffer holds a maximum of 512 words, regardless of how long scanlines are - u32 DataBuffer[512]; - u32 BufferReadPos, BufferWritePos; + bool Transferring; + + // pixel data buffers hold a maximum of 512 words, regardless of how long scanlines are + typedef struct + { + u32 Data[512]; + u32 ReadPos, WritePos; + + } sPixelBuffer; + sPixelBuffer PixelBuffer[2]; + u8 CurPixelBuffer; u32 BufferNumLines; DSi_Camera* CurCamera; static const u32 kIRQInterval; static const u32 kScanlineTime; static const u32 kTransferStart; + + void SwapPixelBuffers(); }; } diff --git a/src/NDS.cpp b/src/NDS.cpp index 1dd7e095..340cd020 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1883,7 +1883,7 @@ void NDS::debug(u32 param) //for (int i = 0; i < 9; i++) // printf("VRAM %c: %02X\n", 'A'+i, GPU->VRAMCNT[i]); - Platform::FileHandle* shit = Platform::OpenFile("debug/pokeplat.bin", FileMode::Write); + /*Platform::FileHandle* shit = Platform::OpenFile("debug/pokeplat.bin", FileMode::Write); Platform::FileWrite(ARM9.ITCM, 0x8000, 1, shit); for (u32 i = 0x02000000; i < 0x02400000; i+=4) { @@ -1900,20 +1900,21 @@ void NDS::debug(u32 param) u32 val = NDS::ARM7Read32(i); Platform::FileWrite(&val, 4, 1, shit); } - Platform::CloseFile(shit); + Platform::CloseFile(shit);*/ /*FILE* - shit = fopen("debug/directboot9.bin", "wb"); + shit = fopen("debug/camera9.bin", "wb"); + fwrite(ARM9.ITCM, 0x8000, 1, shit); for (u32 i = 0x02000000; i < 0x04000000; i+=4) { - u32 val = DSi::ARM9Read32(i); + u32 val = ARM9Read32(i); fwrite(&val, 4, 1, shit); } fclose(shit); shit = fopen("debug/camera7.bin", "wb"); for (u32 i = 0x02000000; i < 0x04000000; i+=4) { - u32 val = DSi::ARM7Read32(i); + u32 val = ARM7Read32(i); fwrite(&val, 4, 1, shit); } fclose(shit);*/ From e5741f992a65f53ea53bab9e4cc72ee9d50fcdd5 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 7 Jul 2025 16:15:58 +0200 Subject: [PATCH 100/106] camera: fix some misc issues --- src/DSi_Camera.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 2475c44f..58582f90 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -144,6 +144,12 @@ void DSi_CamModule::IRQ(u32 param) void DSi_CamModule::TransferScanline(u32 line) { + if (Cnt & (1<<4)) + { + Transferring = false; + return; + } + if (line == 0) { if (!(Cnt & (1<<15))) @@ -153,9 +159,6 @@ void DSi_CamModule::TransferScanline(u32 line) Transferring = true; } - if (Cnt & (1<<4)) - return; - sPixelBuffer* buffer = &PixelBuffer[CurPixelBuffer]; u32* dstbuf = &buffer->Data[buffer->WritePos]; int maxlen = 512 - buffer->WritePos; @@ -324,12 +327,13 @@ u32 DSi_CamModule::Read32(u32 addr) case 0x04004204: { sPixelBuffer* buffer = &PixelBuffer[CurPixelBuffer ^ 1]; - u32 ret = buffer->Data[buffer->ReadPos]; - if (Cnt & (1<<15)) - { - if (buffer->ReadPos < buffer->WritePos) - buffer->ReadPos++; - } + u32 ret; + if (buffer->ReadPos < buffer->WritePos) + ret = buffer->Data[buffer->ReadPos++]; + else if (buffer->ReadPos > 0) + ret = buffer->Data[buffer->ReadPos - 1]; + else + ret = buffer->Data[0]; return ret; } @@ -421,7 +425,7 @@ void DSi_CamModule::Write16(u32 addr, u16 val) } void DSi_CamModule::Write32(u32 addr, u32 val) -{ +{printf("cam write32 %08X %08X\n", addr, val); switch (addr) { case 0x04004210: From f8b48719008daa99e4500e1e8a8132176eec5480 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 7 Jul 2025 16:19:56 +0200 Subject: [PATCH 101/106] oops --- src/DSi_Camera.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 58582f90..70da8877 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -425,7 +425,7 @@ void DSi_CamModule::Write16(u32 addr, u16 val) } void DSi_CamModule::Write32(u32 addr, u32 val) -{printf("cam write32 %08X %08X\n", addr, val); +{ switch (addr) { case 0x04004210: From 72cba5daab7657aeb2eb882e5c48f8011f7823db Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 8 Jul 2025 16:04:17 +0200 Subject: [PATCH 102/106] camera: misc fix --- src/DSi_Camera.cpp | 23 +++++++++++++++-------- src/DSi_Camera.h | 1 + 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 70da8877..55c9c144 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -299,6 +299,13 @@ void DSi_CamModule::SwapPixelBuffers() } } +bool DSi_CamModule::IsTransferring() +{ + if (Cnt & (1<<15)) return true; + if (Transferring) return true; + return false; +} + u8 DSi_CamModule::Read8(u32 addr) { @@ -313,7 +320,7 @@ u16 DSi_CamModule::Read16(u32 addr) switch (addr) { case 0x04004200: return ModuleCnt; - case 0x04004202: return Cnt | (Transferring ? 0x8000 : 0); + case 0x04004202: return Cnt | (Transferring ? (1<<15) : 0); } Log(LogLevel::Debug, "unknown DSi cam read16 %08X\n", addr); @@ -382,7 +389,7 @@ void DSi_CamModule::Write16(u32 addr, u16 val) { // checkme u16 oldmask; - if ((Cnt & 0x8000) || Transferring) + if (IsTransferring()) { val &= 0x8F20; oldmask = 0x601F; @@ -404,19 +411,19 @@ void DSi_CamModule::Write16(u32 addr, u16 val) return; case 0x04004210: - if (Cnt & (1<<15)) return; + if (IsTransferring()) return; CropStart = (CropStart & 0x01FF0000) | (val & 0x03FE); return; case 0x04004212: - if (Cnt & (1<<15)) return; + if (IsTransferring()) return; CropStart = (CropStart & 0x03FE) | ((val & 0x01FF) << 16); return; case 0x04004214: - if (Cnt & (1<<15)) return; + if (IsTransferring()) return; CropEnd = (CropEnd & 0x01FF0000) | (val & 0x03FE); return; case 0x04004216: - if (Cnt & (1<<15)) return; + if (IsTransferring()) return; CropEnd = (CropEnd & 0x03FE) | ((val & 0x01FF) << 16); return; } @@ -429,11 +436,11 @@ void DSi_CamModule::Write32(u32 addr, u32 val) switch (addr) { case 0x04004210: - if (Cnt & (1<<15)) return; + if (IsTransferring()) return; CropStart = val & 0x01FF03FE; return; case 0x04004214: - if (Cnt & (1<<15)) return; + if (IsTransferring()) return; CropEnd = val & 0x01FF03FE; return; } diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index c00e4f4c..4e93e0a8 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -137,6 +137,7 @@ private: static const u32 kTransferStart; void SwapPixelBuffers(); + bool IsTransferring(); }; } From 3263ab11c268dd1802959ccb6ac5a82ef845fa48 Mon Sep 17 00:00:00 2001 From: Sparronator <86388887+Sparronator9999@users.noreply.github.com> Date: Wed, 9 Jul 2025 03:52:36 +1000 Subject: [PATCH 103/106] Make SPU audio single-buffered (audio latency improvement) (#2286) * SPU audio latency improvements Basically reverts audio buffer handling to what it was before commit 05b94ef, but with the mutexes kept for thread safety (which the referenced commit was trying to do). The SPU audio buffer should still be thread-safe in theory... right? * Audio output improvements This commit changes the audio output buffer to be configured by a variable, and fixes the case where the sound driver may change the buffer size after calling SDL_OpenAudioDevice (e.g. if the buffer size is set too low for the driver to handle). --- src/NDS.cpp | 1 - src/SPU.cpp | 90 +++++++++--------------- src/SPU.h | 12 ++-- src/frontend/qt_sdl/EmuInstance.h | 1 + src/frontend/qt_sdl/EmuInstanceAudio.cpp | 15 ++-- 5 files changed, 48 insertions(+), 71 deletions(-) diff --git a/src/NDS.cpp b/src/NDS.cpp index 340cd020..5fd65bea 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1045,7 +1045,6 @@ u32 NDS::RunFrame() ARM7Timestamp-SysTimestamp, GPU.GPU3D.Timestamp-SysTimestamp); #endif - SPU.TransferOutput(); break; } diff --git a/src/SPU.cpp b/src/SPU.cpp index 0f0c286d..b3688793 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -207,11 +207,10 @@ SPU::SPU(melonDS::NDS& nds, AudioBitDepth bitdepth, AudioInterpolation interpola ApplyBias = true; Degrade10Bit = false; - memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2); + memset(OutputBuffer, 0, 2*OutputBufferSize*2); - OutputBackbufferWritePosition = 0; - OutputFrontBufferReadPosition = 0; - OutputFrontBufferWritePosition = 0; + OutputBufferReadPos = 0; + OutputBufferWritePos = 0; } SPU::~SPU() @@ -242,11 +241,10 @@ void SPU::Reset() void SPU::Stop() { Platform::Mutex_Lock(AudioLock); - memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2); + memset(OutputBuffer, 0, 2*OutputBufferSize*2); - OutputBackbufferWritePosition = 0; - OutputFrontBufferReadPosition = 0; - OutputFrontBufferWritePosition = 0; + OutputBufferReadPos = 0; + OutputBufferWritePos = 0; Platform::Mutex_Unlock(AudioLock); } @@ -942,67 +940,49 @@ void SPU::Mix(u32 dummy) rightoutput &= 0xFFFFFFC0; } - // OutputBufferFrame can never get full because it's - // transfered to OutputBuffer at the end of the frame - // FIXME: apparently this does happen!!! - if (OutputBackbufferWritePosition * 2 < OutputBufferSize - 1) + Platform::Mutex_Lock(AudioLock); + OutputBuffer[OutputBufferWritePos++] = leftoutput >> 1; + OutputBuffer[OutputBufferWritePos++] = rightoutput >> 1; + + OutputBufferWritePos &= ((2*OutputBufferSize)-1); + + if (OutputBufferWritePos == OutputBufferReadPos) { - OutputBackbuffer[OutputBackbufferWritePosition ] = leftoutput >> 1; - OutputBackbuffer[OutputBackbufferWritePosition + 1] = rightoutput >> 1; - OutputBackbufferWritePosition += 2; + // advance the read position too, to avoid losing the entire FIFO + OutputBufferReadPos += 2; + OutputBufferReadPos &= ((2*OutputBufferSize)-1); } + Platform::Mutex_Unlock(AudioLock); NDS.ScheduleEvent(Event_SPU, true, 1024, 0, 0); } -void SPU::TransferOutput() -{ - Platform::Mutex_Lock(AudioLock); - for (u32 i = 0; i < OutputBackbufferWritePosition; i += 2) - { - OutputFrontBuffer[OutputFrontBufferWritePosition ] = OutputBackbuffer[i ]; - OutputFrontBuffer[OutputFrontBufferWritePosition + 1] = OutputBackbuffer[i + 1]; - - OutputFrontBufferWritePosition += 2; - OutputFrontBufferWritePosition &= OutputBufferSize*2-1; - if (OutputFrontBufferWritePosition == OutputFrontBufferReadPosition) - { - // advance the read position too, to avoid losing the entire FIFO - OutputFrontBufferReadPosition += 2; - OutputFrontBufferReadPosition &= OutputBufferSize*2-1; - } - } - OutputBackbufferWritePosition = 0; - Platform::Mutex_Unlock(AudioLock);; -} - void SPU::TrimOutput() { Platform::Mutex_Lock(AudioLock); const int halflimit = (OutputBufferSize / 2); - int readpos = OutputFrontBufferWritePosition - (halflimit*2); + int readpos = OutputBufferWritePos - (halflimit*2); if (readpos < 0) readpos += (OutputBufferSize*2); - OutputFrontBufferReadPosition = readpos; + OutputBufferReadPos = readpos; Platform::Mutex_Unlock(AudioLock); } void SPU::DrainOutput() { Platform::Mutex_Lock(AudioLock); - OutputFrontBufferWritePosition = 0; - OutputFrontBufferReadPosition = 0; + OutputBufferReadPos = 0; + OutputBufferWritePos = 0; Platform::Mutex_Unlock(AudioLock); } void SPU::InitOutput() { Platform::Mutex_Lock(AudioLock); - memset(OutputBackbuffer, 0, 2*OutputBufferSize*2); - memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2); - OutputFrontBufferReadPosition = 0; - OutputFrontBufferWritePosition = 0; + memset(OutputBuffer, 0, 2*OutputBufferSize*2); + OutputBufferReadPos = 0; + OutputBufferWritePos = 0; Platform::Mutex_Unlock(AudioLock); } @@ -1011,10 +991,10 @@ int SPU::GetOutputSize() const Platform::Mutex_Lock(AudioLock); int ret; - if (OutputFrontBufferWritePosition >= OutputFrontBufferReadPosition) - ret = OutputFrontBufferWritePosition - OutputFrontBufferReadPosition; + if (OutputBufferWritePos >= OutputBufferReadPos) + ret = OutputBufferWritePos - OutputBufferReadPos; else - ret = (OutputBufferSize*2) - OutputFrontBufferReadPosition + OutputFrontBufferWritePosition; + ret = (OutputBufferSize*2) - OutputBufferReadPos + OutputBufferWritePos; ret >>= 1; @@ -1043,10 +1023,10 @@ void SPU::Sync(bool wait) { Platform::Mutex_Lock(AudioLock); - int readpos = OutputFrontBufferWritePosition - (halflimit*2); + int readpos = OutputBufferWritePos - (halflimit*2); if (readpos < 0) readpos += (OutputBufferSize*2); - OutputFrontBufferReadPosition = readpos; + OutputBufferReadPos = readpos; Platform::Mutex_Unlock(AudioLock); } @@ -1055,7 +1035,7 @@ void SPU::Sync(bool wait) int SPU::ReadOutput(s16* data, int samples) { Platform::Mutex_Lock(AudioLock); - if (OutputFrontBufferReadPosition == OutputFrontBufferWritePosition) + if (OutputBufferReadPos == OutputBufferWritePos) { Platform::Mutex_Unlock(AudioLock); return 0; @@ -1063,13 +1043,11 @@ int SPU::ReadOutput(s16* data, int samples) for (int i = 0; i < samples; i++) { - *data++ = OutputFrontBuffer[OutputFrontBufferReadPosition]; - *data++ = OutputFrontBuffer[OutputFrontBufferReadPosition + 1]; + *data++ = OutputBuffer[OutputBufferReadPos++]; + *data++ = OutputBuffer[OutputBufferReadPos++]; + OutputBufferReadPos &= ((2*OutputBufferSize)-1); - OutputFrontBufferReadPosition += 2; - OutputFrontBufferReadPosition &= ((2*OutputBufferSize)-1); - - if (OutputFrontBufferWritePosition == OutputFrontBufferReadPosition) + if (OutputBufferWritePos == OutputBufferReadPos) { Platform::Mutex_Unlock(AudioLock); return i+1; diff --git a/src/SPU.h b/src/SPU.h index fa93273d..85921a5b 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -241,7 +241,6 @@ public: int GetOutputSize() const; void Sync(bool wait); int ReadOutput(s16* data, int samples); - void TransferOutput(); u8 Read8(u32 addr); u16 Read16(u32 addr); @@ -251,14 +250,11 @@ public: void Write32(u32 addr, u32 val); private: - static const u32 OutputBufferSize = 2*2048; + static const u32 OutputBufferSize = 2*1024; // TODO: configurable audio buffer sizes? melonDS::NDS& NDS; - s16 OutputBackbuffer[2 * OutputBufferSize] {}; - u32 OutputBackbufferWritePosition = 0; - - s16 OutputFrontBuffer[2 * OutputBufferSize] {}; - u32 OutputFrontBufferWritePosition = 0; - u32 OutputFrontBufferReadPosition = 0; + s16 OutputBuffer[2 * OutputBufferSize] {}; + u32 OutputBufferWritePos = 0; + u32 OutputBufferReadPos = 0; Platform::Mutex* AudioLock; diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 295e9bf6..b77ee917 100755 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -305,6 +305,7 @@ private: SDL_AudioDeviceID audioDevice; int audioFreq; + int audioBufSize; float audioSampleFrac; bool audioMuted; SDL_cond* audioSyncCond; diff --git a/src/frontend/qt_sdl/EmuInstanceAudio.cpp b/src/frontend/qt_sdl/EmuInstanceAudio.cpp index 2c925801..c8bb8913 100644 --- a/src/frontend/qt_sdl/EmuInstanceAudio.cpp +++ b/src/frontend/qt_sdl/EmuInstanceAudio.cpp @@ -73,8 +73,8 @@ void EmuInstance::audioCallback(void* data, Uint8* stream, int len) // resample incoming audio to match the output sample rate int len_in = inst->audioGetNumSamplesOut(len); - if (len_in > 1024) len_in = 1024; - s16 buf_in[1024*2]; + if (len_in > inst->audioBufSize) len_in = inst->audioBufSize; + s16 buf_in[inst->audioBufSize*2]; int num_in; SDL_LockMutex(inst->audioSyncLock); @@ -416,16 +416,17 @@ void EmuInstance::audioInit() audioSyncCond = SDL_CreateCond(); audioSyncLock = SDL_CreateMutex(); - audioFreq = 48000; // TODO: make configurable? + audioFreq = 48000; // TODO: make both of these configurable? + audioBufSize = 1024; SDL_AudioSpec whatIwant, whatIget; memset(&whatIwant, 0, sizeof(SDL_AudioSpec)); whatIwant.freq = audioFreq; whatIwant.format = AUDIO_S16LSB; whatIwant.channels = 2; - whatIwant.samples = 1024; + whatIwant.samples = audioBufSize; whatIwant.callback = audioCallback; whatIwant.userdata = this; - audioDevice = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + audioDevice = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE); if (!audioDevice) { Platform::Log(Platform::LogLevel::Error, "Audio init failed: %s\n", SDL_GetError()); @@ -433,7 +434,9 @@ void EmuInstance::audioInit() else { audioFreq = whatIget.freq; + audioBufSize = whatIget.samples; Platform::Log(Platform::LogLevel::Info, "Audio output frequency: %d Hz\n", audioFreq); + Platform::Log(Platform::LogLevel::Info, "Audio output buffer size: %d samples\n", audioBufSize); SDL_PauseAudioDevice(audioDevice, 1); } @@ -479,7 +482,7 @@ void EmuInstance::audioSync() if (audioDevice) { SDL_LockMutex(audioSyncLock); - while (nds->SPU.GetOutputSize() > 1024) + while (nds->SPU.GetOutputSize() > audioBufSize) { int ret = SDL_CondWaitTimeout(audioSyncCond, audioSyncLock, 500); if (ret == SDL_MUTEX_TIMEDOUT) break; From 608a4151b8a8bff248e87b1621bb7ca376df6434 Mon Sep 17 00:00:00 2001 From: Jakly <102590697+Jaklyy@users.noreply.github.com> Date: Tue, 8 Jul 2025 15:39:43 -0400 Subject: [PATCH 104/106] fix full bios boot not being set properly on initial DSI class creation (#2366) --- src/DSi.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/DSi.cpp b/src/DSi.cpp index c9f73c21..047d0669 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -113,6 +113,8 @@ DSi::DSi(DSiArgs&& args, void* userdata) noexcept : NWRAM_A = JIT.Memory.GetNWRAM_A(); NWRAM_B = JIT.Memory.GetNWRAM_B(); NWRAM_C = JIT.Memory.GetNWRAM_C(); + + SetFullBIOSBoot(args.FullBIOSBoot); } DSi::~DSi() noexcept From e099cfdc5a38141135e9d5747bebae8f640978c4 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 8 Jul 2025 23:11:36 +0200 Subject: [PATCH 105/106] =?UTF-8?q?BAHAHAHAAHAHAHAHAAHAHAHA-*~=C2=B0+|?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-macos.yml | 2 +- .github/workflows/build-ubuntu.yml | 2 +- .github/workflows/build-windows.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index cd25e40c..cf9c7f45 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -14,7 +14,7 @@ env: MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_BUILD_PROVIDER: GitHub Actions - MELONDS_VERSION_SUFFIX: " RC" +# MELONDS_VERSION_SUFFIX: " RC" jobs: build-macos: diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 9d701524..9ddd9cf1 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -13,7 +13,7 @@ env: MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_BUILD_PROVIDER: GitHub Actions - MELONDS_VERSION_SUFFIX: " RC" +# MELONDS_VERSION_SUFFIX: " RC" jobs: build: diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 92d45d4d..f582c379 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -14,7 +14,7 @@ env: MELONDS_GIT_BRANCH: ${{ github.ref }} MELONDS_GIT_HASH: ${{ github.sha }} MELONDS_BUILD_PROVIDER: GitHub Actions - MELONDS_VERSION_SUFFIX: " RC" +# MELONDS_VERSION_SUFFIX: " RC" jobs: build: From 13a9825c9a84fdbf42d0d4b922f9c2e0920ed19e Mon Sep 17 00:00:00 2001 From: KostaSaizo7 <39852873+KostaSaizo7@users.noreply.github.com> Date: Sun, 13 Jul 2025 23:34:38 +0300 Subject: [PATCH 106/106] le plus important (#2375) blarg --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2cf42c2d..e08ca2b8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@