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