mirror of https://github.com/mgba-emu/mgba.git
Beginning of rerecording code, ability to record/replay from a savegame
This commit is contained in:
parent
c50bd95258
commit
14636f5b8a
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue