mirror of https://github.com/sm64pc/sm64pc.git
Add support for saves of both endiannesses.
Porting from testing. Originally by @fgsfdsfgs.
This commit is contained in:
parent
330f44285f
commit
69ff11e459
|
@ -15,6 +15,12 @@
|
||||||
#define MENU_DATA_MAGIC 0x4849
|
#define MENU_DATA_MAGIC 0x4849
|
||||||
#define SAVE_FILE_MAGIC 0x4441
|
#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");
|
STATIC_ASSERT(sizeof(struct SaveBuffer) == EEPROM_SIZE, "eeprom buffer size must match");
|
||||||
|
|
||||||
extern struct SaveBuffer gSaveBuffer;
|
extern struct SaveBuffer gSaveBuffer;
|
||||||
|
@ -50,6 +56,38 @@ static void stub_save_file_1(void) {
|
||||||
UNUSED s32 pad;
|
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.
|
* Read from EEPROM to a given address.
|
||||||
* The EEPROM address is computed using the offset of the destination address from gSaveBuffer.
|
* 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.
|
* 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
|
* 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.
|
* 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;
|
s32 status = 1;
|
||||||
|
|
||||||
if (gEepromProbe != 0) {
|
if (gEepromProbe != 0) {
|
||||||
s32 triesLeft = 4;
|
s32 triesLeft = 4;
|
||||||
u32 offset = (u32)((u8 *) buffer - (u8 *) &gSaveBuffer) >> 3;
|
u32 offset = (u32)baseofs >> 3;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
#ifdef VERSION_SH
|
#ifdef VERSION_SH
|
||||||
|
@ -106,6 +144,41 @@ static s32 write_eeprom_data(void *buffer, s32 size) {
|
||||||
return status;
|
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
|
* Sum the bytes in data to data + size - 2. The last two bytes are ignored
|
||||||
* because that is where the checksum is stored.
|
* 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]));
|
bcopy(&gSaveBuffer.menuData[srcSlot], &gSaveBuffer.menuData[destSlot], sizeof(gSaveBuffer.menuData[destSlot]));
|
||||||
|
|
||||||
// Write destination data to EEPROM
|
// 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) {
|
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]));
|
bcopy(&gSaveBuffer.menuData[0], &gSaveBuffer.menuData[1], sizeof(gSaveBuffer.menuData[1]));
|
||||||
|
|
||||||
// Write to EEPROM
|
// Write to EEPROM
|
||||||
write_eeprom_data(gSaveBuffer.menuData, sizeof(gSaveBuffer.menuData));
|
write_eeprom_menudata(0, 2);
|
||||||
|
|
||||||
gMainMenuDataModified = FALSE;
|
gMainMenuDataModified = FALSE;
|
||||||
}
|
}
|
||||||
|
@ -245,8 +318,35 @@ static void restore_save_file_data(s32 fileIndex, s32 srcSlot) {
|
||||||
sizeof(gSaveBuffer.files[fileIndex][destSlot]));
|
sizeof(gSaveBuffer.files[fileIndex][destSlot]));
|
||||||
|
|
||||||
// Write destination data to EEPROM
|
// Write destination data to EEPROM
|
||||||
write_eeprom_data(&gSaveBuffer.files[fileIndex][destSlot],
|
write_eeprom_savefile(fileIndex, destSlot, 1);
|
||||||
sizeof(gSaveBuffer.files[fileIndex][destSlot]));
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
void save_file_do_save(s32 fileIndex) {
|
||||||
|
@ -260,7 +360,7 @@ void save_file_do_save(s32 fileIndex) {
|
||||||
sizeof(gSaveBuffer.files[fileIndex][1]));
|
sizeof(gSaveBuffer.files[fileIndex][1]));
|
||||||
|
|
||||||
// Write to EEPROM
|
// Write to EEPROM
|
||||||
write_eeprom_data(gSaveBuffer.files[fileIndex], sizeof(gSaveBuffer.files[fileIndex]));
|
write_eeprom_savefile(fileIndex, 0, 2);
|
||||||
|
|
||||||
gSaveFileModified = FALSE;
|
gSaveFileModified = FALSE;
|
||||||
}
|
}
|
||||||
|
@ -298,6 +398,9 @@ void save_file_load_all(void) {
|
||||||
bzero(&gSaveBuffer, sizeof(gSaveBuffer));
|
bzero(&gSaveBuffer, sizeof(gSaveBuffer));
|
||||||
read_eeprom_data(&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.
|
// 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[0], sizeof(gSaveBuffer.menuData[0]), MENU_DATA_MAGIC);
|
||||||
validSlots |= verify_save_block_signature(&gSaveBuffer.menuData[1], sizeof(gSaveBuffer.menuData[1]),MENU_DATA_MAGIC) << 1;
|
validSlots |= verify_save_block_signature(&gSaveBuffer.menuData[1], sizeof(gSaveBuffer.menuData[1]),MENU_DATA_MAGIC) << 1;
|
||||||
|
|
Loading…
Reference in New Issue