all repos — mgba @ 34647ffdca68da94f4fb5b51df4a8dc43758ad77

mGBA Game Boy Advance Emulator

src/gb/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 <mgba/internal/gb/serialize.h>
  7
  8#include <mgba/internal/gb/io.h>
  9#include <mgba/internal/gb/timer.h>
 10#include <mgba/internal/lr35902/lr35902.h>
 11
 12mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate", "gb.serialize");
 13
 14const uint32_t GB_SAVESTATE_MAGIC = 0x00400000;
 15const uint32_t GB_SAVESTATE_VERSION = 0x00000001;
 16
 17void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
 18	STORE_32LE(GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, 0, &state->versionMagic);
 19	STORE_32LE(gb->romCrc32, 0, &state->romCrc32);
 20	STORE_32LE(gb->timing.masterCycles, 0, &state->masterCycles);
 21
 22	if (gb->memory.rom) {
 23		memcpy(state->title, ((struct GBCartridge*) gb->memory.rom)->titleLong, sizeof(state->title));
 24	} else {
 25		memset(state->title, 0, sizeof(state->title));
 26	}
 27
 28	state->model = gb->model;
 29
 30	state->cpu.a = gb->cpu->a;
 31	state->cpu.f = gb->cpu->f.packed;
 32	state->cpu.b = gb->cpu->b;
 33	state->cpu.c = gb->cpu->c;
 34	state->cpu.d = gb->cpu->d;
 35	state->cpu.e = gb->cpu->e;
 36	state->cpu.h = gb->cpu->h;
 37	state->cpu.l = gb->cpu->l;
 38	STORE_16LE(gb->cpu->sp, 0, &state->cpu.sp);
 39	STORE_16LE(gb->cpu->pc, 0, &state->cpu.pc);
 40
 41	STORE_32LE(gb->cpu->cycles, 0, &state->cpu.cycles);
 42	STORE_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent);
 43
 44	STORE_16LE(gb->cpu->index, 0, &state->cpu.index);
 45	state->cpu.bus = gb->cpu->bus;
 46	state->cpu.executionState = gb->cpu->executionState;
 47	STORE_16LE(gb->cpu->irqVector, 0, &state->cpu.irqVector);
 48
 49	GBSerializedCpuFlags flags = 0;
 50	flags = GBSerializedCpuFlagsSetCondition(flags, gb->cpu->condition);
 51	flags = GBSerializedCpuFlagsSetIrqPending(flags, gb->cpu->irqPending);
 52	flags = GBSerializedCpuFlagsSetDoubleSpeed(flags, gb->doubleSpeed);
 53	flags = GBSerializedCpuFlagsSetEiPending(flags, mTimingIsScheduled(&gb->timing, &gb->eiPending));
 54	STORE_32LE(flags, 0, &state->cpu.flags);
 55	STORE_32LE(gb->eiPending.when - mTimingCurrentTime(&gb->timing), 0, &state->cpu.eiPending);
 56
 57	GBMemorySerialize(gb, state);
 58	GBIOSerialize(gb, state);
 59	GBVideoSerialize(&gb->video, state);
 60	GBTimerSerialize(&gb->timer, state);
 61	GBAudioSerialize(&gb->audio, state);
 62}
 63
 64bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
 65	bool error = false;
 66	int32_t check;
 67	uint32_t ucheck;
 68	int16_t check16;
 69	uint16_t ucheck16;
 70	LOAD_32LE(ucheck, 0, &state->versionMagic);
 71	if (ucheck > GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION) {
 72		mLOG(GB_STATE, WARN, "Invalid or too new savestate: expected %08X, got %08X", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck);
 73		error = true;
 74	} else if (ucheck < GB_SAVESTATE_MAGIC) {
 75		mLOG(GB_STATE, WARN, "Invalid savestate: expected %08X, got %08X", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck);
 76		error = true;
 77	} else if (ucheck < GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION) {
 78		mLOG(GB_STATE, WARN, "Old savestate: expected %08X, got %08X, continuing anyway", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck);
 79	}
 80
 81	if (gb->memory.rom && memcmp(state->title, ((struct GBCartridge*) gb->memory.rom)->titleLong, sizeof(state->title))) {
 82		mLOG(GB_STATE, WARN, "Savestate is for a different game");
 83		error = true;
 84	}
 85	LOAD_32LE(ucheck, 0, &state->romCrc32);
 86	if (ucheck != gb->romCrc32) {
 87		mLOG(GB_STATE, WARN, "Savestate is for a different version of the game");
 88	}
 89	LOAD_32LE(check, 0, &state->cpu.cycles);
 90	if (check < 0) {
 91		mLOG(GB_STATE, WARN, "Savestate is corrupted: CPU cycles are negative");
 92		error = true;
 93	}
 94	if (state->cpu.executionState != LR35902_CORE_FETCH) {
 95		mLOG(GB_STATE, WARN, "Savestate is corrupted: Execution state is not FETCH");
 96		error = true;
 97	}
 98	if (check >= (int32_t) DMG_LR35902_FREQUENCY) {
 99		mLOG(GB_STATE, WARN, "Savestate is corrupted: CPU cycles are too high");
100		error = true;
101	}
102	LOAD_16LE(check16, 0, &state->video.x);
103	if (check16 < 0 || check16 > GB_VIDEO_HORIZONTAL_PIXELS) {
104		mLOG(GB_STATE, WARN, "Savestate is corrupted: video x is out of range");
105		error = true;
106	}
107	LOAD_16LE(check16, 0, &state->video.ly);
108	if (check16 < 0 || check16 > GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
109		mLOG(GB_STATE, WARN, "Savestate is corrupted: video y is out of range");
110		error = true;
111	}
112	LOAD_16LE(ucheck16, 0, &state->memory.dmaDest);
113	if (ucheck16 + state->memory.dmaRemaining > GB_SIZE_OAM) {
114		mLOG(GB_STATE, WARN, "Savestate is corrupted: DMA destination is out of range");
115		error = true;
116	}
117	LOAD_16LE(ucheck16, 0, &state->video.bcpIndex);
118	if (ucheck16 >= 0x40) {
119		mLOG(GB_STATE, WARN, "Savestate is corrupted: BCPS is out of range");
120	}
121	LOAD_16LE(ucheck16, 0, &state->video.ocpIndex);
122	if (ucheck16 >= 0x40) {
123		mLOG(GB_STATE, WARN, "Savestate is corrupted: OCPS is out of range");
124	}
125	if (error) {
126		return false;
127	}
128	gb->timing.root = NULL;
129
130	gb->cpu->a = state->cpu.a;
131	gb->cpu->f.packed = state->cpu.f;
132	gb->cpu->b = state->cpu.b;
133	gb->cpu->c = state->cpu.c;
134	gb->cpu->d = state->cpu.d;
135	gb->cpu->e = state->cpu.e;
136	gb->cpu->h = state->cpu.h;
137	gb->cpu->l = state->cpu.l;
138	LOAD_16LE(gb->cpu->sp, 0, &state->cpu.sp);
139	LOAD_16LE(gb->cpu->pc, 0, &state->cpu.pc);
140
141	LOAD_16LE(gb->cpu->index, 0, &state->cpu.index);
142	gb->cpu->bus = state->cpu.bus;
143	gb->cpu->executionState = state->cpu.executionState;
144	LOAD_16LE(gb->cpu->irqVector, 0, &state->cpu.irqVector);
145
146	GBSerializedCpuFlags flags;
147	LOAD_32LE(flags, 0, &state->cpu.flags);
148	gb->cpu->condition = GBSerializedCpuFlagsGetCondition(flags);
149	gb->cpu->irqPending = GBSerializedCpuFlagsGetIrqPending(flags);
150	gb->doubleSpeed = GBSerializedCpuFlagsGetDoubleSpeed(flags);
151	gb->audio.timingFactor = gb->doubleSpeed + 1;
152
153	uint32_t when;
154	LOAD_32LE(when, 0, &state->cpu.eiPending);
155	if (GBSerializedCpuFlagsIsEiPending(flags)) {
156		mTimingSchedule(&gb->timing, &gb->eiPending, when);
157	}
158
159	LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles);
160	LOAD_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent);
161	gb->timing.root = NULL;
162
163	gb->model = state->model;
164
165	if (gb->model < GB_MODEL_CGB) {
166		gb->audio.style = GB_AUDIO_DMG;
167	} else {
168		gb->audio.style = GB_AUDIO_CGB;
169	}
170
171	GBMemoryDeserialize(gb, state);
172	GBIODeserialize(gb, state);
173	GBVideoDeserialize(&gb->video, state);
174	GBTimerDeserialize(&gb->timer, state);
175	GBAudioDeserialize(&gb->audio, state);
176
177	gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
178
179	return true;
180}