mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-08-14 18:48:04 -07:00
Save and Randomizer Version Control (#5557)
* Add version control for Rachael saves and old rando saves. * Change spoiler drop success sound to puzzle success chime. Add spoiler drop fail sound (sys_error). Improve path sanitizer operation. Add check for a spoiler having a version and it equaling running version. Deletes spoiler CVar if spoiler becomes unusuable while running, and prevents loading dropped spoilers that don't match. * clang * Remove backslash escaping from Sanitize. Remove duplicate Sanitize from Context and make it use SohUtils. Fix typo.
This commit is contained in:
parent
0b9fe2d9b9
commit
69792e9717
6 changed files with 83 additions and 275 deletions
|
@ -11,6 +11,7 @@
|
|||
#include "fishsanity.h"
|
||||
#include "macros.h"
|
||||
#include "3drando/hints.hpp"
|
||||
#include "soh/util.h"
|
||||
#include "../kaleido.h"
|
||||
|
||||
#include <fstream>
|
||||
|
@ -370,25 +371,8 @@ GetItemEntry Context::GetFinalGIEntry(const RandomizerCheck rc, const bool check
|
|||
return giEntry;
|
||||
}
|
||||
|
||||
std::string sanitize(std::string stringValue) {
|
||||
// Add backslashes.
|
||||
for (auto i = stringValue.begin();;) {
|
||||
auto const pos =
|
||||
std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
|
||||
if (pos == stringValue.end()) {
|
||||
break;
|
||||
}
|
||||
i = std::next(stringValue.insert(pos, '\\'), 2);
|
||||
}
|
||||
|
||||
// Removes others.
|
||||
std::erase_if(stringValue, [](char const c) { return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; });
|
||||
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
void Context::ParseSpoiler(const char* spoilerFileName) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return;
|
||||
}
|
||||
|
@ -397,6 +381,7 @@ void Context::ParseSpoiler(const char* spoilerFileName) {
|
|||
try {
|
||||
nlohmann::json spoilerFileJson;
|
||||
spoilerFileStream >> spoilerFileJson;
|
||||
spoilerFileStream.close();
|
||||
ParseHashIconIndexesJson(spoilerFileJson);
|
||||
Rando::Settings::GetInstance()->ParseJson(spoilerFileJson);
|
||||
ParseItemLocationsJson(spoilerFileJson);
|
||||
|
|
|
@ -361,10 +361,20 @@ std::unordered_map<s16, s16> getItemIdToItemId = {
|
|||
bool Randomizer::SpoilerFileExists(const char* spoilerFileName) {
|
||||
if (strcmp(spoilerFileName, "") != 0) {
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
if (spoilerFileStream) {
|
||||
nlohmann::json contents;
|
||||
spoilerFileStream >> contents;
|
||||
spoilerFileStream.close();
|
||||
if (contents.contains("version") &&
|
||||
strcmp(std::string(contents["version"]).c_str(), (char*)gBuildVersion) == 0) {
|
||||
return true;
|
||||
} else {
|
||||
SohGui::RegisterPopup(
|
||||
"Old Spoiler Version",
|
||||
"The spoiler file located at\n" + std::string(spoilerFileName) +
|
||||
"\nwas made by a version that doesn't match the currently running version.\n" +
|
||||
"Loading for this file has been cancelled.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -119,10 +119,8 @@ SaveManager::SaveManager() {
|
|||
AddLoadFunction("base", 4, LoadBaseVersion4);
|
||||
AddSaveFunction("base", 4, SaveBase, true, SECTION_PARENT_NONE);
|
||||
|
||||
AddLoadFunction("randomizer", 1, LoadRandomizerVersion1);
|
||||
AddLoadFunction("randomizer", 2, LoadRandomizerVersion2);
|
||||
AddLoadFunction("randomizer", 3, LoadRandomizerVersion3);
|
||||
AddSaveFunction("randomizer", 3, SaveRandomizer, true, SECTION_PARENT_NONE);
|
||||
AddLoadFunction("randomizer", 1, LoadRandomizer);
|
||||
AddSaveFunction("randomizer", 1, SaveRandomizer, true, SECTION_PARENT_NONE);
|
||||
|
||||
AddInitFunction(InitFileImpl);
|
||||
|
||||
|
@ -157,234 +155,7 @@ SaveManager::SaveManager() {
|
|||
}
|
||||
}
|
||||
|
||||
// RANDOTODO should we just have dummy functions that raise warnings instead if these aren't supported?
|
||||
void SaveManager::LoadRandomizerVersion1() {
|
||||
auto randoContext = Rando::Context::GetInstance();
|
||||
RandomizerCheck location = RC_UNKNOWN_CHECK;
|
||||
for (int i = 0; i < RC_MAX; i++) {
|
||||
SaveManager::Instance->LoadData("check" + std::to_string(i), location);
|
||||
SaveManager::Instance->LoadStruct("get" + std::to_string(i), [&]() {
|
||||
SaveManager::Instance->LoadData("rgID", randoContext->GetItemLocation(location)->RefPlacedItem());
|
||||
if (randoContext->GetItemLocation(location)->GetPlacedRandomizerGet() == RG_ICE_TRAP) {
|
||||
randoContext->overrides[location].SetLocation(location);
|
||||
SaveManager::Instance->LoadData("fakeRgID", randoContext->overrides[location].RefLooksLike());
|
||||
SaveManager::Instance->LoadData("trickName", randoContext->overrides[location].GetTrickName().english);
|
||||
SaveManager::Instance->LoadData("trickName", randoContext->overrides[location].GetTrickName().french);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < randoContext->hashIconIndexes.size(); i++) {
|
||||
SaveManager::Instance->LoadData("seed" + std::to_string(i), randoContext->hashIconIndexes[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < RSK_MAX; i++) {
|
||||
int key, value;
|
||||
SaveManager::Instance->LoadData("sk" + std::to_string(i), key);
|
||||
SaveManager::Instance->LoadData("sv" + std::to_string(i), value);
|
||||
randoContext->GetOption(RandomizerSettingKey(key)).Set(value);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
RandomizerCheck check;
|
||||
char hintText[200];
|
||||
SaveManager::Instance->LoadData("hc" + std::to_string(i), check);
|
||||
for (int j = 0; j < ARRAY_COUNT(hintText); j++) {
|
||||
SaveManager::Instance->LoadData("ht" + std::to_string(i) + "-" + std::to_string(j), hintText[j]);
|
||||
}
|
||||
RandomizerHint stoneHint = Rando::StaticData::oldVerHintOrder[i - Rando::StaticData::oldVerGossipStoneStart];
|
||||
randoContext->AddHint(stoneHint, Rando::Hint(stoneHint, { CustomMessage(hintText) }));
|
||||
}
|
||||
|
||||
char childAltarText[250];
|
||||
for (int i = 0; i < ARRAY_COUNT(childAltarText); i++) {
|
||||
SaveManager::Instance->LoadData("cat" + std::to_string(i), childAltarText[i]);
|
||||
}
|
||||
randoContext->AddHint(RH_ALTAR_CHILD, Rando::Hint(RH_ALTAR_CHILD, { CustomMessage(childAltarText) }));
|
||||
|
||||
char adultAltarText[750];
|
||||
for (int i = 0; i < ARRAY_COUNT(adultAltarText); i++) {
|
||||
SaveManager::Instance->LoadData("aat" + std::to_string(i), adultAltarText[i]);
|
||||
}
|
||||
randoContext->AddHint(RH_ALTAR_ADULT, Rando::Hint(RH_ALTAR_ADULT, { CustomMessage(adultAltarText) }));
|
||||
|
||||
char ganonHintText[150];
|
||||
for (int i = 0; i < ARRAY_COUNT(ganonHintText); i++) {
|
||||
SaveManager::Instance->LoadData("ght" + std::to_string(i), ganonHintText[i]);
|
||||
}
|
||||
randoContext->AddHint(RH_GANONDORF_HINT, Rando::Hint(RH_GANONDORF_HINT, { CustomMessage(ganonHintText) }));
|
||||
|
||||
char ganonText[250];
|
||||
for (int i = 0; i < ARRAY_COUNT(ganonText); i++) {
|
||||
SaveManager::Instance->LoadData("gt" + std::to_string(i), ganonText[i]);
|
||||
}
|
||||
randoContext->AddHint(RH_GANONDORF_JOKE, Rando::Hint(RH_GANONDORF_JOKE, { CustomMessage(ganonText) }));
|
||||
|
||||
SaveManager::Instance->LoadData("triforcePiecesCollected",
|
||||
gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected);
|
||||
|
||||
SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.ship.pendingIceTrapCount);
|
||||
|
||||
size_t merchantPricesSize = 0;
|
||||
if (randoContext->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_OFF)) {
|
||||
merchantPricesSize += NUM_SCRUBS;
|
||||
}
|
||||
if (randoContext->GetOption(RSK_SHOPSANITY).Is(RO_SHOPSANITY_OFF)) {
|
||||
merchantPricesSize += NUM_SHOP_ITEMS;
|
||||
}
|
||||
|
||||
SaveManager::Instance->LoadArray("merchantPrices", merchantPricesSize, [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
RandomizerCheck rc;
|
||||
SaveManager::Instance->LoadData("check", rc);
|
||||
uint32_t price;
|
||||
SaveManager::Instance->LoadData("price", price);
|
||||
randoContext->GetItemLocation(rc)->SetCustomPrice(price);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// RANDOTODO if we actually support this, be less lazy
|
||||
void SaveManager::LoadRandomizerVersion2() {
|
||||
auto randoContext = Rando::Context::GetInstance();
|
||||
SaveManager::Instance->LoadArray("itemLocations", RC_MAX, [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
SaveManager::Instance->LoadData("rgID", randoContext->GetItemLocation(i)->RefPlacedItem());
|
||||
RandomizerGet rg = RG_NONE;
|
||||
SaveManager::Instance->LoadData("fakeRgID", rg, RG_NONE);
|
||||
if (rg != RG_NONE) {
|
||||
randoContext->overrides[static_cast<RandomizerCheck>(i)] =
|
||||
Rando::ItemOverride(static_cast<RandomizerCheck>(i), rg);
|
||||
SaveManager::Instance->LoadData(
|
||||
"trickName", randoContext->overrides[static_cast<RandomizerCheck>(i)].GetTrickName().english);
|
||||
SaveManager::Instance->LoadData(
|
||||
"trickName", randoContext->overrides[static_cast<RandomizerCheck>(i)].GetTrickName().french);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
auto entranceCtx = randoContext->GetEntranceShuffler();
|
||||
SaveManager::Instance->LoadArray("entrances", ARRAY_COUNT(entranceCtx->entranceOverrides), [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
SaveManager::Instance->LoadData("type", entranceCtx->entranceOverrides[i].type);
|
||||
SaveManager::Instance->LoadData("index", entranceCtx->entranceOverrides[i].index);
|
||||
SaveManager::Instance->LoadData("destination", entranceCtx->entranceOverrides[i].destination);
|
||||
SaveManager::Instance->LoadData("override", entranceCtx->entranceOverrides[i].override);
|
||||
SaveManager::Instance->LoadData("overrideDestination",
|
||||
entranceCtx->entranceOverrides[i].overrideDestination);
|
||||
});
|
||||
});
|
||||
|
||||
SaveManager::Instance->LoadArray("seed", randoContext->hashIconIndexes.size(), [&](size_t i) {
|
||||
SaveManager::Instance->LoadData("", randoContext->hashIconIndexes[i]);
|
||||
});
|
||||
|
||||
std::string inputSeed;
|
||||
SaveManager::Instance->LoadData("inputSeed", inputSeed);
|
||||
randoContext->SetSeedString(inputSeed);
|
||||
|
||||
uint32_t finalSeed;
|
||||
SaveManager::Instance->LoadData("finalSeed", finalSeed);
|
||||
randoContext->SetSeed(finalSeed);
|
||||
|
||||
SaveManager::Instance->LoadArray("randoSettings", RSK_MAX, [&](size_t i) {
|
||||
int value = 0;
|
||||
SaveManager::Instance->LoadData("", value);
|
||||
randoContext->GetOption(RandomizerSettingKey(i)).Set(value);
|
||||
});
|
||||
|
||||
SaveManager::Instance->LoadArray("hintLocations", RH_ZR_OPEN_GROTTO_GOSSIP_STONE + 1, [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
RandomizerCheck rc = RC_UNKNOWN_CHECK;
|
||||
SaveManager::Instance->LoadData("check", rc);
|
||||
if (rc != RC_UNKNOWN_CHECK) {
|
||||
std::string hintText;
|
||||
SaveManager::Instance->LoadData("hintText", hintText);
|
||||
RandomizerHint stoneHint =
|
||||
Rando::StaticData::oldVerHintOrder[rc - Rando::StaticData::oldVerGossipStoneStart];
|
||||
randoContext->AddHint(stoneHint, Rando::Hint(stoneHint, { CustomMessage(hintText) }));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
std::string childAltarText;
|
||||
SaveManager::Instance->LoadData("childAltarText", childAltarText);
|
||||
randoContext->AddHint(RH_ALTAR_CHILD, Rando::Hint(RH_ALTAR_CHILD, { CustomMessage(childAltarText) }));
|
||||
std::string adultAltarText;
|
||||
SaveManager::Instance->LoadData("adultAltarText", adultAltarText);
|
||||
randoContext->AddHint(RH_ALTAR_ADULT, Rando::Hint(RH_ALTAR_ADULT, { CustomMessage(adultAltarText) }));
|
||||
std::string ganonHintText;
|
||||
SaveManager::Instance->LoadData("ganonHintText", ganonHintText);
|
||||
randoContext->AddHint(RH_GANONDORF_HINT, Rando::Hint(RH_GANONDORF_HINT, { CustomMessage(ganonHintText) }));
|
||||
std::string ganonText;
|
||||
SaveManager::Instance->LoadData("ganonText", ganonText);
|
||||
randoContext->AddHint(RH_GANONDORF_JOKE, Rando::Hint(RH_GANONDORF_JOKE, { CustomMessage(ganonText) }));
|
||||
std::string dampeText;
|
||||
SaveManager::Instance->LoadData("dampeText", dampeText);
|
||||
randoContext->AddHint(RH_DAMPES_DIARY, Rando::Hint(RH_DAMPES_DIARY, { CustomMessage(dampeText) }));
|
||||
std::string gregHintText;
|
||||
SaveManager::Instance->LoadData("gregHintText", gregHintText);
|
||||
randoContext->AddHint(RH_GREG_RUPEE, Rando::Hint(RH_GREG_RUPEE, { CustomMessage(gregHintText) }));
|
||||
std::string sheikText;
|
||||
SaveManager::Instance->LoadData("sheikText", sheikText);
|
||||
randoContext->AddHint(RH_SHEIK_HINT, Rando::Hint(RH_SHEIK_HINT, { CustomMessage(sheikText) }));
|
||||
std::string sariaText;
|
||||
SaveManager::Instance->LoadData("sariaText", sariaText);
|
||||
randoContext->AddHint(RH_SARIA_HINT, Rando::Hint(RH_SARIA_HINT, { CustomMessage(sariaText) }));
|
||||
std::string fishingPoleText;
|
||||
SaveManager::Instance->LoadData("fishingPoleText", fishingPoleText);
|
||||
randoContext->AddHint(RH_FISHING_POLE, Rando::Hint(RH_FISHING_POLE, { CustomMessage(fishingPoleText) }));
|
||||
std::string warpMinuetText;
|
||||
SaveManager::Instance->LoadData("warpMinuetText", warpMinuetText);
|
||||
randoContext->AddHint(RH_MINUET_WARP_LOC, Rando::Hint(RH_MINUET_WARP_LOC, { CustomMessage(warpMinuetText) }));
|
||||
std::string warpBoleroText;
|
||||
SaveManager::Instance->LoadData("warpBoleroText", warpBoleroText);
|
||||
randoContext->AddHint(RH_BOLERO_WARP_LOC, Rando::Hint(RH_BOLERO_WARP_LOC, { CustomMessage(warpBoleroText) }));
|
||||
std::string warpSerenadeText;
|
||||
SaveManager::Instance->LoadData("warpSerenadeText", warpSerenadeText);
|
||||
randoContext->AddHint(RH_SERENADE_WARP_LOC, Rando::Hint(RH_SERENADE_WARP_LOC, { CustomMessage(warpSerenadeText) }));
|
||||
std::string warpRequiemText;
|
||||
SaveManager::Instance->LoadData("warpRequiemText", warpRequiemText);
|
||||
randoContext->AddHint(RH_REQUIEM_WARP_LOC, Rando::Hint(RH_REQUIEM_WARP_LOC, { CustomMessage(warpRequiemText) }));
|
||||
std::string warpNocturneText;
|
||||
SaveManager::Instance->LoadData("warpNocturneText", warpNocturneText);
|
||||
randoContext->AddHint(RH_NOCTURNE_WARP_LOC, Rando::Hint(RH_NOCTURNE_WARP_LOC, { CustomMessage(warpNocturneText) }));
|
||||
std::string warpPreludeText;
|
||||
SaveManager::Instance->LoadData("warpPreludeText", warpPreludeText);
|
||||
randoContext->AddHint(RH_PRELUDE_WARP_LOC, Rando::Hint(RH_PRELUDE_WARP_LOC, { CustomMessage(warpPreludeText) }));
|
||||
|
||||
SaveManager::Instance->LoadData("triforcePiecesCollected",
|
||||
gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected);
|
||||
|
||||
SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.ship.pendingIceTrapCount);
|
||||
|
||||
std::shared_ptr<Randomizer> randomizer = OTRGlobals::Instance->gRandomizer;
|
||||
|
||||
size_t merchantPricesSize = 0;
|
||||
SaveManager::Instance->LoadData("merchantPricesSize", merchantPricesSize);
|
||||
|
||||
SaveManager::Instance->LoadArray("merchantPrices", merchantPricesSize, [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
RandomizerCheck rc;
|
||||
SaveManager::Instance->LoadData("check", rc);
|
||||
uint32_t price;
|
||||
SaveManager::Instance->LoadData("price", price);
|
||||
randoContext->GetItemLocation(rc)->SetCustomPrice(price);
|
||||
});
|
||||
});
|
||||
|
||||
size_t mqDungeonCount;
|
||||
SaveManager::Instance->LoadData("masterQuestDungeonCount", mqDungeonCount, (size_t)0);
|
||||
|
||||
randoContext->GetDungeons()->ClearAllMQ();
|
||||
SaveManager::Instance->LoadArray("masterQuestDungeons", mqDungeonCount, [&](size_t i) {
|
||||
uint16_t scene;
|
||||
SaveManager::Instance->LoadData("", scene);
|
||||
randoContext->GetDungeons()->GetDungeonFromScene(SceneID(scene))->SetMQ();
|
||||
});
|
||||
}
|
||||
|
||||
void SaveManager::LoadRandomizerVersion3() {
|
||||
void SaveManager::LoadRandomizer() {
|
||||
auto randoContext = Rando::Context::GetInstance();
|
||||
SaveManager::Instance->LoadArray("itemLocations", RC_MAX, [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
|
@ -1343,8 +1114,57 @@ void SaveManager::LoadFile(int fileNum) {
|
|||
switch (saveBlock["version"].get<int>()) {
|
||||
case 1:
|
||||
for (auto& block : saveBlock["sections"].items()) {
|
||||
int sectionVersion = block.value()["version"];
|
||||
std::string sectionName = block.key();
|
||||
if (sectionName == "randomizer") {
|
||||
bool hasStats = saveBlock["sections"].contains("sohStats");
|
||||
if (block.value()["data"].contains("aat0") || !hasStats) { // Rachael rando data
|
||||
SohGui::RegisterPopup(
|
||||
"Loading old file",
|
||||
"The file in slot " + std::to_string(fileNum + 1) +
|
||||
" appears to contain randomizer data, but is a very old format.\n" +
|
||||
"The randomizer data has been removed, and this file will be treated as a vanilla "
|
||||
"file.\n" +
|
||||
"If this was a randomizer file, the file will not work, and should be deleted.");
|
||||
input.close();
|
||||
saveMtx.unlock();
|
||||
SaveFile(fileNum);
|
||||
return;
|
||||
}
|
||||
s16 major = saveBlock["sections"]["sohStats"]["data"]["buildVersionMajor"];
|
||||
s16 minor = saveBlock["sections"]["sohStats"]["data"]["buildVersionMinor"];
|
||||
s16 patch = saveBlock["sections"]["sohStats"]["data"]["buildVersionPatch"];
|
||||
// block loading outdated rando save
|
||||
if (!(major == gBuildVersionMajor && minor == gBuildVersionMinor &&
|
||||
patch == gBuildVersionPatch)) {
|
||||
input.close();
|
||||
std::string newFileName = Ship::Context::GetPathRelativeToAppDirectory("Save") +
|
||||
("/file" + std::to_string(fileNum + 1) + "-" +
|
||||
std::to_string(GetUnixTimestamp()) + ".bak");
|
||||
std::filesystem::path newFile(newFileName);
|
||||
|
||||
#if defined(__SWITCH__) || defined(__WIIU__)
|
||||
copy_file(fileName.c_str(), newFile.c_str());
|
||||
#else
|
||||
std::filesystem::copy_file(fileName, newFile);
|
||||
#endif
|
||||
|
||||
std::filesystem::remove(fileName);
|
||||
SohGui::RegisterPopup(
|
||||
"Outdated Randomizer Save",
|
||||
"The SoH version in the file in slot " + std::to_string(fileNum + 1) +
|
||||
" does not match the currently running version.\n" +
|
||||
"Non-matching rando saves are unsupported, and the file has been renamed to\n" +
|
||||
" " + newFileName + "\n" +
|
||||
"If this was not in error, the file should be deleted.");
|
||||
saveMtx.unlock();
|
||||
SaveFile(fileNum);
|
||||
return;
|
||||
}
|
||||
}
|
||||
int sectionVersion = block.value()["version"];
|
||||
if (sectionName == "randomizer" && sectionVersion != 1) {
|
||||
sectionVersion = 1;
|
||||
}
|
||||
if (!sectionLoadHandlers.contains(sectionName)) {
|
||||
// Unloadable sections aren't necessarily errors, they are probably mods that were unloaded
|
||||
// TODO report in a more noticeable manner
|
||||
|
|
|
@ -166,11 +166,7 @@ class SaveManager {
|
|||
static void InitFileDebug();
|
||||
static void InitFileMaxed();
|
||||
|
||||
static void LoadRandomizerVersion1();
|
||||
static void LoadRandomizerVersion2();
|
||||
static void LoadRandomizerVersion3();
|
||||
static void LoadTrackerData();
|
||||
static void SaveTrackerData(SaveContext* saveContext, int sectionID, bool fullSave);
|
||||
static void LoadRandomizer();
|
||||
static void SaveRandomizer(SaveContext* saveContext, int sectionID, bool fullSave);
|
||||
|
||||
static void LoadBaseVersion1();
|
||||
|
|
|
@ -369,17 +369,6 @@ void SohUtils::CopyStringToCharArray(char* destination, std::string source, size
|
|||
}
|
||||
|
||||
std::string SohUtils::Sanitize(std::string stringValue) {
|
||||
// Add backslashes.
|
||||
for (auto i = stringValue.begin();;) {
|
||||
auto const pos =
|
||||
std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
|
||||
if (pos == stringValue.end()) {
|
||||
break;
|
||||
}
|
||||
i = std::next(stringValue.insert(pos, '\\'), 2);
|
||||
}
|
||||
|
||||
// Removes others.
|
||||
stringValue.erase(std::remove_if(stringValue.begin(), stringValue.end(),
|
||||
[](char const c) { return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }),
|
||||
stringValue.end());
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "soh/SaveManager.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
#include "soh/ResourceManagerHelpers.h"
|
||||
#include "soh/ShipUtils.h"
|
||||
|
||||
typedef struct {
|
||||
s16 left;
|
||||
|
@ -1053,20 +1054,27 @@ void FileChoose_UpdateRandomizer() {
|
|||
if (!SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) &&
|
||||
!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) {
|
||||
CVarSetString(CVAR_GENERAL("SpoilerLog"), "");
|
||||
Randomizer_SetSpoilerLoaded(false);
|
||||
}
|
||||
|
||||
if (CVarGetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0) != 0 ||
|
||||
!(Randomizer_IsSeedGenerated() || Randomizer_IsSpoilerLoaded()) &&
|
||||
SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) && !fileSelectSpoilerFileLoaded) {
|
||||
if (CVarGetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0) != 0) {
|
||||
CVarSetString(CVAR_GENERAL("SpoilerLog"), CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), ""));
|
||||
Audio_PlayFanfare(NA_BGM_HORSE_GOAL);
|
||||
if (SpoilerFileExists(CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), ""))) {
|
||||
CVarSetString(CVAR_GENERAL("SpoilerLog"), CVarGetString(CVAR_GENERAL("RandomizerDroppedFile"), ""));
|
||||
Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME);
|
||||
} else {
|
||||
Sfx_PlaySfxCentered(NA_SE_SY_ERROR);
|
||||
}
|
||||
}
|
||||
const char* fileLoc = CVarGetString(CVAR_GENERAL("SpoilerLog"), "");
|
||||
CVarSetInteger(CVAR_GENERAL("RandomizerNewFileDropped"), 0);
|
||||
CVarSetString(CVAR_GENERAL("RandomizerDroppedFile"), "");
|
||||
Randomizer_ParseSpoiler(fileLoc);
|
||||
fileSelectSpoilerFileLoaded = true;
|
||||
if (!Ship_IsCStringEmpty(fileLoc)) {
|
||||
Randomizer_ParseSpoiler(fileLoc);
|
||||
fileSelectSpoilerFileLoaded = true;
|
||||
}
|
||||
|
||||
if (SpoilerFileExists(CVarGetString(CVAR_GENERAL("SpoilerLog"), "")) &&
|
||||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue