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