diff --git a/mm/2s2h/BenJsonConversions.hpp b/mm/2s2h/BenJsonConversions.hpp index 57c044c5f..849d5d01f 100644 --- a/mm/2s2h/BenJsonConversions.hpp +++ b/mm/2s2h/BenJsonConversions.hpp @@ -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(); - itemEquips.cButtonSlots[x][y] = j.at("cButtonSlots")[x][y].get(); - } + 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(); - 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(); - 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(); - 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(); - } + 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); diff --git a/mm/2s2h/BenPort.cpp b/mm/2s2h/BenPort.cpp index 73cac26b8..145db1cb0 100644 --- a/mm/2s2h/BenPort.cpp +++ b/mm/2s2h/BenPort.cpp @@ -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(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(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; } } } diff --git a/mm/include/z64save.h b/mm/include/z64save.h index 865b0650a..aed5487a8 100644 --- a/mm/include/z64save.h +++ b/mm/include/z64save.h @@ -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"