all repos — mgba @ 79301a365f7f9fb58ca91beba38e4b413d0a9d56

mGBA Game Boy Advance Emulator

src/gba/gba-rr.c (view raw)

  1#include "gba-rr.h"
  2
  3#include "gba.h"
  4#include "util/vfs.h"
  5
  6enum {
  7	GBA_RR_BLOCK_SIZE = 1018
  8};
  9
 10struct GBARRBlock {
 11	union GBARRInput {
 12		struct {
 13			uint16_t keys : 10;
 14			uint16_t : 4;
 15			bool reset : 1;
 16			bool : 1;
 17		};
 18		uint16_t packed;
 19	} inputs[GBA_RR_BLOCK_SIZE];
 20	size_t numInputs;
 21	struct GBARRBlock* next;
 22};
 23
 24static void _allocBlock(struct GBARRContext* rr);
 25
 26void GBARRContextCreate(struct GBA* gba) {
 27	gba->rr = calloc(1, sizeof(*gba->rr));
 28}
 29
 30void GBARRContextDestroy(struct GBA* gba) {
 31	if (!gba->rr) {
 32		return;
 33	}
 34
 35	struct GBARRBlock* block = gba->rr->rootBlock;
 36	while (block) {
 37		struct GBARRBlock* nextBlock = block->next;
 38		free(block);
 39		block = nextBlock;
 40	}
 41	gba->rr->rootBlock = 0;
 42	gba->rr->currentBlock = 0;
 43	gba->rr->playbackBlock = 0;
 44	free(gba->rr);
 45}
 46
 47bool GBARRStartPlaying(struct GBA* gba) {
 48	if (!gba->rr) {
 49		return false;
 50	}
 51	if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
 52		return false;
 53	}
 54
 55	gba->rr->playbackBlock = gba->rr->rootBlock;
 56	gba->rr->inputId = 0;
 57	return !!gba->rr->playbackBlock;
 58}
 59
 60void GBARRStopPlaying(struct GBA* gba) {
 61	if (!gba->rr) {
 62		return;
 63	}
 64
 65	gba->rr->playbackBlock = 0;
 66}
 67
 68bool GBARRStartRecording(struct GBA* gba) {
 69	if (!gba->rr) {
 70		GBARRContextCreate(gba);
 71	}
 72	if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
 73		return false;
 74	}
 75
 76	gba->rr->isRecording = true;
 77	return true;
 78}
 79
 80void GBARRStopRecording(struct GBA* gba) {
 81	if (!gba->rr) {
 82		return;
 83	}
 84
 85	gba->rr->isRecording = false;
 86}
 87
 88bool GBARRIsPlaying(struct GBA* gba) {
 89	if (!gba->rr) {
 90		return false;
 91	}
 92	return gba->rr->playbackBlock;
 93}
 94
 95bool GBARRIsRecording(struct GBA* gba) {
 96	if (!gba->rr) {
 97		return false;
 98	}
 99	return gba->rr->isRecording;
100}
101
102void GBARRNextFrame(struct GBA* gba) {
103	if (!GBARRIsRecording(gba)) {
104		return;
105	}
106
107	struct GBARRContext* rr = gba->rr;
108
109	++rr->frames;
110	if (!rr->inputThisFrame) {
111		++rr->lagFrames;
112	}
113
114	rr->inputThisFrame = false;
115}
116
117void GBARRLogInput(struct GBA* gba, uint16_t input) {
118	if (!GBARRIsRecording(gba)) {
119		return;
120	}
121
122	struct GBARRContext* rr = gba->rr;
123	if (!rr->currentBlock) {
124		_allocBlock(rr);
125	}
126
127	size_t currentId = rr->currentBlock->numInputs;
128	if (currentId == GBA_RR_BLOCK_SIZE) {
129		_allocBlock(rr);
130		currentId = 0;
131	}
132
133	rr->currentBlock->inputs[currentId].keys = input;
134	++rr->currentBlock->numInputs;
135	rr->inputThisFrame = true;
136}
137
138uint16_t GBARRQueryInput(struct GBA* gba) {
139	if (!GBARRIsPlaying(gba)) {
140		return 0;
141	}
142
143	struct GBARRBlock* block = gba->rr->playbackBlock;
144	size_t inputId = gba->rr->inputId;
145	uint16_t keys = 0;
146
147	keys = block->inputs[inputId].keys;
148	++inputId;
149	if (inputId == GBA_RR_BLOCK_SIZE) {
150		inputId = 0;
151		gba->rr->playbackBlock = gba->rr->playbackBlock->next;
152	} else if (!gba->rr->playbackBlock->next && gba->rr->playbackBlock->numInputs == inputId) {
153		gba->rr->playbackBlock = 0;
154	}
155	gba->rr->inputId = inputId;
156	return keys;
157}
158
159void _allocBlock(struct GBARRContext* rr) {
160	struct GBARRBlock* block = calloc(1, sizeof(*rr->currentBlock));
161
162	if (!rr->currentBlock) {
163		rr->currentBlock = block;
164		rr->rootBlock = block;
165		return;
166	}
167
168	rr->currentBlock->next = block;
169	rr->currentBlock = block;
170}