all repos — mgba @ fade9941370ae801487c8ab39822f41fdd6a6f75

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
  6#define BINEXT ".log"
  7
  8static enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf);
  9static bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag);
 10static bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag);
 11
 12void GBARRContextCreate(struct GBA* gba) {
 13	if (gba->rr) {
 14		return;
 15	}
 16
 17	gba->rr = calloc(1, sizeof(*gba->rr));
 18}
 19
 20void GBARRContextDestroy(struct GBA* gba) {
 21	if (!gba->rr) {
 22		return;
 23	}
 24
 25	if (GBARRIsPlaying(gba->rr)) {
 26		GBARRStopPlaying(gba->rr);
 27	}
 28	if (GBARRIsRecording(gba->rr)) {
 29		GBARRStopRecording(gba->rr);
 30	}
 31
 32	free(gba->rr);
 33	gba->rr = 0;
 34}
 35
 36bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) {
 37	if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) {
 38		return false;
 39	}
 40	rr->streamDir = stream;
 41	rr->movieStream = 0;
 42	rr->streamId = 1;
 43	rr->maxStreamId = 1;
 44	return true;
 45}
 46
 47bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) {
 48	if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) {
 49		return false;
 50	}
 51	rr->movieStream = 0;
 52	rr->streamId = streamId;
 53	char buffer[14];
 54	snprintf(buffer, sizeof(buffer), "%u" BINEXT, streamId);
 55	if (GBARRIsRecording(rr)) {
 56		rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_TRUNC | O_CREAT | O_WRONLY);
 57	} else if (GBARRIsPlaying(rr)) {
 58		rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY);
 59		rr->peekedTag = TAG_INVALID;
 60		if (!rr->movieStream || !_seekTag(rr, rr->movieStream, TAG_BEGIN)) {
 61			GBARRStopPlaying(rr);
 62		}
 63	}
 64	rr->frames = 0;
 65	rr->lagFrames = 0;
 66	return true;
 67}
 68
 69bool GBARRIncrementStream(struct GBARRContext* rr) {
 70	uint32_t newStreamId = rr->maxStreamId + 1;
 71	uint32_t oldStreamId = rr->streamId;
 72	if (GBARRIsRecording(rr) && rr->movieStream) {
 73		_emitTag(rr, rr->movieStream, TAG_END);
 74		_emitTag(rr, rr->movieStream, TAG_FRAME_COUNT);
 75		rr->movieStream->write(rr->movieStream, &rr->frames, sizeof(rr->frames));
 76		_emitTag(rr, rr->movieStream, TAG_LAG_COUNT);
 77		rr->movieStream->write(rr->movieStream, &rr->lagFrames, sizeof(rr->lagFrames));
 78		_emitTag(rr, rr->movieStream, TAG_NEXT_TIME);
 79		rr->movieStream->write(rr->movieStream, &newStreamId, sizeof(newStreamId));
 80	}
 81	if (!GBARRLoadStream(rr, newStreamId)) {
 82		return false;
 83	}
 84	rr->maxStreamId = newStreamId;
 85	_emitTag(rr, rr->movieStream, TAG_PREVIOUSLY);
 86	rr->movieStream->write(rr->movieStream, &oldStreamId, sizeof(oldStreamId));
 87	_emitTag(rr, rr->movieStream, TAG_BEGIN);
 88	return true;
 89}
 90
 91bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) {
 92	if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) {
 93		return false;
 94	}
 95
 96	char buffer[14];
 97	snprintf(buffer, sizeof(buffer), "%u.log", rr->streamId);
 98	rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY);
 99	rr->autorecord = autorecord;
