Savestate loading during recording and replaying

This commit is contained in:
Jeffrey Pfau 2014-07-30 00:13:11 -07:00
parent 28218b2465
commit 5ca6888840
3 changed files with 73 additions and 7 deletions

View File

@ -3,11 +3,13 @@
#include "gba.h" #include "gba.h"
#include "util/vfs.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 enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf);
static bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag); 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 _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag);
static bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf);
void GBARRContextCreate(struct GBA* gba) { void GBARRContextCreate(struct GBA* gba) {
if (gba->rr) { if (gba->rr) {
@ -28,6 +30,9 @@ void GBARRContextDestroy(struct GBA* gba) {
if (GBARRIsRecording(gba->rr)) { if (GBARRIsRecording(gba->rr)) {
GBARRStopRecording(gba->rr); GBARRStopRecording(gba->rr);
} }
if (gba->rr->metadataFile) {
gba->rr->metadataFile->close(gba->rr->metadataFile);
}
free(gba->rr); free(gba->rr);
gba->rr = 0; gba->rr = 0;
@ -37,7 +42,19 @@ bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) {
if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) { if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) {
return false; return false;
} }
if (rr->metadataFile && !rr->metadataFile->close(rr->metadataFile)) {
return false;
}
rr->streamDir = stream; 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->movieStream = 0;
rr->streamId = 1; rr->streamId = 1;
rr->maxStreamId = 1; rr->maxStreamId = 1;
@ -53,7 +70,13 @@ bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) {
char buffer[14]; char buffer[14];
snprintf(buffer, sizeof(buffer), "%u" BINEXT, streamId); snprintf(buffer, sizeof(buffer), "%u" BINEXT, streamId);
if (GBARRIsRecording(rr)) { 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)) { } else if (GBARRIsPlaying(rr)) {
rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY); rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY);
rr->peekedTag = TAG_INVALID; rr->peekedTag = TAG_INVALID;
@ -85,6 +108,9 @@ bool GBARRIncrementStream(struct GBARRContext* rr) {
_emitTag(rr, rr->movieStream, TAG_PREVIOUSLY); _emitTag(rr, rr->movieStream, TAG_PREVIOUSLY);
rr->movieStream->write(rr->movieStream, &oldStreamId, sizeof(oldStreamId)); rr->movieStream->write(rr->movieStream, &oldStreamId, sizeof(oldStreamId));
_emitTag(rr, rr->movieStream, TAG_BEGIN); _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; return true;
} }
@ -139,6 +165,12 @@ bool GBARRStartRecording(struct GBARRContext* rr) {
return false; 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; rr->isRecording = true;
return true; return true;
} }
@ -182,7 +214,13 @@ void GBARRNextFrame(struct GBARRContext* rr) {
rr->inputThisFrame = false; rr->inputThisFrame = false;
} else { } else {
if (!_seekTag(rr, rr->movieStream, TAG_FRAME)) { if (!_seekTag(rr, rr->movieStream, TAG_FRAME)) {
uint32_t endStreamId = rr->streamId;
GBARRStopPlaying(rr); GBARRStopPlaying(rr);
if (rr->autorecord) {
rr->isRecording = true;
GBARRLoadStream(rr, endStreamId);
GBARRIncrementStream(rr);
}
} }
} }
} }
@ -211,6 +249,15 @@ uint16_t GBARRQueryInput(struct GBARRContext* rr) {
return rr->currentInput; 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) { enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) {
if (!rr || !vf) { if (!rr || !vf) {
return TAG_EOF; return TAG_EOF;
@ -266,10 +313,8 @@ enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) {
bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) { bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) {
enum GBARRTag readTag; enum GBARRTag readTag;
while ((readTag = _readTag(rr, vf)) != tag) { while ((readTag = _readTag(rr, vf)) != tag) {
if (readTag == TAG_END) { if (vf == rr->movieStream && readTag == TAG_END) {
rr->nextTime = 0; if (!GBARRSkipSegment(rr)) {
while (_readTag(rr, vf) != TAG_EOF);
if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) {
return false; return false;
} }
vf = rr->movieStream; vf = rr->movieStream;
@ -282,5 +327,15 @@ bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) {
bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag) { bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag) {
UNUSED(rr); 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;
} }

View File

@ -48,9 +48,11 @@ struct GBARRContext {
uint32_t lagFrames; uint32_t lagFrames;
uint32_t streamId; uint32_t streamId;
uint32_t maxStreamId; uint32_t maxStreamId;
off_t maxStreamIdOffset;
// Streaming state // Streaming state
struct VDir* streamDir; struct VDir* streamDir;
struct VFile* metadataFile;
struct VFile* movieStream; struct VFile* movieStream;
uint16_t currentInput; uint16_t currentInput;
enum GBARRTag peekedTag; enum GBARRTag peekedTag;
@ -64,6 +66,7 @@ void GBARRContextDestroy(struct GBA*);
bool GBARRSetStream(struct GBARRContext*, struct VDir*); bool GBARRSetStream(struct GBARRContext*, struct VDir*);
bool GBARRLoadStream(struct GBARRContext*, uint32_t streamId); bool GBARRLoadStream(struct GBARRContext*, uint32_t streamId);
bool GBARRIncrementStream(struct GBARRContext*); bool GBARRIncrementStream(struct GBARRContext*);
bool GBARRSkipSegment(struct GBARRContext*);
bool GBARRStartPlaying(struct GBARRContext*, bool autorecord); bool GBARRStartPlaying(struct GBARRContext*, bool autorecord);
void GBARRStopPlaying(struct GBARRContext*); void GBARRStopPlaying(struct GBARRContext*);

View File

@ -78,6 +78,14 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) {
GBAIODeserialize(gba, state); GBAIODeserialize(gba, state);
GBAVideoDeserialize(&gba->video, state); GBAVideoDeserialize(&gba->video, state);
GBAAudioDeserialize(&gba->audio, 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) { static struct VFile* _getStateVf(struct GBA* gba, struct VDir* dir, int slot, bool write) {