Qt: Port autosave to Qt interface
@@ -13,6 +13,7 @@ - GameShark and Action Replay button support
- AGBPrint support - Debugger: Conditional breakpoints and watchpoints - Ability to select GB/GBC/SGB BIOS on console ports + - Optional automatic state saving/loading Bugfixes: - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) - GB Serialize: Fix audio state loading
@@ -28,8 +28,9 @@ #endif
#include <mgba-util/math.h> #include <mgba-util/vfs.h> +#define AUTOSAVE_GRANULARITY 600 + using namespace QGBA; - CoreController::CoreController(mCore* core, QObject* parent) : QObject(parent)@@ -48,6 +49,12 @@ m_activeBuffer = &m_buffers[0];
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), size.width()); + m_resetActions.append([this]() { + if (m_autoload) { + mCoreLoadState(m_threadContext.core, 0, m_loadStateFlags); + } + }); + m_threadContext.startCallback = [](mCoreThread* context) { CoreController* controller = static_cast<CoreController*>(context->userData);@@ -60,6 +67,8 @@ #endif
default: break; } + + controller->updateFastForward(); if (controller->m_multiplayer) { controller->m_multiplayer->attachGame(controller);@@ -79,10 +88,6 @@ controller->m_override->identify(context->core);
controller->m_override->apply(context->core); } - if (mCoreLoadState(context->core, 0, controller->m_loadStateFlags)) { - mCoreDeleteState(context->core, 0); - } - controller->m_resetActions.clear(); QSize size = controller->screenDimensions();@@ -100,11 +105,23 @@
m_threadContext.frameCallback = [](mCoreThread* context) { CoreController* controller = static_cast<CoreController*>(context->userData); + if (controller->m_autosaveCounter == AUTOSAVE_GRANULARITY) { + if (controller->m_autosave) { + mCoreSaveState(context->core, 0, controller->m_saveStateFlags); + } + controller->m_autosaveCounter = 0; + } + ++controller->m_autosaveCounter; + controller->finishFrame(); }; m_threadContext.cleanCallback = [](mCoreThread* context) { CoreController* controller = static_cast<CoreController*>(context->userData); + + if (controller->m_autosave) { + mCoreSaveState(context->core, 0, controller->m_saveStateFlags); + } controller->clearMultiplayerController(); QMetaObject::invokeMethod(controller, "stopping");@@ -126,7 +143,8 @@ m_threadContext.logger.d.log = [](mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
mThreadLogger* logContext = reinterpret_cast<mThreadLogger*>(logger); mCoreThread* context = logContext->p; - static const char* savestateMessage = "State %i loaded"; + static const char* savestateMessage = "State %i saved"; + static const char* loadstateMessage = "State %i loaded"; static const char* savestateFailedMessage = "State %i failed to load"; static int biosCat = -1; static int statusCat = -1;@@ -152,7 +170,7 @@ } else
#endif if (category == statusCat) { // Slot 0 is reserved for suspend points - if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) { + if (strncmp(loadstateMessage, format, strlen(loadstateMessage)) == 0) { va_list argc; va_copy(argc, args); int slot = va_arg(argc, int);@@ -160,7 +178,7 @@ va_end(argc);
if (slot == 0) { format = "Loaded suspend state"; } - } else if (strncmp(savestateFailedMessage, format, strlen(savestateFailedMessage)) == 0) { + } else if (strncmp(savestateFailedMessage, format, strlen(savestateFailedMessage)) == 0 || strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) { va_list argc; va_copy(argc, args); int slot = va_arg(argc, int);@@ -232,10 +250,14 @@ m_fastForwardRatio = config->getOption("fastForwardRatio", m_fastForwardRatio).toFloat();
m_videoSync = config->getOption("videoSync", m_videoSync).toInt(); m_audioSync = config->getOption("audioSync", m_audioSync).toInt(); m_fpsTarget = config->getOption("fpsTarget").toFloat(); + m_autosave = config->getOption("autosave", false).toInt(); + m_autoload = config->getOption("autoload", true).toInt(); m_autofireThreshold = config->getOption("autofireThreshold", m_autofireThreshold).toInt(); - updateFastForward(); mCoreLoadForeignConfig(m_threadContext.core, config->config()); - mCoreThreadRewindParamsChanged(&m_threadContext); + if (hasStarted()) { + updateFastForward(); + mCoreThreadRewindParamsChanged(&m_threadContext); + } } #ifdef USE_DEBUGGERS
@@ -195,6 +195,10 @@
bool m_audioSync = AUDIO_SYNC; bool m_videoSync = VIDEO_SYNC; + bool m_autosave; + bool m_autoload; + int m_autosaveCounter; + int m_fastForward = false; int m_fastForwardForced = false; float m_fastForwardRatio = -1.f;
@@ -357,6 +357,8 @@ saveSetting("preload", m_ui.preload);
saveSetting("showFps", m_ui.showFps); saveSetting("cheatAutoload", m_ui.cheatAutoload); saveSetting("cheatAutosave", m_ui.cheatAutosave); + saveSetting("autoload", m_ui.autoload); + saveSetting("autosave", m_ui.autosave); if (m_ui.fastForwardUnbounded->isChecked()) { saveSetting("fastForwardRatio", "-1");@@ -478,6 +480,8 @@ loadSetting("preload", m_ui.preload);
loadSetting("showFps", m_ui.showFps, true); loadSetting("cheatAutoload", m_ui.cheatAutoload, true); loadSetting("cheatAutosave", m_ui.cheatAutosave, true); + loadSetting("autoload", m_ui.autoload, true); + loadSetting("autosave", m_ui.autosave, false); m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt());
@@ -512,7 +512,14 @@ <enum>Qt::Horizontal</enum>
</property> </widget> </item> - <item row="11" column="1"> + <item row="13" column="0" colspan="2"> + <widget class="Line" name="line_16"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="14" column="1"> <widget class="QCheckBox" name="cheatAutosave"> <property name="text"> <string>Automatically save cheats</string>@@ -522,10 +529,30 @@ <bool>true</bool>
</property> </widget> </item> - <item row="12" column="1"> + <item row="15" column="1"> <widget class="QCheckBox" name="cheatAutoload"> <property name="text"> <string>Automatically load cheats</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="11" column="1"> + <widget class="QCheckBox" name="autosave"> + <property name="text"> + <string>Automatically save state</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="12" column="1"> + <widget class="QCheckBox" name="autoload"> + <property name="text"> + <string>Automatically load state</string> </property> <property name="checked"> <bool>true</bool>
@@ -1858,8 +1858,8 @@ m_controller->loadPatch(m_pendingPatch);
m_pendingPatch = QString(); } - m_controller->start(); m_controller->loadConfig(m_config); + m_controller->start(); } WindowBackground::WindowBackground(QWidget* parent)