diff --git a/src/gba/gba-io.c b/src/gba/gba-io.c index 0dcc7251d..3bab51102 100644 --- a/src/gba/gba-io.c +++ b/src/gba/gba-io.c @@ -375,12 +375,12 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { break; case REG_KEYINPUT: - if (GBARRIsPlaying(gba)) { - return 0x3FF ^ GBARRQueryInput(gba); + if (GBARRIsPlaying(gba->rr)) { + return 0x3FF ^ GBARRQueryInput(gba->rr); } else if (gba->keySource) { uint16_t input = *gba->keySource; - if (GBARRIsRecording(gba)) { - GBARRLogInput(gba, input); + if (GBARRIsRecording(gba->rr)) { + GBARRLogInput(gba->rr, input); } return 0x3FF ^ input; } diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 20151c087..0f21181ed 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -3,29 +3,13 @@ #include "gba.h" #include "util/vfs.h" -enum { - GBA_RR_BLOCK_SIZE = 1018 -}; - #define FILE_INPUTS "input.log" -struct GBARRBlock { - union GBARRInput { - struct { - uint16_t keys : 10; - uint16_t : 4; - bool reset : 1; - bool : 1; - }; - uint16_t packed; - } inputs[GBA_RR_BLOCK_SIZE]; - size_t numInputs; - struct GBARRBlock* next; -}; - -static void _allocBlock(struct GBARRContext* rr); - void GBARRContextCreate(struct GBA* gba) { + if (gba->rr) { + return; + } + gba->rr = calloc(1, sizeof(*gba->rr)); } @@ -34,148 +18,59 @@ void GBARRContextDestroy(struct GBA* gba) { return; } - struct GBARRBlock* block = gba->rr->rootBlock; - while (block) { - struct GBARRBlock* nextBlock = block->next; - free(block); - block = nextBlock; - } - gba->rr->rootBlock = 0; - gba->rr->currentBlock = 0; - gba->rr->playbackBlock = 0; free(gba->rr); gba->rr = 0; } -bool GBARRSave(struct GBARRContext* rr, struct VDir* vdir) { - if (!rr) { +bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) { + if (rr->inputsStream && !rr->inputsStream->close(rr->inputsStream)) { return false; } - - struct VFile* inputs = vdir->openFile(vdir, FILE_INPUTS, O_WRONLY | O_CREAT | O_TRUNC); - if (!inputs) { - return false; - } - - ssize_t written = 0; - struct GBARRBlock* inputBlock; - for (inputBlock = rr->rootBlock; inputBlock; inputBlock = inputBlock->next) { - ssize_t thisWrite = inputs->write(inputs, inputBlock->inputs, sizeof(*inputBlock->inputs) * inputBlock->numInputs); - if (!thisWrite) { - written = -1; - break; - } - written += thisWrite; - } - - if (!inputs->close(inputs)) { - return false; - } - - return written >= 0; + rr->streamDir = stream; + rr->inputsStream = stream->openFile(stream, FILE_INPUTS, O_CREAT | O_RDWR); + return !!rr->inputsStream; } -bool GBARRLoad(struct GBARRContext* rr, struct VDir* vdir) { - if (!rr) { +bool GBARRStartPlaying(struct GBARRContext* rr) { + if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) { return false; } - struct VFile* inputs = vdir->openFile(vdir, FILE_INPUTS, O_RDONLY); - if (!inputs) { - return false; - } - - struct GBARRBlock block = { - .next = 0, - .numInputs = GBA_RR_BLOCK_SIZE - }; - - ssize_t read; - do { - read = inputs->read(inputs, block.inputs, sizeof(block.inputs)); - if (read) { - struct GBARRBlock* newBlock = calloc(1, sizeof(*rr->currentBlock)); - memcpy(newBlock, &block, sizeof(*newBlock)); - if (!rr->rootBlock) { - rr->rootBlock = newBlock; - } - if (rr->currentBlock) { - rr->currentBlock->next = newBlock; - } - rr->currentBlock = newBlock; - newBlock->numInputs = read / sizeof(block.inputs[0]); - } - } while (read > 0); - - if (!inputs->close(inputs)) { - return false; - } - - return read >= 0; + rr->isPlaying = true; + rr->inputId = 0; + return rr->inputsStream->seek(rr->inputsStream, 0, SEEK_SET) == 0; } -bool GBARRStartPlaying(struct GBA* gba) { - if (!gba->rr) { - return false; - } - if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) { - return false; - } - - gba->rr->playbackBlock = gba->rr->rootBlock; - gba->rr->inputId = 0; - return !!gba->rr->playbackBlock; +void GBARRStopPlaying(struct GBARRContext* rr) { + rr->isPlaying = 0; } -void GBARRStopPlaying(struct GBA* gba) { - if (!gba->rr) { - return; - } - - gba->rr->playbackBlock = 0; -} - -bool GBARRStartRecording(struct GBA* gba) { - if (!gba->rr) { - GBARRContextCreate(gba); - } - if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) { +bool GBARRStartRecording(struct GBARRContext* rr) { + if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) { return false; } - gba->rr->isRecording = true; + rr->isRecording = true; return true; } -void GBARRStopRecording(struct GBA* gba) { - if (!gba->rr) { +void GBARRStopRecording(struct GBARRContext* rr) { + rr->isRecording = false; +} + +bool GBARRIsPlaying(struct GBARRContext* rr) { + return rr && rr->isPlaying; +} + +bool GBARRIsRecording(struct GBARRContext* rr) { + return rr && rr->isRecording; +} + +void GBARRNextFrame(struct GBARRContext* rr) { + if (!GBARRIsRecording(rr)) { return; } - gba->rr->isRecording = false; -} - -bool GBARRIsPlaying(struct GBA* gba) { - if (!gba->rr) { - return false; - } - return gba->rr->playbackBlock; -} - -bool GBARRIsRecording(struct GBA* gba) { - if (!gba->rr) { - return false; - } - return gba->rr->isRecording; -} - -void GBARRNextFrame(struct GBA* gba) { - if (!GBARRIsRecording(gba)) { - return; - } - - struct GBARRContext* rr = gba->rr; - ++rr->frames; if (!rr->inputThisFrame) { ++rr->lagFrames; @@ -184,57 +79,21 @@ void GBARRNextFrame(struct GBA* gba) { rr->inputThisFrame = false; } -void GBARRLogInput(struct GBA* gba, uint16_t input) { - if (!GBARRIsRecording(gba)) { +void GBARRLogInput(struct GBARRContext* rr, uint16_t keys) { + if (!GBARRIsRecording(rr)) { return; } - struct GBARRContext* rr = gba->rr; - if (!rr->currentBlock) { - _allocBlock(rr); - } - - size_t currentId = rr->currentBlock->numInputs; - if (currentId == GBA_RR_BLOCK_SIZE) { - _allocBlock(rr); - currentId = 0; - } - - rr->currentBlock->inputs[currentId].keys = input; - ++rr->currentBlock->numInputs; + rr->inputsStream->write(rr->inputsStream, &keys, sizeof(keys)); rr->inputThisFrame = true; } -uint16_t GBARRQueryInput(struct GBA* gba) { - if (!GBARRIsPlaying(gba)) { +uint16_t GBARRQueryInput(struct GBARRContext* rr) { + if (!GBARRIsPlaying(rr)) { return 0; } - struct GBARRBlock* block = gba->rr->playbackBlock; - size_t inputId = gba->rr->inputId; - uint16_t keys = 0; - - keys = block->inputs[inputId].keys; - ++inputId; - if (inputId == GBA_RR_BLOCK_SIZE) { - inputId = 0; - gba->rr->playbackBlock = gba->rr->playbackBlock->next; - } else if (!gba->rr->playbackBlock->next && gba->rr->playbackBlock->numInputs == inputId) { - gba->rr->playbackBlock = 0; - } - gba->rr->inputId = inputId; + uint16_t keys; + rr->inputsStream->read(rr->inputsStream, &keys, sizeof(keys)); return keys; } - -void _allocBlock(struct GBARRContext* rr) { - struct GBARRBlock* block = calloc(1, sizeof(*rr->currentBlock)); - - if (!rr->currentBlock) { - rr->currentBlock = block; - rr->rootBlock = block; - return; - } - - rr->currentBlock->next = block; - rr->currentBlock = block; -} diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index 41baa6cd0..25b6ebce2 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -8,36 +8,38 @@ struct VDir; struct VFile; struct GBARRContext { - struct GBARRBlock* rootBlock; - struct GBARRBlock* currentBlock; - // Playback state - struct GBARRBlock* playbackBlock; + bool isPlaying; size_t inputId; // Recording state bool isRecording; bool inputThisFrame; + + // Metadata uint32_t frames; uint32_t lagFrames; + + // Streaming state + struct VDir* streamDir; + struct VFile* inputsStream; }; void GBARRContextCreate(struct GBA*); void GBARRContextDestroy(struct GBA*); -bool GBARRSave(struct GBARRContext*, struct VDir*); -bool GBARRLoad(struct GBARRContext*, struct VDir*); +bool GBARRSetStream(struct GBARRContext*, struct VDir*); -bool GBARRStartPlaying(struct GBA*); -void GBARRStopPlaying(struct GBA*); -bool GBARRStartRecording(struct GBA*); -void GBARRStopRecording(struct GBA*); +bool GBARRStartPlaying(struct GBARRContext*); +void GBARRStopPlaying(struct GBARRContext*); +bool GBARRStartRecording(struct GBARRContext*); +void GBARRStopRecording(struct GBARRContext*); -bool GBARRIsPlaying(struct GBA*); -bool GBARRIsRecording(struct GBA*); +bool GBARRIsPlaying(struct GBARRContext*); +bool GBARRIsRecording(struct GBARRContext*); -void GBARRNextFrame(struct GBA*); -void GBARRLogInput(struct GBA*, uint16_t input); -uint16_t GBARRQueryInput(struct GBA*); +void GBARRNextFrame(struct GBARRContext*); +void GBARRLogInput(struct GBARRContext*, uint16_t input); +uint16_t GBARRQueryInput(struct GBARRContext*); #endif diff --git a/src/gba/gba-video.c b/src/gba/gba-video.c index 1141388de..a8ae1c7a2 100644 --- a/src/gba/gba-video.c +++ b/src/gba/gba-video.c @@ -110,7 +110,9 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) { GBASyncPostFrame(video->p->sync); break; case VIDEO_VERTICAL_TOTAL_PIXELS - 1: - GBARRNextFrame(video->p); + if (video->p->rr) { + GBARRNextFrame(video->p->rr); + } video->inVblank = 0; break; case VIDEO_VERTICAL_TOTAL_PIXELS: diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index aa2b2ef2b..774ed8dbb 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -102,8 +102,8 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents return; case SDLK_ESCAPE: GBAThreadInterrupt(context); - GBARRStopPlaying(context->gba); - GBARRStopRecording(context->gba); + GBARRStopPlaying(context->gba->rr); + GBARRStopRecording(context->gba->rr); GBAThreadContinue(context); return; default: @@ -128,6 +128,24 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents case SDLK_r: GBAThreadReset(context); break; + case SDLK_t: + GBAThreadReset(context); + GBAThreadInterrupt(context); + GBARRContextCreate(context->gba); + GBARRSetStream(context->gba->rr, context->stateDir); + GBARRStopPlaying(context->gba->rr); + GBARRStartRecording(context->gba->rr); + GBAThreadContinue(context); + break; + case SDLK_y: + GBAThreadReset(context); + GBAThreadInterrupt(context); + GBARRContextCreate(context->gba); + GBARRSetStream(context->gba->rr, context->stateDir); + GBARRStopRecording(context->gba->rr); + GBARRStartPlaying(context->gba->rr); + GBAThreadContinue(context); + break; default: break; } @@ -148,27 +166,6 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents GBASaveState(context->gba, event->keysym.sym - SDLK_F1); GBAThreadContinue(context); break; - case SDLK_t: - if (context->stateDir) { - GBAThreadInterrupt(context); - GBARRStopPlaying(context->gba); - GBARRSave(context->gba->rr, context->stateDir); - GBAThreadContinue(context); - } - break; - case SDLK_y: - if (context->stateDir) { - GBAThreadReset(context); - GBAThreadInterrupt(context); - GBARRStopRecording(context->gba); - GBARRContextDestroy(context->gba); - GBARRContextCreate(context->gba); - if (GBARRLoad(context->gba->rr, context->stateDir)) { - GBARRStartPlaying(context->gba); - } - GBAThreadContinue(context); - } - break; default: break; } @@ -188,20 +185,6 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents GBALoadState(context->gba, event->keysym.sym - SDLK_F1); GBAThreadContinue(context); break; - case SDLK_t: - GBAThreadReset(context); - GBAThreadInterrupt(context); - GBARRStopPlaying(context->gba); - GBARRStartRecording(context->gba); - GBAThreadContinue(context); - break; - case SDLK_y: - GBAThreadReset(context); - GBAThreadInterrupt(context); - GBARRStopRecording(context->gba); - GBARRStartPlaying(context->gba); - GBAThreadContinue(context); - break; default: break; }