Add support for saves of both endiannesses.
Porting from testing. Originally by @fgsfdsfgs.
This commit is contained in:
parent
e0533abb54
commit
3007a5e2a4
|
@ -15,6 +15,12 @@
|
|||
#define MENU_DATA_MAGIC 0x4849
|
||||
#define SAVE_FILE_MAGIC 0x4441
|
||||
|
||||
#define BSWAP16(x) \
|
||||
( (((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00) )
|
||||
#define BSWAP32(x) \
|
||||
( (((x) >> 24) & 0x000000FF) | (((x) >> 8) & 0x0000FF00) | \
|
||||
(((x) << 8) & 0x00FF0000) | (((x) << 24) & 0xFF000000) )
|
||||
|
||||
STATIC_ASSERT(sizeof(struct SaveBuffer) == EEPROM_SIZE, "eeprom buffer size must match");
|
||||
|
||||
extern struct SaveBuffer gSaveBuffer;
|
||||
|
@ -50,6 +56,38 @@ static void stub_save_file_1(void) {
|
|||
UNUSED s32 pad;
|
||||
}
|
||||
|
||||
/**
|
||||
* Byteswap all multibyte fields in a SaveBlockSignature.
|
||||
*/
|
||||
static inline void bswap_signature(struct SaveBlockSignature *data) {
|
||||
data->magic = BSWAP16(data->magic);
|
||||
data->chksum = BSWAP16(data->chksum); // valid as long as the checksum is a literal sum
|
||||
}
|
||||
|
||||
/**
|
||||
* Byteswap all multibyte fields in a MainMenuSaveData.
|
||||
*/
|
||||
static inline void bswap_menudata(struct MainMenuSaveData *data) {
|
||||
for (int i = 0; i < NUM_SAVE_FILES; ++i)
|
||||
data->coinScoreAges[i] = BSWAP32(data->coinScoreAges[i]);
|
||||
data->soundMode = BSWAP16(data->soundMode);
|
||||
#ifdef VERSION_EU
|
||||
data->language = BSWAP16(data->language);
|
||||
#endif
|
||||
bswap_signature(&data->signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Byteswap all multibyte fields in a SaveFile.
|
||||
*/
|
||||
static inline void bswap_savefile(struct SaveFile *data) {
|
||||
data->capPos[0] = BSWAP16(data->capPos[0]);
|
||||
data->capPos[1] = BSWAP16(data->capPos[1]);
|
||||
data->capPos[2] = BSWAP16(data->capPos[2]);
|
||||
data->flags = BSWAP32(data->flags);
|
||||
bswap_signature(&data->signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read from EEPROM to a given address.
|
||||
* The EEPROM address is computed using the offset of the destination address from gSaveBuffer.
|
||||
|
@ -80,16 +118,16 @@ static s32 read_eeprom_data(void *buffer, s32 size) {
|
|||
|
||||
/**
|
||||
* Write data to EEPROM.
|
||||
* The EEPROM address is computed using the offset of the source address from gSaveBuffer.
|
||||
* The EEPROM address was originally computed using the offset of the source address from gSaveBuffer.
|
||||
* Try at most 4 times, and return 0 on success. On failure, return the status returned from
|
||||
* osEepromLongWrite. Unlike read_eeprom_data, return 1 if EEPROM isn't loaded.
|
||||
*/
|
||||
static s32 write_eeprom_data(void *buffer, s32 size) {
|
||||
static s32 write_eeprom_data(void *buffer, s32 size, const uintptr_t baseofs) {
|
||||
s32 status = 1;
|
||||
|
||||
if (gEepromProbe != 0) {
|
||||
s32 triesLeft = 4;
|
||||
u32 offset = (u32)((u8 *) buffer - (u8 *) &gSaveBuffer) >> 3;
|
||||
u32 offset = (u32)baseofs >> 3;
|
||||
|
||||
do {
|
||||
#ifdef VERSION_SH
|
||||
|
@ -106,6 +144,41 @@ static s32 write_eeprom_data(void *buffer, s32 size) {
|
|||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrappers that byteswap the data on LE platforms before writing it to 'EEPROM'
|
||||
*/
|
||||
|
||||
static inline s32 write_eeprom_savefile(const u32 file, const u32 slot, const u32 num) {
|
||||
// calculate the EEPROM address using the file number and slot
|
||||
const uintptr_t ofs = (u8*)&gSaveBuffer.files[file][slot] - (u8*)&gSaveBuffer;
|
||||
|
||||
#if IS_BIG_ENDIAN
|
||||
return write_eeprom_data(&gSaveBuffer.files[file][slot], num * sizeof(struct SaveFile), ofs);
|
||||
#else
|
||||
// byteswap the data and then write it
|
||||
struct SaveFile sf[num];
|
||||
bcopy(&gSaveBuffer.files[file][slot], sf, num * sizeof(sf[0]));
|
||||
for (u32 i = 0; i < num; ++i) bswap_savefile(&sf[i]);
|
||||
return write_eeprom_data(&sf, sizeof(sf), ofs);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline s32 write_eeprom_menudata(const u32 slot, const u32 num) {
|
||||
// calculate the EEPROM address using the slot
|
||||
const uintptr_t ofs = (u8*)&gSaveBuffer.menuData[slot] - (u8*)&gSaveBuffer;
|
||||
|
||||
#if IS_BIG_ENDIAN
|
||||
return write_eeprom_data(&gSaveBuffer.menuData[slot], num * sizeof(struct MainMenuSaveData), ofs);
|
||||
#else
|
||||
// byteswap the data and then write it
|
||||
struct MainMenuSaveData md[num];
|
||||
bcopy(&gSaveBuffer.menuData[slot], md, num * sizeof(md[0]));
|
||||
for (u32 i = 0; i < num; ++i) bswap_menudata(&md[i]);
|
||||
return write_eeprom_data(&md, sizeof(md), ofs);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sum the bytes in data to data + size - 2. The last two bytes are ignored
|
||||
* because that is where the checksum is stored.
|
||||
|
@ -157,7 +230,7 @@ static void restore_main_menu_data(s32 srcSlot) {
|
|||
bcopy(&gSaveBuffer.menuData[srcSlot], &gSaveBuffer.menuData[destSlot], sizeof(gSaveBuffer.menuData[destSlot]));
|
||||
|
||||
// Write destination data to EEPROM
|
||||
write_eeprom_data(&gSaveBuffer.menuData[destSlot], sizeof(gSaveBuffer.menuData[destSlot]));
|
||||
write_eeprom_menudata(destSlot, 1);
|
||||
}
|
||||
|
||||
static void save_main_menu_data(void) {
|
||||
|
@ -169,7 +242,7 @@ static void save_main_menu_data(void) {
|
|||
bcopy(&gSaveBuffer.menuData[0], &gSaveBuffer.menuData[1], sizeof(gSaveBuffer.menuData[1]));
|
||||
|
||||
// Write to EEPROM
|
||||
write_eeprom_data(gSaveBuffer.menuData, sizeof(gSaveBuffer.menuData));
|
||||
write_eeprom_menudata(0, 2);
|
||||
|
||||
gMainMenuDataModified = FALSE;
|
||||
}
|
||||
|
@ -245,8 +318,35 @@ static void restore_save_file_data(s32 fileIndex, s32 srcSlot) {
|
|||
sizeof(gSaveBuffer.files[fileIndex][destSlot]));
|
||||
|
||||
// Write destination data to EEPROM
|
||||
write_eeprom_data(&gSaveBuffer.files[fileIndex][destSlot],
|
||||
sizeof(gSaveBuffer.files[fileIndex][destSlot]));
|
||||
write_eeprom_savefile(fileIndex, destSlot, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the 'EEPROM' save has different endianness (e.g. it's from an actual N64).
|
||||
*/
|
||||
static u8 save_file_need_bswap(const struct SaveBuffer *buf) {
|
||||
// check all signatures just in case
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (buf->menuData[i].signature.magic == BSWAP16(MENU_DATA_MAGIC))
|
||||
return TRUE;
|
||||
for (int j = 0; j < NUM_SAVE_FILES; ++j) {
|
||||
if (buf->files[j][i].signature.magic == BSWAP16(SAVE_FILE_MAGIC))
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Byteswap all multibyte fields in a SaveBuffer.
|
||||
*/
|
||||
static void save_file_bswap(struct SaveBuffer *buf) {
|
||||
bswap_menudata(buf->menuData + 0);
|
||||
bswap_menudata(buf->menuData + 1);
|
||||
for (int i = 0; i < NUM_SAVE_FILES; ++i) {
|
||||
bswap_savefile(buf->files[i] + 0);
|
||||
bswap_savefile(buf->files[i] + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void save_file_do_save(s32 fileIndex) {
|
||||
|
@ -260,7 +360,7 @@ void save_file_do_save(s32 fileIndex) {
|
|||
sizeof(gSaveBuffer.files[fileIndex][1]));
|
||||
|
||||
// Write to EEPROM
|
||||
write_eeprom_data(gSaveBuffer.files[fileIndex], sizeof(gSaveBuffer.files[fileIndex]));
|
||||
write_eeprom_savefile(fileIndex, 0, 2);
|
||||
|
||||
gSaveFileModified = FALSE;
|
||||
}
|
||||
|
@ -298,6 +398,9 @@ void save_file_load_all(void) {
|
|||
bzero(&gSaveBuffer, sizeof(gSaveBuffer));
|
||||
read_eeprom_data(&gSaveBuffer, sizeof(gSaveBuffer));
|
||||
|
||||
if (save_file_need_bswap(&gSaveBuffer))
|
||||
save_file_bswap(&gSaveBuffer);
|
||||
|
||||
// Verify the main menu data and create a backup copy if only one of the slots is valid.
|
||||
validSlots = verify_save_block_signature(&gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0]), MENU_DATA_MAGIC);
|
||||
validSlots |= verify_save_block_signature(&gSaveBuffer.menuData[1], sizeof(gSaveBuffer.menuData[1]),MENU_DATA_MAGIC) << 1;
|
||||
|
|
Loading…
Reference in New Issue