bool GBARRReinitStream(struct GBARRContext* rr, enum GBARRInitFrom initFrom) { if (!rr) { return false; } if (rr->metadataFile) { rr->metadataFile->truncate(rr->metadataFile, 0); } else { rr->metadataFile = rr->streamDir->openFile(rr->streamDir, METADATA_FILENAME, O_CREAT | O_TRUNC | O_RDWR); } _emitMagic(rr, rr->metadataFile); rr->initFrom = initFrom; rr->initFromOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR); _emitTag(rr, rr->metadataFile, TAG_INIT | initFrom); rr->streamId = 0; rr->maxStreamId = 0; _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->rrCount = 0; _emitTag(rr, rr->metadataFile, TAG_RR_COUNT); rr->rrCountOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR); rr->metadataFile->write(rr->metadataFile, &rr->rrCount, sizeof(rr->rrCount)); return true; }
bool _emitEnd(struct GBAMGMContext* mgm, struct VFile* vf) { // TODO: Error check _emitTag(mgm, vf, TAG_END); _emitTag(mgm, vf, TAG_FRAME_COUNT); vf->write(vf, &mgm->d.frames, sizeof(mgm->d.frames)); _emitTag(mgm, vf, TAG_LAG_COUNT); vf->write(vf, &mgm->d.lagFrames, sizeof(mgm->d.lagFrames)); _emitTag(mgm, vf, TAG_NEXT_TIME); uint32_t newStreamId = 0; vf->write(vf, &newStreamId, sizeof(newStreamId)); return true; }
bool _emitEnd(struct GBARRContext* rr, struct VFile* vf) { // TODO: Error check _emitTag(rr, vf, TAG_END); _emitTag(rr, vf, TAG_FRAME_COUNT); vf->write(vf, &rr->frames, sizeof(rr->frames)); _emitTag(rr, vf, TAG_LAG_COUNT); vf->write(vf, &rr->lagFrames, sizeof(rr->lagFrames)); _emitTag(rr, vf, TAG_NEXT_TIME); uint32_t newStreamId = 0; vf->write(vf, &newStreamId, sizeof(newStreamId)); return true; }
void GBARRLogInput(struct GBARRContext* rr, uint16_t keys) { if (!GBARRIsRecording(rr)) { return; } if (keys != rr->currentInput) { _emitTag(rr, rr->movieStream, TAG_INPUT); rr->movieStream->write(rr->movieStream, &keys, sizeof(keys)); rr->currentInput = keys; } GBALog(0, GBA_LOG_DEBUG, "[RR] Input log: %03X", rr->currentInput); rr->inputThisFrame = true; }
void GBAMGMNextFrame(struct GBARRContext* rr) { if (!rr->isRecording(rr) && !rr->isPlaying(rr)) { return; } struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; if (rr->isPlaying(rr)) { while (mgm->peekedTag == TAG_INPUT) { _readTag(mgm, mgm->movieStream); GBALog(0, GBA_LOG_WARN, "[RR] Desync detected!"); } if (mgm->peekedTag == TAG_LAG) { GBALog(0, GBA_LOG_DEBUG, "[RR] Lag frame marked in stream"); if (mgm->inputThisFrame) { GBALog(0, GBA_LOG_WARN, "[RR] Lag frame in stream does not match movie"); } } } ++mgm->d.frames; GBALog(0, GBA_LOG_DEBUG, "[RR] Frame: %u", mgm->d.frames); if (!mgm->inputThisFrame) { ++mgm->d.lagFrames; GBALog(0, GBA_LOG_DEBUG, "[RR] Lag frame: %u", mgm->d.lagFrames); } if (rr->isRecording(rr)) { if (!mgm->inputThisFrame) { _emitTag(mgm, mgm->movieStream, TAG_LAG); } _emitTag(mgm, mgm->movieStream, TAG_FRAME); mgm->inputThisFrame = false; } else { if (!_seekTag(mgm, mgm->movieStream, TAG_FRAME)) { _streamEndReached(mgm); } } }
void GBAMGMLogInput(struct GBARRContext* rr, uint16_t keys) { if (!rr->isRecording(rr)) { return; } struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; if (keys != mgm->currentInput) { _emitTag(mgm, mgm->movieStream, TAG_INPUT); mgm->movieStream->write(mgm->movieStream, &keys, sizeof(keys)); mgm->currentInput = keys; } GBALog(0, GBA_LOG_DEBUG, "[RR] Input log: %03X", mgm->currentInput); mgm->inputThisFrame = true; }
bool _incrementStream(struct GBAMGMContext* mgm, bool recursive) { uint32_t newStreamId = mgm->maxStreamId + 1; uint32_t oldStreamId = mgm->streamId; if (mgm->d.isRecording(&mgm->d) && mgm->movieStream) { if (!_markStreamNext(mgm, newStreamId, recursive)) { return false; } } if (!_loadStream(mgm, newStreamId)) { return false; } GBALog(0, GBA_LOG_DEBUG, "[RR] New segment: %u", newStreamId); _emitMagic(mgm, mgm->movieStream); mgm->maxStreamId = newStreamId; _emitTag(mgm, mgm->movieStream, TAG_PREVIOUSLY); mgm->movieStream->write(mgm->movieStream, &oldStreamId, sizeof(oldStreamId)); _emitTag(mgm, mgm->movieStream, TAG_BEGIN); mgm->metadataFile->seek(mgm->metadataFile, mgm->maxStreamIdOffset, SEEK_SET); mgm->metadataFile->write(mgm->metadataFile, &mgm->maxStreamId, sizeof(mgm->maxStreamId)); mgm->previously = oldStreamId; return true; }
bool GBARRStartRecording(struct GBARRContext* rr) { if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) { 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; return GBARRIncrementStream(rr, false); }
bool GBARRIncrementStream(struct GBARRContext* rr, bool recursive) { uint32_t newStreamId = rr->maxStreamId + 1; uint32_t oldStreamId = rr->streamId; if (GBARRIsRecording(rr) && rr->movieStream) { if (!_markStreamNext(rr, newStreamId, recursive)) { return false; } } if (!GBARRLoadStream(rr, newStreamId)) { return false; } GBALog(0, GBA_LOG_DEBUG, "[RR] New segment: %u", newStreamId); _emitMagic(rr, rr->movieStream); rr->maxStreamId = newStreamId; _emitTag(rr, rr->movieStream, TAG_PREVIOUSLY); rr->movieStream->write(rr->movieStream, &oldStreamId, sizeof(oldStreamId)); _emitTag(rr, rr->movieStream, TAG_BEGIN); rr->metadataFile->seek(rr->metadataFile, rr->maxStreamIdOffset, SEEK_SET); rr->metadataFile->write(rr->metadataFile, &rr->maxStreamId, sizeof(rr->maxStreamId)); rr->previously = oldStreamId; return true; }
bool GBAMGMStartRecording(struct GBARRContext* rr) { if (rr->isRecording(rr) || rr->isPlaying(rr)) { return false; } struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; if (!mgm->maxStreamIdOffset) { _emitTag(mgm, mgm->metadataFile, TAG_MAX_STREAM); mgm->maxStreamIdOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR); mgm->metadataFile->write(mgm->metadataFile, &mgm->maxStreamId, sizeof(mgm->maxStreamId)); } mgm->isRecording = true; return _incrementStream(mgm, false); }
bool GBAMGMCreateStream(struct GBAMGMContext* mgm, enum GBARRInitFrom initFrom) { if (mgm->metadataFile) { mgm->metadataFile->truncate(mgm->metadataFile, 0); } else { mgm->metadataFile = mgm->streamDir->openFile(mgm->streamDir, METADATA_FILENAME, O_CREAT | O_TRUNC | O_RDWR); } _emitMagic(mgm, mgm->metadataFile); mgm->d.initFrom = initFrom; mgm->initFromOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR); _emitTag(mgm, mgm->metadataFile, TAG_INIT | initFrom); mgm->streamId = 0; mgm->maxStreamId = 0; _emitTag(mgm, mgm->metadataFile, TAG_MAX_STREAM); mgm->maxStreamIdOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR); mgm->metadataFile->write(mgm->metadataFile, &mgm->maxStreamId, sizeof(mgm->maxStreamId)); mgm->d.rrCount = 0; _emitTag(mgm, mgm->metadataFile, TAG_RR_COUNT); mgm->rrCountOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR); mgm->metadataFile->write(mgm->metadataFile, &mgm->d.rrCount, sizeof(mgm->d.rrCount)); return true; }