2015-02-03 07:52:37 +00:00
|
|
|
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
2014-12-03 08:39:06 +00:00
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2015-02-03 07:52:37 +00:00
|
|
|
#include "savedata.h"
|
2013-04-28 02:59:41 +00:00
|
|
|
|
2015-02-03 07:52:37 +00:00
|
|
|
#include "gba/gba.h"
|
2015-03-21 08:12:39 +00:00
|
|
|
#include "gba/serialize.h"
|
2014-04-03 06:50:20 +00:00
|
|
|
|
|
|
|
#include "util/memory.h"
|
2014-07-17 06:55:09 +00:00
|
|
|
#include "util/vfs.h"
|
2013-04-28 02:59:41 +00:00
|
|
|
|
2013-09-22 09:13:03 +00:00
|
|
|
#include <errno.h>
|
2013-04-28 02:59:41 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
|
2015-03-28 10:25:46 +00:00
|
|
|
#define FLASH_SETTLE_CYCLES 18000
|
|
|
|
|
2013-09-24 06:04:15 +00:00
|
|
|
static void _flashSwitchBank(struct GBASavedata* savedata, int bank);
|
|
|
|
static void _flashErase(struct GBASavedata* savedata);
|
|
|
|
static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart);
|
|
|
|
|
2014-07-16 09:57:04 +00:00
|
|
|
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
|
2015-01-12 08:59:48 +00:00
|
|
|
savedata->type = SAVEDATA_AUTODETECT;
|
2013-04-28 02:59:41 +00:00
|
|
|
savedata->data = 0;
|
2013-09-24 06:04:15 +00:00
|
|
|
savedata->command = EEPROM_COMMAND_NULL;
|
|
|
|
savedata->flashState = FLASH_STATE_RAW;
|
2014-07-16 09:57:04 +00:00
|
|
|
savedata->vf = vf;
|
2014-08-05 05:24:43 +00:00
|
|
|
savedata->realVf = vf;
|
2014-08-05 06:46:33 +00:00
|
|
|
savedata->mapMode = MAP_WRITE;
|
2013-04-28 02:59:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GBASavedataDeinit(struct GBASavedata* savedata) {
|
2014-07-16 09:08:54 +00:00
|
|
|
if (savedata->vf) {
|
|
|
|
switch (savedata->type) {
|
|
|
|
case SAVEDATA_SRAM:
|
|
|
|
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_SRAM);
|
|
|
|
break;
|
|
|
|
case SAVEDATA_FLASH512:
|
|
|
|
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH512);
|
|
|
|
break;
|
|
|
|
case SAVEDATA_FLASH1M:
|
|
|
|
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH1M);
|
|
|
|
break;
|
|
|
|
case SAVEDATA_EEPROM:
|
|
|
|
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM);
|
|
|
|
break;
|
2015-01-12 08:59:48 +00:00
|
|
|
case SAVEDATA_FORCE_NONE:
|
|
|
|
case SAVEDATA_AUTODETECT:
|
2014-07-16 09:08:54 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
savedata->vf = 0;
|
|
|
|
} else {
|
|
|
|
switch (savedata->type) {
|
|
|
|
case SAVEDATA_SRAM:
|
|
|
|
mappedMemoryFree(savedata->data, SIZE_CART_SRAM);
|
|
|
|
break;
|
|
|
|
case SAVEDATA_FLASH512:
|
|
|
|
mappedMemoryFree(savedata->data, SIZE_CART_FLASH512);
|
|
|
|
break;
|
|
|
|
case SAVEDATA_FLASH1M:
|
|
|
|
mappedMemoryFree(savedata->data, SIZE_CART_FLASH1M);
|
|
|
|
break;
|
|
|
|
case SAVEDATA_EEPROM:
|
|
|
|
mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
|
|
|
|
break;
|
2015-01-12 08:59:48 +00:00
|
|
|
case SAVEDATA_FORCE_NONE:
|
|
|
|
case SAVEDATA_AUTODETECT:
|
2014-07-16 09:08:54 +00:00
|
|
|
break;
|
|
|
|
}
|
2013-10-18 15:42:15 +00:00
|
|
|
}
|
2014-08-05 05:24:43 +00:00
|
|
|
savedata->data = 0;
|
2015-01-12 08:59:48 +00:00
|
|
|
savedata->type = SAVEDATA_AUTODETECT;
|
2013-04-28 02:59:41 +00:00
|
|
|
}
|
|
|
|
|
2014-08-04 08:11:46 +00:00
|
|
|
void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf) {
|
|
|
|
GBASavedataDeinit(savedata);
|
2014-08-05 05:24:43 +00:00
|
|
|
savedata->vf = vf;
|
2014-08-05 06:46:33 +00:00
|
|
|
savedata->mapMode = MAP_READ;
|
2014-08-05 05:24:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GBASavedataUnmask(struct GBASavedata* savedata) {
|
2014-10-22 05:08:20 +00:00
|
|
|
if (savedata->mapMode != MAP_READ) {
|
|
|
|
return;
|
|
|
|
}
|
2014-08-05 05:24:43 +00:00
|
|
|
GBASavedataDeinit(savedata);
|
|
|
|
savedata->vf = savedata->realVf;
|
2014-08-05 06:46:33 +00:00
|
|
|
savedata->mapMode = MAP_WRITE;
|
2014-08-04 08:11:46 +00:00
|
|
|
}
|
|
|
|
|
2014-08-05 06:37:37 +00:00
|
|
|
bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
|
|
|
|
if (savedata->data) {
|
|
|
|
switch (savedata->type) {
|
|
|
|
case SAVEDATA_SRAM:
|
|
|
|
return out->write(out, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM;
|
|
|
|
case SAVEDATA_FLASH512:
|
|
|
|
return out->write(out, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512;
|
|
|
|
case SAVEDATA_FLASH1M:
|
|
|
|
return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
|
|
|
|
case SAVEDATA_EEPROM:
|
|
|
|
return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
|
2015-01-12 08:59:48 +00:00
|
|
|
case SAVEDATA_AUTODETECT:
|
|
|
|
case SAVEDATA_FORCE_NONE:
|
2014-08-05 06:37:37 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else if (savedata->vf) {
|
|
|
|
off_t read = 0;
|
|
|
|
uint8_t buffer[2048];
|
|
|
|
do {
|
|
|
|
read = savedata->vf->read(savedata->vf, buffer, sizeof(buffer));
|
|
|
|
out->write(out, buffer, read);
|
|
|
|
} while (read == sizeof(buffer));
|
|
|
|
return read >= 0;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-28 10:25:46 +00:00
|
|
|
void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type, bool realisticTiming) {
|
2015-01-25 23:21:48 +00:00
|
|
|
if (savedata->type != SAVEDATA_AUTODETECT) {
|
|
|
|
struct VFile* vf = savedata->vf;
|
|
|
|
GBASavedataDeinit(savedata);
|
|
|
|
GBASavedataInit(savedata, vf);
|
|
|
|
}
|
2014-12-31 06:56:17 +00:00
|
|
|
switch (type) {
|
|
|
|
case SAVEDATA_FLASH512:
|
|
|
|
case SAVEDATA_FLASH1M:
|
|
|
|
savedata->type = type;
|
2015-03-28 10:25:46 +00:00
|
|
|
GBASavedataInitFlash(savedata, realisticTiming);
|
2014-12-31 06:56:17 +00:00
|
|
|
break;
|
|
|
|
case SAVEDATA_EEPROM:
|
|
|
|
GBASavedataInitEEPROM(savedata);
|
|
|
|
break;
|
|
|
|
case SAVEDATA_SRAM:
|
|
|
|
GBASavedataInitSRAM(savedata);
|
|
|
|
break;
|
2015-01-12 08:59:48 +00:00
|
|
|
case SAVEDATA_FORCE_NONE:
|
|
|
|
savedata->type = SAVEDATA_FORCE_NONE;
|
|
|
|
break;
|
|
|
|
case SAVEDATA_AUTODETECT:
|
2014-12-31 06:56:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-28 10:25:46 +00:00
|
|
|
void GBASavedataInitFlash(struct GBASavedata* savedata, bool realisticTiming) {
|
2015-01-12 08:59:48 +00:00
|
|
|
if (savedata->type == SAVEDATA_AUTODETECT) {
|
2013-10-12 05:03:27 +00:00
|
|
|
savedata->type = SAVEDATA_FLASH512;
|
|
|
|
}
|
|
|
|
if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {
|
|
|
|
GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
|
|
|
|
return;
|
|
|
|
}
|
2014-12-21 22:49:54 +00:00
|
|
|
size_t flashSize = SIZE_CART_FLASH512;
|
2013-09-22 09:13:03 +00:00
|
|
|
off_t end;
|
2014-07-16 09:08:54 +00:00
|
|
|
if (!savedata->vf) {
|
2013-09-22 09:13:03 +00:00
|
|
|
end = 0;
|
2014-01-15 08:24:06 +00:00
|
|
|
savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M);
|
2013-09-22 09:13:03 +00:00
|
|
|
} else {
|
2015-01-11 08:35:22 +00:00
|
|
|
end = savedata->vf->size(savedata->vf);
|
2013-09-22 09:13:03 +00:00
|
|
|
if (end < SIZE_CART_FLASH512) {
|
2014-07-16 09:08:54 +00:00
|
|
|
savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
|
2014-12-21 22:49:54 +00:00
|
|
|
flashSize = SIZE_CART_FLASH1M;
|
2013-09-22 09:13:03 +00:00
|
|
|
}
|
2014-08-05 06:46:33 +00:00
|
|
|
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, savedata->mapMode);
|
2013-04-28 02:59:41 +00:00
|
|
|
}
|
2014-01-15 08:24:06 +00:00
|
|
|
|
2013-09-24 06:04:15 +00:00
|
|
|
savedata->currentBank = savedata->data;
|
2015-03-28 10:25:46 +00:00
|
|
|
savedata->dust = 0;
|
|
|
|
savedata->realisticTiming = realisticTiming;
|
2013-04-28 02:59:41 +00:00
|
|
|
if (end < SIZE_CART_FLASH512) {
|
2014-12-21 22:49:54 +00:00
|
|
|
memset(&savedata->data[end], 0xFF, flashSize - end);
|
2013-04-28 02:59:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-28 03:25:31 +00:00
|
|
|
void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
|
2015-01-12 08:59:48 +00:00
|
|
|
if (savedata->type == SAVEDATA_AUTODETECT) {
|
2013-10-12 05:03:27 +00:00
|
|
|
savedata->type = SAVEDATA_EEPROM;
|
|
|
|
} else {
|
|
|
|
GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
|
|
|
|
return;
|
|
|
|
}
|
2013-09-22 09:13:03 +00:00
|
|
|
off_t end;
|
2014-07-16 09:08:54 +00:00
|
|
|
if (!savedata->vf) {
|
2013-09-22 09:13:03 +00:00
|
|
|
end = 0;
|
2014-01-15 08:24:06 +00:00
|
|
|
savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
|
2013-09-22 09:13:03 +00:00
|
|
|
} else {
|
2015-01-11 08:35:22 +00:00
|
|
|
end = savedata->vf->size(savedata->vf);
|
2013-09-22 09:13:03 +00:00
|
|
|
if (end < SIZE_CART_EEPROM) {
|
2014-07-16 09:08:54 +00:00
|
|
|
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
|
2013-09-22 09:13:03 +00:00
|
|
|
}
|
2014-08-05 06:46:33 +00:00
|
|
|
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
|
2013-04-28 03:25:31 +00:00
|
|
|
}
|
|
|
|
if (end < SIZE_CART_EEPROM) {
|
|
|
|
memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-28 02:59:41 +00:00
|
|
|
void GBASavedataInitSRAM(struct GBASavedata* savedata) {
|
2015-01-12 08:59:48 +00:00
|
|
|
if (savedata->type == SAVEDATA_AUTODETECT) {
|
2013-10-12 05:03:27 +00:00
|
|
|
savedata->type = SAVEDATA_SRAM;
|
|
|
|
} else {
|
|
|
|
GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
|
|
|
|
return;
|
|
|
|
}
|
2013-04-28 02:59:41 +00:00
|
|
|
off_t end;
|
2014-07-16 09:08:54 +00:00
|
|
|
if (!savedata->vf) {
|
2013-04-28 02:59:41 +00:00
|
|
|
end = 0;
|
2014-01-15 08:24:06 +00:00
|
|
|
savedata->data = anonymousMemoryMap(SIZE_CART_SRAM);
|
2013-04-28 02:59:41 +00:00
|
|
|
} else {
|
2015-01-11 08:35:22 +00:00
|
|
|
end = savedata->vf->size(savedata->vf);
|
2013-04-28 02:59:41 +00:00
|
|
|
if (end < SIZE_CART_SRAM) {
|
2014-07-16 09:08:54 +00:00
|
|
|
savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM);
|
2013-04-28 02:59:41 +00:00
|
|
|
}
|
2014-08-05 06:46:33 +00:00
|
|
|
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, savedata->mapMode);
|
2013-04-28 02:59:41 +00:00
|
|
|
}
|
2014-01-15 08:24:06 +00:00
|
|
|
|
2013-04-28 02:59:41 +00:00
|
|
|
if (end < SIZE_CART_SRAM) {
|
|
|
|
memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-24 06:04:15 +00:00
|
|
|
uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) {
|
|
|
|
if (savedata->command == FLASH_COMMAND_ID) {
|
|
|
|
if (savedata->type == SAVEDATA_FLASH512) {
|
|
|
|
if (address < 2) {
|
|
|
|
return FLASH_MFG_PANASONIC >> (address * 8);
|
|
|
|
}
|
|
|
|
} else if (savedata->type == SAVEDATA_FLASH1M) {
|
|
|
|
if (address < 2) {
|
|
|
|
return FLASH_MFG_SANYO >> (address * 8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-03-28 10:25:46 +00:00
|
|
|
if (savedata->dust > 0 && (address >> 12) == savedata->settling) {
|
|
|
|
--savedata->dust;
|
|
|
|
return 0x5F;
|
|
|
|
}
|
2013-09-24 06:04:15 +00:00
|
|
|
return savedata->currentBank[address];
|
|
|
|
}
|
2013-04-28 03:25:31 +00:00
|
|
|
|
2013-09-24 06:04:15 +00:00
|
|
|
void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8_t value) {
|
|
|
|
switch (savedata->flashState) {
|
|
|
|
case FLASH_STATE_RAW:
|
|
|
|
switch (savedata->command) {
|
|
|
|
case FLASH_COMMAND_PROGRAM:
|
|
|
|
savedata->currentBank[address] = value;
|
|
|
|
savedata->command = FLASH_COMMAND_NONE;
|
|
|
|
break;
|
|
|
|
case FLASH_COMMAND_SWITCH_BANK:
|
|
|
|
if (address == 0 && value < 2) {
|
|
|
|
_flashSwitchBank(savedata, value);
|
|
|
|
} else {
|
|
|
|
GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash bank switch");
|
|
|
|
savedata->command = FLASH_COMMAND_NONE;
|
|
|
|
}
|
2013-09-25 11:48:27 +00:00
|
|
|
savedata->command = FLASH_COMMAND_NONE;
|
2013-09-24 06:04:15 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (address == FLASH_BASE_HI && value == FLASH_COMMAND_START) {
|
|
|
|
savedata->flashState = FLASH_STATE_START;
|
|
|
|
} else {
|
|
|
|
GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FLASH_STATE_START:
|
|
|
|
if (address == FLASH_BASE_LO && value == FLASH_COMMAND_CONTINUE) {
|
|
|
|
savedata->flashState = FLASH_STATE_CONTINUE;
|
|
|
|
} else {
|
|
|
|
GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
|
|
|
|
savedata->flashState = FLASH_STATE_RAW;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FLASH_STATE_CONTINUE:
|
|
|
|
savedata->flashState = FLASH_STATE_RAW;
|
|
|
|
if (address == FLASH_BASE_HI) {
|
|
|
|
switch (savedata->command) {
|
|
|
|
case FLASH_COMMAND_NONE:
|
|
|
|
switch (value) {
|
|
|
|
case FLASH_COMMAND_ERASE:
|
|
|
|
case FLASH_COMMAND_ID:
|
|
|
|
case FLASH_COMMAND_PROGRAM:
|
|
|
|
case FLASH_COMMAND_SWITCH_BANK:
|
|
|
|
savedata->command = value;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash operation: %#02x", value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FLASH_COMMAND_ERASE:
|
|
|
|
switch (value) {
|
|
|
|
case FLASH_COMMAND_ERASE_CHIP:
|
|
|
|
_flashErase(savedata);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
savedata->command = FLASH_COMMAND_NONE;
|
|
|
|
break;
|
|
|
|
case FLASH_COMMAND_ID:
|
|
|
|
if (value == FLASH_COMMAND_TERMINATE) {
|
|
|
|
savedata->command = FLASH_COMMAND_NONE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
GBALog(0, GBA_LOG_ERROR, "Flash entered bad state: %#02x", savedata->command);
|
|
|
|
savedata->command = FLASH_COMMAND_NONE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (savedata->command == FLASH_COMMAND_ERASE) {
|
|
|
|
if (value == FLASH_COMMAND_ERASE_SECTOR) {
|
|
|
|
_flashEraseSector(savedata, address);
|
|
|
|
savedata->command = FLASH_COMMAND_NONE;
|
|
|
|
} else {
|
|
|
|
GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2013-04-28 02:59:41 +00:00
|
|
|
}
|
2013-04-28 03:25:31 +00:00
|
|
|
|
2013-04-28 04:51:58 +00:00
|
|
|
void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
|
|
|
|
switch (savedata->command) {
|
|
|
|
// Read header
|
|
|
|
case EEPROM_COMMAND_NULL:
|
|
|
|
default:
|
|
|
|
savedata->command = value & 0x1;
|
|
|
|
break;
|
|
|
|
case EEPROM_COMMAND_PENDING:
|
|
|
|
savedata->command <<= 1;
|
|
|
|
savedata->command |= value & 0x1;
|
|
|
|
if (savedata->command == EEPROM_COMMAND_WRITE) {
|
|
|
|
savedata->writeAddress = 0;
|
|
|
|
} else {
|
|
|
|
savedata->readAddress = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
// Do commands
|
|
|
|
case EEPROM_COMMAND_WRITE:
|
|
|
|
// Write
|
|
|
|
if (writeSize > 65) {
|
|
|
|
savedata->writeAddress <<= 1;
|
|
|
|
savedata->writeAddress |= (value & 0x1) << 6;
|
|
|
|
} else if (writeSize == 1) {
|
|
|
|
savedata->command = EEPROM_COMMAND_NULL;
|
2015-05-09 07:09:37 +00:00
|
|
|
} else if ((savedata->writeAddress >> 3) < SIZE_CART_EEPROM) {
|
2013-04-28 04:51:58 +00:00
|
|
|
uint8_t current = savedata->data[savedata->writeAddress >> 3];
|
|
|
|
current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7)));
|
|
|
|
current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7));
|
|
|
|
savedata->data[savedata->writeAddress >> 3] = current;
|
|
|
|
++savedata->writeAddress;
|
2015-05-09 07:09:37 +00:00
|
|
|
} else {
|
|
|
|
GBALog(0, GBA_LOG_GAME_ERROR, "Writing beyond end of EEPROM: %08X", (savedata->writeAddress >> 3));
|
2013-04-28 04:51:58 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case EEPROM_COMMAND_READ_PENDING:
|
|
|
|
// Read
|
|
|
|
if (writeSize > 1) {
|
|
|
|
savedata->readAddress <<= 1;
|
|
|
|
if (value & 0x1) {
|
|
|
|
savedata->readAddress |= 0x40;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
savedata->readBitsRemaining = 68;
|
|
|
|
savedata->command = EEPROM_COMMAND_READ;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2013-04-28 03:25:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
|
2013-04-28 04:51:58 +00:00
|
|
|
if (savedata->command != EEPROM_COMMAND_READ) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
--savedata->readBitsRemaining;
|
|
|
|
if (savedata->readBitsRemaining < 64) {
|
|
|
|
int step = 63 - savedata->readBitsRemaining;
|
2015-05-09 07:09:37 +00:00
|
|
|
uint32_t address = (savedata->readAddress + step) >> 3;
|
|
|
|
if (address >= SIZE_CART_EEPROM) {
|
|
|
|
GBALog(0, GBA_LOG_GAME_ERROR, "Reading beyond end of EEPROM: %08X", address);
|
|
|
|
return 0xFF;
|
|
|
|
}
|
|
|
|
uint8_t data = savedata->data[address] >> (0x7 - (step & 0x7));
|
2013-04-28 04:51:58 +00:00
|
|
|
if (!savedata->readBitsRemaining) {
|
|
|
|
savedata->command = EEPROM_COMMAND_NULL;
|
|
|
|
}
|
|
|
|
return data & 0x1;
|
|
|
|
}
|
2013-04-28 03:25:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2013-09-24 06:04:15 +00:00
|
|
|
|
2015-03-21 08:12:39 +00:00
|
|
|
void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state, bool includeData) {
|
|
|
|
state->savedata.type = savedata->type;
|
|
|
|
state->savedata.command = savedata->command;
|
|
|
|
state->savedata.flashState = savedata->flashState;
|
|
|
|
state->savedata.flashBank = savedata->currentBank == &savedata->data[0x10000];
|
|
|
|
state->savedata.readBitsRemaining = savedata->readBitsRemaining;
|
|
|
|
state->savedata.readAddress = savedata->readAddress;
|
|
|
|
state->savedata.writeAddress = savedata->writeAddress;
|
2015-03-28 10:25:46 +00:00
|
|
|
state->savedata.settlingSector = savedata->settling;
|
|
|
|
state->savedata.settlingDust = savedata->dust;
|
2015-03-21 08:12:39 +00:00
|
|
|
|
|
|
|
UNUSED(includeData); // TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerializedState* state, bool includeData) {
|
|
|
|
if (state->savedata.type == SAVEDATA_FORCE_NONE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (savedata->type != state->savedata.type) {
|
2015-03-28 10:25:46 +00:00
|
|
|
GBASavedataForceType(savedata, state->savedata.type, savedata->realisticTiming);
|
2015-03-21 08:12:39 +00:00
|
|
|
}
|
|
|
|
savedata->command = state->savedata.command;
|
|
|
|
savedata->flashState = state->savedata.flashState;
|
|
|
|
savedata->readBitsRemaining = state->savedata.readBitsRemaining;
|
|
|
|
savedata->readAddress = state->savedata.readAddress;
|
|
|
|
savedata->writeAddress = state->savedata.writeAddress;
|
2015-03-28 10:25:46 +00:00
|
|
|
savedata->settling = state->savedata.settlingSector;
|
|
|
|
savedata->dust = state->savedata.settlingDust;
|
|
|
|
|
2015-03-21 08:12:39 +00:00
|
|
|
if (savedata->type == SAVEDATA_FLASH1M) {
|
|
|
|
_flashSwitchBank(savedata, state->savedata.flashBank);
|
|
|
|
}
|
|
|
|
|
|
|
|
UNUSED(includeData); // TODO
|
|
|
|
}
|
|
|
|
|
2013-09-24 06:04:15 +00:00
|
|
|
void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
|
2014-12-21 22:49:54 +00:00
|
|
|
GBALog(0, GBA_LOG_DEBUG, "Performing flash bank switch to bank %i", bank);
|
2013-09-24 06:04:15 +00:00
|
|
|
savedata->currentBank = &savedata->data[bank << 16];
|
2014-12-21 22:49:54 +00:00
|
|
|
if (bank > 0 && savedata->type == SAVEDATA_FLASH512) {
|
2013-09-24 06:04:15 +00:00
|
|
|
savedata->type = SAVEDATA_FLASH1M;
|
2014-10-11 22:46:59 +00:00
|
|
|
if (savedata->vf) {
|
|
|
|
savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
|
2014-12-21 22:49:54 +00:00
|
|
|
memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512);
|
2014-10-11 22:46:59 +00:00
|
|
|
}
|
2013-09-24 06:04:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _flashErase(struct GBASavedata* savedata) {
|
2014-12-21 22:49:54 +00:00
|
|
|
GBALog(0, GBA_LOG_DEBUG, "Performing flash chip erase");
|
|
|
|
size_t size = SIZE_CART_FLASH512;
|
2013-09-24 06:04:15 +00:00
|
|
|
if (savedata->type == SAVEDATA_FLASH1M) {
|
2014-12-21 22:49:54 +00:00
|
|
|
size = SIZE_CART_FLASH1M;
|
2013-09-24 06:04:15 +00:00
|
|
|
}
|
|
|
|
memset(savedata->data, 0xFF, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) {
|
2014-12-21 22:49:54 +00:00
|
|
|
GBALog(0, GBA_LOG_DEBUG, "Performing flash sector erase at 0x%04x", sectorStart);
|
2013-09-24 06:04:15 +00:00
|
|
|
size_t size = 0x1000;
|
|
|
|
if (savedata->type == SAVEDATA_FLASH1M) {
|
2014-12-21 22:49:54 +00:00
|
|
|
GBALog(0, GBA_LOG_DEBUG, "Performing unknown sector-size erase at 0x%04x", sectorStart);
|
2013-09-24 06:04:15 +00:00
|
|
|
}
|
2015-03-28 10:25:46 +00:00
|
|
|
savedata->settling = sectorStart >> 12;
|
|
|
|
if (savedata->realisticTiming) {
|
|
|
|
savedata->dust = FLASH_SETTLE_CYCLES;
|
|
|
|
}
|
2013-09-24 06:04:15 +00:00
|
|
|
memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size);
|
|
|
|
}
|