Beginning of rerecording code, ability to record/replay from a savegame
Jeffrey Pfau jeffrey@endrift.com
Sat, 19 Jul 2014 00:09:18 -0700
7 files changed,
244 insertions(+),
2 deletions(-)
M
src/gba/gba-io.c
→
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 @@ GBATimerUpdateRegister(gba, 3);
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;
A
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; +}
A
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
M
src/gba/gba-video.c
→
src/gba/gba-video.c
@@ -2,6 +2,7 @@ #include "gba-video.h"
#include "gba.h" #include "gba-io.h" +#include "gba-rr.h" #include "gba-serialize.h" #include "gba-thread.h"@@ -97,6 +98,7 @@ }
GBASyncPostFrame(video->p->sync); break; case VIDEO_VERTICAL_TOTAL_PIXELS - 1: + GBARRNextFrame(video->p); video->inVblank = 0; break; case VIDEO_VERTICAL_TOTAL_PIXELS:
M
src/gba/gba.c
→
src/gba/gba.c
@@ -2,6 +2,7 @@ #include "gba.h"
#include "gba-bios.h" #include "gba-io.h" +#include "gba-rr.h" #include "gba-sio.h" #include "gba-thread.h"@@ -158,6 +159,7 @@
GBAMemoryDeinit(gba); GBAVideoDeinit(&gba->video); GBAAudioDeinit(&gba->audio); + GBARRContextDestroy(gba); } void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) {
M
src/gba/gba.h
→
src/gba/gba.h
@@ -98,6 +98,7 @@ uint32_t biosChecksum;
int* keySource; struct GBARotationSource* rotationSource; struct GBARumble* rumble; + struct GBARRContext* rr; void* pristineRom; size_t pristineRomSize; struct VFile* romVf;
M
src/platform/sdl/sdl-events.c
→
src/platform/sdl/sdl-events.c
@@ -2,6 +2,7 @@ #include "sdl-events.h"
#include "debugger/debugger.h" #include "gba-io.h" +#include "gba-rr.h" #include "gba-serialize.h" #include "gba-video.h"@@ -99,6 +100,12 @@ GBAThreadInterrupt(context);
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) {@@ -120,6 +127,20 @@ GBAThreadUnpause(context);
break; 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;