all repos — mgba @ 5825c2ee5f67c28eab64c9b85895dcb92c89bef0

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
 10#define FILE_INPUTS "input.log"
 11
 12struct GBARRBlock {
 13	union GBARRInput {
 14		struct {
 15			uint16_t keys : 10;
 16			uint16_t : 4;
 17			bool reset : 1;
 18			bool : 1;
 19		};
 20		uint16_t packed;
 21	} inputs[GBA_RR_BLOCK_SIZE];
 22	size_t numInputs;
 23	struct GBARRBlock* next;
 24};
 25
 26static void _allocBlock(struct GBARRContext* rr);
 27
 28void GBARRContextCreate(struct GBA* gba) {
 29	gba->rr = calloc(1, sizeof(*gba->rr));
 30}
 31
 32void GBARRContextDestroy(struct GBA* gba) {
 33	if (!gba->rr) {
 34		return;
 35	}
 36
 37	struct GBARRBlock* block = gba->rr->rootBlock;
 38	while (block) {
 39		struct GBARRBlock* nextBlock = block->next;
 40		free(block);
 41		block = nextBlock;
 42	}
 43	gba->rr->rootBlock = 0;
 44	gba->rr->currentBlock = 0;
 45	gba->rr->playbackBlock = 0;
 46	free(gba->rr);
 47	gba->rr = 0;
 48}
 49
 50bool GBARRSave(struct GBARRContext* rr, struct VDir* vdir) {
 51	if (!rr) {
 52		return false;
 53	}
 54
 55	struct VFile* inputs = vdir->openFile(vdir, FILE_INPUTS, O_WRONLY | O_CREAT | O_TRUNC);
 56	if (!inputs) {
 57		return false;
 58	}
 59
 60	ssize_t written = 0;
 61	struct GBARRBlock* inputBlock;
 62	for (inputBlock = rr->rootBlock; inputBlock; inputBlock = inputBlock->next) {
 63		ssize_t thisWrite = inputs->write(inputs, inputBlock->inputs, sizeof(*inputBlock->inputs) * inputBlock->numInputs);
 64		if (!thisWrite) {
 65			written = -1;
 66			break;
 67		}
 68		written += thisWrite;
 69	}
 70
 71	if (!inputs->close(inputs)) {
 72		return false;
 73	}
 74
 75	return written >= 0;
 76}
 77
 78bool GBARRLoad(struct GBARRContext* rr, struct VDir* vdir) {
 79	if (!rr) {
 80		return false;
 81	}
 82
 83	struct VFile* inputs = vdir->openFile(vdir, FILE_INPUTS, O_RDONLY);
 84	if (!inputs) {
 85		return false;
 86	}
 87
 88	struct GBARRBlock block = {
 89		.next = 0,
 90		.numInputs = GBA_RR_BLOCK_SIZE
 91	};
 92
 93	ssize_t read;
 94	do {
 95		read = inputs->read(inputs, block.inputs, sizeof(block.inputs));
 96		if (read) {
 97			struct GBARRBlock* newBlock = calloc(1, sizeof(*rr->currentBlock));
 98			memcpy(newBlock, &block, sizeof(*newBlock));
 99			if (!rr->rootBlock) {
100				rr->rootBlock = newBlock;
101			}
102			if (rr->currentBlock) {
103				rr->currentBlock->next = newBlock;
104			}
105			rr->currentBlock = newBlock;
106			newBlock->numInputs = read / sizeof(block.inputs[0]);
107		}
108	} while (read > 0);
109
110	if (!inputs->close(inputs)) {
111		return false;
112	}
113
114	return read >= 0;
115}
116
117bool GBARRStartPlaying(struct GBA* gba) {
118	if (!gba->rr) {
119		return false;
120	}
121	if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
122		return false;
123	}
124
125	gba->rr->playbackBlock = gba->rr->rootBlock;
126	gba->rr->inputId = 0;
127	return !!gba->rr->playbackBlock;
128}
129
130void GBARRStopPlaying(struct GBA* gba) {
131	if (!gba->rr) {
132		return;
133	}
134
135	gba->rr->playbackBlock = 0;
136}
137
138bool GBARRStartRecording(struct GBA* gba) {
139	if (!gba->rr) {
140		GBARRContextCreate(gba);
141	}
142	if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
143		return false;
144	}
145
146	gba->rr->isRecording = true;
147	return true;
148}
149
150void GBARRStopRecording(struct GBA* gba) {
151	if (!gba->rr) {
152		return;
153	}
154
155	gba->rr->isRecording = false;
156}
157
158bool GBARRIsPlaying(struct GBA* gba) {
159	if (!gba->rr) {
160		return false;
161	}
162	return gba->rr->playbackBlock;
163}
164
165bool GBARRIsRecording(struct GBA* gba) {
166	if (!gba->rr) {
167		return false;
168	}
169	return gba->rr->isRecording;
170}
171
172void GBARRNextFrame(struct GBA* gba) {
173	if (!GBARRIsRecording(gba)) {
174		return;
175	}
176
177	struct GBARRContext* rr = gba->rr;
178
179	++rr->frames;
180	if (!rr->inputThisFrame) {
181		++rr->lagFrames;
182	}
183
184	rr->inputThisFrame = false;
185}
186
187void GBARRLogInput(struct GBA* gba, uint16_t input) {
188	if (!GBARRIsRecording(gba)) {
189		return;
190	}
191
192	struct GBARRContext* rr = gba->rr;
193	if (!rr->currentBlock) {
194		_allocBlock(rr);
195	}
196
197	size_t currentId = rr->currentBlock->numInputs;
198	if (currentId == GBA_RR_BLOCK_SIZE) {
199		_allocBlock(rr);
200		currentId = 0;
201	}
202
203	rr->currentBlock->inputs[currentId].keys = input;
204	++rr->currentBlock->numInputs;
205	rr->inputThisFrame = true;
206}
207
208uint16_t GBARRQueryInput(struct GBA* gba) {
209	if (!GBARRIsPlaying(gba)) {
210		return 0;
211	}
212
213	struct GBARRBlock* block = gba->rr->playbackBlock;
214	size_t inputId = gba->rr->inputId;
215	uint16_t keys = 0;
216
217	keys = block->inputs[inputId].keys;
218	++inputId;
219	if (inputId == GBA_RR_BLOCK_SIZE) {
220		inputId = 0;
221		gba->rr->playbackBlock = gba->rr->playbackBlock->next;
222	} else if (!gba->rr->playbackBlock->next && gba->rr->playbackBlock->numInputs == inputId) {
223		gba->rr->playbackBlock = 0;
224	}
225	gba->rr->inputId = inputId;
226	return keys;
227}
228
229void _allocBlock(struct GBARRContext* rr) {
230	struct GBARRBlock* block = calloc(1, sizeof(*rr->currentBlock));
231
232	if (!rr->currentBlock) {
233		rr->currentBlock = block;
234		rr->rootBlock = block;
235		return;
236	}
237
238	rr->currentBlock->next = block;
239	rr->currentBlock = block;
240}