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