src/gba/gba-serialize.c (view raw)
1#include "gba-serialize.h"
2
3#include "gba-audio.h"
4#include "gba-io.h"
5#include "gba-thread.h"
6
7#include "util/memory.h"
8#include "util/vfs.h"
9
10#include <fcntl.h>
11
12const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
13
14void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
15 state->versionMagic = GBA_SAVESTATE_MAGIC;
16 state->biosChecksum = gba->biosChecksum;
17 state->romCrc32 = gba->romCrc32;
18
19 state->id = ((struct GBACartridge*) gba->memory.rom)->id;
20 memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title));
21
22 memcpy(state->cpu.gprs, gba->cpu->gprs, sizeof(state->cpu.gprs));
23 state->cpu.cpsr = gba->cpu->cpsr;
24 state->cpu.spsr = gba->cpu->spsr;
25 state->cpu.cycles = gba->cpu->cycles;
26 state->cpu.nextEvent = gba->cpu->nextEvent;
27 memcpy(state->cpu.bankedRegisters, gba->cpu->bankedRegisters, 6 * 7 * sizeof(int32_t));
28 memcpy(state->cpu.bankedSPSRs, gba->cpu->bankedSPSRs, 6 * sizeof(int32_t));
29
30 GBAMemorySerialize(&gba->memory, state);
31 GBAIOSerialize(gba, state);
32 GBAVideoSerialize(&gba->video, state);
33 GBAAudioSerialize(&gba->audio, state);
34}
35
36void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) {
37 if (state->versionMagic != GBA_SAVESTATE_MAGIC) {
38 GBALog(gba, GBA_LOG_WARN, "Invalid or too new savestate");
39 return;
40 }
41 if (state->biosChecksum != gba->biosChecksum) {
42 GBALog(gba, GBA_LOG_WARN, "Savestate created using a different version of the BIOS");
43 if (state->cpu.gprs[ARM_PC] < SIZE_BIOS && state->cpu.gprs[ARM_PC] >= 0x20) {
44 return;
45 }
46 }
47 if (state->id != ((struct GBACartridge*) gba->memory.rom)->id || memcmp(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title))) {
48 GBALog(gba, GBA_LOG_WARN, "Savestate is for a different game");
49 return;
50 }
51 if (state->romCrc32 != gba->romCrc32) {
52 GBALog(gba, GBA_LOG_WARN, "Savestate is for a different version of the game");
53 }
54 memcpy(gba->cpu->gprs, state->cpu.gprs, sizeof(gba->cpu->gprs));
55 gba->cpu->cpsr = state->cpu.cpsr;
56 gba->cpu->spsr = state->cpu.spsr;
57 gba->cpu->cycles = state->cpu.cycles;
58 gba->cpu->nextEvent = state->cpu.nextEvent;
59 memcpy(gba->cpu->bankedRegisters, state->cpu.bankedRegisters, 6 * 7 * sizeof(int32_t));
60 memcpy(gba->cpu->bankedSPSRs, state->cpu.bankedSPSRs, 6 * sizeof(int32_t));
61 gba->cpu->executionMode = gba->cpu->cpsr.t ? MODE_THUMB : MODE_ARM;
62 gba->cpu->privilegeMode = gba->cpu->cpsr.priv;
63 gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
64
65 GBAMemoryDeserialize(&gba->memory, state);
66 GBAIODeserialize(gba, state);
67 GBAVideoDeserialize(&gba->video, state);
68 GBAAudioDeserialize(&gba->audio, state);
69}
70
71static struct VFile* _getStateVf(struct GBA* gba, int slot, bool write) {
72 char path[PATH_MAX];
73 path[PATH_MAX - 1] = '\0';
74 snprintf(path, PATH_MAX - 1, "%s.ss%d", gba->activeFile, slot);
75 struct VFile* vf = VFileOpen(path, write ? (O_CREAT | O_RDWR) : O_RDONLY);
76 if (vf) {
77 vf->truncate(vf, sizeof(struct GBASerializedState));
78 }
79 return vf;
80}
81
82bool GBASaveState(struct GBA* gba, int slot) {
83 struct VFile* vf = _getStateVf(gba, slot, true);
84 if (!vf) {
85 return false;
86 }
87 struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE);
88 GBASerialize(gba, state);
89 vf->unmap(vf, state, sizeof(struct GBASerializedState));
90 vf->close(vf);
91 return true;
92}
93
94bool GBALoadState(struct GBA* gba, int slot) {
95 struct VFile* vf = _getStateVf(gba, slot, false);
96 if (!vf) {
97 return false;
98 }
99 struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_READ);
100 GBADeserialize(gba, state);
101 vf->unmap(vf, state, sizeof(struct GBASerializedState));
102 vf->close(vf);
103 return true;
104}
105
106struct GBASerializedState* GBAAllocateState(void) {
107 return anonymousMemoryMap(sizeof(struct GBASerializedState));
108}
109
110void GBADeallocateState(struct GBASerializedState* state) {
111 mappedMemoryFree(state, sizeof(struct GBASerializedState));
112}
113
114void GBARecordFrame(struct GBAThread* thread) {
115 int offset = thread->rewindBufferWriteOffset;
116 struct GBASerializedState* state = thread->rewindBuffer[offset];
117 if (!state) {
118 state = GBAAllocateState();
119 thread->rewindBuffer[offset] = state;
120 }
121 GBASerialize(thread->gba, state);
122 thread->rewindBufferSize = thread->rewindBufferSize == thread->rewindBufferCapacity ? thread->rewindBufferCapacity : thread->rewindBufferSize + 1;
123 thread->rewindBufferWriteOffset = (offset + 1) % thread->rewindBufferCapacity;
124}
125
126void GBARewind(struct GBAThread* thread, int nStates) {
127 if (nStates > thread->rewindBufferSize || nStates < 0) {
128 nStates = thread->rewindBufferSize;
129 }
130 if (nStates == 0) {
131 return;
132 }
133 int offset = thread->rewindBufferWriteOffset - nStates;
134 if (offset < 0) {
135 offset += thread->rewindBufferSize;
136 }
137 struct GBASerializedState* state = thread->rewindBuffer[offset];
138 if (!state) {
139 return;
140 }
141 thread->rewindBufferSize -= nStates;
142 thread->rewindBufferWriteOffset = (offset + thread->rewindBufferCapacity - nStates) % thread->rewindBufferCapacity;
143 GBADeserialize(thread->gba, state);
144}