all repos — mgba @ dbc6567317605cf03d9c400b97ee3e03a960e04f

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