src/gba/savedata.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 "savedata.h"
7
8#include "gba/gba.h"
9
10#include "util/memory.h"
11#include "util/vfs.h"
12
13#include <errno.h>
14#include <fcntl.h>
15
16static void _flashSwitchBank(struct GBASavedata* savedata, int bank);
17static void _flashErase(struct GBASavedata* savedata);
18static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart);
19
20void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
21 savedata->type = SAVEDATA_AUTODETECT;
22 savedata->data = 0;
23 savedata->command = EEPROM_COMMAND_NULL;
24 savedata->flashState = FLASH_STATE_RAW;
25 savedata->vf = vf;
26 savedata->realVf = vf;
27 savedata->mapMode = MAP_WRITE;
28}
29
30void GBASavedataDeinit(struct GBASavedata* savedata) {
31 if (savedata->vf) {
32 switch (savedata->type) {
33 case SAVEDATA_SRAM:
34 savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_SRAM);
35 break;
36 case SAVEDATA_FLASH512:
37 savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH512);
38 break;
39 case SAVEDATA_FLASH1M:
40 savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH1M);
41 break;
42 case SAVEDATA_EEPROM:
43 savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM);
44 break;
45 case SAVEDATA_FORCE_NONE:
46 case SAVEDATA_AUTODETECT:
47 break;
48 }
49 savedata->vf = 0;
50 } else {
51 switch (savedata->type) {
52 case SAVEDATA_SRAM:
53 mappedMemoryFree(savedata->data, SIZE_CART_SRAM);
54 break;
55 case SAVEDATA_FLASH512:
56 mappedMemoryFree(savedata->data, SIZE_CART_FLASH512);
57 break;
58 case SAVEDATA_FLASH1M:
59 mappedMemoryFree(savedata->data, SIZE_CART_FLASH1M);
60 break;
61 case SAVEDATA_EEPROM:
62 mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
63 break;
64 case SAVEDATA_FORCE_NONE:
65 case SAVEDATA_AUTODETECT:
66 break;
67 }
68 }
69 savedata->data = 0;
70 savedata->type = SAVEDATA_AUTODETECT;
71}
72
73void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf) {
74 GBASavedataDeinit(savedata);
75 savedata->vf = vf;
76 savedata->mapMode = MAP_READ;
77}
78
79void GBASavedataUnmask(struct GBASavedata* savedata) {
80 if (savedata->mapMode != MAP_READ) {
81 return;
82 }
83 GBASavedataDeinit(savedata);
84 savedata->vf = savedata->realVf;
85 savedata->mapMode = MAP_WRITE;
86}
87
88bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
89 if (savedata->data) {
90 switch (savedata->type) {
91 case SAVEDATA_SRAM:
92 return out->write(out, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM;
93 case SAVEDATA_FLASH512:
94 return out->write(out, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512;
95 case SAVEDATA_FLASH1M:
96 return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
97 case SAVEDATA_EEPROM:
98 return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
99 case SAVEDATA_AUTODETECT:
100 case SAVEDATA_FORCE_NONE:
101 return true;
102 }
103 } else if (savedata->vf) {
104 off_t read = 0;
105 uint8_t buffer[2048];
106 do {
107 read = savedata->vf->read(savedata->vf, buffer, sizeof(buffer));
108 out->write(out, buffer, read);
109 } while (read == sizeof(buffer));
110 return read >= 0;
111 }
112 return true;
113}
114
115void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type) {
116 if (savedata->type != SAVEDATA_AUTODETECT) {
117 struct VFile* vf = savedata->vf;
118 GBASavedataDeinit(savedata);
119 GBASavedataInit(savedata, vf);
120 }
121 switch (type) {
122 case SAVEDATA_FLASH512:
123 case SAVEDATA_FLASH1M:
124 savedata->type = type;
125 GBASavedataInitFlash(savedata);
126 break;
127 case SAVEDATA_EEPROM:
128 GBASavedataInitEEPROM(savedata);
129 break;
130 case SAVEDATA_SRAM:
131 GBASavedataInitSRAM(savedata);
132 break;
133 case SAVEDATA_FORCE_NONE:
134 savedata->type = SAVEDATA_FORCE_NONE;
135 break;
136 case SAVEDATA_AUTODETECT:
137 break;
138 }
139}
140
141void GBASavedataInitFlash(struct GBASavedata* savedata) {
142 if (savedata->type == SAVEDATA_AUTODETECT) {
143 savedata->type = SAVEDATA_FLASH512;
144 }
145 if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {
146 GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
147 return;
148 }
149 size_t flashSize = SIZE_CART_FLASH512;
150 off_t end;
151 if (!savedata->vf) {
152 end = 0;
153 savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M);
154 } else {
155 end = savedata->vf->size(savedata->vf);
156 if (end < SIZE_CART_FLASH512) {
157 savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
158 flashSize = SIZE_CART_FLASH1M;
159 }
160 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, savedata->mapMode);
161 }
162
163 savedata->currentBank = savedata->data;
164 if (end < SIZE_CART_FLASH512) {
165 memset(&savedata->data[end], 0xFF, flashSize - end);
166 }
167}
168
169void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
170 if (savedata->type == SAVEDATA_AUTODETECT) {
171 savedata->type = SAVEDATA_EEPROM;
172 } else {
173 GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
174 return;
175 }
176 off_t end;
177 if (!savedata->vf) {
178 end = 0;
179 savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
180 } else {
181 end = savedata->vf->size(savedata->vf);
182 if (end < SIZE_CART_EEPROM) {
183 savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
184 }
185 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
186 }
187 if (end < SIZE_CART_EEPROM) {
188 memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);
189 }
190}
191
192void GBASavedataInitSRAM(struct GBASavedata* savedata) {
193 if (savedata->type == SAVEDATA_AUTODETECT) {
194 savedata->type = SAVEDATA_SRAM;
195 } else {
196 GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
197 return;
198 }
199 off_t end;
200 if (!savedata->vf) {
201 end = 0;
202 savedata->data = anonymousMemoryMap(SIZE_CART_SRAM);
203 } else {
204 end = savedata->vf->size(savedata->vf);
205 if (end < SIZE_CART_SRAM) {
206 savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM);
207 }
208 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, savedata->mapMode);
209 }
210
211 if (end < SIZE_CART_SRAM) {
212 memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end);
213 }
214}
215
216uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) {
217 if (savedata->command == FLASH_COMMAND_ID) {
218 if (savedata->type == SAVEDATA_FLASH512) {
219 if (address < 2) {
220 return FLASH_MFG_PANASONIC >> (address * 8);
221 }
222 } else if (savedata->type == SAVEDATA_FLASH1M) {
223 if (address < 2) {
224 return FLASH_MFG_SANYO >> (address * 8);
225 }
226 }
227 }
228 return savedata->currentBank[address];
229}
230
231void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8_t value) {
232 switch (savedata->flashState) {
233 case FLASH_STATE_RAW:
234 switch (savedata->command) {
235 case FLASH_COMMAND_PROGRAM:
236 savedata->currentBank[address] = value;
237 savedata->command = FLASH_COMMAND_NONE;
238 break;
239 case FLASH_COMMAND_SWITCH_BANK:
240 if (address == 0 && value < 2) {
241 _flashSwitchBank(savedata, value);
242 } else {
243 GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash bank switch");
244 savedata->command = FLASH_COMMAND_NONE;
245 }
246 savedata->command = FLASH_COMMAND_NONE;
247 break;
248 default:
249 if (address == FLASH_BASE_HI && value == FLASH_COMMAND_START) {
250 savedata->flashState = FLASH_STATE_START;
251 } else {
252 GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
253 }
254 break;
255 }
256 break;
257 case FLASH_STATE_START:
258 if (address == FLASH_BASE_LO && value == FLASH_COMMAND_CONTINUE) {
259 savedata->flashState = FLASH_STATE_CONTINUE;
260 } else {
261 GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
262 savedata->flashState = FLASH_STATE_RAW;
263 }
264 break;
265 case FLASH_STATE_CONTINUE:
266 savedata->flashState = FLASH_STATE_RAW;
267 if (address == FLASH_BASE_HI) {
268 switch (savedata->command) {
269 case FLASH_COMMAND_NONE:
270 switch (value) {
271 case FLASH_COMMAND_ERASE:
272 case FLASH_COMMAND_ID:
273 case FLASH_COMMAND_PROGRAM:
274 case FLASH_COMMAND_SWITCH_BANK:
275 savedata->command = value;
276 break;
277 default:
278 GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash operation: %#02x", value);
279 break;
280 }
281 break;
282 case FLASH_COMMAND_ERASE:
283 switch (value) {
284 case FLASH_COMMAND_ERASE_CHIP:
285 _flashErase(savedata);
286 break;
287 default:
288 GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
289 break;
290 }
291 savedata->command = FLASH_COMMAND_NONE;
292 break;
293 case FLASH_COMMAND_ID:
294 if (value == FLASH_COMMAND_TERMINATE) {
295 savedata->command = FLASH_COMMAND_NONE;
296 }
297 break;
298 default:
299 GBALog(0, GBA_LOG_ERROR, "Flash entered bad state: %#02x", savedata->command);
300 savedata->command = FLASH_COMMAND_NONE;
301 break;
302 }
303 } else if (savedata->command == FLASH_COMMAND_ERASE) {
304 if (value == FLASH_COMMAND_ERASE_SECTOR) {
305 _flashEraseSector(savedata, address);
306 savedata->command = FLASH_COMMAND_NONE;
307 } else {
308 GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
309 }
310 }
311 break;
312 }
313}
314
315void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
316 switch (savedata->command) {
317 // Read header
318 case EEPROM_COMMAND_NULL:
319 default:
320 savedata->command = value & 0x1;
321 break;
322 case EEPROM_COMMAND_PENDING:
323 savedata->command <<= 1;
324 savedata->command |= value & 0x1;
325 if (savedata->command == EEPROM_COMMAND_WRITE) {
326 savedata->writeAddress = 0;
327 } else {
328 savedata->readAddress = 0;
329 }
330 break;
331 // Do commands
332 case EEPROM_COMMAND_WRITE:
333 // Write
334 if (writeSize > 65) {
335 savedata->writeAddress <<= 1;
336 savedata->writeAddress |= (value & 0x1) << 6;
337 } else if (writeSize == 1) {
338 savedata->command = EEPROM_COMMAND_NULL;
339 } else {
340 uint8_t current = savedata->data[savedata->writeAddress >> 3];
341 current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7)));
342 current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7));
343 savedata->data[savedata->writeAddress >> 3] = current;
344 ++savedata->writeAddress;
345 }
346 break;
347 case EEPROM_COMMAND_READ_PENDING:
348 // Read
349 if (writeSize > 1) {
350 savedata->readAddress <<= 1;
351 if (value & 0x1) {
352 savedata->readAddress |= 0x40;
353 }
354 } else {
355 savedata->readBitsRemaining = 68;
356 savedata->command = EEPROM_COMMAND_READ;
357 }
358 break;
359 }
360}
361
362uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
363 if (savedata->command != EEPROM_COMMAND_READ) {
364 return 1;
365 }
366 --savedata->readBitsRemaining;
367 if (savedata->readBitsRemaining < 64) {
368 int step = 63 - savedata->readBitsRemaining;
369 uint8_t data = savedata->data[(savedata->readAddress + step) >> 3] >> (0x7 - (step & 0x7));
370 if (!savedata->readBitsRemaining) {
371 savedata->command = EEPROM_COMMAND_NULL;
372 }
373 return data & 0x1;
374 }
375 return 0;
376}
377
378void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
379 GBALog(0, GBA_LOG_DEBUG, "Performing flash bank switch to bank %i", bank);
380 savedata->currentBank = &savedata->data[bank << 16];
381 if (bank > 0 && savedata->type == SAVEDATA_FLASH512) {
382 savedata->type = SAVEDATA_FLASH1M;
383 if (savedata->vf) {
384 savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
385 memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512);
386 }
387 }
388}
389
390void _flashErase(struct GBASavedata* savedata) {
391 GBALog(0, GBA_LOG_DEBUG, "Performing flash chip erase");
392 size_t size = SIZE_CART_FLASH512;
393 if (savedata->type == SAVEDATA_FLASH1M) {
394 size = SIZE_CART_FLASH1M;
395 }
396 memset(savedata->data, 0xFF, size);
397}
398
399void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) {
400 GBALog(0, GBA_LOG_DEBUG, "Performing flash sector erase at 0x%04x", sectorStart);
401 size_t size = 0x1000;
402 if (savedata->type == SAVEDATA_FLASH1M) {
403 GBALog(0, GBA_LOG_DEBUG, "Performing unknown sector-size erase at 0x%04x", sectorStart);
404 }
405 memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size);
406}