From 14636f5b8a73f70667932cd119fe110c3ffb26fc Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 19 Jul 2014 00:09:18 -0700 Subject: [PATCH] Beginning of rerecording code, ability to record/replay from a savegame --- src/gba/gba-io.c | 11 ++- src/gba/gba-rr.c | 170 ++++++++++++++++++++++++++++++++++ src/gba/gba-rr.h | 39 ++++++++ src/gba/gba-video.c | 2 + src/gba/gba.c | 2 + src/gba/gba.h | 1 + src/platform/sdl/sdl-events.c | 21 +++++ 7 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 src/gba/gba-rr.c create mode 100644 src/gba/gba-rr.h diff --git a/src/gba/gba-io.c b/src/gba/gba-io.c index 6bd7a5ddf..0dcc7251d 100644 --- a/src/gba/gba-io.c +++ b/src/gba/gba-io.c @@ -1,5 +1,6 @@ #include "gba-io.h" +#include "gba-rr.h" #include "gba-serialize.h" #include "gba-sio.h" #include "gba-video.h" @@ -374,8 +375,14 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { break; case REG_KEYINPUT: - if (gba->keySource) { - return 0x3FF ^ *gba->keySource; + if (GBARRIsPlaying(gba)) { + return 0x3FF ^ GBARRQueryInput(gba); + } else if (gba->keySource) { + uint16_t input = *gba->keySource; + if (GBARRIsRecording(gba)) { + GBARRLogInput(gba, input); + } + return 0x3FF ^ input; } break; diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c new file mode 100644 index 000000000..9d5ae058c --- /dev/null +++ b/src/gba/gba-rr.c @@ -0,0 +1,170 @@ +#include "gba-rr.h" + +#include "gba.h" +#include "util/vfs.h" + +enum { + GBA_RR_BLOCK_SIZE = 1018 +}; + +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) { + gba->rr = calloc(1, sizeof(*gba->rr)); +} + +void GBARRContextDestroy(struct GBA* gba) { + if (!gba->rr) { + 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); +} + +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 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)) { + return false; + } + + gba->rr->isRecording = true; + return true; +} + +void GBARRStopRecording(struct GBA* gba) { + if (!gba->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; + } + + rr->inputThisFrame = false; +} + +void GBARRLogInput(struct GBA* gba, uint16_t input) { + if (!GBARRIsRecording(gba)) { + 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->inputThisFrame = true; +} + +uint16_t GBARRQueryInput(struct GBA* gba) { + if (!GBARRIsPlaying(gba)) { + 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; + 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 new file mode 100644 index 000000000..4f1e11edf --- /dev/null +++ b/src/gba/gba-rr.h @@ -0,0 +1,39 @@ +#ifndef GBA_RR_H +#define GBA_RR_H + +#include "common.h" + +struct GBA; +struct VFile; + +struct GBARRContext { + struct GBARRBlock* rootBlock; + struct GBARRBlock* currentBlock; + + // Playback state + struct GBARRBlock* playbackBlock; + size_t inputId; + + // Recording state + bool isRecording; + bool inputThisFrame; + uint32_t frames; + uint32_t lagFrames; +}; + +void GBARRContextCreate(struct GBA*); +void GBARRContextDestroy(struct GBA*); + +bool GBARRStartPlaying(struct GBA*); +void GBARRStopPlaying(struct GBA*); +bool GBARRStartRecording(struct GBA*); +void GBARRStopRecording(struct GBA*); + +bool GBARRIsPlaying(struct GBA*); +bool GBARRIsRecording(struct GBA*); + +void GBARRNextFrame(struct GBA*); +void GBARRLogInput(struct GBA*, uint16_t input); +uint16_t GBARRQueryInput(struct GBA*); + +#endif diff --git a/src/gba/gba-video.c b/src/gba/gba-video.c index 72c0466e9..37fbfc772 100644 --- a/src/gba/gba-video.c +++ b/src/gba/gba-video.c @@ -2,6 +2,7 @@ #include "gba.h" #include "gba-io.h" +#include "gba-rr.h" #include "gba-serialize.h" #include "gba-thread.h" @@ -97,6 +98,7 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) { GBASyncPostFrame(video->p->sync); break; case VIDEO_VERTICAL_TOTAL_PIXELS - 1: + GBARRNextFrame(video->p); video->inVblank = 0; break; case VIDEO_VERTICAL_TOTAL_PIXELS: diff --git a/src/gba/gba.c b/src/gba/gba.c index b81016601..1243ed98e 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -2,6 +2,7 @@ #include "gba-bios.h" #include "gba-io.h" +#include "gba-rr.h" #include "gba-sio.h" #include "gba-thread.h" @@ -158,6 +159,7 @@ void GBADestroy(struct GBA* gba) { GBAMemoryDeinit(gba); GBAVideoDeinit(&gba->video); GBAAudioDeinit(&gba->audio); + GBARRContextDestroy(gba); } void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) { diff --git a/src/gba/gba.h b/src/gba/gba.h index cf4b303db..ecea8a4f1 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -98,6 +98,7 @@ struct GBA { int* keySource; struct GBARotationSource* rotationSource; struct GBARumble* rumble; + struct GBARRContext* rr; void* pristineRom; size_t pristineRomSize; struct VFile* romVf; diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 54e57e4bd..ed4920c67 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -2,6 +2,7 @@ #include "debugger/debugger.h" #include "gba-io.h" +#include "gba-rr.h" #include "gba-serialize.h" #include "gba-video.h" @@ -99,6 +100,12 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents GBARewind(context, 10); GBAThreadContinue(context); return; + case SDLK_ESCAPE: + GBAThreadInterrupt(context); + GBARRStopPlaying(context->gba); + GBARRStopRecording(context->gba); + GBAThreadContinue(context); + return; default: if (event->type == SDL_KEYDOWN) { if (event->keysym.mod & GUI_MOD) { @@ -121,6 +128,20 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents case SDLK_r: GBAThreadReset(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; }