diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index bc52423d6..be3c8c80a 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -1,6 +1,7 @@ #include "gba-rr.h" #include "gba.h" +#include "gba-serialize.h" #include "util/vfs.h" #define BINARY_EXT ".dat" @@ -14,6 +15,9 @@ static bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag ta static bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag); static bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf); +static struct VFile* _openSavedata(struct GBARRContext* rr, int flags); +static struct VFile* _openSavestate(struct GBARRContext* rr, int flags); + void GBARRContextCreate(struct GBA* gba) { if (gba->rr) { return; @@ -36,21 +40,63 @@ void GBARRContextDestroy(struct GBA* gba) { if (gba->rr->metadataFile) { gba->rr->metadataFile->close(gba->rr->metadataFile); } + if (gba->rr->savedata) { + gba->rr->savedata->close(gba->rr->savedata); + } free(gba->rr); gba->rr = 0; } -void GBARRAlterSavedata(struct GBA* gba) { +void GBARRSaveState(struct GBA* gba) { if (!gba || !gba->rr) { return; } if (gba->rr->initFrom & INIT_FROM_SAVEGAME) { - // TOOD + if (gba->rr->savedata) { + gba->rr->savedata->close(gba->rr->savedata); + } + gba->rr->savedata = _openSavedata(gba->rr, O_TRUNC | O_CREAT | O_WRONLY); + GBASavedataClone(&gba->memory.savedata, gba->rr->savedata); + gba->rr->savedata->close(gba->rr->savedata); + gba->rr->savedata = _openSavedata(gba->rr, O_RDONLY); + GBASavedataMask(&gba->memory.savedata, gba->rr->savedata); } else { GBASavedataMask(&gba->memory.savedata, 0); } + + if (gba->rr->initFrom & INIT_FROM_SAVESTATE) { + struct VFile* vf = _openSavestate(gba->rr, O_TRUNC | O_CREAT | O_RDWR); + GBASaveStateNamed(gba, vf, false); + vf->close(vf); + } else { + ARMReset(gba->cpu); + } +} + +void GBARRLoadState(struct GBA* gba) { + if (!gba || !gba->rr) { + return; + } + + if (gba->rr->initFrom & INIT_FROM_SAVEGAME) { + if (gba->rr->savedata) { + gba->rr->savedata->close(gba->rr->savedata); + } + gba->rr->savedata = _openSavedata(gba->rr, O_RDONLY); + GBASavedataMask(&gba->memory.savedata, gba->rr->savedata); + } else { + GBASavedataMask(&gba->memory.savedata, 0); + } + + if (gba->rr->initFrom & INIT_FROM_SAVESTATE) { + struct VFile* vf = _openSavestate(gba->rr, O_RDONLY); + GBALoadStateNamed(gba, vf); + vf->close(vf); + } else { + ARMReset(gba->cpu); + } } bool GBARRInitStream(struct GBARRContext* rr, struct VDir* stream) { @@ -427,3 +473,11 @@ bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf) { rr->maxStreamIdOffset = vf->seek(vf, 0, SEEK_SET); return true; } + +struct VFile* _openSavedata(struct GBARRContext* rr, int flags) { + return rr->streamDir->openFile(rr->streamDir, "movie.sav", flags); +} + +struct VFile* _openSavestate(struct GBARRContext* rr, int flags) { + return rr->streamDir->openFile(rr->streamDir, "movie.ssm", flags); +} diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index c8533c55d..f0c40550f 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -65,6 +65,8 @@ struct GBARRContext { enum GBARRInitFrom initFrom; off_t initFromOffset; + struct VFile* savedata; + // Streaming state struct VDir* streamDir; struct VFile* metadataFile; @@ -77,7 +79,8 @@ struct GBARRContext { void GBARRContextCreate(struct GBA*); void GBARRContextDestroy(struct GBA*); -void GBARRAlterSavedata(struct GBA*); +void GBARRSaveState(struct GBA*); +void GBARRLoadState(struct GBA*); bool GBARRInitStream(struct GBARRContext*, struct VDir*); bool GBARRReinitStream(struct GBARRContext*, enum GBARRInitFrom); diff --git a/src/gba/gba-savedata.c b/src/gba/gba-savedata.c index 01a49de06..02fadf7a8 100644 --- a/src/gba/gba-savedata.c +++ b/src/gba/gba-savedata.c @@ -72,6 +72,32 @@ void GBASavedataUnmask(struct GBASavedata* savedata) { savedata->vf = savedata->realVf; } +bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) { + if (savedata->data) { + switch (savedata->type) { + case SAVEDATA_SRAM: + return out->write(out, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM; + case SAVEDATA_FLASH512: + return out->write(out, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512; + case SAVEDATA_FLASH1M: + return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M; + case SAVEDATA_EEPROM: + return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM; + case SAVEDATA_NONE: + return true; + } + } else if (savedata->vf) { + off_t read = 0; + uint8_t buffer[2048]; + do { + read = savedata->vf->read(savedata->vf, buffer, sizeof(buffer)); + out->write(out, buffer, read); + } while (read == sizeof(buffer)); + return read >= 0; + } + return true; +} + void GBASavedataInitFlash(struct GBASavedata* savedata) { if (savedata->type == SAVEDATA_NONE) { savedata->type = SAVEDATA_FLASH512; diff --git a/src/gba/gba-savedata.h b/src/gba/gba-savedata.h index e3acec6da..5cb5c517b 100644 --- a/src/gba/gba-savedata.h +++ b/src/gba/gba-savedata.h @@ -75,6 +75,7 @@ void GBASavedataDeinit(struct GBASavedata* savedata); void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf); void GBASavedataUnmask(struct GBASavedata* savedata); +bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out); void GBASavedataInitFlash(struct GBASavedata* savedata); void GBASavedataInitEEPROM(struct GBASavedata* savedata); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 130effdf3..47c4a95a6 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -123,31 +123,26 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents break; case SDLK_t: if (context->stateDir) { - GBAThreadReset(context); GBAThreadInterrupt(context); GBARRContextCreate(context->gba); if (!GBARRIsRecording(context->gba->rr)) { + GBARRStopPlaying(context->gba->rr); GBARRInitStream(context->gba->rr, context->stateDir); GBARRReinitStream(context->gba->rr, INIT_EX_NIHILO); - GBARRStopPlaying(context->gba->rr); GBARRStartRecording(context->gba->rr); - GBARRAlterSavedata(context->gba); + GBARRSaveState(context->gba); } GBAThreadContinue(context); } break; case SDLK_y: if (context->stateDir) { - GBAThreadReset(context); GBAThreadInterrupt(context); GBARRContextCreate(context->gba); - GBARRInitStream(context->gba->rr, context->stateDir); GBARRStopRecording(context->gba->rr); + GBARRInitStream(context->gba->rr, context->stateDir); GBARRStartPlaying(context->gba->rr, false); - if (context->gba->rr->initFrom & INIT_FROM_SAVESTATE) { - // TODO - } - GBARRAlterSavedata(context->gba); + GBARRLoadState(context->gba); GBAThreadContinue(context); } break;