src/gba/serialize.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 "serialize.h"
7
8#include "gba/audio.h"
9#include "gba/io.h"
10#include "gba/rr/rr.h"
11#include "gba/supervisor/thread.h"
12#include "gba/video.h"
13
14#include "util/memory.h"
15#include "util/vfs.h"
16
17#include <fcntl.h>
18
19#ifdef USE_PNG
20#include "util/png-io.h"
21#include <png.h>
22#include <zlib.h>
23#endif
24
25const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
26
27void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
28 STORE_32(GBA_SAVESTATE_MAGIC, 0, &state->versionMagic);
29 STORE_32(gba->biosChecksum, 0, &state->biosChecksum);
30 STORE_32(gba->romCrc32, 0, &state->romCrc32);
31
32 if (gba->memory.rom) {
33 state->id = ((struct GBACartridge*) gba->memory.rom)->id;
34 memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title));
35 } else {
36 state->id = 0;
37 memset(state->title, 0, sizeof(state->title));
38 }
39
40 int i;
41 for (i = 0; i < 16; ++i) {
42 STORE_32(gba->cpu->gprs[i], i * sizeof(state->cpu.gprs[0]), state->cpu.gprs);
43 }
44 STORE_32(gba->cpu->cpsr.packed, 0, &state->cpu.cpsr.packed);
45 STORE_32(gba->cpu->spsr.packed, 0, &state->cpu.spsr.packed);
46 STORE_32(gba->cpu->cycles, 0, &state->cpu.cycles);
47 STORE_32(gba->cpu->nextEvent, 0, &state->cpu.nextEvent);
48 for (i = 0; i < 6; ++i) {
49 int j;
50 for (j = 0; j < 7; ++j) {
51 STORE_32(gba->cpu->bankedRegisters[i][j], (i * 7 + j) * sizeof(gba->cpu->bankedRegisters[0][0]), state->cpu.bankedRegisters);
52 }
53 STORE_32(gba->cpu->bankedSPSRs[i], i * sizeof(gba->cpu->bankedSPSRs[0]), state->cpu.bankedSPSRs);
54 }
55
56 state->biosPrefetch = gba->memory.biosPrefetch;
57 STORE_32(gba->cpu->prefetch[0], 0, state->cpuPrefetch);
58 STORE_32(gba->cpu->prefetch[1], 4, state->cpuPrefetch);
59
60 GBAMemorySerialize(&gba->memory, state);
61 GBAIOSerialize(gba, state);
62 GBAVideoSerialize(&gba->video, state);
63 GBAAudioSerialize(&gba->audio, state);
64 GBASavedataSerialize(&gba->memory.savedata, state, false);
65
66 state->associatedStreamId = 0;
67 if (gba->rr) {
68 gba->rr->stateSaved(gba->rr, state);
69 }
70}
71
72bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
73 bool error = false;
74 int32_t check;
75 uint32_t ucheck;
76 LOAD_32(ucheck, 0, &state->versionMagic);
77 if (ucheck != GBA_SAVESTATE_MAGIC) {
78 GBALog(gba, GBA_LOG_WARN, "Invalid or too new savestate: expected %08X, got %08X", GBA_SAVESTATE_MAGIC, ucheck);
79 error = true;
80 }
81 LOAD_32(ucheck, 0, &state->biosChecksum);
82 if (ucheck != gba->biosChecksum) {
83 GBALog(gba, GBA_LOG_WARN, "Savestate created using a different version of the BIOS: expected %08X, got %08X", gba->biosChecksum, ucheck);
84 uint32_t pc;
85 LOAD_32(pc, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs);
86 if (pc < SIZE_BIOS && pc >= 0x20) {
87 error = true;
88 }
89 }
90 if (gba->memory.rom && (state->id != ((struct GBACartridge*) gba->memory.rom)->id || memcmp(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title)))) {
91 GBALog(gba, GBA_LOG_WARN, "Savestate is for a different game");
92 error = true;
93 } else if (!gba->memory.rom && state->id != 0) {
94 GBALog(gba, GBA_LOG_WARN, "Savestate is for a game, but no game loaded");
95 error = true;
96 }
97 LOAD_32(ucheck, 0, &state->romCrc32);
98 if (ucheck != gba->romCrc32) {
99 GBALog(gba, GBA_LOG_WARN, "Savestate is for a different version of the game");
100 }
101 LOAD_32(check, 0, &state->cpu.cycles);
102 if (check < 0) {
103 GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: CPU cycles are negative");
104 error = true;
105 }
106 if (check >= (int32_t) GBA_ARM7TDMI_FREQUENCY) {
107 GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: CPU cycles are too high");
108 error = true;
109 }
110 LOAD_32(check, 0, &state->video.eventDiff);
111 if (check < 0) {
112 GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: video eventDiff is negative");
113 error = true;
114 }
115 LOAD_32(check, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs);
116 int region = (check >> BASE_OFFSET);
117 if ((region == REGION_CART0 || region == REGION_CART1 || region == REGION_CART2) && ((check - WORD_SIZE_ARM) & SIZE_CART0) >= gba->memory.romSize - WORD_SIZE_ARM) {
118 GBALog(gba, GBA_LOG_WARN, "Savestate created using a differently sized version of the ROM");
119 error = true;
120 }
121 if (error) {
122 return false;
123 }
124 size_t i;
125 for (i = 0; i < 16; ++i) {
126 LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs);
127 }
128 LOAD_32(gba->cpu->cpsr.packed, 0, &state->cpu.cpsr.packed);
129 LOAD_32(gba->cpu->spsr.packed, 0, &state->cpu.spsr.packed);
130 LOAD_32(gba->cpu->cycles, 0, &state->cpu.cycles);
131 LOAD_32(gba->cpu->nextEvent, 0, &state->cpu.nextEvent);
132 for (i = 0; i < 6; ++i) {
133 int j;
134 for (j = 0; j < 7; ++j) {
135 LOAD_32(gba->cpu->bankedRegisters[i][j], (i * 7 + j) * sizeof(gba->cpu->bankedRegisters[0][0]), state->cpu.bankedRegisters);
136 }
137 LOAD_32(gba->cpu->bankedSPSRs[i], i * sizeof(gba->cpu->bankedSPSRs[0]), state->cpu.bankedSPSRs);
138 }
139 gba->cpu->privilegeMode = gba->cpu->cpsr.priv;
140 gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
141 if (state->biosPrefetch) {
142 LOAD_32(gba->memory.biosPrefetch, 0, &state->biosPrefetch);
143 }
144 if (gba->cpu->cpsr.t) {
145 gba->cpu->executionMode = MODE_THUMB;
146 if (state->cpuPrefetch[0] && state->cpuPrefetch[1]) {
147 LOAD_32(gba->cpu->prefetch[0], 0, state->cpuPrefetch);
148 LOAD_32(gba->cpu->prefetch[1], 4, state->cpuPrefetch);
149 gba->cpu->prefetch[0] &= 0xFFFF;
150 gba->cpu->prefetch[1] &= 0xFFFF;
151 } else {
152 // Maintain backwards compat
153 LOAD_16(gba->cpu->prefetch[0], (gba->cpu->gprs[ARM_PC] - WORD_SIZE_THUMB) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
154 LOAD_16(gba->cpu->prefetch[1], (gba->cpu->gprs[ARM_PC]) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
155 }
156 } else {
157 gba->cpu->executionMode = MODE_ARM;
158 if (state->cpuPrefetch[0] && state->cpuPrefetch[1]) {
159 LOAD_32(gba->cpu->prefetch[0], 0, state->cpuPrefetch);
160 LOAD_32(gba->cpu->prefetch[1], 4, state->cpuPrefetch);
161 } else {
162 // Maintain backwards compat
163 LOAD_32(gba->cpu->prefetch[0], (gba->cpu->gprs[ARM_PC] - WORD_SIZE_ARM) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
164 LOAD_32(gba->cpu->prefetch[1], (gba->cpu->gprs[ARM_PC]) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
165 }
166 }
167
168 GBAMemoryDeserialize(&gba->memory, state);
169 GBAIODeserialize(gba, state);
170 GBAVideoDeserialize(&gba->video, state);
171 GBAAudioDeserialize(&gba->audio, state);
172 GBASavedataDeserialize(&gba->memory.savedata, state, false);
173
174 if (gba->rr) {
175 gba->rr->stateLoaded(gba->rr, state);
176 }
177 return true;
178}
179
180struct VFile* GBAGetState(struct GBA* gba, struct VDir* dir, int slot, bool write) {
181 char suffix[5] = { '\0' };
182 snprintf(suffix, sizeof(suffix), ".ss%d", slot);
183 return VDirOptionalOpenFile(dir, gba->activeFile, "savestate", suffix, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
184}
185
186#ifdef USE_PNG
187static bool _savePNGState(struct GBA* gba, struct VFile* vf) {
188 unsigned stride;
189 const void* pixels = 0;
190 gba->video.renderer->getPixels(gba->video.renderer, &stride, &pixels);
191 if (!pixels) {
192 return false;
193 }
194
195 struct GBASerializedState* state = GBAAllocateState();
196 if (!state) {
197 return false;
198 }
199 GBASerialize(gba, state);
200 uLongf len = compressBound(sizeof(*state));
201 void* buffer = malloc(len);
202 if (!buffer) {
203 GBADeallocateState(state);
204 return false;
205 }
206 compress(buffer, &len, (const Bytef*) state, sizeof(*state));
207 GBADeallocateState(state);
208
209 png_structp png = PNGWriteOpen(vf);
210 png_infop info = PNGWriteHeader(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
211 if (!png || !info) {
212 PNGWriteClose(png, info);
213 free(buffer);
214 return false;
215 }
216 PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, stride, pixels);
217 PNGWriteCustomChunk(png, "gbAs", len, buffer);
218 PNGWriteClose(png, info);
219 free(buffer);
220 return true;
221}
222
223static int _loadPNGChunkHandler(png_structp png, png_unknown_chunkp chunk) {
224 if (strcmp((const char*) chunk->name, "gbAs") != 0) {
225 return 0;
226 }
227 struct GBASerializedState* state = GBAAllocateState();
228 uLongf len = sizeof(*state);
229 uncompress((Bytef*) state, &len, chunk->data, chunk->size);
230 if (!GBADeserialize(png_get_user_chunk_ptr(png), state)) {
231 GBADeallocateState(state);
232 longjmp(png_jmpbuf(png), 1);
233 }
234 GBADeallocateState(state);
235 return 1;
236}
237
238static bool _loadPNGState(struct GBA* gba, struct VFile* vf) {
239 png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES);
240 png_infop info = png_create_info_struct(png);
241 png_infop end = png_create_info_struct(png);
242 if (!png || !info || !end) {
243 PNGReadClose(png, info, end);
244 return false;
245 }
246 uint32_t* pixels = malloc(VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4);
247 if (!pixels) {
248 PNGReadClose(png, info, end);
249 return false;
250 }
251
252 PNGInstallChunkHandler(png, gba, _loadPNGChunkHandler, "gbAs");
253 bool success = PNGReadHeader(png, info);
254 success = success && PNGReadPixels(png, info, pixels, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, VIDEO_HORIZONTAL_PIXELS);
255 success = success && PNGReadFooter(png, end);
256 PNGReadClose(png, info, end);
257 if (success) {
258 gba->video.renderer->putPixels(gba->video.renderer, VIDEO_HORIZONTAL_PIXELS, pixels);
259 GBASyncForceFrame(gba->sync);
260 }
261
262 free(pixels);
263 return success;
264}
265#endif
266
267#ifndef _3DS
268bool GBASaveState(struct GBAThread* threadContext, struct VDir* dir, int slot, bool screenshot) {
269 struct VFile* vf = GBAGetState(threadContext->gba, dir, slot, true);
270 if (!vf) {
271 return false;
272 }
273 bool success = GBASaveStateNamed(threadContext->gba, vf, screenshot);
274 vf->close(vf);
275 if (success) {
276#if SAVESTATE_DEBUG
277 vf = GBAGetState(threadContext->gba, dir, slot, false);
278 if (vf) {
279 struct GBA* backup = anonymousMemoryMap(sizeof(*backup));
280 memcpy(backup, threadContext->gba, sizeof(*backup));
281 memset(threadContext->gba->memory.io, 0, sizeof(threadContext->gba->memory.io));
282 memset(threadContext->gba->timers, 0, sizeof(threadContext->gba->timers));
283 GBALoadStateNamed(threadContext->gba, vf);
284 if (memcmp(backup, threadContext->gba, sizeof(*backup))) {
285 char suffix[16] = { '\0' };
286 struct VFile* vf2;
287 snprintf(suffix, sizeof(suffix), ".dump.0.%d", slot);
288 vf2 = VDirOptionalOpenFile(dir, threadContext->gba->activeFile, "savestate", suffix, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
289 if (vf2) {
290 vf2->write(vf2, backup, sizeof(*backup));
291 vf2->close(vf2);
292 }
293 snprintf(suffix, sizeof(suffix), ".dump.1.%d", slot);
294 vf2 = VDirOptionalOpenFile(dir, threadContext->gba->activeFile, "savestate", suffix, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
295 if (vf2) {
296 vf2->write(vf2, threadContext->gba, sizeof(*threadContext->gba));
297 vf2->close(vf2);
298 }
299 }
300 mappedMemoryFree(backup, sizeof(*backup));
301 vf->close(vf);
302 }
303#endif
304 GBALog(threadContext->gba, GBA_LOG_STATUS, "State %i saved", slot);
305 } else {
306 GBALog(threadContext->gba, GBA_LOG_STATUS, "State %i failed to save", slot);
307 }
308
309 return success;
310}
311
312bool GBALoadState(struct GBAThread* threadContext, struct VDir* dir, int slot) {
313 struct VFile* vf = GBAGetState(threadContext->gba, dir, slot, false);
314 if (!vf) {
315 return false;
316 }
317 threadContext->rewindBufferSize = 0;
318 bool success = GBALoadStateNamed(threadContext->gba, vf);
319 vf->close(vf);
320 if (success) {
321 GBALog(threadContext->gba, GBA_LOG_STATUS, "State %i loaded", slot);
322 } else {
323 GBALog(threadContext->gba, GBA_LOG_STATUS, "State %i failed to load", slot);
324 }
325 return success;
326}
327#endif
328
329bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, bool screenshot) {
330#ifdef USE_PNG
331 if (!screenshot) {
332#else
333 UNUSED(screenshot);
334#endif
335 vf->truncate(vf, sizeof(struct GBASerializedState));
336 struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE);
337 if (!state) {
338 return false;
339 }
340 GBASerialize(gba, state);
341 vf->unmap(vf, state, sizeof(struct GBASerializedState));
342 return true;
343#ifdef USE_PNG
344 }
345 else {
346 return _savePNGState(gba, vf);
347 }
348#endif
349 return false;
350}
351
352bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf) {
353#ifdef USE_PNG
354 if (isPNG(vf)) {
355 return _loadPNGState(gba, vf);
356 }
357#endif
358 if (vf->size(vf) < (ssize_t) sizeof(struct GBASerializedState)) {
359 return false;
360 }
361 struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_READ);
362 if (!state) {
363 return false;
364 }
365 bool success = GBADeserialize(gba, state);
366 vf->unmap(vf, state, sizeof(struct GBASerializedState));
367 return success;
368}
369
370struct GBASerializedState* GBAAllocateState(void) {
371 return anonymousMemoryMap(sizeof(struct GBASerializedState));
372}
373
374void GBADeallocateState(struct GBASerializedState* state) {
375 mappedMemoryFree(state, sizeof(struct GBASerializedState));
376}
377
378void GBARecordFrame(struct GBAThread* thread) {
379 int offset = thread->rewindBufferWriteOffset;
380 struct GBASerializedState* state = thread->rewindBuffer[offset];
381 if (!state) {
382 state = GBAAllocateState();
383 thread->rewindBuffer[offset] = state;
384 }
385 GBASerialize(thread->gba, state);
386
387 if (thread->rewindScreenBuffer) {
388 unsigned stride;
389 const uint8_t* pixels = 0;
390 thread->gba->video.renderer->getPixels(thread->gba->video.renderer, &stride, (const void**) &pixels);
391 if (pixels) {
392 size_t y;
393 for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) {
394 memcpy(&thread->rewindScreenBuffer[(offset * VIDEO_VERTICAL_PIXELS + y) * VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL], &pixels[y * stride * BYTES_PER_PIXEL], VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
395 }
396 }
397 }
398 thread->rewindBufferSize = thread->rewindBufferSize == thread->rewindBufferCapacity ? thread->rewindBufferCapacity : thread->rewindBufferSize + 1;
399 thread->rewindBufferWriteOffset = (offset + 1) % thread->rewindBufferCapacity;
400}
401
402void GBARewindSettingsChanged(struct GBAThread* threadContext, int newCapacity, int newInterval) {
403 if (newCapacity == threadContext->rewindBufferCapacity && newInterval == threadContext->rewindBufferInterval) {
404 return;
405 }
406 threadContext->rewindBufferInterval = newInterval;
407 threadContext->rewindBufferNext = threadContext->rewindBufferInterval;
408 threadContext->rewindBufferSize = 0;
409 if (threadContext->rewindBuffer) {
410 int i;
411 for (i = 0; i < threadContext->rewindBufferCapacity; ++i) {
412 GBADeallocateState(threadContext->rewindBuffer[i]);
413 }
414 free(threadContext->rewindBuffer);
415 free(threadContext->rewindScreenBuffer);
416 }
417 threadContext->rewindBufferCapacity = newCapacity;
418 if (threadContext->rewindBufferCapacity > 0) {
419 threadContext->rewindBuffer = calloc(threadContext->rewindBufferCapacity, sizeof(struct GBASerializedState*));
420 threadContext->rewindScreenBuffer = calloc(threadContext->rewindBufferCapacity, VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
421 } else {
422 threadContext->rewindBuffer = 0;
423 threadContext->rewindScreenBuffer = 0;
424 }
425}
426
427int GBARewind(struct GBAThread* thread, int nStates) {
428 if (nStates > thread->rewindBufferSize || nStates < 0) {
429 nStates = thread->rewindBufferSize;
430 }
431 if (nStates == 0) {
432 return 0;
433 }
434 int offset = thread->rewindBufferWriteOffset - nStates;
435 if (offset < 0) {
436 offset += thread->rewindBufferCapacity;
437 }
438 struct GBASerializedState* state = thread->rewindBuffer[offset];
439 if (!state) {
440 return 0;
441 }
442 thread->rewindBufferSize -= nStates;
443 thread->rewindBufferWriteOffset = offset;
444 GBADeserialize(thread->gba, state);
445 if (thread->rewindScreenBuffer) {
446 thread->gba->video.renderer->putPixels(thread->gba->video.renderer, VIDEO_HORIZONTAL_PIXELS, &thread->rewindScreenBuffer[offset * VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL]);
447 }
448 return nStates;
449}
450
451void GBARewindAll(struct GBAThread* thread) {
452 GBARewind(thread, thread->rewindBufferSize);
453}
454
455void GBATakeScreenshot(struct GBA* gba, struct VDir* dir) {
456#ifdef USE_PNG
457 unsigned stride;
458 const void* pixels = 0;
459 struct VFile* vf = VDirOptionalOpenIncrementFile(dir, gba->activeFile, "screenshot", "-", ".png", O_CREAT | O_TRUNC | O_WRONLY);
460 bool success = false;
461 if (vf) {
462 gba->video.renderer->getPixels(gba->video.renderer, &stride, &pixels);
463 png_structp png = PNGWriteOpen(vf);
464 png_infop info = PNGWriteHeader(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
465 success = PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, stride, pixels);
466 PNGWriteClose(png, info);
467 vf->close(vf);
468 }
469 if (success) {
470 GBALog(gba, GBA_LOG_STATUS, "Screenshot saved");
471 return;
472 }
473#endif
474 GBALog(gba, GBA_LOG_STATUS, "Failed to take screenshot");
475}