mirror of https://github.com/mgba-emu/mgba.git
Feature: Added loading savestates from command line (fixes #1125)
This commit is contained in:
parent
182efc916e
commit
f8fb86ef79
1
CHANGES
1
CHANGES
|
@ -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:
|
||||
|
|
|
@ -18,7 +18,7 @@ struct mArguments {
|
|||
char* fname;
|
||||
char* patch;
|
||||
char* cheatsFile;
|
||||
char* movie;
|
||||
char* savestate;
|
||||
char* bios;
|
||||
int logLevel;
|
||||
int frameskip;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -206,6 +206,7 @@ private:
|
|||
bool m_autoresume = false;
|
||||
bool m_wasOpened = false;
|
||||
QString m_pendingPatch;
|
||||
QString m_pendingState;
|
||||
|
||||
bool m_hitUnimplementedBiosCall;
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue