mirror of
https://github.com/izzy2lost/2ship2harkinian-Android.git
synced 2026-06-19 01:20:08 -07:00
Fix save corruption from non null-terminated cstrings (variation) (#230)
* verify save slots better; allow global.sav to work again * fix save corruption and restore global.sav * add comments
This commit is contained in:
@@ -17,15 +17,18 @@ void to_json(json& j, const ItemEquips& itemEquips) {
|
||||
void from_json(const json& j, ItemEquips& itemEquips) {
|
||||
j.at("equipment").get_to(itemEquips.equipment);
|
||||
// buttonItems and cButtonSlots are arrays of arrays, so we need to manually parse them
|
||||
for (int x = 0; x < 4; x++) {
|
||||
for (int y = 0; y < 4; y++) {
|
||||
itemEquips.buttonItems[x][y] = j.at("buttonItems")[x][y].get<u8>();
|
||||
itemEquips.cButtonSlots[x][y] = j.at("cButtonSlots")[x][y].get<u8>();
|
||||
}
|
||||
for (int i = 0; i < ARRAY_COUNT(itemEquips.buttonItems); i++) {
|
||||
j.at("buttonItems").at(i).get_to(itemEquips.buttonItems[i]);
|
||||
j.at("cButtonSlots").at(i).get_to(itemEquips.cButtonSlots[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(json& j, const Inventory& inventory) {
|
||||
// Setup and copy u8 arrays to avoid json treating char[] as strings
|
||||
// These char[] are not null-terminated, so saving as strings causes overflow/corruption
|
||||
uint8_t dekuPlaygroundPlayerName[3][8];
|
||||
memcpy(dekuPlaygroundPlayerName, inventory.dekuPlaygroundPlayerName, sizeof(dekuPlaygroundPlayerName));
|
||||
|
||||
j = json{
|
||||
{ "items", inventory.items },
|
||||
{ "ammo", inventory.ammo },
|
||||
@@ -35,7 +38,7 @@ void to_json(json& j, const Inventory& inventory) {
|
||||
{ "dungeonKeys", inventory.dungeonKeys },
|
||||
{ "defenseHearts", inventory.defenseHearts },
|
||||
{ "strayFairies", inventory.strayFairies },
|
||||
{ "dekuPlaygroundPlayerName", inventory.dekuPlaygroundPlayerName },
|
||||
{ "dekuPlaygroundPlayerName", dekuPlaygroundPlayerName },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,12 +51,9 @@ void from_json(const json& j, Inventory& inventory) {
|
||||
j.at("dungeonKeys").get_to(inventory.dungeonKeys);
|
||||
j.at("defenseHearts").get_to(inventory.defenseHearts);
|
||||
j.at("strayFairies").get_to(inventory.strayFairies);
|
||||
// dekuPlaygroundPlayerName is an array of char arrays, so we need to manually parse it
|
||||
for (int i = 0; i < 3; i++) {
|
||||
std::string name = j.at("dekuPlaygroundPlayerName")[i].get<std::string>();
|
||||
for (int j = 0; j < 8; j++) {
|
||||
inventory.dekuPlaygroundPlayerName[i][j] = name[j];
|
||||
}
|
||||
// dekuPlaygroundPlayerName is an array of arrays, so we need to manually parse it
|
||||
for (int i = 0; i < ARRAY_COUNT(inventory.dekuPlaygroundPlayerName); i++) {
|
||||
j.at("dekuPlaygroundPlayerName").at(i).get_to(inventory.dekuPlaygroundPlayerName[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,10 +80,17 @@ void from_json(const json& j, PermanentSceneFlags& permanentSceneFlags) {
|
||||
}
|
||||
|
||||
void to_json(json& j, const SavePlayerData& savePlayerData) {
|
||||
// Setup and copy u8 arrays to avoid json treating char[] as strings
|
||||
// These char[] are not null-terminated, so saving as strings causes overflow/corruption
|
||||
u8 newf[6];
|
||||
u8 playerName[8];
|
||||
memcpy(newf, savePlayerData.newf, sizeof(newf));
|
||||
memcpy(playerName, savePlayerData.playerName, sizeof(playerName));
|
||||
|
||||
j = json{
|
||||
{ "newf", savePlayerData.newf },
|
||||
{ "newf", newf },
|
||||
{ "threeDayResetCount", savePlayerData.threeDayResetCount },
|
||||
{ "playerName", savePlayerData.playerName },
|
||||
{ "playerName", playerName },
|
||||
{ "healthCapacity", savePlayerData.healthCapacity },
|
||||
{ "health", savePlayerData.health },
|
||||
{ "magicLevel", savePlayerData.magicLevel },
|
||||
@@ -103,16 +110,9 @@ void to_json(json& j, const SavePlayerData& savePlayerData) {
|
||||
}
|
||||
|
||||
void from_json(const json& j, SavePlayerData& savePlayerData) {
|
||||
// newf is an array of chars, so we need to manually parse it
|
||||
std::string newf = j.at("newf").get<std::string>();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
savePlayerData.newf[i] = newf[i];
|
||||
}
|
||||
j.at("newf").get_to(savePlayerData.newf);
|
||||
j.at("threeDayResetCount").get_to(savePlayerData.threeDayResetCount);
|
||||
std::string playerName = j.at("playerName").get<std::string>();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
savePlayerData.playerName[i] = playerName[i];
|
||||
}
|
||||
j.at("playerName").get_to(savePlayerData.playerName);
|
||||
j.at("healthCapacity").get_to(savePlayerData.healthCapacity);
|
||||
j.at("health").get_to(savePlayerData.health);
|
||||
j.at("magicLevel").get_to(savePlayerData.magicLevel);
|
||||
@@ -224,10 +224,8 @@ void from_json(const json& j, SaveInfo& saveInfo) {
|
||||
j.at("bombersCaughtNum").get_to(saveInfo.bombersCaughtNum);
|
||||
j.at("bombersCaughtOrder").get_to(saveInfo.bombersCaughtOrder);
|
||||
// lotteryCodes is an array of arrays, so we need to manually parse it
|
||||
for (int x = 0; x < 3; x++) {
|
||||
for (int y = 0; y < 3; y++) {
|
||||
saveInfo.lotteryCodes[x][y] = j.at("lotteryCodes")[x][y].get<s8>();
|
||||
}
|
||||
for (int i = 0; i < ARRAY_COUNT(saveInfo.lotteryCodes); i++) {
|
||||
j.at("lotteryCodes").at(i).get_to(saveInfo.lotteryCodes[i]);
|
||||
}
|
||||
j.at("spiderHouseMaskOrder").get_to(saveInfo.spiderHouseMaskOrder);
|
||||
j.at("bomberCode").get_to(saveInfo.bomberCode);
|
||||
|
||||
+10
-12
@@ -1587,18 +1587,17 @@ extern "C" void BenSysFlashrom_WriteData(u8* saveBuffer, u32 pageNum, u32 pageCo
|
||||
FlashSlotFile flashSlotFile = FLASH_SLOT_FILE_UNAVAILABLE;
|
||||
bool isBackup = false;
|
||||
for (u32 i = 0; i < ARRAY_COUNT(gFlashSaveStartPages) - 1; i++) {
|
||||
if (pageNum == gFlashSaveStartPages[i]) {
|
||||
// Verify that the requested pages align with expected values
|
||||
if (pageNum == (u32)gFlashSaveStartPages[i] &&
|
||||
(pageCount == (u32)gFlashSaveNumPages[i] || pageCount == (u32)gFlashSpecialSaveNumPages[i])) {
|
||||
flashSlotFile = static_cast<FlashSlotFile>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude debug file from saving
|
||||
if (flashSlotFile == FLASH_SLOT_FILE_UNAVAILABLE || gSaveContext.fileNum == 255) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (flashSlotFile) {
|
||||
case FLASH_SLOT_FILE_UNAVAILABLE:
|
||||
return;
|
||||
case FLASH_SLOT_FILE_1_NEW_CYCLE_BACKUP:
|
||||
case FLASH_SLOT_FILE_2_NEW_CYCLE_BACKUP:
|
||||
isBackup = true;
|
||||
@@ -1653,17 +1652,17 @@ extern "C" s32 BenSysFlashrom_ReadData(void* saveBuffer, u32 pageNum, u32 pageCo
|
||||
FlashSlotFile flashSlotFile = FLASH_SLOT_FILE_UNAVAILABLE;
|
||||
bool isBackup = false;
|
||||
for (u32 i = 0; i < ARRAY_COUNT(gFlashSaveStartPages) - 1; i++) {
|
||||
if (pageNum == gFlashSaveStartPages[i]) {
|
||||
// Verify that the requested pages align with expected values
|
||||
if (pageNum == (u32)gFlashSaveStartPages[i] &&
|
||||
(pageCount == (u32)gFlashSaveNumPages[i] || pageCount == (u32)gFlashSpecialSaveNumPages[i])) {
|
||||
flashSlotFile = static_cast<FlashSlotFile>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flashSlotFile == FLASH_SLOT_FILE_UNAVAILABLE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (flashSlotFile) {
|
||||
case FLASH_SLOT_FILE_UNAVAILABLE:
|
||||
return -1;
|
||||
case FLASH_SLOT_FILE_1_NEW_CYCLE_BACKUP:
|
||||
case FLASH_SLOT_FILE_2_NEW_CYCLE_BACKUP:
|
||||
isBackup = true;
|
||||
@@ -1712,7 +1711,6 @@ extern "C" s32 BenSysFlashrom_ReadData(void* saveBuffer, u32 pageNum, u32 pageCo
|
||||
|
||||
memcpy(saveBuffer, &saveOptions, sizeof(SaveOptions));
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,6 +214,7 @@ typedef struct Inventory {
|
||||
/* 0x5A */ s8 dungeonKeys[9]; // "key_register"
|
||||
/* 0x63 */ s8 defenseHearts;
|
||||
/* 0x64 */ s8 strayFairies[10]; // "orange_fairy"
|
||||
// 2S2H [Comment] These char[] are not null-terminated, they will not work correctly in string functions
|
||||
/* 0x6E */ char dekuPlaygroundPlayerName[3][8]; // "degnuts_memory_name" Stores playerName (8 char) over (3 days) when getting a new high score
|
||||
} Inventory; // size = 0x88
|
||||
|
||||
@@ -262,6 +263,7 @@ typedef struct SaveOptions {
|
||||
} SaveOptions; // size = 0x6
|
||||
|
||||
typedef struct SavePlayerData {
|
||||
// 2S2H [Comment] These char[] are not null-terminated, they will not work correctly in string functions
|
||||
/* 0x00 */ char newf[6]; // "newf" Will always be "ZELDA3 for a valid save
|
||||
/* 0x06 */ u16 threeDayResetCount; // "savect"
|
||||
/* 0x08 */ char playerName[8]; // "player_name"
|
||||
|
||||
Reference in New Issue
Block a user