mirror of https://github.com/mgba-emu/mgba.git
Rewrite rr to be streaming-only
This commit is contained in:
parent
a95e2c06b7
commit
74dae5033b
|
@ -375,12 +375,12 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case REG_KEYINPUT:
|
case REG_KEYINPUT:
|
||||||
if (GBARRIsPlaying(gba)) {
|
if (GBARRIsPlaying(gba->rr)) {
|
||||||
return 0x3FF ^ GBARRQueryInput(gba);
|
return 0x3FF ^ GBARRQueryInput(gba->rr);
|
||||||
} else if (gba->keySource) {
|
} else if (gba->keySource) {
|
||||||
uint16_t input = *gba->keySource;
|
uint16_t input = *gba->keySource;
|
||||||
if (GBARRIsRecording(gba)) {
|
if (GBARRIsRecording(gba->rr)) {
|
||||||
GBARRLogInput(gba, input);
|
GBARRLogInput(gba->rr, input);
|
||||||
}
|
}
|
||||||
return 0x3FF ^ input;
|
return 0x3FF ^ input;
|
||||||
}
|
}
|
||||||
|
|
221
src/gba/gba-rr.c
221
src/gba/gba-rr.c
|
@ -3,29 +3,13 @@
|
||||||
#include "gba.h"
|
#include "gba.h"
|
||||||
#include "util/vfs.h"
|
#include "util/vfs.h"
|
||||||
|
|
||||||
enum {
|
|
||||||
GBA_RR_BLOCK_SIZE = 1018
|
|
||||||
};
|
|
||||||
|
|
||||||
#define FILE_INPUTS "input.log"
|
#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) {
|
void GBARRContextCreate(struct GBA* gba) {
|
||||||
|
if (gba->rr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
gba->rr = calloc(1, sizeof(*gba->rr));
|
gba->rr = calloc(1, sizeof(*gba->rr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,148 +18,59 @@ void GBARRContextDestroy(struct GBA* gba) {
|
||||||
return;
|
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);
|
free(gba->rr);
|
||||||
gba->rr = 0;
|
gba->rr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBARRSave(struct GBARRContext* rr, struct VDir* vdir) {
|
bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) {
|
||||||
if (!rr) {
|
if (rr->inputsStream && !rr->inputsStream->close(rr->inputsStream)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
rr->streamDir = stream;
|
||||||
struct VFile* inputs = vdir->openFile(vdir, FILE_INPUTS, O_WRONLY | O_CREAT | O_TRUNC);
|
rr->inputsStream = stream->openFile(stream, FILE_INPUTS, O_CREAT | O_RDWR);
|
||||||
if (!inputs) {
|
return !!rr->inputsStream;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBARRLoad(struct GBARRContext* rr, struct VDir* vdir) {
|
bool GBARRStartPlaying(struct GBARRContext* rr) {
|
||||||
if (!rr) {
|
if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VFile* inputs = vdir->openFile(vdir, FILE_INPUTS, O_RDONLY);
|
rr->isPlaying = true;
|
||||||
if (!inputs) {
|
rr->inputId = 0;
|
||||||
return false;
|
return rr->inputsStream->seek(rr->inputsStream, 0, SEEK_SET) == 0;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBARRStartPlaying(struct GBA* gba) {
|
void GBARRStopPlaying(struct GBARRContext* rr) {
|
||||||
if (!gba->rr) {
|
rr->isPlaying = 0;
|
||||||
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) {
|
bool GBARRStartRecording(struct GBARRContext* rr) {
|
||||||
if (!gba->rr) {
|
if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gba->rr->playbackBlock = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GBARRStartRecording(struct GBA* gba) {
|
|
||||||
if (!gba->rr) {
|
|
||||||
GBARRContextCreate(gba);
|
|
||||||
}
|
|
||||||
if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
gba->rr->isRecording = true;
|
rr->isRecording = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBARRStopRecording(struct GBA* gba) {
|
void GBARRStopRecording(struct GBARRContext* rr) {
|
||||||
if (!gba->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;
|
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;
|
++rr->frames;
|
||||||
if (!rr->inputThisFrame) {
|
if (!rr->inputThisFrame) {
|
||||||
++rr->lagFrames;
|
++rr->lagFrames;
|
||||||
|
@ -184,57 +79,21 @@ void GBARRNextFrame(struct GBA* gba) {
|
||||||
rr->inputThisFrame = false;
|
rr->inputThisFrame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBARRLogInput(struct GBA* gba, uint16_t input) {
|
void GBARRLogInput(struct GBARRContext* rr, uint16_t keys) {
|
||||||
if (!GBARRIsRecording(gba)) {
|
if (!GBARRIsRecording(rr)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GBARRContext* rr = gba->rr;
|
rr->inputsStream->write(rr->inputsStream, &keys, sizeof(keys));
|
||||||
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;
|
rr->inputThisFrame = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t GBARRQueryInput(struct GBA* gba) {
|
uint16_t GBARRQueryInput(struct GBARRContext* rr) {
|
||||||
if (!GBARRIsPlaying(gba)) {
|
if (!GBARRIsPlaying(rr)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GBARRBlock* block = gba->rr->playbackBlock;
|
uint16_t keys;
|
||||||
size_t inputId = gba->rr->inputId;
|
rr->inputsStream->read(rr->inputsStream, &keys, sizeof(keys));
|
||||||
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;
|
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,36 +8,38 @@ struct VDir;
|
||||||
struct VFile;
|
struct VFile;
|
||||||
|
|
||||||
struct GBARRContext {
|
struct GBARRContext {
|
||||||
struct GBARRBlock* rootBlock;
|
|
||||||
struct GBARRBlock* currentBlock;
|
|
||||||
|
|
||||||
// Playback state
|
// Playback state
|
||||||
struct GBARRBlock* playbackBlock;
|
bool isPlaying;
|
||||||
size_t inputId;
|
size_t inputId;
|
||||||
|
|
||||||
// Recording state
|
// Recording state
|
||||||
bool isRecording;
|
bool isRecording;
|
||||||
bool inputThisFrame;
|
bool inputThisFrame;
|
||||||
|
|
||||||
|
// Metadata
|
||||||
uint32_t frames;
|
uint32_t frames;
|
||||||
uint32_t lagFrames;
|
uint32_t lagFrames;
|
||||||
|
|
||||||
|
// Streaming state
|
||||||
|
struct VDir* streamDir;
|
||||||
|
struct VFile* inputsStream;
|
||||||
};
|
};
|
||||||
|
|
||||||
void GBARRContextCreate(struct GBA*);
|
void GBARRContextCreate(struct GBA*);
|
||||||
void GBARRContextDestroy(struct GBA*);
|
void GBARRContextDestroy(struct GBA*);
|
||||||
|
|
||||||
bool GBARRSave(struct GBARRContext*, struct VDir*);
|
bool GBARRSetStream(struct GBARRContext*, struct VDir*);
|
||||||
bool GBARRLoad(struct GBARRContext*, struct VDir*);
|
|
||||||
|
|
||||||
bool GBARRStartPlaying(struct GBA*);
|
bool GBARRStartPlaying(struct GBARRContext*);
|
||||||
void GBARRStopPlaying(struct GBA*);
|
void GBARRStopPlaying(struct GBARRContext*);
|
||||||
bool GBARRStartRecording(struct GBA*);
|
bool GBARRStartRecording(struct GBARRContext*);
|
||||||
void GBARRStopRecording(struct GBA*);
|
void GBARRStopRecording(struct GBARRContext*);
|
||||||
|
|
||||||
bool GBARRIsPlaying(struct GBA*);
|
bool GBARRIsPlaying(struct GBARRContext*);
|
||||||
bool GBARRIsRecording(struct GBA*);
|
bool GBARRIsRecording(struct GBARRContext*);
|
||||||
|
|
||||||
void GBARRNextFrame(struct GBA*);
|
void GBARRNextFrame(struct GBARRContext*);
|
||||||
void GBARRLogInput(struct GBA*, uint16_t input);
|
void GBARRLogInput(struct GBARRContext*, uint16_t input);
|
||||||
uint16_t GBARRQueryInput(struct GBA*);
|
uint16_t GBARRQueryInput(struct GBARRContext*);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -110,7 +110,9 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
|
||||||
GBASyncPostFrame(video->p->sync);
|
GBASyncPostFrame(video->p->sync);
|
||||||
break;
|
break;
|
||||||
case VIDEO_VERTICAL_TOTAL_PIXELS - 1:
|
case VIDEO_VERTICAL_TOTAL_PIXELS - 1:
|
||||||
GBARRNextFrame(video->p);
|
if (video->p->rr) {
|
||||||
|
GBARRNextFrame(video->p->rr);
|
||||||
|
}
|
||||||
video->inVblank = 0;
|
video->inVblank = 0;
|
||||||
break;
|
break;
|
||||||
case VIDEO_VERTICAL_TOTAL_PIXELS:
|
case VIDEO_VERTICAL_TOTAL_PIXELS:
|
||||||
|
|
|
@ -102,8 +102,8 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
|
||||||
return;
|
return;
|
||||||
case SDLK_ESCAPE:
|
case SDLK_ESCAPE:
|
||||||
GBAThreadInterrupt(context);
|
GBAThreadInterrupt(context);
|
||||||
GBARRStopPlaying(context->gba);
|
GBARRStopPlaying(context->gba->rr);
|
||||||
GBARRStopRecording(context->gba);
|
GBARRStopRecording(context->gba->rr);
|
||||||
GBAThreadContinue(context);
|
GBAThreadContinue(context);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
|
@ -128,6 +128,24 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
|
||||||
case SDLK_r:
|
case SDLK_r:
|
||||||
GBAThreadReset(context);
|
GBAThreadReset(context);
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -148,27 +166,6 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
|
||||||
GBASaveState(context->gba, event->keysym.sym - SDLK_F1);
|
GBASaveState(context->gba, event->keysym.sym - SDLK_F1);
|
||||||
GBAThreadContinue(context);
|
GBAThreadContinue(context);
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -188,20 +185,6 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
|
||||||
GBALoadState(context->gba, event->keysym.sym - SDLK_F1);
|
GBALoadState(context->gba, event->keysym.sym - SDLK_F1);
|
||||||
GBAThreadContinue(context);
|
GBAThreadContinue(context);
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue