all repos — mgba @ f8fb86ef7986d6935234589972a792349c3c1d71

mGBA Game Boy Advance Emulator

Feature: Added loading savestates from command line (fixes #1125)
Vicki Pfau vi@endrift.com
Sat, 14 Jul 2018 14:18:16 -0700
commit

f8fb86ef7986d6935234589972a792349c3c1d71

parent

182efc916e0a21522f2277742fedbc23fb4c8aac

M CHANGESCHANGES

@@ -69,6 +69,7 @@ - GB: Fix VRAM/palette locking (fixes mgba.io/i/1109)

- GB Video: Darken colors in GBA mode - FFmpeg: Support libswresample (fixes mgba.io/i/1120, mgba.io/b/123) - FFmpeg: Support lossless h.264 encoding + - Feature: Added loading savestates from command line 0.6.3: (2017-04-14) Bugfixes:
M include/mgba/feature/commandline.hinclude/mgba/feature/commandline.h

@@ -18,7 +18,7 @@ struct mArguments {

char* fname; char* patch; char* cheatsFile; - char* movie; + char* savestate; char* bios; int logLevel; int frameskip;
M src/feature/commandline.csrc/feature/commandline.c

@@ -39,7 +39,7 @@ { "gdb", no_argument, 0, 'g' },

#endif { "help", no_argument, 0, 'h' }, { "log-level", required_argument, 0, 'l' }, - { "movie", required_argument, 0, 'v' }, + { "savestate", required_argument, 0, 't' }, { "patch", required_argument, 0, 'p' }, { "version", no_argument, 0, '\0' }, { 0, 0, 0, 0 }

@@ -68,7 +68,7 @@

bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct mSubParser* subparser) { int ch; char options[64] = - "b:c:C:hl:p:s:v:" + "b:c:C:hl:p:s:t:" #ifdef USE_EDITLINE "d" #endif

@@ -132,8 +132,8 @@ break;

case 's': args->frameskip = atoi(optarg); break; - case 'v': - args->movie = strdup(optarg); + case 't': + args->savestate = strdup(optarg); break; default: if (subparser) {

@@ -179,8 +179,8 @@

free(args->patch); args->patch = 0; - free(args->movie); - args->movie = 0; + free(args->savestate); + args->savestate = 0; free(args->cheatsFile); args->cheatsFile = 0;

@@ -244,7 +244,7 @@ #ifdef USE_GDB_STUB

puts(" -g, --gdb Start GDB session (default port 2345)"); #endif puts(" -l, --log-level N Log level mask"); - puts(" -v, --movie FILE Play back a movie of recorded input"); + puts(" -t, --savestate FILE Load savestate when starting"); puts(" -p, --patch FILE Apply a specified patch file when running"); puts(" -s, --frameskip N Skip every N frames"); puts(" --version Print version and exit");
M src/platform/qt/CoreController.cppsrc/platform/qt/CoreController.cpp

@@ -465,6 +465,26 @@ }

}); } +void CoreController::loadState(const QString& path) { + m_statePath = path; + mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) { + CoreController* controller = static_cast<CoreController*>(context->userData); + VFile* vf = VFileDevice::open(controller->m_statePath, O_RDONLY); + if (!vf) { + return; + } + if (!controller->m_backupLoadState.isOpen()) { + controller->m_backupLoadState = VFileMemChunk(nullptr, 0); + } + mCoreSaveStateNamed(context->core, controller->m_backupLoadState, controller->m_saveStateFlags); + if (mCoreLoadStateNamed(context->core, vf, controller->m_loadStateFlags)) { + emit controller->frameAvailable(); + emit controller->stateLoaded(); + } + vf->close(vf); + }); +} + void CoreController::saveState(int slot) { if (slot > 0) { m_stateSlot = slot;

@@ -478,6 +498,25 @@ vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size());

vf->close(vf); } mCoreSaveState(context->core, controller->m_stateSlot, controller->m_saveStateFlags); + }); +} + +void CoreController::saveState(const QString& path) { + m_statePath = path; + mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) { + CoreController* controller = static_cast<CoreController*>(context->userData); + VFile* vf = VFileDevice::open(controller->m_statePath, O_RDONLY); + if (vf) { + controller->m_backupSaveState.resize(vf->size(vf)); + vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size()); + vf->close(vf); + } + vf = VFileDevice::open(controller->m_statePath, O_WRONLY | O_CREAT | O_TRUNC); + if (!vf) { + return; + } + mCoreSaveStateNamed(context->core, vf, controller->m_saveStateFlags); + vf->close(vf); }); }
M src/platform/qt/CoreController.hsrc/platform/qt/CoreController.h

