From 9873da0eb7d1b167de686a37d9e5d7902ef8f98f Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 27 Jul 2014 19:06:30 -0700 Subject: [PATCH] Ability to chunk movie streams into multiple files --- src/gba/gba-rr.c | 66 +++++++++++++++++++++++++++++++++++++---- src/gba/gba-rr.h | 4 +++ src/gba/gba-serialize.c | 8 +++++ src/gba/gba-serialize.h | 10 +++++-- 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 1a037c9cf..afc785a6f 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -3,8 +3,6 @@ #include "gba.h" #include "util/vfs.h" -#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); @@ -32,15 +30,55 @@ bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) { } rr->streamDir = stream; rr->movieStream = 0; + rr->streamId = 1; return true; } +bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) { + if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) { + return false; + } + rr->movieStream = 0; + rr->streamId = streamId; + char buffer[14]; + snprintf(buffer, sizeof(buffer), "%u.log", streamId); + if (GBARRIsRecording(rr)) { + rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_TRUNC | O_CREAT | O_WRONLY); + } else if (GBARRIsPlaying(rr)) { + rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY); + rr->peekedTag = TAG_INVALID; + if (!rr->movieStream || !_seekTag(rr, rr->movieStream, TAG_BEGIN)) { + GBARRStopPlaying(rr); + } + } + return true; +} + +uint32_t GBARRIncrementStream(struct GBARRContext* rr) { + uint32_t newStreamId = rr->streamId + 1; + uint32_t oldStreamId = rr->streamId; + if (GBARRIsRecording(rr) && rr->movieStream) { + _emitTag(rr, rr->movieStream, TAG_END); + _emitTag(rr, rr->movieStream, TAG_NEXT_TIME); + rr->movieStream->write(rr->movieStream, &newStreamId, sizeof(newStreamId)); + } + if (!GBARRLoadStream(rr, newStreamId)) { + return 0; + } + _emitTag(rr, rr->movieStream, TAG_PREVIOUSLY); + rr->movieStream->write(rr->movieStream, &oldStreamId, sizeof(oldStreamId)); + _emitTag(rr, rr->movieStream, TAG_BEGIN); + return rr->streamId; +} + bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) { if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) { return false; } - rr->movieStream = rr->streamDir->openFile(rr->streamDir, FILE_INPUTS, O_RDONLY); + char buffer[14]; + snprintf(buffer, sizeof(buffer), "%u.log", rr->streamId); + rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY); rr->autorecord = autorecord; rr->peekedTag = TAG_INVALID; _readTag(rr, rr->movieStream); // Discard the buffer @@ -71,7 +109,9 @@ bool GBARRStartRecording(struct GBARRContext* rr) { return false; } - rr->movieStream = rr->streamDir->openFile(rr->streamDir, FILE_INPUTS, O_TRUNC | O_CREAT | O_WRONLY); + char buffer[14]; + snprintf(buffer, sizeof(buffer), "%u.log", rr->streamId); + rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_TRUNC | O_CREAT | O_WRONLY); if (!_emitTag(rr, rr->movieStream, TAG_BEGIN)) { rr->movieStream->close(rr->movieStream); rr->movieStream = 0; @@ -156,10 +196,14 @@ enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { case TAG_INPUT: vf->read(vf, &rr->currentInput, sizeof(uint16_t)); break; + case TAG_NEXT_TIME: + vf->read(vf, &rr->nextTime, sizeof(rr->nextTime)); + break; case TAG_FRAME: case TAG_LAG: case TAG_BEGIN: case TAG_END: + case TAG_PREVIOUSLY: case TAG_INVALID: break; } @@ -176,7 +220,19 @@ 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; + if (rr->peekedTag == TAG_NEXT_TIME) { + while (_readTag(rr, vf) != TAG_END) { + if (!rr->nextTime) { + return false; + } + } + if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) { + return false; + } + vf = rr->movieStream; + } else { + return false; + } } } return true; diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index ad9817d9a..2c2339f75 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -30,18 +30,22 @@ struct GBARRContext { // Metadata uint32_t frames; uint32_t lagFrames; + uint32_t streamId; // Streaming state struct VDir* streamDir; struct VFile* movieStream; uint16_t currentInput; enum GBARRTag peekedTag; + uint32_t nextTime; }; void GBARRContextCreate(struct GBA*); void GBARRContextDestroy(struct GBA*); bool GBARRSetStream(struct GBARRContext*, struct VDir*); +bool GBARRLoadStream(struct GBARRContext*, uint32_t streamId); +uint32_t GBARRIncrementStream(struct GBARRContext*); bool GBARRStartPlaying(struct GBARRContext*, bool autorecord); void GBARRStopPlaying(struct GBARRContext*); diff --git a/src/gba/gba-serialize.c b/src/gba/gba-serialize.c index a27136a9b..11a979c07 100644 --- a/src/gba/gba-serialize.c +++ b/src/gba/gba-serialize.c @@ -2,6 +2,7 @@ #include "gba-audio.h" #include "gba-io.h" +#include "gba-rr.h" #include "gba-thread.h" #include "gba-video.h" @@ -35,6 +36,13 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { GBAIOSerialize(gba, state); GBAVideoSerialize(&gba->video, state); GBAAudioSerialize(&gba->audio, state); + + if (GBARRIsRecording(gba->rr)) { + state->associatedStreamId = gba->rr->streamId; + GBARRIncrementStream(gba->rr); + } else { + state->associatedStreamId = 0; + } } void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) { diff --git a/src/gba/gba-serialize.h b/src/gba/gba-serialize.h index 6dd01869c..af73c45f9 100644 --- a/src/gba/gba-serialize.h +++ b/src/gba/gba-serialize.h @@ -139,7 +139,9 @@ const uint32_t GBA_SAVESTATE_MAGIC; * | bit 0: Is read enabled * | bit 1: Gyroscope sample is edge * | bits 2 - 15: Reserved - * 0x002C0 - 0x003FF: Reserved (leave zero) + * 0x002C0 - 0x002FF: Reserved (leave zero) + * 0x00300 - 0x00303: Associated movie stream ID for record/replay (or 0 if no stream) + * 0x00304 - 0x003FF: Reserved (leave zero) * 0x00400 - 0x007FF: I/O memory * 0x00800 - 0x00BFF: Palette * 0x00C00 - 0x00FFF: OAM @@ -248,7 +250,11 @@ struct GBASerializedState { unsigned reserved : 14; } gpio; - uint32_t reserved[80]; + uint32_t reservedGpio[16]; + + uint32_t associatedStreamId; + + uint32_t reserved[63]; uint16_t io[SIZE_IO >> 1]; uint16_t pram[SIZE_PALETTE_RAM >> 1];