diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index 31bf36d3c..a6e5a47e8 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -7,6 +7,7 @@ extern "C" { #include "functions.h" #include "macros.h" #include "../UIWidgets.hpp" +#include "soh/util.h" #include #include @@ -286,10 +287,8 @@ extern "C" char* GameplayStats_GetCurrentTime() { } void LoadStatsVersion1() { - std::string buildVersion; - SaveManager::Instance->LoadData("buildVersion", buildVersion); - strncpy(gSaveContext.sohStats.buildVersion, buildVersion.c_str(), ARRAY_COUNT(gSaveContext.sohStats.buildVersion) - 1); - gSaveContext.sohStats.buildVersion[ARRAY_COUNT(gSaveContext.sohStats.buildVersion) - 1] = 0; + SaveManager::Instance->LoadCharArray("buildVersion", gSaveContext.sohStats.buildVersion, + ARRAY_COUNT(gSaveContext.sohStats.buildVersion)); SaveManager::Instance->LoadData("buildVersionMajor", gSaveContext.sohStats.buildVersionMajor); SaveManager::Instance->LoadData("buildVersionMinor", gSaveContext.sohStats.buildVersionMinor); SaveManager::Instance->LoadData("buildVersionPatch", gSaveContext.sohStats.buildVersionPatch); @@ -683,8 +682,8 @@ void InitStats(bool isDebug) { gSaveContext.sohStats.entrancesDiscovered[entrancesIdx] = 0; } - strncpy(gSaveContext.sohStats.buildVersion, (const char*) gBuildVersion, sizeof(gSaveContext.sohStats.buildVersion) - 1); - gSaveContext.sohStats.buildVersion[sizeof(gSaveContext.sohStats.buildVersion) - 1] = 0; + SohUtils::CopyStringToCharArray(gSaveContext.sohStats.buildVersion, std::string((char*)gBuildVersion), + ARRAY_COUNT(gSaveContext.sohStats.buildVersion)); gSaveContext.sohStats.buildVersionMajor = gBuildVersionMajor; gSaveContext.sohStats.buildVersionMinor = gBuildVersionMinor; gSaveContext.sohStats.buildVersionPatch = gBuildVersionPatch; diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 9050fcd80..1a46b0390 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -30,6 +30,7 @@ #include #include "randomizer_settings_window.h" #include "savefile.h" +#include "soh/util.h" extern "C" uint32_t ResourceMgr_IsGameMasterQuest(); extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum); @@ -1298,24 +1299,20 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) { bool success = false; - // Have all these use strncpy so that the null terminator is copied - // and also set the last index to null for safety try { json spoilerFileJson; spoilerFileStream >> spoilerFileJson; - std::string childAltarJsonText = spoilerFileJson["childAltar"]["hintText"].get(); - std::string formattedChildAltarText = FormatJsonHintText(childAltarJsonText); - strncpy(gSaveContext.childAltarText, formattedChildAltarText.c_str(), sizeof(gSaveContext.childAltarText) - 1); - gSaveContext.childAltarText[sizeof(gSaveContext.childAltarText) - 1] = 0; + SohUtils::CopyStringToCharArray(gSaveContext.childAltarText, + FormatJsonHintText(spoilerFileJson["childAltar"]["hintText"]), + ARRAY_COUNT(gSaveContext.childAltarText)); gSaveContext.rewardCheck[0] = SpoilerfileCheckNameToEnum[spoilerFileJson["childAltar"]["rewards"]["emeraldLoc"]]; gSaveContext.rewardCheck[1] = SpoilerfileCheckNameToEnum[spoilerFileJson["childAltar"]["rewards"]["rubyLoc"]]; gSaveContext.rewardCheck[2] = SpoilerfileCheckNameToEnum[spoilerFileJson["childAltar"]["rewards"]["sapphireLoc"]]; - std::string adultAltarJsonText = spoilerFileJson["adultAltar"]["hintText"].get(); - std::string formattedAdultAltarText = FormatJsonHintText(adultAltarJsonText); - strncpy(gSaveContext.adultAltarText, formattedAdultAltarText.c_str(), sizeof(gSaveContext.adultAltarText) - 1); - gSaveContext.adultAltarText[sizeof(gSaveContext.adultAltarText) - 1] = 0; + SohUtils::CopyStringToCharArray(gSaveContext.adultAltarText, + FormatJsonHintText(spoilerFileJson["adultAltar"]["hintText"]), + ARRAY_COUNT(gSaveContext.adultAltarText)); gSaveContext.rewardCheck[3] = SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["forestMedallionLoc"]]; gSaveContext.rewardCheck[4] = SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["fireMedallionLoc"]]; gSaveContext.rewardCheck[5] = SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["waterMedallionLoc"]]; @@ -1323,65 +1320,42 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) { gSaveContext.rewardCheck[7] = SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["spiritMedallionLoc"]]; gSaveContext.rewardCheck[8] = SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["lightMedallionLoc"]]; - std::string ganonHintJsonText = spoilerFileJson["ganonHintText"].get(); - std::string formattedGanonHintJsonText = FormatJsonHintText(ganonHintJsonText); - strncpy(gSaveContext.ganonHintText, formattedGanonHintJsonText.c_str(), sizeof(gSaveContext.ganonHintText) - 1); - gSaveContext.ganonHintText[sizeof(gSaveContext.ganonHintText) - 1] = 0; + SohUtils::CopyStringToCharArray(gSaveContext.ganonHintText, spoilerFileJson["ganonHintText"], + ARRAY_COUNT(gSaveContext.ganonHintText)); gSaveContext.masterSwordHintCheck = SpoilerfileCheckNameToEnum[spoilerFileJson["masterSwordHintLoc"]]; - std::string ganonJsonText = spoilerFileJson["ganonText"].get(); - std::string formattedGanonJsonText = FormatJsonHintText(ganonJsonText); - strncpy(gSaveContext.ganonText, formattedGanonJsonText.c_str(), sizeof(gSaveContext.ganonText) - 1); - gSaveContext.ganonText[sizeof(gSaveContext.ganonText) - 1] = 0; + SohUtils::CopyStringToCharArray(gSaveContext.ganonText, spoilerFileJson["ganonText"], + ARRAY_COUNT(gSaveContext.ganonText)); - std::string dampeJsonText = spoilerFileJson["dampeText"].get(); - std::string formattedDampeJsonText = FormatJsonHintText(dampeJsonText); - strncpy(gSaveContext.dampeText, formattedDampeJsonText.c_str(), sizeof(gSaveContext.dampeText) - 1); - gSaveContext.dampeText[sizeof(gSaveContext.dampeText) - 1] = 0; + SohUtils::CopyStringToCharArray(gSaveContext.dampeText, spoilerFileJson["dampeText"], + ARRAY_COUNT(gSaveContext.dampeText)); gSaveContext.dampeCheck = SpoilerfileCheckNameToEnum[spoilerFileJson["dampeHintLoc"]]; - std::string gregJsonText = spoilerFileJson["gregText"].get(); - std::string formattedGregJsonText = FormatJsonHintText(gregJsonText); - strncpy(gSaveContext.gregHintText, formattedGregJsonText.c_str(), sizeof(gSaveContext.gregHintText) - 1); - gSaveContext.gregHintText[sizeof(gSaveContext.gregHintText) - 1] = 0; + SohUtils::CopyStringToCharArray(gSaveContext.gregHintText, spoilerFileJson["gregText"], + ARRAY_COUNT(gSaveContext.gregHintText)); gSaveContext.gregCheck = SpoilerfileCheckNameToEnum[spoilerFileJson["gregLoc"]]; - std::string sheikJsonText = spoilerFileJson["sheikText"].get(); - std::string formattedSheikJsonText = FormatJsonHintText(sheikJsonText); - strncpy(gSaveContext.sheikText, formattedSheikJsonText.c_str(), sizeof(gSaveContext.sheikText) - 1); - gSaveContext.sheikText[sizeof(gSaveContext.sheikText) - 1] = 0; + SohUtils::CopyStringToCharArray(gSaveContext.sheikText, spoilerFileJson["sheikText"], + ARRAY_COUNT(gSaveContext.sheikText)); gSaveContext.lightArrowHintCheck = SpoilerfileCheckNameToEnum[spoilerFileJson["lightArrowHintLoc"]]; - std::string sariaJsonText = spoilerFileJson["sariaText"].get(); - std::string formattedSariaJsonText = FormatJsonHintText(sariaJsonText); - strncpy(gSaveContext.sariaText, formattedSariaJsonText.c_str(), sizeof(gSaveContext.sariaText) - 1); - gSaveContext.sariaText[sizeof(gSaveContext.sariaText) - 1] = 0; + SohUtils::CopyStringToCharArray(gSaveContext.sariaText, spoilerFileJson["sariaText"], + ARRAY_COUNT(gSaveContext.sariaText)); gSaveContext.sariaCheck = SpoilerfileCheckNameToEnum[spoilerFileJson["sariaHintLoc"]]; - std::string warpMinuetJsonText = spoilerFileJson["warpMinuetText"].get(); - strncpy(gSaveContext.warpMinuetText, warpMinuetJsonText.c_str(), sizeof(gSaveContext.warpMinuetText) - 1); - gSaveContext.warpMinuetText[sizeof(gSaveContext.warpMinuetText) - 1] = 0; - - std::string warpBoleroJsonText = spoilerFileJson["warpBoleroText"].get(); - strncpy(gSaveContext.warpBoleroText, warpBoleroJsonText.c_str(), sizeof(gSaveContext.warpBoleroText) - 1); - gSaveContext.warpBoleroText[sizeof(gSaveContext.warpBoleroText) - 1] = 0; - - std::string warpSerenadeJsonText = spoilerFileJson["warpSerenadeText"].get(); - strncpy(gSaveContext.warpSerenadeText, warpSerenadeJsonText.c_str(), sizeof(gSaveContext.warpSerenadeText) - 1); - gSaveContext.warpSerenadeText[sizeof(gSaveContext.warpSerenadeText) - 1] = 0; - - std::string warpRequiemJsonText = spoilerFileJson["warpRequiemText"].get(); - strncpy(gSaveContext.warpRequiemText, warpRequiemJsonText.c_str(), sizeof(gSaveContext.warpRequiemText) - 1); - gSaveContext.warpRequiemText[sizeof(gSaveContext.warpRequiemText) - 1] = 0; - - std::string warpNocturneJsonText = spoilerFileJson["warpNocturneText"].get(); - strncpy(gSaveContext.warpNocturneText, warpNocturneJsonText.c_str(), sizeof(gSaveContext.warpNocturneText) - 1); - gSaveContext.warpNocturneText[sizeof(gSaveContext.warpNocturneText) - 1] = 0; - - std::string warpPreludeJsonText = spoilerFileJson["warpPreludeText"].get(); - strncpy(gSaveContext.warpPreludeText, warpPreludeJsonText.c_str(), sizeof(gSaveContext.warpPreludeText) - 1); - gSaveContext.warpPreludeText[sizeof(gSaveContext.warpPreludeText) - 1] = 0; + SohUtils::CopyStringToCharArray(gSaveContext.warpMinuetText, spoilerFileJson["warpMinuetText"], + ARRAY_COUNT(gSaveContext.warpMinuetText)); + SohUtils::CopyStringToCharArray(gSaveContext.warpBoleroText, spoilerFileJson["warpBoleroText"], + ARRAY_COUNT(gSaveContext.warpBoleroText)); + SohUtils::CopyStringToCharArray(gSaveContext.warpSerenadeText, spoilerFileJson["warpSerenadeText"], + ARRAY_COUNT(gSaveContext.warpSerenadeText)); + SohUtils::CopyStringToCharArray(gSaveContext.warpRequiemText, spoilerFileJson["warpRequiemText"], + ARRAY_COUNT(gSaveContext.warpRequiemText)); + SohUtils::CopyStringToCharArray(gSaveContext.warpNocturneText, spoilerFileJson["warpNocturneText"], + ARRAY_COUNT(gSaveContext.warpNocturneText)); + SohUtils::CopyStringToCharArray(gSaveContext.warpPreludeText, spoilerFileJson["warpPreludeText"], + ARRAY_COUNT(gSaveContext.warpPreludeText)); json hintsJson = spoilerFileJson["hints"]; int index = 0; @@ -1408,10 +1382,7 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) { gSaveContext.hintLocations[index].area = SpoilerfileAreaNameToEnum[hintInfo["area"]]; } - std::string hintMessage = FormatJsonHintText(hintInfo["hint"]); - size_t maxHintTextSize = sizeof(gSaveContext.hintLocations[index].hintText); - strncpy(gSaveContext.hintLocations[index].hintText, hintMessage.c_str(), maxHintTextSize - 1); - gSaveContext.hintLocations[index].hintText[maxHintTextSize - 1] = 0; + SohUtils::CopyStringToCharArray(gSaveContext.hintLocations[index].hintText, hintInfo["hint"], ARRAY_COUNT(gSaveContext.hintLocations[index].hintText)); index++; } @@ -1542,9 +1513,8 @@ void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent index++; } - std::string inputSeed = spoilerFileJson["seed"].get(); - strncpy(gSaveContext.inputSeed, inputSeed.c_str(), sizeof(gSaveContext.inputSeed) - 1); - gSaveContext.inputSeed[sizeof(gSaveContext.inputSeed) - 1] = 0; + SohUtils::CopyStringToCharArray(gSaveContext.inputSeed, spoilerFileJson["seed"], + ARRAY_COUNT(gSaveContext.inputSeed)); gSaveContext.finalSeed = spoilerFileJson["finalSeed"].get(); @@ -1562,8 +1532,8 @@ void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent gSaveContext.itemLocations[randomizerCheck].get.fakeRgID = SpoilerfileGetNameToEnum[itemit.value()]; } else if (itemit.key() == "trickName") { - strncpy(gSaveContext.itemLocations[randomizerCheck].get.trickName, - std::string(itemit.value()).c_str(), MAX_TRICK_NAME_SIZE); + SohUtils::CopyStringToCharArray(gSaveContext.itemLocations[randomizerCheck].get.trickName, + itemit.value(), MAX_TRICK_NAME_SIZE); } } } else { @@ -3186,7 +3156,7 @@ void RandomizerSettingsWindow::DrawElement() { ); ImGui::SameLine(); if (ImGui::Button("New Seed")) { - strncpy(seedString, std::to_string(rand() & 0xFFFFFFFF).c_str(), MAX_SEED_STRING_SIZE); + SohUtils::CopyStringToCharArray(seedString, std::to_string(rand() & 0xFFFFFFFF), MAX_SEED_STRING_SIZE); } UIWidgets::Tooltip("Creates a new random seed value to be used when generating a randomizer"); ImGui::SameLine(); diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index e41abb0fb..bf1144b45 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -1,6 +1,7 @@ #include "SaveManager.h" #include "OTRGlobals.h" #include "Enhancements/game-interactor/GameInteractor.h" +#include "soh/util.h" #include "z64.h" #include "functions.h" @@ -95,9 +96,8 @@ void SaveManager::LoadRandomizerVersion1() { SaveManager::Instance->LoadStruct("get" + std::to_string(i), [&]() { SaveManager::Instance->LoadData("rgID", gSaveContext.itemLocations[i].get.rgID); SaveManager::Instance->LoadData("fakeRgID", gSaveContext.itemLocations[i].get.fakeRgID); - std::string trickName; - SaveManager::Instance->LoadData("trickName", trickName); - strncpy(gSaveContext.itemLocations[i].get.trickName, trickName.c_str(), MAX_TRICK_NAME_SIZE); + SaveManager::Instance->LoadCharArray("trickName", gSaveContext.itemLocations[i].get.trickName, + MAX_TRICK_NAME_SIZE); }); SaveManager::Instance->LoadData("check" + std::to_string(i), gSaveContext.itemLocations[i].check); } @@ -168,9 +168,8 @@ void SaveManager::LoadRandomizerVersion2() { SaveManager::Instance->LoadStruct("", [&]() { SaveManager::Instance->LoadData("rgID", gSaveContext.itemLocations[i].get.rgID); SaveManager::Instance->LoadData("fakeRgID", gSaveContext.itemLocations[i].get.fakeRgID); - std::string trickName; - SaveManager::Instance->LoadData("trickName", trickName); - strncpy(gSaveContext.itemLocations[i].get.trickName, trickName.c_str(), MAX_TRICK_NAME_SIZE); + SaveManager::Instance->LoadCharArray("trickName", gSaveContext.itemLocations[i].get.trickName, + MAX_TRICK_NAME_SIZE); }); }); @@ -189,8 +188,7 @@ void SaveManager::LoadRandomizerVersion2() { }); std::string inputSeed; - SaveManager::Instance->LoadData("inputSeed", inputSeed); - memcpy(gSaveContext.inputSeed, inputSeed.c_str(), inputSeed.length() + 1); + SaveManager::Instance->LoadCharArray("inputSeed", gSaveContext.inputSeed, ARRAY_COUNT(gSaveContext.inputSeed)); SaveManager::Instance->LoadData("finalSeed", gSaveContext.finalSeed); @@ -202,54 +200,41 @@ void SaveManager::LoadRandomizerVersion2() { SaveManager::Instance->LoadArray("hintLocations", ARRAY_COUNT(gSaveContext.hintLocations), [&](size_t i) { SaveManager::Instance->LoadStruct("", [&]() { SaveManager::Instance->LoadData("check", gSaveContext.hintLocations[i].check); - std::string hintText; - SaveManager::Instance->LoadData("hintText", hintText); - memcpy(gSaveContext.hintLocations[i].hintText, hintText.c_str(), hintText.length()); + SaveManager::Instance->LoadCharArray("hintText", gSaveContext.hintLocations[i].hintText, + ARRAY_COUNT(gSaveContext.hintLocations[i].hintText)); }); }); - std::string childAltarText; - SaveManager::Instance->LoadData("childAltarText", childAltarText); - memcpy(gSaveContext.childAltarText, childAltarText.c_str(), childAltarText.length()); - std::string adultAltarText; - SaveManager::Instance->LoadData("adultAltarText", adultAltarText); - memcpy(gSaveContext.adultAltarText, adultAltarText.c_str(), adultAltarText.length()); - std::string ganonHintText; - SaveManager::Instance->LoadData("ganonHintText", ganonHintText); - memcpy(gSaveContext.ganonHintText, ganonHintText.c_str(), ganonHintText.length()); - std::string ganonText; - SaveManager::Instance->LoadData("ganonText", ganonText); - memcpy(gSaveContext.ganonText, ganonText.c_str(), ganonText.length()); - std::string dampeText; - SaveManager::Instance->LoadData("dampeText", dampeText); - memcpy(gSaveContext.dampeText, dampeText.c_str(), dampeText.length()); - std::string gregHintText; - SaveManager::Instance->LoadData("gregHintText", gregHintText); - memcpy(gSaveContext.gregHintText, gregHintText.c_str(), gregHintText.length()); - std::string sheikText; - SaveManager::Instance->LoadData("sheikText", sheikText); - memcpy(gSaveContext.sheikText, sheikText.c_str(), sheikText.length() + 1); - std::string sariaText; - SaveManager::Instance->LoadData("sariaText", sariaText); - memcpy(gSaveContext.sariaText, sariaText.c_str(), sariaText.length() + 1); - std::string warpMinuetText; - SaveManager::Instance->LoadData("warpMinuetText", warpMinuetText); - memcpy(gSaveContext.warpMinuetText, warpMinuetText.c_str(), warpMinuetText.length()); - std::string warpBoleroText; - SaveManager::Instance->LoadData("warpBoleroText", warpBoleroText); - memcpy(gSaveContext.warpBoleroText, warpBoleroText.c_str(), warpBoleroText.length()); - std::string warpSerenadeText; - SaveManager::Instance->LoadData("warpSerenadeText", warpSerenadeText); - memcpy(gSaveContext.warpSerenadeText, warpSerenadeText.c_str(), warpSerenadeText.length()); - std::string warpRequiemText; - SaveManager::Instance->LoadData("warpRequiemText", warpRequiemText); - memcpy(gSaveContext.warpRequiemText, warpRequiemText.c_str(), warpRequiemText.length()); - std::string warpNocturneText; - SaveManager::Instance->LoadData("warpNocturneText", warpNocturneText); - memcpy(gSaveContext.warpNocturneText, warpNocturneText.c_str(), warpNocturneText.length()); - std::string warpPreludeText; - SaveManager::Instance->LoadData("warpPreludeText", warpPreludeText); - memcpy(gSaveContext.warpPreludeText, warpPreludeText.c_str(), warpPreludeText.length()); + SaveManager::Instance->LoadCharArray("childAltarText", gSaveContext.childAltarText, + ARRAY_COUNT(gSaveContext.childAltarText)); + SaveManager::Instance->LoadCharArray("adultAltarText", gSaveContext.adultAltarText, + ARRAY_COUNT(gSaveContext.adultAltarText)); + + SaveManager::Instance->LoadCharArray("ganonHintText", gSaveContext.ganonHintText, + ARRAY_COUNT(gSaveContext.ganonHintText)); + SaveManager::Instance->LoadCharArray("ganonText", gSaveContext.ganonText, ARRAY_COUNT(gSaveContext.ganonText)); + + SaveManager::Instance->LoadCharArray("dampeText", gSaveContext.dampeText, ARRAY_COUNT(gSaveContext.dampeText)); + + SaveManager::Instance->LoadCharArray("gregHintText", gSaveContext.gregHintText, + ARRAY_COUNT(gSaveContext.gregHintText)); + + SaveManager::Instance->LoadCharArray("sheikText", gSaveContext.sheikText, ARRAY_COUNT(gSaveContext.sheikText)); + + SaveManager::Instance->LoadCharArray("sariaText", gSaveContext.sariaText, ARRAY_COUNT(gSaveContext.sariaText)); + + SaveManager::Instance->LoadCharArray("warpMinuetText", gSaveContext.warpMinuetText, + ARRAY_COUNT(gSaveContext.warpMinuetText)); + SaveManager::Instance->LoadCharArray("warpBoleroText", gSaveContext.warpBoleroText, + ARRAY_COUNT(gSaveContext.warpBoleroText)); + SaveManager::Instance->LoadCharArray("warpSerenadeText", gSaveContext.warpSerenadeText, + ARRAY_COUNT(gSaveContext.warpSerenadeText)); + SaveManager::Instance->LoadCharArray("warpRequiemText", gSaveContext.warpRequiemText, + ARRAY_COUNT(gSaveContext.warpRequiemText)); + SaveManager::Instance->LoadCharArray("warpNocturneText", gSaveContext.warpNocturneText, + ARRAY_COUNT(gSaveContext.warpNocturneText)); + SaveManager::Instance->LoadCharArray("warpPreludeText", gSaveContext.warpPreludeText, + ARRAY_COUNT(gSaveContext.warpPreludeText)); SaveManager::Instance->LoadData("adultTradeItems", gSaveContext.adultTradeItems); @@ -466,8 +451,8 @@ void SaveManager::InitMeta(int fileNum) { fileMetaInfo[fileNum].buildVersionMajor = gSaveContext.sohStats.buildVersionMajor; fileMetaInfo[fileNum].buildVersionMinor = gSaveContext.sohStats.buildVersionMinor; fileMetaInfo[fileNum].buildVersionPatch = gSaveContext.sohStats.buildVersionPatch; - strncpy(fileMetaInfo[fileNum].buildVersion, gSaveContext.sohStats.buildVersion, sizeof(fileMetaInfo[fileNum].buildVersion) - 1); - fileMetaInfo[fileNum].buildVersion[sizeof(fileMetaInfo[fileNum].buildVersion) - 1] = 0; + SohUtils::CopyStringToCharArray(fileMetaInfo[fileNum].buildVersion, gSaveContext.sohStats.buildVersion, + ARRAY_COUNT(fileMetaInfo[fileNum].buildVersion)); } void SaveManager::InitFile(bool isDebug) { @@ -1555,10 +1540,8 @@ void SaveManager::LoadBaseVersion3() { SaveManager::Instance->LoadData("gsTokens", gSaveContext.inventory.gsTokens); }); SaveManager::Instance->LoadStruct("sohStats", []() { - std::string buildVersion; - SaveManager::Instance->LoadData("buildVersion", buildVersion); - strncpy(gSaveContext.sohStats.buildVersion, buildVersion.c_str(), ARRAY_COUNT(gSaveContext.sohStats.buildVersion) - 1); - gSaveContext.sohStats.buildVersion[ARRAY_COUNT(gSaveContext.sohStats.buildVersion) - 1] = 0; + SaveManager::Instance->LoadCharArray("buildVersion", gSaveContext.sohStats.buildVersion, + ARRAY_COUNT(gSaveContext.sohStats.buildVersion)); SaveManager::Instance->LoadData("buildVersionMajor", gSaveContext.sohStats.buildVersionMajor); SaveManager::Instance->LoadData("buildVersionMinor", gSaveContext.sohStats.buildVersionMinor); SaveManager::Instance->LoadData("buildVersionPatch", gSaveContext.sohStats.buildVersionPatch); @@ -2046,6 +2029,13 @@ void SaveManager::SaveBase(SaveContext* saveContext, int sectionID, bool fullSav SaveManager::Instance->SaveData("dogParams", saveContext->dogParams); } +// Load a string into a char array based on size and ensuring it is null terminated when overflowed +void SaveManager::LoadCharArray(const std::string& name, char* destination, size_t size) { + std::string temp; + SaveManager::Instance->LoadData(name, temp); + SohUtils::CopyStringToCharArray(destination, temp, size); +} + void SaveManager::SaveArray(const std::string& name, const size_t size, SaveArrayFunc func) { // Create an empty array and set it as the current save context, then call the function that saves an array entry. nlohmann::json* saveJsonContext = currentJsonContext; @@ -2169,8 +2159,8 @@ void SaveManager::CopyZeldaFile(int from, int to) { fileMetaInfo[to].buildVersionMajor = fileMetaInfo[from].buildVersionMajor; fileMetaInfo[to].buildVersionMinor = fileMetaInfo[from].buildVersionMinor; fileMetaInfo[to].buildVersionPatch = fileMetaInfo[from].buildVersionPatch; - strncpy(fileMetaInfo[to].buildVersion, fileMetaInfo[from].buildVersion, sizeof(fileMetaInfo[to].buildVersion) - 1); - fileMetaInfo[to].buildVersion[sizeof(fileMetaInfo[to].buildVersion) - 1] = 0; + SohUtils::CopyStringToCharArray(fileMetaInfo[to].buildVersion, fileMetaInfo[from].buildVersion, + ARRAY_COUNT(fileMetaInfo[to].buildVersion)); } void SaveManager::DeleteZeldaFile(int fileNum) { diff --git a/soh/soh/SaveManager.h b/soh/soh/SaveManager.h index 62eed98b2..892aec923 100644 --- a/soh/soh/SaveManager.h +++ b/soh/soh/SaveManager.h @@ -128,6 +128,8 @@ class SaveManager { } } + void LoadCharArray(const std::string& name, char* destination, size_t size); + // In the LoadArrayFunc func, the name must be "" to load from the array. using LoadArrayFunc = std::function; void LoadArray(const std::string& name, const size_t size, LoadArrayFunc func); diff --git a/soh/soh/util.cpp b/soh/soh/util.cpp index 390728f95..62e1d2dce 100644 --- a/soh/soh/util.cpp +++ b/soh/soh/util.cpp @@ -1,5 +1,6 @@ #include "util.h" +#include #include std::vector sceneNames = { @@ -312,3 +313,8 @@ const std::string& SohUtils::GetItemName(int32_t item) { const std::string& SohUtils::GetQuestItemName(int32_t item) { return questItemNames[item]; } + +void SohUtils::CopyStringToCharArray(char* destination, std::string source, size_t size) { + strncpy(destination, source.c_str(), size - 1); + destination[size - 1] = '\0'; +} diff --git a/soh/soh/util.h b/soh/soh/util.h index 9fd806f18..bdbfcd777 100644 --- a/soh/soh/util.h +++ b/soh/soh/util.h @@ -8,4 +8,8 @@ namespace SohUtils { const std::string& GetItemName(int32_t item); const std::string& GetQuestItemName(int32_t item); + + // Copies a string and ensures the destination is null terminated if the source string is larger than size + // Only up to size-1 characters are copied from the source string + void CopyStringToCharArray(char* destination, std::string source, size_t size); } // namespace SohUtils