all repos — mgba @ 5ca6888840e010552b9f38501fc5594f8ccdbd73

mGBA Game Boy Advance Emulator

Savestate loading during recording and replaying
Jeffrey Pfau jeffrey@endrift.com
Wed, 30 Jul 2014 00:13:11 -0700
commit

5ca6888840e010552b9f38501fc5594f8ccdbd73

parent

28218b24655fa2aeaaecf87525d349f87924ad66

3 files changed, 73 insertions(+), 7 deletions(-)

jump to
M src/gba/gba-rr.csrc/gba/gba-rr.c

@@ -3,11 +3,13 @@

#include "gba.h" #include "util/vfs.h" -#define BINEXT ".log" +#define BINEXT ".dat" +#define METADATA_FILENAME "metadata" BINEXT static enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf); static bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag); static bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag); +static bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf); void GBARRContextCreate(struct GBA* gba) { if (gba->rr) {

@@ -28,6 +30,9 @@ }

if (GBARRIsRecording(gba->rr)) { GBARRStopRecording(gba->rr); } + if (gba->rr->metadataFile) { + gba->rr->metadataFile->close(gba->rr->metadataFile); + } free(gba->rr); gba->rr = 0;

@@ -37,7 +42,19 @@ bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) {

if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) { return false; } + + if (rr->metadataFile && !rr->metadataFile->close(rr->metadataFile)) { + return false; + } + rr->streamDir = stream; + rr->metadataFile = rr->streamDir->openFile(rr->streamDir, METADATA_FILENAME, O_CREAT | O_RDWR); + if (!_parseMetadata(rr, rr->metadataFile)) { + rr->metadataFile->close(rr->metadataFile); + rr->streamDir = 0; + rr->metadataFile = 0; + return false; + } rr->movieStream = 0; rr->streamId = 1; rr->maxStreamId = 1;

@@ -53,7 +70,13 @@ rr->streamId = streamId;

char buffer[14]; snprintf(buffer, sizeof(buffer), "%u" BINEXT, streamId); if (GBARRIsRecording(rr)) { - rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_TRUNC | O_CREAT | O_WRONLY); + int flags = O_CREAT | O_WRONLY; + if (streamId > rr->maxStreamId) { + flags |= O_TRUNC; + } else { + flags |= O_APPEND; + } + rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, flags); } else if (GBARRIsPlaying(rr)) { rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY); rr->peekedTag = TAG_INVALID;

@@ -85,6 +108,9 @@ rr->maxStreamId = newStreamId;

_emitTag(rr, rr->movieStream, TAG_PREVIOUSLY); rr->movieStream->write(rr->movieStream, &oldStreamId, sizeof(oldStreamId)); _emitTag(rr, rr->movieStream, TAG_BEGIN); + + rr->metadataFile->seek(rr->metadataFile, rr->maxStreamIdOffset, SEEK_SET); + rr->metadataFile->write(rr->movieStream, &rr->maxStreamId, sizeof(rr->maxStreamId)); return true; }

@@ -139,6 +165,12 @@ rr->movieStream = 0;

return false; } + if (!rr->maxStreamIdOffset) { + _emitTag(rr, rr->metadataFile, TAG_MAX_STREAM); + rr->maxStreamIdOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR); + rr->metadataFile->write(rr->metadataFile, &rr->maxStreamId, sizeof(rr->maxStreamId)); + } + rr->isRecording = true; return true; }

@@ -182,7 +214,13 @@

rr->inputThisFrame = false; } else { if (!_seekTag(rr, rr->movieStream, TAG_FRAME)) { + uint32_t endStreamId = rr->streamId; GBARRStopPlaying(rr); + if (rr->autorecord) { + rr->isRecording = true; + GBARRLoadStream(rr, endStreamId); + GBARRIncrementStream(rr); + } } } }

@@ -211,6 +249,15 @@ }

return rr->currentInput; } +bool GBARRSkipSegment(struct GBARRContext* rr) { + rr->nextTime = 0; + while (_readTag(rr, rr->movieStream) != TAG_EOF); + if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) { + return false; + } + return true; +} + enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { if (!rr || !vf) { return TAG_EOF;

@@ -266,10 +313,8 @@

bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) { enum GBARRTag readTag; while ((readTag = _readTag(rr, vf)) != tag) { - if (readTag == TAG_END) { - rr->nextTime = 0; - while (_readTag(rr, vf) != TAG_EOF); - if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) { + if (vf == rr->movieStream && readTag == TAG_END) { + if (!GBARRSkipSegment(rr)) { return false; } vf = rr->movieStream;

@@ -282,5 +327,15 @@ }

bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag) { UNUSED(rr); - return vf->write(vf, &tag, 1) == 1; + return vf->write(vf, &tag, sizeof(tag)) == sizeof(tag); +} + +bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf) { + while (_readTag(rr, vf) != TAG_EOF) { + if (rr->peekedTag == TAG_MAX_STREAM) { + rr->maxStreamIdOffset = vf->seek(vf, 0, SEEK_CUR); + } + } + rr->maxStreamIdOffset = vf->seek(vf, 0, SEEK_SET); + return true; }
M src/gba/gba-rr.hsrc/gba/gba-rr.h

@@ -48,9 +48,11 @@ uint32_t frames;

uint32_t lagFrames; uint32_t streamId; uint32_t maxStreamId; + off_t maxStreamIdOffset; // Streaming state struct VDir* streamDir; + struct VFile* metadataFile; struct VFile* movieStream; uint16_t currentInput; enum GBARRTag peekedTag;

@@ -64,6 +66,7 @@

bool GBARRSetStream(struct GBARRContext*, struct VDir*); bool GBARRLoadStream(struct GBARRContext*, uint32_t streamId); bool GBARRIncrementStream(struct GBARRContext*); +bool GBARRSkipSegment(struct GBARRContext*); bool GBARRStartPlaying(struct GBARRContext*, bool autorecord); void GBARRStopPlaying(struct GBARRContext*);
M src/gba/gba-serialize.csrc/gba/gba-serialize.c

@@ -78,6 +78,14 @@ GBAMemoryDeserialize(&gba->memory, state);

GBAIODeserialize(gba, state); GBAVideoDeserialize(&gba->video, state); GBAAudioDeserialize(&gba->audio, state); + + if (GBARRIsRecording(gba->rr)) { + GBARRLoadStream(gba->rr, state->associatedStreamId); + GBARRIncrementStream(gba->rr); + } else if (GBARRIsPlaying(gba->rr)) { + GBARRLoadStream(gba->rr, state->associatedStreamId); + GBARRSkipSegment(gba->rr); + } } static struct VFile* _getStateVf(struct GBA* gba, struct VDir* dir, int slot, bool write) {