@@ -103,7 +103,9 @@ void setFastForward(bool);

void forceFastForward(bool); void loadState(int slot = 0); + void loadState(const QString& path); void saveState(int slot = 0); + void saveState(const QString& path); void loadBackupState(); void saveBackupState();

@@ -189,6 +191,7 @@

VFileDevice m_backupLoadState; QByteArray m_backupSaveState{nullptr}; int m_stateSlot = 1; + QString m_statePath; int m_loadStateFlags; int m_saveStateFlags;
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -170,6 +170,14 @@

void Window::argumentsPassed(mArguments* args) { loadConfig(); + if (args->patch) { + m_pendingPatch = args->patch; + } + + if (args->savestate) { + m_pendingState = args->savestate; + } + if (args->fname) { setController(m_manager->loadGame(args->fname), args->fname); }

@@ -1911,6 +1919,11 @@ }

m_controller->loadConfig(m_config); m_controller->start(); + + if (!m_pendingState.isEmpty()) { + m_controller->loadState(m_pendingState); + m_pendingState = QString(); + } } WindowBackground::WindowBackground(QWidget* parent)
M src/platform/qt/Window.hsrc/platform/qt/Window.h

@@ -206,6 +206,7 @@ QTimer m_focusCheck;

bool m_autoresume = false; bool m_wasOpened = false; QString m_pendingPatch; + QString m_pendingState; bool m_hitUnimplementedBiosCall;
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -25,6 +25,7 @@ #include <mgba/core/cheats.h>

#include <mgba/core/core.h> #include <mgba/core/config.h> #include <mgba/core/input.h> +#include <mgba/core/serialize.h> #include <mgba/core/thread.h> #include <mgba/internal/gba/input.h>

@@ -42,6 +43,12 @@ static bool mSDLInit(struct mSDLRenderer* renderer);

static void mSDLDeinit(struct mSDLRenderer* renderer); static int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args); + +static struct VFile* _state = NULL; + +static void _loadState(struct mCoreThread* thread) { + mCoreLoadStateNamed(thread->core, _state, SAVESTATE_RTC); +} int main(int argc, char** argv) { struct mSDLRenderer renderer = {0};

@@ -232,6 +239,15 @@ mSDLSetScreensaverSuspendable(&renderer->events, renderer->core->opts.suspendScreensaver);

mSDLSuspendScreensaver(&renderer->events); #endif if (mSDLInitAudio(&renderer->audio, &thread)) { + if (args->savestate) { + struct VFile* state = VFileOpen(args->savestate, O_RDONLY); + if (state) { + _state = state; + mCoreThreadRunFunction(&thread, _loadState); + _state = NULL; + state->close(state); + } + } renderer->runloop(renderer, &thread); mSDLPauseAudio(&renderer->audio); if (mCoreThreadHasCrashed(&thread)) {
M src/platform/test/fuzz-main.csrc/platform/test/fuzz-main.c

@@ -26,14 +26,12 @@ "\nAdditional options:\n" \

" -F FRAMES Run for the specified number of FRAMES before exiting\n" \ " -N Disable video rendering entirely\n" \ " -O OFFSET Offset to apply savestate overlay\n" \ - " -S FILE Load a savestate when starting the test\n" \ " -V FILE Overlay a second savestate over the loaded savestate\n" \ struct FuzzOpts { bool noVideo; int frames; size_t overlayOffset; - char* savestate; char* ssOverlay; };

@@ -108,9 +106,8 @@ struct VFile* savestate = 0;

struct VFile* savestateOverlay = 0; size_t overlayOffset; - if (fuzzOpts.savestate) { - savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY); - free(fuzzOpts.savestate); + if (args.savestate) { + savestate = VFileOpen(args.savestate, O_RDONLY); } if (fuzzOpts.ssOverlay) { overlayOffset = fuzzOpts.overlayOffset;

@@ -200,9 +197,6 @@ return true;

case 'O': opts->overlayOffset = strtoul(arg, 0, 10); return !errno; - case 'S': - opts->savestate = strdup(arg); - return true; case 'V': opts->ssOverlay = strdup(arg); return true;