Feature: Added loading savestates from command line (fixes #1125)

This commit is contained in:
Vicki Pfau 2018-07-14 14:18:16 -07:00
parent 182efc916e
commit f8fb86ef79
9 changed files with 83 additions and 16 deletions

View File

@ -69,6 +69,7 @@ Misc:
- 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:

View File

@ -18,7 +18,7 @@ struct mArguments {
char* fname;
char* patch;
char* cheatsFile;
char* movie;
char* savestate;
char* bios;
int logLevel;
int frameskip;

View File

@ -39,7 +39,7 @@ static const struct option _options[] = {
#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 @@ static void _tableApply(const char* key, void* value, void* user) {
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 @@ bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct
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 @@ void freeArguments(struct mArguments* args) {
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 @@ void usage(const char* arg0, const char* extraOptions) {
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");

View File

@ -465,6 +465,26 @@ void CoreController::loadState(int slot) {
});
}
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;
@ -481,6 +501,25 @@ void CoreController::saveState(int slot) {
});
}
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);
});
}
void CoreController::loadBackupState() {
if (!m_backupLoadState.isOpen()) {
return;

View File

@ -103,7 +103,9 @@ public slots:
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 @@ private:
VFileDevice m_backupLoadState;
QByteArray m_backupSaveState{nullptr};
int m_stateSlot = 1;
QString m_statePath;
int m_loadStateFlags;
int m_saveStateFlags;

View File

@ -170,6 +170,14 @@ Window::~Window() {
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 @@ void Window::setController(CoreController* controller, const QString& fname) {
m_controller->loadConfig(m_config);
m_controller->start();
if (!m_pendingState.isEmpty()) {
m_controller->loadState(m_pendingState);
m_pendingState = QString();
}
}
WindowBackground::WindowBackground(QWidget* parent)

View File

@ -206,6 +206,7 @@ private:
bool m_autoresume = false;
bool m_wasOpened = false;
QString m_pendingPatch;
QString m_pendingState;
bool m_hitUnimplementedBiosCall;

View File

@ -25,6 +25,7 @@
#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>
@ -43,6 +44,12 @@ 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 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
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)) {

View File

@ -26,14 +26,12 @@
" -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 @@ int main(int argc, char** argv) {
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 @@ static bool _parseFuzzOpts(struct mSubParser* parser, int option, const char* ar
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;