mirror of https://github.com/mgba-emu/mgba.git
Ability to chunk movie streams into multiple files
This commit is contained in:
parent
b115cb564d
commit
9873da0eb7
|
@ -3,8 +3,6 @@
|
||||||
#include "gba.h"
|
#include "gba.h"
|
||||||
#include "util/vfs.h"
|
#include "util/vfs.h"
|
||||||
|
|
||||||
#define FILE_INPUTS "input.log"
|
|
||||||
|
|
||||||
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);
|
||||||
|
@ -32,15 +30,55 @@ bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) {
|
||||||
}
|
}
|
||||||
rr->streamDir = stream;
|
rr->streamDir = stream;
|
||||||
rr->movieStream = 0;
|
rr->movieStream = 0;
|
||||||
|
rr->streamId = 1;
|
||||||
return true;
|
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) {
|
bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) {
|
||||||
if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) {
|
if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) {
|
||||||
return false;
|
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->autorecord = autorecord;
|
||||||
rr->peekedTag = TAG_INVALID;
|
rr->peekedTag = TAG_INVALID;
|
||||||
_readTag(rr, rr->movieStream); // Discard the buffer
|
_readTag(rr, rr->movieStream); // Discard the buffer
|
||||||
|
@ -71,7 +109,9 @@ bool GBARRStartRecording(struct GBARRContext* rr) {
|
||||||
return false;
|
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)) {
|
if (!_emitTag(rr, rr->movieStream, TAG_BEGIN)) {
|
||||||
rr->movieStream->close(rr->movieStream);
|
rr->movieStream->close(rr->movieStream);
|
||||||
rr->movieStream = 0;
|
rr->movieStream = 0;
|
||||||
|
@ -156,10 +196,14 @@ enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) {
|
||||||
case TAG_INPUT:
|
case TAG_INPUT:
|
||||||
vf->read(vf, &rr->currentInput, sizeof(uint16_t));
|
vf->read(vf, &rr->currentInput, sizeof(uint16_t));
|
||||||
break;
|
break;
|
||||||
|
case TAG_NEXT_TIME:
|
||||||
|
vf->read(vf, &rr->nextTime, sizeof(rr->nextTime));
|
||||||
|
break;
|
||||||
case TAG_FRAME:
|
case TAG_FRAME:
|
||||||
case TAG_LAG:
|
case TAG_LAG:
|
||||||
case TAG_BEGIN:
|
case TAG_BEGIN:
|
||||||
case TAG_END:
|
case TAG_END:
|
||||||
|
case TAG_PREVIOUSLY:
|
||||||
case TAG_INVALID:
|
case TAG_INVALID:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -176,9 +220,21 @@ 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 (readTag == TAG_END) {
|
||||||
|
if (rr->peekedTag == TAG_NEXT_TIME) {
|
||||||
|
while (_readTag(rr, vf) != TAG_END) {
|
||||||
|
if (!rr->nextTime) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
vf = rr->movieStream;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,18 +30,22 @@ struct GBARRContext {
|
||||||
// Metadata
|
// Metadata
|
||||||
uint32_t frames;
|
uint32_t frames;
|
||||||
uint32_t lagFrames;
|
uint32_t lagFrames;
|
||||||
|
uint32_t streamId;
|
||||||
|
|
||||||
// Streaming state
|
// Streaming state
|
||||||
struct VDir* streamDir;
|
struct VDir* streamDir;
|
||||||
struct VFile* movieStream;
|
struct VFile* movieStream;
|
||||||
uint16_t currentInput;
|
uint16_t currentInput;
|
||||||
enum GBARRTag peekedTag;
|
enum GBARRTag peekedTag;
|
||||||
|
uint32_t nextTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
void GBARRContextCreate(struct GBA*);
|
void GBARRContextCreate(struct GBA*);
|
||||||
void GBARRContextDestroy(struct GBA*);
|
void GBARRContextDestroy(struct GBA*);
|
||||||
|
|
||||||
bool GBARRSetStream(struct GBARRContext*, struct VDir*);
|
bool GBARRSetStream(struct GBARRContext*, struct VDir*);
|
||||||
|
bool GBARRLoadStream(struct GBARRContext*, uint32_t streamId);
|
||||||
|
uint32_t GBARRIncrementStream(struct GBARRContext*);
|
||||||
|
|
||||||
bool GBARRStartPlaying(struct GBARRContext*, bool autorecord);
|
bool GBARRStartPlaying(struct GBARRContext*, bool autorecord);
|
||||||
void GBARRStopPlaying(struct GBARRContext*);
|
void GBARRStopPlaying(struct GBARRContext*);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "gba-audio.h"
|
#include "gba-audio.h"
|
||||||
#include "gba-io.h"
|
#include "gba-io.h"
|
||||||
|
#include "gba-rr.h"
|
||||||
#include "gba-thread.h"
|
#include "gba-thread.h"
|
||||||
#include "gba-video.h"
|
#include "gba-video.h"
|
||||||
|
|
||||||
|
@ -35,6 +36,13 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
|
||||||
GBAIOSerialize(gba, state);
|
GBAIOSerialize(gba, state);
|
||||||
GBAVideoSerialize(&gba->video, state);
|
GBAVideoSerialize(&gba->video, state);
|
||||||
GBAAudioSerialize(&gba->audio, 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) {
|
void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) {
|
||||||
|
|
|
@ -139,7 +139,9 @@ const uint32_t GBA_SAVESTATE_MAGIC;
|
||||||
* | bit 0: Is read enabled
|
* | bit 0: Is read enabled
|
||||||
* | bit 1: Gyroscope sample is edge
|
* | bit 1: Gyroscope sample is edge
|
||||||
* | bits 2 - 15: Reserved
|
* | 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
|
* 0x00400 - 0x007FF: I/O memory
|
||||||
* 0x00800 - 0x00BFF: Palette
|
* 0x00800 - 0x00BFF: Palette
|
||||||
* 0x00C00 - 0x00FFF: OAM
|
* 0x00C00 - 0x00FFF: OAM
|
||||||
|
@ -248,7 +250,11 @@ struct GBASerializedState {
|
||||||
unsigned reserved : 14;
|
unsigned reserved : 14;
|
||||||
} gpio;
|
} gpio;
|
||||||
|
|
||||||
uint32_t reserved[80];
|
uint32_t reservedGpio[16];
|
||||||
|
|
||||||
|
uint32_t associatedStreamId;
|
||||||
|
|
||||||
|
uint32_t reserved[63];
|
||||||
|
|
||||||
uint16_t io[SIZE_IO >> 1];
|
uint16_t io[SIZE_IO >> 1];
|
||||||
uint16_t pram[SIZE_PALETTE_RAM >> 1];
|
uint16_t pram[SIZE_PALETTE_RAM >> 1];
|
||||||
|
|
Loading…
Reference in New Issue