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) {
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, O_CREAT | O_RDWR);
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);
84 if (!vf) {
85 return false;
86 }
87 struct GBASerializedState* state = GBAMapState(vf);
88 GBASerialize(gba, state);
89 GBAUnmapState(vf, state);
90 vf->close(vf);
91 return true;
92}
93
94bool GBALoadState(struct GBA* gba, int slot) {
95 struct VFile* vf = _getStateVf(gba, slot);
96 if (!vf) {
97 return false;
98 }
99 struct GBASerializedState* state = GBAMapState(vf);
100 GBADeserialize(gba, state);
101 GBAUnmapState(vf, state);
102 vf->close(vf);
103 return true;
104}
105
106struct GBASerializedState* GBAMapState(struct VFile* vf) {
107 return vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE);
108}
109
110void GBAUnmapState(struct VFile* vf, struct GBASerializedState* state) {
111 vf->unmap(vf, state, sizeof(struct GBASerializedState));
112}
113
114struct GBASerializedState* GBAAllocateState(void) {
115 return anonymousMemoryMap(sizeof(struct GBASerializedState));
116}
117
118void GBADeallocateState(struct GBASerializedState* state) {
119 mappedMemoryFree(state, sizeof(struct GBASerializedState));
120}
121
122void GBARecordFrame(struct GBAThread* thread) {
123 int offset = thread->rewindBufferWriteOffset;
124 struct GBASerializedState* state = thread->rewindBuffer[offset];
125 if (!state) {
126 state = GBAAllocateState();
127 thread->rewindBuffer[offset] = state;
128 }
129 GBASerialize(thread->gba, state);
130 thread->rewindBufferSize = thread->rewindBufferSize == thread->rewindBufferCapacity ? thread->rewindBufferCapacity : thread->rewindBufferSize + 1;
131 thread->rewindBufferWriteOffset = (offset + 1) % thread->rewindBufferCapacity;
132}
133
134void GBARewind(struct GBAThread* thread, int nStates) {
135 if (nStates > thread->rewindBufferSize || nStates < 0) {
136 nStates = thread->rewindBufferSize;
137 }
138 if (nStates == 0) {
139 return;
140 }
141 int offset = thread->rewindBufferWriteOffset - nStates;
142 if (offset < 0) {
143 offset += thread->rewindBufferSize;
144 }
145 struct GBASerializedState* state = thread->rewindBuffer[offset];
146 if (!state) {
147 return;
148 }
149 thread->rewindBufferSize -= nStates;
150 thread->rewindBufferWriteOffset = (offset + thread->rewindBufferCapacity - nStates) % thread->rewindBufferCapacity;
151 GBADeserialize(thread->gba, state);
152}