all repos — mgba @ f4dc546da68a757b6e8c04a379583909902f3628

mGBA Game Boy Advance Emulator

src/gba/serialize.c (view raw)

  1/* Copyright (c) 2013-2016 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 "serialize.h"
  7
  8#include "core/serialize.h"
  9#include "core/sync.h"
 10#include "gba/audio.h"
 11#include "gba/cheats.h"
 12#include "gba/io.h"
 13#include "gba/rr/rr.h"
 14#include "gba/video.h"
 15
 16#include "util/memory.h"
 17#include "util/vfs.h"
 18
 19#include <fcntl.h>
 20#ifdef _MSC_VER
 21#include <time.h>
 22#else
 23#include <sys/time.h>
 24#endif
 25
 26#ifdef USE_PNG
 27#include "util/png-io.h"
 28#include <png.h>
 29#include <zlib.h>
 30#endif
 31
 32const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
 33const uint32_t GBA_SAVESTATE_VERSION = 0x00000001;
 34
 35mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate");
 36
 37struct GBABundledState {
 38	struct GBASerializedState* state;
 39	struct mStateExtdata* extdata;
 40};
 41
 42void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
 43	STORE_32(GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, 0, &state->versionMagic);
 44	STORE_32(gba->biosChecksum, 0, &state->biosChecksum);
 45	STORE_32(gba->romCrc32, 0, &state->romCrc32);
 46
 47	if (gba->memory.rom) {
 48		state->id = ((struct GBACartridge*) gba->memory.rom)->id;
 49		memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title));
 50	} else {
 51		state->id = 0;
 52		memset(state->title, 0, sizeof(state->title));
 53	}
 54
 55	int i;
 56	for (i = 0; i < 16; ++i) {
 57		STORE_32(gba->cpu->gprs[i], i * sizeof(state->cpu.gprs[0]), state->cpu.gprs);
 58	}
 59	STORE_32(gba->cpu->cpsr.packed, 0, &state->cpu.cpsr.packed);
 60	STORE_32(gba->cpu->spsr.packed, 0, &state->cpu.spsr.packed);
 61	STORE_32(gba->cpu->cycles, 0, &state->cpu.cycles);
 62	STORE_32(gba->cpu->nextEvent, 0, &state->cpu.nextEvent);
 63	for (i = 0; i < 6; ++i) {
 64		int j;
 65		for (j = 0; j < 7; ++j) {
 66			STORE_32(gba->cpu->bankedRegisters[i][j], (i * 7 + j) * sizeof(gba->cpu->bankedRegisters[0][0]), state->cpu.bankedRegisters);
 67		}
 68		STORE_32(gba->cpu->bankedSPSRs[i], i * sizeof(gba->cpu->bankedSPSRs[0]), state->cpu.bankedSPSRs);
 69	}
 70
 71	state->biosPrefetch = gba->memory.biosPrefetch;
 72	STORE_32(gba->cpu->prefetch[0], 0, state->cpuPrefetch);
 73	STORE_32(gba->cpu->prefetch[1], 4, state->cpuPrefetch);
 74
 75	GBAMemorySerialize(&gba->memory, state);
 76	GBAIOSerialize(gba, state);
 77	GBAVideoSerialize(&gba->video, state);
 78	GBAAudioSerialize(&gba->audio, state);
 79	GBASavedataSerialize(&gba->memory.savedata, state);
 80
 81#ifndef _MSC_VER
 82	struct timeval tv;
 83	if (!gettimeofday(&tv, 0)) {
 84		uint64_t usec = tv.tv_usec;
 85		usec += tv.tv_sec * 1000000LL;
 86		STORE_64(usec, 0, &state->creationUsec);
 87	}
 88#else
 89	struct timespec ts;
 90	if (timespec_get(&ts, TIME_UTC)) {
 91		uint64_t usec = ts.tv_nsec / 1000;
 92		usec += ts.tv_sec * 1000000LL;
 93		STORE_64(usec, 0, &state->creationUsec);
 94	}
 95#endif
 96	else {
 97		state->creationUsec = 0;
 98	}
 99	state->associatedStreamId = 0;
100	if (gba->rr) {
101		gba->rr->stateSaved(gba->rr, state);
102	}
103}
104
105bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
106	bool error = false;
107	int32_t check;
108	uint32_t ucheck;
109	LOAD_32(ucheck, 0, &state->versionMagic);
110	if (ucheck > GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION) {
111		mLOG(GBA_STATE, WARN, "Invalid or too new savestate: expected %08X, got %08X", GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, ucheck);
112		error = true;
113	} else if (ucheck < GBA_SAVESTATE_MAGIC) {
114		mLOG(GBA_STATE, WARN, "Invalid savestate: expected %08X, got %08X", GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, ucheck);
115		error = true;
116	} else if (ucheck < GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION) {
117		mLOG(GBA_STATE, WARN, "Old savestate: expected %08X, got %08X, continuing anyway", GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, ucheck);
118	}
119	LOAD_32(ucheck, 0, &state->biosChecksum);
120	if (ucheck != gba->biosChecksum) {
121		mLOG(GBA_STATE, WARN, "Savestate created using a different version of the BIOS: expected %08X, got %08X", gba->biosChecksum, ucheck);
122		uint32_t pc;
123		LOAD_32(pc, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs);
124		if (pc < SIZE_BIOS && pc >= 0x20) {
125			error = true;
126		}
127	}
128	if (gba->memory.rom && (state->id != ((struct GBACartridge*) gba->memory.rom)->id || memcmp(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title)))) {
129		mLOG(GBA_STATE, WARN, "Savestate is for a different game");
130		error = true;
131	} else if (!gba->memory.rom && state->id != 0) {
132		mLOG(GBA_STATE, WARN, "Savestate is for a game, but no game loaded");
133		error = true;
134	}
135	LOAD_32(ucheck, 0, &state->romCrc32);
136	if (ucheck != gba->romCrc32) {
137		mLOG(GBA_STATE, WARN, "Savestate is for a different version of the game");
138	}
139	LOAD_32(check, 0, &state->cpu.cycles);
140	if (check < 0) {
141		mLOG(GBA_STATE, WARN, "Savestate is corrupted: CPU cycles are negative");
142		error = true;
143	}
144	if (check >= (int32_t) GBA_ARM7TDMI_FREQUENCY) {
145		mLOG(GBA_STATE, WARN, "Savestate is corrupted: CPU cycles are too high");
146		error = true;
147	}
148	LOAD_32(check, 0, &state->video.eventDiff);
149	if (check < 0) {
150		mLOG(GBA_STATE, WARN, "Savestate is corrupted: video eventDiff is negative");
151		error = true;
152	}
153	LOAD_32(check, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs);
154	int region = (check >> BASE_OFFSET);
155	if ((region == REGION_CART0 || region == REGION_CART1 || region == REGION_CART2) && ((check - WORD_SIZE_ARM) & SIZE_CART0) >= gba->memory.romSize - WORD_SIZE_ARM) {
156		mLOG(GBA_STATE, WARN, "Savestate created using a differently sized version of the ROM");
157		error = true;
158	}
159	if (error) {
160		return false;
161	}
162	size_t i;
163	for (i = 0; i < 16; ++i) {
164		LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs);
165	}
166	LOAD_32(gba->cpu->cpsr.packed, 0, &state->cpu.cpsr.packed);
167	LOAD_32(gba->cpu->spsr.packed, 0, &state->cpu.spsr.packed);
168	LOAD_32(gba->cpu->cycles, 0, &state->cpu.cycles);
169	LOAD_32(gba->cpu->nextEvent, 0, &state->cpu.nextEvent);
170	for (i = 0; i < 6; ++i) {
171		int j;
172		for (j = 0; j < 7; ++j) {
173			LOAD_32(gba->cpu->bankedRegisters[i][j], (i * 7 + j) * sizeof(gba->cpu->bankedRegisters[0][0]), state->cpu.bankedRegisters);
174		}
175		LOAD_32(gba->cpu->bankedSPSRs[i], i * sizeof(gba->cpu->bankedSPSRs[0]), state->cpu.bankedSPSRs);
176	}
177	gba->cpu->privilegeMode = gba->cpu->cpsr.priv;
178	gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
179	if (state->biosPrefetch) {
180		LOAD_32(gba->memory.biosPrefetch, 0, &state->biosPrefetch);
181	}
182	if (gba->cpu->cpsr.t) {
183		gba->cpu->executionMode = MODE_THUMB;
184		if (state->cpuPrefetch[0] && state->cpuPrefetch[1]) {
185			LOAD_32(gba->cpu->prefetch[0], 0, state->cpuPrefetch);
186			LOAD_32(gba->cpu->prefetch[1], 4, state->cpuPrefetch);
187			gba->cpu->prefetch[0] &= 0xFFFF;
188			gba->cpu->prefetch[1] &= 0xFFFF;
189		} else {
190			// Maintain backwards compat
191			LOAD_16(gba->cpu->prefetch[0], (gba->cpu->gprs[ARM_PC] - WORD_SIZE_THUMB) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
192			LOAD_16(gba->cpu->prefetch[1], (gba->cpu->gprs[ARM_PC]) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
193		}
194	} else {
195		gba->cpu->executionMode = MODE_ARM;
196		if (state->cpuPrefetch[0] && state->cpuPrefetch[1]) {
197			LOAD_32(gba->cpu->prefetch[0], 0, state->cpuPrefetch);
198			LOAD_32(gba->cpu->prefetch[1], 4, state->cpuPrefetch);
199		} else {
200			// Maintain backwards compat
201			LOAD_32(gba->cpu->prefetch[0], (gba->cpu->gprs[ARM_PC] - WORD_SIZE_ARM) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
202			LOAD_32(gba->cpu->prefetch[1], (gba->cpu->gprs[ARM_PC]) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
203		}
204	}
205
206	GBAMemoryDeserialize(&gba->memory, state);
207	GBAIODeserialize(gba, state);
208	GBAVideoDeserialize(&gba->video, state);
209	GBAAudioDeserialize(&gba->audio, state);
210	GBASavedataDeserialize(&gba->memory.savedata, state);
211
212	if (gba->rr) {
213		gba->rr->stateLoaded(gba->rr, state);
214	}
215	return true;
216}
217
218#ifdef USE_PNG
219static bool _savePNGState(struct GBA* gba, struct VFile* vf, struct mStateExtdata* extdata) {
220	unsigned stride;
221	const void* pixels = 0;
222	gba->video.renderer->getPixels(gba->video.renderer, &stride, &pixels);
223	if (!pixels) {
224		return false;
225	}
226
227	struct GBASerializedState* state = GBAAllocateState();
228	if (!state) {
229		return false;
230	}
231	GBASerialize(gba, state);
232	uLongf len = compressBound(sizeof(*state));
233	void* buffer = malloc(len);
234	if (!buffer) {
235		GBADeallocateState(state);
236		return false;
237	}
238	compress(buffer, &len, (const Bytef*) state, sizeof(*state));
239	GBADeallocateState(state);
240
241	png_structp png = PNGWriteOpen(vf);
242	png_infop info = PNGWriteHeader(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
243	if (!png || !info) {
244		PNGWriteClose(png, info);
245		free(buffer);
246		return false;
247	}
248	PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, stride, pixels);
249	PNGWriteCustomChunk(png, "gbAs", len, buffer);
250	if (extdata) {
251		uint32_t i;
252		for (i = 1; i < EXTDATA_MAX; ++i) {
253			if (!extdata->data[i].data) {
254				continue;
255			}
256			uLongf len = compressBound(extdata->data[i].size) + sizeof(uint32_t) * 2;
257			uint32_t* data = malloc(len);
258			if (!data) {
259				continue;
260			}
261			STORE_32(i, 0, data);
262			STORE_32(extdata->data[i].size, sizeof(uint32_t), data);
263			compress((Bytef*) (data + 2), &len, extdata->data[i].data, extdata->data[i].size);
264			PNGWriteCustomChunk(png, "gbAx", len + sizeof(uint32_t) * 2, data);
265			free(data);
266		}
267	}
268	PNGWriteClose(png, info);
269	free(buffer);
270	return true;
271}
272
273static int _loadPNGChunkHandler(png_structp png, png_unknown_chunkp chunk) {
274	struct GBABundledState* bundle = png_get_user_chunk_ptr(png);
275	if (!bundle) {
276		return 0;
277	}
278	if (!strcmp((const char*) chunk->name, "gbAs")) {
279		struct GBASerializedState* state = bundle->state;
280		if (!state) {
281			return 0;
282		}
283		uLongf len = sizeof(*state);
284		uncompress((Bytef*) state, &len, chunk->data, chunk->size);
285		return 1;
286	}
287	if (!strcmp((const char*) chunk->name, "gbAx")) {
288		struct mStateExtdata* extdata = bundle->extdata;
289		if (!extdata) {
290			return 0;
291		}
292		struct mStateExtdataItem item;
293		if (chunk->size < sizeof(uint32_t) * 2) {
294			return 0;
295		}
296		uint32_t tag;
297		LOAD_32(tag, 0, chunk->data);
298		LOAD_32(item.size, sizeof(uint32_t), chunk->data);
299		uLongf len = item.size;
300		if (item.size < 0 || tag == EXTDATA_NONE || tag >= EXTDATA_MAX) {
301			return 0;
302		}
303		item.data = malloc(item.size);
304		item.clean = free;
305		if (!item.data) {
306			return 0;
307		}
308		const uint8_t* data = chunk->data;
309		data += sizeof(uint32_t) * 2;
310		uncompress((Bytef*) item.data, &len, data, chunk->size);
311		item.size = len;
312		mStateExtdataPut(extdata, tag, &item);
313		return 1;
314	}
315	return 0;
316}
317
318static struct GBASerializedState* _loadPNGState(struct VFile* vf, struct mStateExtdata* extdata) {
319	png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES);
320	png_infop info = png_create_info_struct(png);
321	png_infop end = png_create_info_struct(png);
322	if (!png || !info || !end) {
323		PNGReadClose(png, info, end);
324		return false;
325	}
326	uint32_t* pixels = malloc(VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4);
327	if (!pixels) {
328		PNGReadClose(png, info, end);
329		return false;
330	}
331
332	struct GBASerializedState* state = GBAAllocateState();
333	struct GBABundledState bundle = {
334		.state = state,
335		.extdata = extdata
336	};
337
338	PNGInstallChunkHandler(png, &bundle, _loadPNGChunkHandler, "gbAs gbAx");
339	bool success = PNGReadHeader(png, info);
340	success = success && PNGReadPixels(png, info, pixels, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, VIDEO_HORIZONTAL_PIXELS);
341	success = success && PNGReadFooter(png, end);
342	PNGReadClose(png, info, end);
343
344	if (success) {
345		struct mStateExtdataItem item = {
346			.size = VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4,
347			.data = pixels,
348			.clean = free
349		};
350		mStateExtdataPut(extdata, EXTDATA_SCREENSHOT, &item);
351	} else {
352		free(pixels);
353		GBADeallocateState(state);
354		return 0;
355	}
356	return state;
357}
358#endif
359
360bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, int flags) {
361	struct mStateExtdata extdata;
362	mStateExtdataInit(&extdata);
363	if (flags & SAVESTATE_SAVEDATA) {
364		// TODO: A better way to do this would be nice
365		void* sram = malloc(SIZE_CART_FLASH1M);
366		struct VFile* svf = VFileFromMemory(sram, SIZE_CART_FLASH1M);
367		if (GBASavedataClone(&gba->memory.savedata, svf)) {
368			struct mStateExtdataItem item = {
369				.size = svf->seek(svf, 0, SEEK_CUR),
370				.data = sram,
371				.clean = free
372			};
373			mStateExtdataPut(&extdata, EXTDATA_SAVEDATA, &item);
374		} else {
375			free(sram);
376		}
377		svf->close(svf);
378	}
379	struct VFile* cheatVf = 0;
380	if (flags & SAVESTATE_CHEATS && gba->cpu->components && gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE]) {
381		struct mCheatDevice* device = (struct mCheatDevice*) gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE];
382		cheatVf = VFileMemChunk(0, 0);
383		if (cheatVf) {
384			mCheatSaveFile(device, cheatVf);
385			struct mStateExtdataItem item = {
386				.size = cheatVf->size(cheatVf),
387				.data = cheatVf->map(cheatVf, cheatVf->size(cheatVf), MAP_READ),
388				.clean = 0
389			};
390			mStateExtdataPut(&extdata, EXTDATA_CHEATS, &item);
391		}
392	};
393#ifdef USE_PNG
394	if (!(flags & SAVESTATE_SCREENSHOT)) {
395#else
396	UNUSED(flags);
397#endif
398		vf->truncate(vf, sizeof(struct GBASerializedState));
399		struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE);
400		if (!state) {
401			mStateExtdataDeinit(&extdata);
402			if (cheatVf) {
403				cheatVf->close(cheatVf);
404			}
405			return false;
406		}
407		GBASerialize(gba, state);
408		vf->unmap(vf, state, sizeof(struct GBASerializedState));
409		vf->seek(vf, sizeof(struct GBASerializedState), SEEK_SET);
410		mStateExtdataSerialize(&extdata, vf);
411		mStateExtdataDeinit(&extdata);
412		if (cheatVf) {
413			cheatVf->close(cheatVf);
414		}
415		return true;
416#ifdef USE_PNG
417	}
418	else {
419		bool success = _savePNGState(gba, vf, &extdata);
420		mStateExtdataDeinit(&extdata);
421		return success;
422	}
423#endif
424	mStateExtdataDeinit(&extdata);
425	return false;
426}
427
428struct GBASerializedState* GBAExtractState(struct VFile* vf, struct mStateExtdata* extdata) {
429#ifdef USE_PNG
430	if (isPNG(vf)) {
431		return _loadPNGState(vf, extdata);
432	}
433#endif
434	vf->seek(vf, 0, SEEK_SET);
435	if (vf->size(vf) < (ssize_t) sizeof(struct GBASerializedState)) {
436		return false;
437	}
438	struct GBASerializedState* state = GBAAllocateState();
439	if (vf->read(vf, state, sizeof(*state)) != sizeof(*state)) {
440		GBADeallocateState(state);
441		return 0;
442	}
443	if (extdata) {
444		mStateExtdataDeserialize(extdata, vf);
445	}
446	return state;
447}
448
449bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf, int flags) {
450	struct mStateExtdata extdata;
451	mStateExtdataInit(&extdata);
452	struct GBASerializedState* state = GBAExtractState(vf, &extdata);
453	if (!state) {
454		return false;
455	}
456	bool success = GBADeserialize(gba, state);
457	GBADeallocateState(state);
458
459	struct mStateExtdataItem item;
460	if (flags & SAVESTATE_SCREENSHOT && mStateExtdataGet(&extdata, EXTDATA_SCREENSHOT, &item)) {
461		if (item.size >= VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4) {
462			gba->video.renderer->putPixels(gba->video.renderer, VIDEO_HORIZONTAL_PIXELS, item.data);
463			mCoreSyncForceFrame(gba->sync);
464		} else {
465			mLOG(GBA_STATE, WARN, "Savestate includes invalid screenshot");
466		}
467	}
468	if (flags & SAVESTATE_SAVEDATA && mStateExtdataGet(&extdata, EXTDATA_SAVEDATA, &item)) {
469		struct VFile* svf = VFileFromMemory(item.data, item.size);
470		GBASavedataLoad(&gba->memory.savedata, svf);
471		if (svf) {
472			svf->close(svf);
473		}
474	}
475	if (flags & SAVESTATE_CHEATS && gba->cpu->components && gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE] && mStateExtdataGet(&extdata, EXTDATA_CHEATS, &item)) {
476		if (item.size) {
477			struct mCheatDevice* device = (struct mCheatDevice*) gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE];
478			struct VFile* svf = VFileFromMemory(item.data, item.size);
479			if (svf) {
480				mCheatDeviceClear(device);
481				mCheatParseFile(device, svf);
482				svf->close(svf);
483			}
484		}
485	}
486	mStateExtdataDeinit(&extdata);
487	return success;
488}
489
490struct GBASerializedState* GBAAllocateState(void) {
491	return anonymousMemoryMap(sizeof(struct GBASerializedState));
492}
493
494void GBADeallocateState(struct GBASerializedState* state) {
495	mappedMemoryFree(state, sizeof(struct GBASerializedState));
496}
497
498// TODO: Put back rewind