all repos — mgba @ 7172e6428cf6c880ae2c70ca35cfaf237e58cd7f

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	ssize_t read;
 90	do {
 91		read = inputs->read(inputs, block.inputs, sizeof(block.inputs));
 92		if (read) {
 93			struct GBARRBlock* newBlock = calloc(1, sizeof(*rr->currentBlock));
 94			memcpy(newBlock, &block, sizeof(*newBlock));
 95			if (!rr->rootBlock) {
 96				rr->rootBlock = newBlock;
 97			}
 98			if (rr->currentBlock) {
 99				rr->currentBlock->next = newBlock;
100			}
101			rr->currentBlock = newBlock;
102		}
103	} while (read > 0);
104
105	if (!inputs->close(inputs)) {
106		return false;
107	}
108
109	return read >= 0;
110}
111
112bool GBARRStartPlaying(struct GBA* gba) {
113	if (!gba->rr) {
114		return false;
115	}
116	if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
117		return false;
118	}
119
120	gba->rr->playbackBlock = gba->rr->rootBlock;
121	gba->rr->inputId = 0;
122	return !!gba->rr->playbackBlock;
123}
124
125void GBARRStopPlaying(struct GBA* gba) {
126	if (!gba->rr) {
127		return;
128	}
129
130	gba->rr->playbackBlock = 0;
131}
132
133bool GBARRStartRecording(struct GBA* gba) {
134	if (!gba->rr) {
135		GBARRContextCreate(gba);
136	}
137	if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
138		return false;
139	}
140
141	gba->rr->isRecording = true;
142	return true;
143}
144
145void GBARRStopRecording(struct GBA* gba) {
146	if (!gba->rr) {
147		return;
148	}
149
150	gba->rr->isRecording = false;
151}
152
153bool GBARRIsPlaying(struct GBA* gba) {
154	if (!gba->rr) {
155		return false;
156	}
157	return gba->rr->playbackBlock;
158}
159
160bool GBARRIsRecording(struct GBA* gba) {
161	if (!gba->rr) {
162		return false;
163	}
164	return gba->rr->isRecording;
165}
166
167void GBARRNextFrame(struct GBA* gba) {
168	if (!GBARRIsRecording(gba)) {
169		return;
170	}
171
172	struct GBARRContext* rr = gba->rr;
173
174	++rr->frames;
175	if (!rr->inputThisFrame) {
176		++rr->lagFrames;
177	}
178
179	rr->inputThisFrame = false;
180}
181
182void GBARRLogInput(struct GBA* gba, uint16_t input) {
183	if (!GBARRIsRecording(gba)) {
184		return;
185	}
186
187	struct GBARRContext* rr = gba->rr;
188	if (!rr->currentBlock) {
189		_allocBlock(rr);
190	}
191
192	size_t currentId = rr->currentBlock->numInputs;
193	if (currentId == GBA_RR_BLOCK_SIZE) {
194		_allocBlock(rr);
195		currentId = 0;
196	}
197
198	rr->currentBlock->inputs[currentId].keys = input;
199	++rr->currentBlock->numInputs;
200	rr->inputThisFrame = true;
201}
202
203uint16_t GBARRQueryInput(struct GBA* gba) {
204	if (!GBARRIsPlaying(gba)) {
205		return 0;
206	}
207
208	struct GBARRBlock* block = gba->rr->playbackBlock;
209	size_t inputId = gba->rr->inputId;
210	uint16_t keys = 0;
211
212	keys = block->inputs[inputId].keys;
213	++inputId;
214	if (inputId == GBA_RR_BLOCK_SIZE) {
215		inputId = 0;
216		gba->rr->playbackBlock = gba->rr->playbackBlock->next;
217	} else if (!gba->rr->playbackBlock->next && gba->rr->playbackBlock->numInputs == inputId) {
218		gba->rr->playbackBlock = 0;
219	}
220	gba->rr->inputId = inputId;
221	return keys;
222}
223
224void _allocBlock(struct GBARRContext* rr) {
225	struct GBARRBlock* block = calloc(1, sizeof(*rr->currentBlock));
226
227	if (!rr->currentBlock) {
228		rr->currentBlock = block;
229		rr->rootBlock = block;
230		return;
231	}
232
233	rr->currentBlock->next = block;
234	rr->currentBlock = block;
235}