src/gba/extra/sharkport.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 "sharkport.h"
7
8#include "gba/gba.h"
9#include "util/vfs.h"
10
11static const char* const SHARKPORT_HEADER = "SharkPortSave";
12
13bool GBASavedataImportSharkPort(struct GBA* gba, struct VFile* vf, bool testChecksum) {
14 union {
15 char c[0x1C];
16 int32_t i;
17 } buffer;
18 if (vf->read(vf, &buffer.i, 4) < 4) {
19 return false;
20 }
21 int32_t size;
22 LOAD_32(size, 0, &buffer.i);
23 if (size != (int32_t) strlen(SHARKPORT_HEADER)) {
24 return false;
25 }
26 if (vf->read(vf, buffer.c, size) < size) {
27 return false;
28 }
29 if (memcmp(SHARKPORT_HEADER, buffer.c, size) != 0) {
30 return false;
31 }
32 if (vf->read(vf, &buffer.i, 4) < 4) {
33 return false;
34 }
35 LOAD_32(size, 0, &buffer.i);
36 if (size != 0x000F0000) {
37 // What is this value?
38 return false;
39 }
40
41 // Skip first three fields
42 if (vf->read(vf, &buffer.i, 4) < 4) {
43 return false;
44 }
45 LOAD_32(size, 0, &buffer.i);
46 if (vf->seek(vf, size, SEEK_CUR) < 0) {
47 return false;
48 }
49
50 if (vf->read(vf, &buffer.i, 4) < 4) {
51 return false;
52 }
53 LOAD_32(size, 0, &buffer.i);
54 if (vf->seek(vf, size, SEEK_CUR) < 0) {
55 return false;
56 }
57
58 if (vf->read(vf, &buffer.i, 4) < 4) {
59 return false;
60 }
61 LOAD_32(size, 0, &buffer.i);
62 if (vf->seek(vf, size, SEEK_CUR) < 0) {
63 return false;
64 }
65
66 // Read payload
67 if (vf->read(vf, &buffer.i, 4) < 4) {
68 return false;
69 }
70 LOAD_32(size, 0, &buffer.i);
71 if (size < 0x1C || size > SIZE_CART_FLASH1M + 0x1C) {
72 return false;
73 }
74 char* payload = malloc(size);
75 if (vf->read(vf, payload, size) < size) {
76 goto cleanup;
77 }
78
79 struct GBACartridge* cart = (struct GBACartridge*) gba->memory.rom;
80 memcpy(buffer.c, &cart->title, 16);
81 buffer.c[0x10] = 0;
82 buffer.c[0x11] = 0;
83 buffer.c[0x12] = cart->checksum;
84 buffer.c[0x13] = cart->maker;
85 buffer.c[0x14] = 1;
86 buffer.c[0x15] = 0;
87 buffer.c[0x16] = 0;
88 buffer.c[0x17] = 0;
89 buffer.c[0x18] = 0;
90 buffer.c[0x19] = 0;
91 buffer.c[0x1A] = 0;
92 buffer.c[0x1B] = 0;
93 if (memcmp(buffer.c, payload, 0x1C) != 0) {
94 goto cleanup;
95 }
96
97 uint32_t checksum;
98 if (vf->read(vf, &buffer.i, 4) < 4) {
99 goto cleanup;
100 }
101 LOAD_32(checksum, 0, &buffer.i);
102
103 if (testChecksum) {
104 uint32_t calcChecksum = 0;
105 int i;
106 for (i = 0; i < size; ++i) {
107 calcChecksum += ((int32_t) payload[i]) << (calcChecksum % 24);
108 }
109
110 if (calcChecksum != checksum) {
111 goto cleanup;
112 }
113 }
114
115 uint32_t copySize = size - 0x1C;
116 switch (gba->memory.savedata.type) {
117 case SAVEDATA_SRAM:
118 if (copySize > SIZE_CART_SRAM) {
119 copySize = SIZE_CART_SRAM;
120 }
121 break;
122 case SAVEDATA_FLASH512:
123 if (copySize > SIZE_CART_FLASH512) {
124 GBASavedataForceType(&gba->memory.savedata, SAVEDATA_FLASH1M, gba->memory.savedata.realisticTiming);
125 }
126 // Fall through
127 case SAVEDATA_FLASH1M:
128 if (copySize > SIZE_CART_FLASH1M) {
129 copySize = SIZE_CART_FLASH1M;
130 }
131 break;
132 case SAVEDATA_EEPROM:
133 if (copySize > SIZE_CART_EEPROM) {
134 copySize = SAVEDATA_EEPROM;
135 }
136 break;
137 case SAVEDATA_FORCE_NONE:
138 case SAVEDATA_AUTODETECT:
139 goto cleanup;
140 }
141
142 memcpy(gba->memory.savedata.data, &payload[0x1C], copySize);
143
144 free(payload);
145 return true;
146
147cleanup:
148 free(payload);
149 return false;
150}
151
152bool GBASavedataExportSharkPort(const struct GBA* gba, struct VFile* vf) {
153 union {
154 char c[0x1C];
155 int32_t i;
156 } buffer;
157 int32_t size = strlen(SHARKPORT_HEADER);
158 STORE_32(size, 0, &buffer.i);
159 if (vf->write(vf, &buffer.i, 4) < 4) {
160 return false;
161 }
162 if (vf->write(vf, SHARKPORT_HEADER, size) < size) {
163 return false;
164 }
165
166 size = 0x000F0000;
167 STORE_32(size, 0, &buffer.i);
168 if (vf->write(vf, &buffer.i, 4) < 4) {
169 return false;
170 }
171
172 const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
173 size = sizeof(cart->title);
174 STORE_32(size, 0, &buffer.i);
175 if (vf->write(vf, &buffer.i, 4) < 4) {
176 return false;
177 }
178 if (vf->write(vf, cart->title, size) < 4) {
179 return false;
180 }
181
182 time_t t = time(0);
183 struct tm* tm = localtime(&t);
184 size = strftime(&buffer.c[4], sizeof(buffer.c) - 4, "%m/%d/%Y %I:%M:%S %p", tm);
185 STORE_32(size, 0, &buffer.i);
186 if (vf->write(vf, buffer.c, size + 4) < size + 4) {
187 return false;
188 }
189
190 // Last field is blank
191 size = 0;
192 STORE_32(size, 0, &buffer.i);
193 if (vf->write(vf, &buffer.i, 4) < 4) {
194 return false;
195 }
196
197 // Write payload
198 size = 0x1C;
199 switch (gba->memory.savedata.type) {
200 case SAVEDATA_SRAM:
201 size += SIZE_CART_SRAM;
202 break;
203 case SAVEDATA_FLASH512:
204 size += SIZE_CART_FLASH512;
205 break;
206 case SAVEDATA_FLASH1M:
207 size += SIZE_CART_FLASH1M;
208 break;
209 case SAVEDATA_EEPROM:
210 size += SIZE_CART_EEPROM;
211 break;
212 case SAVEDATA_FORCE_NONE:
213 case SAVEDATA_AUTODETECT:
214 return false;
215 }
216 STORE_32(size, 0, &buffer.i);
217 if (vf->write(vf, &buffer.i, 4) < 4) {
218 return false;
219 }
220 size -= 0x1C;
221
222 memcpy(buffer.c, &cart->title, 16);
223 buffer.c[0x10] = 0;
224 buffer.c[0x11] = 0;
225 buffer.c[0x12] = cart->checksum;
226 buffer.c[0x13] = cart->maker;
227 buffer.c[0x14] = 1;
228 buffer.c[0x15] = 0;
229 buffer.c[0x16] = 0;
230 buffer.c[0x17] = 0;
231 buffer.c[0x18] = 0;
232 buffer.c[0x19] = 0;
233 buffer.c[0x1A] = 0;
234 buffer.c[0x1B] = 0;
235 if (vf->write(vf, buffer.c, 0x1C) < 0x1C) {
236 return false;
237 }
238
239 uint32_t checksum = 0;
240 int i;
241 for (i = 0; i < 0x1C; ++i) {
242 checksum += buffer.c[i] << (checksum % 24);
243 }
244
245 if (vf->write(vf, gba->memory.savedata.data, size) < size) {
246 return false;
247 }
248
249 for (i = 0; i < size; ++i) {
250 checksum += ((char) gba->memory.savedata.data[i]) << (checksum % 24);
251 }
252
253 STORE_32(checksum, 0, &buffer.i);
254 if (vf->write(vf, &buffer.i, 4) < 4) {
255 return false;
256 }
257
258 return true;
259}