diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 91450a374..1a037c9cf 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -5,6 +5,10 @@ #define FILE_INPUTS "input.log" +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); + void GBARRContextCreate(struct GBA* gba) { if (gba->rr) { return; @@ -23,12 +27,12 @@ void GBARRContextDestroy(struct GBA* gba) { } bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) { - if (rr->inputsStream && !rr->inputsStream->close(rr->inputsStream)) { + if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) { return false; } rr->streamDir = stream; - rr->inputsStream = stream->openFile(stream, FILE_INPUTS, O_CREAT | O_RDWR); - return !!rr->inputsStream; + rr->movieStream = 0; + return true; } bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) { @@ -36,19 +40,30 @@ bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) { return false; } + rr->movieStream = rr->streamDir->openFile(rr->streamDir, FILE_INPUTS, O_RDONLY); rr->autorecord = autorecord; - if (rr->inputsStream->seek(rr->inputsStream, 0, SEEK_SET) < 0) { - return false; - } - if (rr->inputsStream->read(rr->inputsStream, &rr->nextInput, sizeof(rr->nextInput)) != sizeof(rr->nextInput)) { + rr->peekedTag = TAG_INVALID; + _readTag(rr, rr->movieStream); // Discard the buffer + enum GBARRTag tag = _readTag(rr, rr->movieStream); + if (tag != TAG_BEGIN) { + rr->movieStream->close(rr->movieStream); + rr->movieStream = 0; return false; } + rr->isPlaying = true; return true; } void GBARRStopPlaying(struct GBARRContext* rr) { + if (!GBARRIsPlaying(rr)) { + return; + } rr->isPlaying = false; + if (rr->movieStream) { + rr->movieStream->close(rr->movieStream); + rr->movieStream = 0; + } } bool GBARRStartRecording(struct GBARRContext* rr) { @@ -56,12 +71,27 @@ bool GBARRStartRecording(struct GBARRContext* rr) { return false; } + rr->movieStream = rr->streamDir->openFile(rr->streamDir, FILE_INPUTS, O_TRUNC | O_CREAT | O_WRONLY); + if (!_emitTag(rr, rr->movieStream, TAG_BEGIN)) { + rr->movieStream->close(rr->movieStream); + rr->movieStream = 0; + return false; + } + rr->isRecording = true; return true; } void GBARRStopRecording(struct GBARRContext* rr) { + if (!GBARRIsRecording(rr)) { + return; + } rr->isRecording = false; + if (rr->movieStream) { + _emitTag(rr, rr->movieStream, TAG_END); + rr->movieStream->close(rr->movieStream); + rr->movieStream = 0; + } } bool GBARRIsPlaying(struct GBARRContext* rr) { @@ -73,7 +103,7 @@ bool GBARRIsRecording(struct GBARRContext* rr) { } void GBARRNextFrame(struct GBARRContext* rr) { - if (!GBARRIsRecording(rr)) { + if (!GBARRIsRecording(rr) && !GBARRIsPlaying(rr)) { return; } @@ -82,7 +112,18 @@ void GBARRNextFrame(struct GBARRContext* rr) { ++rr->lagFrames; } - rr->inputThisFrame = false; + if (GBARRIsRecording(rr)) { + if (!rr->inputThisFrame) { + _emitTag(rr, rr->movieStream, TAG_LAG); + } + _emitTag(rr, rr->movieStream, TAG_FRAME); + + rr->inputThisFrame = false; + } else { + if (!_seekTag(rr, rr->movieStream, TAG_FRAME)) { + GBARRStopPlaying(rr); + } + } } void GBARRLogInput(struct GBARRContext* rr, uint16_t keys) { @@ -90,7 +131,11 @@ void GBARRLogInput(struct GBARRContext* rr, uint16_t keys) { return; } - rr->inputsStream->write(rr->inputsStream, &keys, sizeof(keys)); + if (keys != rr->currentInput) { + _emitTag(rr, rr->movieStream, TAG_INPUT); + rr->movieStream->write(rr->movieStream, &keys, sizeof(keys)); + rr->currentInput = keys; + } rr->inputThisFrame = true; } @@ -99,10 +144,45 @@ uint16_t GBARRQueryInput(struct GBARRContext* rr) { return 0; } - uint16_t keys = rr->nextInput; - rr->isPlaying = rr->inputsStream->read(rr->inputsStream, &rr->nextInput, sizeof(rr->nextInput)) == sizeof(rr->nextInput); - if (!rr->isPlaying && rr->autorecord) { - rr->isRecording = true; + if (rr->peekedTag == TAG_INPUT) { + _readTag(rr, rr->movieStream); } - return keys; + return rr->currentInput; +} + +enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { + enum GBARRTag tag = rr->peekedTag; + switch (tag) { + case TAG_INPUT: + vf->read(vf, &rr->currentInput, sizeof(uint16_t)); + break; + case TAG_FRAME: + case TAG_LAG: + case TAG_BEGIN: + case TAG_END: + case TAG_INVALID: + break; + } + + uint8_t tagBuffer; + if (vf->read(vf, &tagBuffer, 1) != 1) { + tagBuffer = TAG_END; + } + rr->peekedTag = tagBuffer; + return tag; +} + +bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) { + enum GBARRTag readTag; + while ((readTag = _readTag(rr, vf)) != tag) { + if (readTag == TAG_END) { + return false; + } + } + return true; +} + +bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag) { + UNUSED(rr); + return vf->write(vf, &tag, 1) == 1; } diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index eba3300f6..ad9817d9a 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -7,11 +7,21 @@ struct GBA; struct VDir; struct VFile; +enum GBARRTag { + TAG_INVALID = 0x00, + TAG_INPUT = 0x01, + TAG_FRAME = 0x02, + TAG_LAG = 0x03, + TAG_BEGIN = 0x10, + TAG_END = 0x11, + TAG_PREVIOUSLY = 0x12, + TAG_NEXT_TIME = 0x13 +}; + struct GBARRContext { // Playback state bool isPlaying; bool autorecord; - uint16_t nextInput; // Recording state bool isRecording; @@ -23,7 +33,9 @@ struct GBARRContext { // Streaming state struct VDir* streamDir; - struct VFile* inputsStream; + struct VFile* movieStream; + uint16_t currentInput; + enum GBARRTag peekedTag; }; void GBARRContextCreate(struct GBA*);