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