all repos — mgba @ 0e8e55e8eda47f9ba7d7b8c5867b725cfa1f7f9b

mGBA Game Boy Advance Emulator

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

  1/* Copyright (c) 2013-2015 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "vbm.h"
  7
  8#include "gba/gba.h"
  9#include "gba/serialize.h"
 10#include "util/vfs.h"
 11
 12#ifdef USE_ZLIB
 13#include <zlib.h>
 14#endif
 15
 16static const char VBM_MAGIC[] = "VBM\x1A";
 17
 18static void GBAVBMContextDestroy(struct GBARRContext*);
 19
 20static bool GBAVBMStartPlaying(struct GBARRContext*, bool autorecord);
 21static void GBAVBMStopPlaying(struct GBARRContext*);
 22static bool GBAVBMStartRecording(struct GBARRContext*);
 23static void GBAVBMStopRecording(struct GBARRContext*);
 24
 25static bool GBAVBMIsPlaying(const struct GBARRContext*);
 26static bool GBAVBMIsRecording(const struct GBARRContext*);
 27
 28static void GBAVBMNextFrame(struct GBARRContext*);
 29static uint16_t GBAVBMQueryInput(struct GBARRContext*);
 30
 31static void GBAVBMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state);
 32static void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state);
 33
 34static struct VFile* GBAVBMOpenSavedata(struct GBARRContext*, int flags);
 35static struct VFile* GBAVBMOpenSavestate(struct GBARRContext*, int flags);
 36
 37void GBAVBMContextCreate(struct GBAVBMContext* vbm) {
 38	memset(vbm, 0, sizeof(*vbm));
 39
 40	vbm->d.destroy = GBAVBMContextDestroy;
 41
 42	vbm->d.startPlaying = GBAVBMStartPlaying;
 43	vbm->d.stopPlaying = GBAVBMStopPlaying;
 44	vbm->d.startRecording = GBAVBMStartRecording;
 45	vbm->d.stopRecording = GBAVBMStopRecording;
 46
 47	vbm->d.isPlaying = GBAVBMIsPlaying;
 48	vbm->d.isRecording = GBAVBMIsRecording;
 49
 50	vbm->d.nextFrame = GBAVBMNextFrame;
 51	vbm->d.logInput = 0;
 52	vbm->d.queryInput = GBAVBMQueryInput;
 53
 54	vbm->d.stateSaved = GBAVBMStateSaved;
 55	vbm->d.stateLoaded = GBAVBMStateLoaded;
 56
 57	vbm->d.openSavedata = GBAVBMOpenSavedata;
 58	vbm->d.openSavestate = GBAVBMOpenSavestate;
 59}
 60
 61bool GBAVBMStartPlaying(struct GBARRContext* rr, bool autorecord) {
 62	if (rr->isRecording(rr) || rr->isPlaying(rr) || autorecord) {
 63		return false;
 64	}
 65
 66	struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
 67	vbm->isPlaying = true;
 68	vbm->vbmFile->seek(vbm->vbmFile, vbm->inputOffset, SEEK_SET);
 69	return true;
 70}
 71
 72void GBAVBMStopPlaying(struct GBARRContext* rr) {
 73	if (!rr->isPlaying(rr)) {
 74		return;
 75	}
 76
 77	struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
 78	vbm->isPlaying = false;
 79}
 80
 81bool GBAVBMStartRecording(struct GBARRContext* rr) {
 82	UNUSED(rr);
 83	return false;
 84}
 85
 86void GBAVBMStopRecording(struct GBARRContext* rr) {
 87	UNUSED(rr);
 88}
 89
 90bool GBAVBMIsPlaying(const struct GBARRContext* rr) {
 91	struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
 92	return vbm->isPlaying;
 93}
 94
 95bool GBAVBMIsRecording(const struct GBARRContext* rr) {
 96	UNUSED(rr);
 97	return false;
 98}
 99
100void GBAVBMNextFrame(struct GBARRContext* rr) {
101	if (!rr->isPlaying(rr)) {
102		return;
103	}
104
105	struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
106	vbm->vbmFile->seek(vbm->vbmFile, sizeof(uint16_t), SEEK_CUR);
107}
108
109uint16_t GBAVBMQueryInput(struct GBARRContext* rr) {
110	if (!rr->isPlaying(rr)) {
111		return 0;
112	}
113
114	struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
115	uint16_t input;
116	vbm->vbmFile->read(vbm->vbmFile, &input, sizeof(input));
117	vbm->vbmFile->seek(vbm->vbmFile, -sizeof(input), SEEK_CUR);
118	return input & 0x3FF;
119}
120
121void GBAVBMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state) {
122	UNUSED(rr);
123	UNUSED(state);
124}
125
126void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state) {
127	UNUSED(rr);
128	UNUSED(state);
129}
130
131struct VFile* GBAVBMOpenSavedata(struct GBARRContext* rr, int flags) {
132	UNUSED(flags);
133#ifndef USE_ZLIB
134	UNUSED(rr);
135	return 0;
136#else
137	struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
138	off_t pos = vbm->vbmFile->seek(vbm->vbmFile, 0, SEEK_CUR);
139	uint32_t saveType, flashSize, sramOffset;
140	vbm->vbmFile->seek(vbm->vbmFile, 0x18, SEEK_SET);
141	vbm->vbmFile->read(vbm->vbmFile, &saveType, sizeof(saveType));
142	vbm->vbmFile->read(vbm->vbmFile, &flashSize, sizeof(flashSize));
143	vbm->vbmFile->seek(vbm->vbmFile, 0x38, SEEK_SET);
144	vbm->vbmFile->read(vbm->vbmFile, &sramOffset, sizeof(sramOffset));
145	if (!sramOffset) {
146		vbm->vbmFile->seek(vbm->vbmFile, pos, SEEK_SET);
147		return 0;
148	}
149	vbm->vbmFile->seek(vbm->vbmFile, sramOffset, SEEK_SET);
150	struct VFile* save = VFileMemChunk(0, 0);
151	size_t size;
152	switch (saveType) {
153	case 1:
154		size = SIZE_CART_SRAM;
155		break;
156	case 2:
157		size = flashSize;
158		if (size > SIZE_CART_FLASH1M) {
159			size = SIZE_CART_FLASH1M;
160		}
161		break;
162	case 3:
163		size = SIZE_CART_EEPROM;
164		break;
165	default:
166		size = SIZE_CART_FLASH1M;
167		break;
168	}
169	uLong zlen = vbm->inputOffset - sramOffset;
170	char buffer[8761];
171	char* zbuffer = malloc(zlen);
172	vbm->vbmFile->read(vbm->vbmFile, zbuffer, zlen);
173	z_stream zstr;
174	zstr.zalloc = Z_NULL;
175	zstr.zfree = Z_NULL;
176	zstr.opaque = Z_NULL;
177	zstr.avail_in = zlen;
178	zstr.next_in = (Bytef*) zbuffer;
179	zstr.avail_out = 0;
180	inflateInit2(&zstr, 31);
181	// Skip header, we know where the save file is
182	zstr.avail_out = sizeof(buffer);
183	zstr.next_out = (Bytef*) &buffer;
184	int err = inflate(&zstr, 0);
185	while (err != Z_STREAM_END && !zstr.avail_out) {
186		zstr.avail_out = sizeof(buffer);
187		zstr.next_out = (Bytef*) &buffer;
188		int err = inflate(&zstr, 0);
189		if (err < 0) {
190			break;
191		}
192		save->write(save, buffer, sizeof(buffer) - zstr.avail_out);
193	}
194	inflateEnd(&zstr);
195	vbm->vbmFile->seek(vbm->vbmFile, pos, SEEK_SET);
196	return save;
197#endif
198}
199
200struct VFile* GBAVBMOpenSavestate(struct GBARRContext* rr, int flags) {
201	UNUSED(rr);
202	UNUSED(flags);
203	return 0;
204}
205
206void GBAVBMContextDestroy(struct GBARRContext* rr) {
207	struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
208	if (vbm->vbmFile) {
209		vbm->vbmFile->close(vbm->vbmFile);
210	}
211}
212
213bool GBAVBMSetStream(struct GBAVBMContext* vbm, struct VFile* vf) {
214	vf->seek(vf, 0, SEEK_SET);
215	char magic[4];
216	vf->read(vf, magic, sizeof(magic));
217	if (memcmp(magic, VBM_MAGIC, sizeof(magic)) != 0) {
218		return false;
219	}
220
221	uint32_t id;
222	vf->read(vf, &id, sizeof(id));
223	if (id != 1) {
224		return false;
225	}
226
227	vf->seek(vf, 4, SEEK_CUR);
228	vf->read(vf, &vbm->d.frames, sizeof(vbm->d.frames));
229	vf->read(vf, &vbm->d.rrCount, sizeof(vbm->d.rrCount));
230
231	uint8_t flags;
232	vf->read(vf, &flags, sizeof(flags));
233	if (flags & 2) {
234#if USE_ZLIB
235		vbm->d.initFrom = INIT_FROM_SAVEGAME;
236#else
237		// zlib is needed to parse the savegame
238		return false;
239#endif
240	}
241	if (flags & 1) {
242		// Incompatible savestate format
243		return false;
244	}
245
246	vf->seek(vf, 1, SEEK_CUR);
247	vf->read(vf, &flags, sizeof(flags));
248	if ((flags & 0x7) != 1) {
249		// Non-GBA movie
250		return false;
251	}
252
253	// TODO: parse more flags
254
255	vf->seek(vf, 0x3C, SEEK_SET);
256	vf->read(vf, &vbm->inputOffset, sizeof(vbm->inputOffset));
257	vf->seek(vf, vbm->inputOffset, SEEK_SET);
258	vbm->vbmFile = vf;
259	return true;
260}