100	rr->peekedTag = TAG_INVALID;
101	_readTag(rr, rr->movieStream); // Discard the buffer
102	enum GBARRTag tag = _readTag(rr, rr->movieStream);
103	if (tag != TAG_BEGIN) {
104		rr->movieStream->close(rr->movieStream);
105		rr->movieStream = 0;
106		return false;
107	}
108
109	rr->isPlaying = true;
110	return true;
111}
112
113void GBARRStopPlaying(struct GBARRContext* rr) {
114	if (!GBARRIsPlaying(rr)) {
115		return;
116	}
117	rr->isPlaying = false;
118	if (rr->movieStream) {
119		rr->movieStream->close(rr->movieStream);
120		rr->movieStream = 0;
121	}
122}
123
124bool GBARRStartRecording(struct GBARRContext* rr) {
125	if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) {
126		return false;
127	}
128
129	char buffer[14];
130	snprintf(buffer, sizeof(buffer), "%u.log", rr->streamId);
131	rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_TRUNC | O_CREAT | O_WRONLY);
132	if (!_emitTag(rr, rr->movieStream, TAG_BEGIN)) {
133		rr->movieStream->close(rr->movieStream);
134		rr->movieStream = 0;
135		return false;
136	}
137
138	rr->isRecording = true;
139	return true;
140}
141
142void GBARRStopRecording(struct GBARRContext* rr) {
143	if (!GBARRIsRecording(rr)) {
144		return;
145	}
146	rr->isRecording = false;
147	if (rr->movieStream) {
148		_emitTag(rr, rr->movieStream, TAG_END);
149		rr->movieStream->close(rr->movieStream);
150		rr->movieStream = 0;
151	}
152}
153
154bool GBARRIsPlaying(struct GBARRContext* rr) {
155	return rr && rr->isPlaying;
156}
157
158bool GBARRIsRecording(struct GBARRContext* rr) {
159	return rr && rr->isRecording;
160}
161
162void GBARRNextFrame(struct GBARRContext* rr) {
163	if (!GBARRIsRecording(rr) && !GBARRIsPlaying(rr)) {
164		return;
165	}
166
167	++rr->frames;
168	if (!rr->inputThisFrame) {
169		++rr->lagFrames;
170	}
171
172	if (GBARRIsRecording(rr)) {
173		if (!rr->inputThisFrame) {
174			_emitTag(rr, rr->movieStream, TAG_LAG);
175		}
176		_emitTag(rr, rr->movieStream, TAG_FRAME);
177
178		rr->inputThisFrame = false;
179	} else {
180		if (!_seekTag(rr, rr->movieStream, TAG_FRAME)) {
181			GBARRStopPlaying(rr);
182		}
183	}
184}
185
186void GBARRLogInput(struct GBARRContext* rr, uint16_t keys) {
187	if (!GBARRIsRecording(rr)) {
188		return;
189	}
190
191	if (keys != rr->currentInput) {
192		_emitTag(rr, rr->movieStream, TAG_INPUT);
193		rr->movieStream->write(rr->movieStream, &keys, sizeof(keys));
194		rr->currentInput = keys;
195	}
196	rr->inputThisFrame = true;
197}
198
199uint16_t GBARRQueryInput(struct GBARRContext* rr) {
200	if (!GBARRIsPlaying(rr)) {
201		return 0;
202	}
203
204	if (rr->peekedTag == TAG_INPUT) {
205		_readTag(rr, rr->movieStream);
206	}
207	return rr->currentInput;
208}
209
210enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) {
211	enum GBARRTag tag = rr->peekedTag;
212	switch (tag) {
213	case TAG_INPUT:
214		vf->read(vf, &rr->currentInput, sizeof(uint16_t));
215		break;
216	case TAG_PREVIOUSLY:
217		vf->read(vf, &rr->previously, sizeof(rr->previously));
218		break;
219	case TAG_NEXT_TIME:
220		vf->read(vf, &rr->nextTime, sizeof(rr->nextTime));
221		break;
222	case TAG_MAX_STREAM:
223		vf->read(vf, &rr->maxStreamId, sizeof(rr->maxStreamId));
224		break;
225	case TAG_FRAME_COUNT:
226		vf->read(vf, &rr->frames, sizeof(rr->frames));
227		break;
228	case TAG_LAG_COUNT:
229		vf->read(vf, &rr->lagFrames, sizeof(rr->lagFrames));
230		break;
231
232	// To be spec'd
233	case TAG_RR_COUNT:
234	case TAG_INIT_TYPE:
235	case TAG_AUTHOR:
236	case TAG_COMMENT:
237		break;
238
239	// Empty markers
240	case TAG_FRAME:
241	case TAG_LAG:
242	case TAG_BEGIN:
243	case TAG_END:
244	case TAG_INVALID:
245	case TAG_EOF:
246		break;
247	}
248
249	uint8_t tagBuffer;
250	if (vf->read(vf, &tagBuffer, 1) != 1) {
251		rr->peekedTag = TAG_EOF;
252	} else {
253		rr->peekedTag = tagBuffer;
254	}
255	return tag;
256}
257
258bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) {
259	enum GBARRTag readTag;
260	while ((readTag = _readTag(rr, vf)) != tag) {
261		if (readTag == TAG_END) {
262			while (_readTag(rr, vf) != TAG_EOF);
263			if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) {
264				return false;
265			}
266			vf = rr->movieStream;
267		} else if (readTag == TAG_EOF) {
268			return false;
269		}
270	}
271	return true;
272}
273
274bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag) {
275	UNUSED(rr);
276	return vf->write(vf, &tag, 1) == 1;
277}