src/gba/gba-savedata.c (view raw)
1/* Copyright (c) 2013-2014 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 "gba-savedata.h"
7
8#include "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 switch (type) {
117 case SAVEDATA_FLASH512:
118 case SAVEDATA_FLASH1M:
119 savedata->type = type;
120 GBASavedataInitFlash(savedata);
121 break;
122 case SAVEDATA_EEPROM:
123 GBASavedataInitEEPROM(savedata);
124 break;
125 case SAVEDATA_SRAM:
126 GBASavedataInitSRAM(savedata);
127 break;
128 case SAVEDATA_FORCE_NONE:
129 savedata->type = SAVEDATA_FORCE_NONE;
130 break;
131 case SAVEDATA_AUTODETECT:
132 break;
133 }
134}
135
136void GBASavedataInitFlash(struct GBASavedata* savedata) {
137 if (savedata->type == SAVEDATA_AUTODETECT) {
138 savedata->type = SAVEDATA_FLASH512;
139 }
140 if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {
141 GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
142 return;
143 }
144 size_t flashSize = SIZE_CART_FLASH512;
145 off_t end;
146 if (!savedata->vf) {
147 end = 0;
148 savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M);
149 } else {
150 end = savedata->vf->size(savedata->vf);
151 if (end < SIZE_CART_FLASH512) {
152 savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
153 flashSize = SIZE_CART_FLASH1M;
154 }
155 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, savedata->mapMode);
156 }
157
158 savedata->currentBank = savedata->data;
159 if (end < SIZE_CART_FLASH512) {
160 memset(&savedata->data[end], 0xFF, flashSize - end);
161 }
162}
163
164void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
165 if (savedata->type == SAVEDATA_AUTODETECT) {
166 savedata->type = SAVEDATA_EEPROM;
167 } else {
168 GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
169 return;
170 }
171 off_t end;
172 if (!savedata->vf) {
173 end = 0;
174 savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
175 } else {
176 end = savedata->vf->size(savedata->vf);
177 if (end < SIZE_CART_EEPROM) {
178 savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
179 }
180 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
181 }
182 if (end < SIZE_CART_EEPROM) {
183 memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);
184 }
185}
186
187void GBASavedataInitSRAM(struct GBASavedata* savedata) {
188 if (savedata->type == SAVEDATA_AUTODETECT) {
189 savedata->type = SAVEDATA_SRAM;
190 } else {
191 GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
192 return;
193 }
194 off_t end;
195 if (!savedata->vf) {
196 end = 0;
197 savedata->data = anonymousMemoryMap(SIZE_CART_SRAM);
198 } else {
199 end = savedata->vf->size(savedata->vf);
200 if (end < SIZE_CART_SRAM) {
201 savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM);
202 }
203 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, savedata->mapMode);
204 }
205
206 if (end < SIZE_CART_SRAM) {
207 memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end);
208 }
209}
210
211uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) {
212 if (savedata->command == FLASH_COMMAND_ID) {
213 if (savedata->type == SAVEDATA_FLASH512) {
214 if (address < 2) {
215 return FLASH_MFG_PANASONIC >> (address * 8);
216 }
217 } else if (savedata->type == SAVEDATA_FLASH1M) {
218 if (address < 2) {
219 return FLASH_MFG_SANYO >> (address * 8);
220 }
221 }
222 }
223 return savedata->currentBank[address];
224}
225
226void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8_t value) {
227 switch (savedata->flashState) {
228 case FLASH_STATE_RAW:
229 switch (savedata->command) {
230 case FLASH_COMMAND_PROGRAM:
231 savedata->currentBank[address] = value;
232 savedata->command = FLASH_COMMAND_NONE;
233 break;
234 case FLASH_COMMAND_SWITCH_BANK:
235 if (address == 0 && value < 2) {
236 _flashSwitchBank(savedata, value);
237 } else {
238 GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash bank switch");
239 savedata->command = FLASH_COMMAND_NONE;
240 }
241 savedata->command = FLASH_COMMAND_NONE;
242 break;
243 default:
244 if (address == FLASH_BASE_HI && value == FLASH_COMMAND_START) {
245 savedata->flashState = FLASH_STATE_START;
246 } else {
247 GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
248 }
249 break;
250 }
251 break;
252 case FLASH_STATE_START:
253 if (address == FLASH_BASE_LO && value == FLASH_COMMAND_CONTINUE) {
254 savedata->flashState = FLASH_STATE_CONTINUE;
255 } else {
256 GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
257 savedata->flashState = FLASH_STATE_RAW;
258 }
259 break;
260 case FLASH_STATE_CONTINUE:
261 savedata->flashState = FLASH_STATE_RAW;
262 if (address == FLASH_BASE_HI) {
263 switch (savedata->command) {
264 case FLASH_COMMAND_NONE:
265 switch (value) {
266 case FLASH_COMMAND_ERASE:
267 case FLASH_COMMAND_ID:
268 case FLASH_COMMAND_PROGRAM:
269 case FLASH_COMMAND_SWITCH_BANK:
270 savedata->command = value;
271 break;
272 default:
273 GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash operation: %#02x", value);
274 break;
275 }
276 break;
277 case FLASH_COMMAND_ERASE:
278 switch (value) {
279 case FLASH_COMMAND_ERASE_CHIP:
280 _flashErase(savedata);
281 break;
282 default:
283 GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
284 break;
285 }
286 savedata->command = FLASH_COMMAND_NONE;
287 break;
288 case FLASH_COMMAND_ID:
289 if (value == FLASH_COMMAND_TERMINATE) {
290 savedata->command = FLASH_COMMAND_NONE;
291 }
292 break;
293 default:
294 GBALog(0, GBA_LOG_ERROR, "Flash entered bad state: %#02x", savedata->command);
295 savedata->command = FLASH_COMMAND_NONE;
296 break;
297 }
298 } else if (savedata->command == FLASH_COMMAND_ERASE) {
299 if (value == FLASH_COMMAND_ERASE_SECTOR) {
300 _flashEraseSector(savedata, address);
301 savedata->command = FLASH_COMMAND_NONE;
302 } else {
303 GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
304 }
305 }
306 break;
307 }
308}
309
310void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
311 switch (savedata->command) {
312 // Read header
313 case EEPROM_COMMAND_NULL:
314 default:
315 savedata->command = value & 0x1;
316 break;
317 case EEPROM_COMMAND_PENDING:
318 savedata->command <<= 1;
319 savedata->command |= value & 0x1;
320 if (savedata->command == EEPROM_COMMAND_WRITE) {
321 savedata->addressBits = writeSize - 64 - 2;
322 savedata->writeAddress = 0;
323 } else {
324 savedata->addressBits = writeSize - 2;
325 savedata->readAddress = 0;
326 }
327 break;
328 // Do commands
329 case EEPROM_COMMAND_WRITE:
330 // Write
331 if (writeSize > 65) {
332 savedata->writeAddress <<= 1;
333 savedata->writeAddress |= (value & 0x1) << 6;
334 } else if (writeSize == 1) {
335 savedata->command = EEPROM_COMMAND_NULL;
336 savedata->writePending = 1;
337 } else {
338 uint8_t current = savedata->data[savedata->writeAddress >> 3];
339 current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7)));
340 current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7));
341 savedata->data[savedata->writeAddress >> 3] = current;
342 ++savedata->writeAddress;
343 }
344 break;
345 case EEPROM_COMMAND_READ_PENDING:
346 // Read
347 if (writeSize > 1) {
348 savedata->readAddress <<= 1;
349 if (value & 0x1) {
350 savedata->readAddress |= 0x40;
351 }
352 } else {
353 savedata->readBitsRemaining = 68;
354 savedata->command = EEPROM_COMMAND_READ;
355 }
356 break;
357 }
358}
359
360uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
361 if (savedata->command != EEPROM_COMMAND_READ) {
362 return 1;
363 }
364 --savedata->readBitsRemaining;
365 if (savedata->readBitsRemaining < 64) {
366 int step = 63 - savedata->readBitsRemaining;
367 uint8_t data = savedata->data[(savedata->readAddress + step) >> 3] >> (0x7 - (step & 0x7));
368 if (!savedata->readBitsRemaining) {
369 savedata->command = EEPROM_COMMAND_NULL;
370 }
371 return data & 0x1;
372 }
373 return 0;
374}
375
376void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
377 GBALog(0, GBA_LOG_DEBUG, "Performing flash bank switch to bank %i", bank);
378 savedata->currentBank = &savedata->data[bank << 16];
379 if (bank > 0 && savedata->type == SAVEDATA_FLASH512) {
380 savedata->type = SAVEDATA_FLASH1M;
381 if (savedata->vf) {
382 savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
383 memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512);
384 }
385 }
386}
387
388void _flashErase(struct GBASavedata* savedata) {
389 GBALog(0, GBA_LOG_DEBUG, "Performing flash chip erase");
390 size_t size = SIZE_CART_FLASH512;
391 if (savedata->type == SAVEDATA_FLASH1M) {
392 size = SIZE_CART_FLASH1M;
393 }
394 memset(savedata->data, 0xFF, size);
395}
396
397void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) {
398 GBALog(0, GBA_LOG_DEBUG, "Performing flash sector erase at 0x%04x", sectorStart);
399 size_t size = 0x1000;
400 if (savedata->type == SAVEDATA_FLASH1M) {
401 GBALog(0, GBA_LOG_DEBUG, "Performing unknown sector-size erase at 0x%04x", sectorStart);
402 }
403 memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size);
404}