all repos — mgba @ 45b8ffb9cfae7dc1e44756cdd0192fb8978bdf3d

mGBA Game Boy Advance Emulator

New movie format
Jeffrey Pfau jeffrey@endrift.com
Sun, 27 Jul 2014 18:22:39 -0700
commit

45b8ffb9cfae7dc1e44756cdd0192fb8978bdf3d

parent

f39d7e364057a2eb014fc0c63e5eb285344d2a97

2 files changed, 109 insertions(+), 17 deletions(-)

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

@@ -5,6 +5,10 @@ #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); + void GBARRContextCreate(struct GBA* gba) { if (gba->rr) { return;

@@ -23,12 +27,12 @@ gba->rr = 0;

} 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 @@ if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) {

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 @@ if (GBARRIsRecording(rr) || GBARRIsPlaying(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 @@ return rr && rr->isRecording;

} void GBARRNextFrame(struct GBARRContext* rr) { - if (!GBARRIsRecording(rr)) { + if (!GBARRIsRecording(rr) && !GBARRIsPlaying(rr)) { return; }

@@ -82,7 +112,18 @@ if (!rr->inputThisFrame) {

++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 @@ if (!GBARRIsRecording(rr)) {

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 @@ if (!GBARRIsPlaying(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 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 keys; + return true; +} + +bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag) { + UNUSED(rr); + return vf->write(vf, &tag, 1) == 1; }
M src/gba/gba-rr.hsrc/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 @@ uint32_t lagFrames;

// Streaming state struct VDir* streamDir; - struct VFile* inputsStream; + struct VFile* movieStream; + uint16_t currentInput; + enum GBARRTag peekedTag; }; void GBARRContextCreate(struct GBA*);