mirror of https://github.com/mgba-emu/mgba.git
Savestate loading during recording and replaying
This commit is contained in:
parent
28218b2465
commit
5ca6888840
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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*);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue