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 "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) {
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 uint32_t calcChecksum = 0;
104 int i;
105 for (i = 0; i < size; ++i) {
106 calcChecksum += payload[i] << (calcChecksum % 24);
107 }
108
109 if (calcChecksum != checksum) {
110 goto cleanup;
111 }
112
113 uint32_t copySize = size - 0x1C;
114 switch (gba->memory.savedata.type) {
115 case SAVEDATA_SRAM:
116 if (copySize > SIZE_CART_SRAM) {
117 copySize = SIZE_CART_SRAM;
118 }
119 break;
120 case SAVEDATA_FLASH512:
121 if (copySize > SIZE_CART_FLASH512) {
122 GBASavedataForceType(&gba->memory.savedata, SAVEDATA_FLASH1M, gba->memory.savedata.realisticTiming);
123 }
124 // Fall through
125 case SAVEDATA_FLASH1M:
126 if (copySize > SIZE_CART_FLASH1M) {
127 copySize = SIZE_CART_FLASH1M;
128 }
129 break;
130 case SAVEDATA_EEPROM:
131 if (copySize > SIZE_CART_EEPROM) {
132 copySize = SAVEDATA_EEPROM;
133 }
134 break;
135 case SAVEDATA_FORCE_NONE:
136 case SAVEDATA_AUTODETECT:
137 goto cleanup;
138 }
139
140 memcpy(gba->memory.savedata.data, &payload[0x1C], copySize);
141
142 free(payload);
143 return true;
144
145cleanup:
146 free(payload);
147 return false;
148}
149
150
151bool GBASavedataExportSharkPort(const struct GBA* gba, struct VFile* vf) {
152 union {
153 char c[0x1C];
154 int32_t i;
155 } buffer;
156 int32_t size = strlen(SHARKPORT_HEADER);
157 STORE_32(size, 0, &buffer.i);
158 if (vf->write(vf, &buffer.i, 4) < 4) {
159 return false;
160 }
161 if (vf->write(vf, SHARKPORT_HEADER, size) < size) {
162 return false;
163 }
164
165 size = 0x000F0000;
166 STORE_32(size, 0, &buffer.i);
167 if (vf->write(vf, &buffer.i, 4) < 4) {
168 return false;
169 }
170
171 const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
172 size = sizeof(cart->title);
173 STORE_32(size, 0, &buffer.i);
174 if (vf->write(vf, &buffer.i, 4) < 4) {
175 return false;
176 }
177 if (vf->write(vf, cart->title, size) < 4) {
178 return false;
179 }
180
181 time_t t = time(0);
182 struct tm* tm = localtime(&t);
183 size = strftime(&buffer.c[4], sizeof(buffer.c) - 4, "%m/%d/%Y %I:%M:%S %p", tm);
184 STORE_32(size, 0, &buffer.i);
185 if (vf->write(vf, buffer.c, size + 4) < size + 4) {
186 return false;
187 }
188
189 // Last field is blank
190 size = 0;
191 STORE_32(size, 0, &buffer.i);
192 if (vf->write(vf, &buffer.i, 4) < 4) {
193 return false;
194 }
195
196 // Write payload
197 size = 0x1C;
198 switch (gba->memory.savedata.type) {
199 case SAVEDATA_SRAM:
200 size += SIZE_CART_SRAM;
201 break;
202 case SAVEDATA_FLASH512:
203 size += SIZE_CART_FLASH512;
204 break;
205 case SAVEDATA_FLASH1M:
206 size += SIZE_CART_FLASH1M;
207 break;
208 case SAVEDATA_EEPROM:
209 size += SIZE_CART_EEPROM;
210 break;
211 case SAVEDATA_FORCE_NONE:
212 case SAVEDATA_AUTODETECT:
213 return false;
214 }
215 STORE_32(size, 0, &buffer.i);
216 if (vf->write(vf, &buffer.i, 4) < 4) {
217 return false;
218 }
219 size -= 0x1C;
220
221 memcpy(buffer.c, cart->title, 16);
222 buffer.c[0x10] = 0;
223 buffer.c[0x11] = 0;
224 buffer.c[0x12] = cart->checksum;
225 buffer.c[0x13] = cart->maker;
226 buffer.c[0x14] = 1;
227 buffer.c[0x15] = 0;
228 buffer.c[0x16] = 0;
229 buffer.c[0x17] = 0;
230 buffer.c[0x18] = 0;
231 buffer.c[0x19] = 0;
232 buffer.c[0x1A] = 0;
233 buffer.c[0x1B] = 0;
234 if (vf->write(vf, buffer.c, 0x1C) < 0x1C) {
235 return false;
236 }
237
238 uint32_t checksum = 0;
239 int i;
240 for (i = 0; i < 0x1C; ++i) {
241 checksum += buffer.c[i] << (checksum % 24);
242 }
243
244 if (vf->write(vf, gba->memory.savedata.data, size) < size) {
245 return false;
246 }
247
248 for (i = 0; i < size; ++i) {
249 checksum += ((char) gba->memory.savedata.data[i]) << (checksum % 24);
250 }
251
252 STORE_32(checksum, 0, &buffer.i);
253 if (vf->write(vf, &buffer.i, 4) < 4) {
254 return false;
255 }
256
257 return true;
258}