diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 809ccda09..4f8ef259b 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -94,6 +94,7 @@ void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state); #include #include +#include #define DEFINE_HOOK(name, type) \ struct name { \ @@ -193,6 +194,7 @@ public: DEFINE_HOOK(OnSetGameLanguage, void()); + DEFINE_HOOK(OnFileDropped, void(std::string filePath)); DEFINE_HOOK(OnAssetAltChange, void()); // Helpers diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index fe1c4f4e2..4b9a84eff 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -364,31 +364,16 @@ std::unordered_map SpoilerfileSettingNameToEn #pragma GCC push_options #pragma GCC optimize ("O0") bool Randomizer::SpoilerFileExists(const char* spoilerFileName) { - try { - if (strcmp(spoilerFileName, "") != 0) { - std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName)); - if (!spoilerFileStream) { - return false; - } - - json spoilerFileJson; - spoilerFileStream >> spoilerFileJson; - - if (!spoilerFileJson.contains("version") || !spoilerFileJson.contains("finalSeed")) { - return false; - } - + if (strcmp(spoilerFileName, "") != 0) { + std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName)); + if (!spoilerFileStream) { + return false; + } else { return true; } - - return false; - } catch (std::exception& e) { - SPDLOG_ERROR("Error checking if spoiler file exists: {}", e.what()); - return false; - } catch (...) { - SPDLOG_ERROR("Error checking if spoiler file exists"); - return false; } + + return false; } #pragma GCC pop_options #pragma optimize("", on) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 872cf5130..a789aa8d9 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -118,6 +118,8 @@ CrowdControl* CrowdControl::Instance; #include "soh/config/ConfigUpdaters.h" +void SoH_ProcessDroppedFiles(std::string filePath); + OTRGlobals* OTRGlobals::Instance; SaveManager* SaveManager::Instance; CustomMessageManager* CustomMessageManager::Instance; @@ -1057,6 +1059,11 @@ extern "C" void InitOTR() { InitMods(); ActorDB::AddBuiltInCustomActors(); + // #region SOH [Randomizer] TODO: Remove these and refactor spoiler file handling for randomizer + CVarClear("gRandomizerNewFileDropped"); + CVarClear("gRandomizerDroppedFile"); + // #endregion + GameInteractor::Instance->RegisterGameHook(SoH_ProcessDroppedFiles); time_t now = time(NULL); tm *tm_now = localtime(&now); @@ -1233,6 +1240,16 @@ extern "C" void Graph_StartFrame() { } } #endif + + if (CVarGetInteger("gNewFileDropped", 0)) { + std::string filePath = SohUtils::Sanitize(CVarGetString("gDroppedFile", "")); + if (!filePath.empty()) { + GameInteractor::Instance->ExecuteHooks(filePath); + } + CVarClear("gNewFileDropped"); + CVarClear("gDroppedFile"); + } + OTRGlobals::Instance->context->GetWindow()->StartFrame(); } @@ -2580,70 +2597,74 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla gfx_register_blended_texture(name, mask, replacement); } -// #region SOH [TODO] Ideally this should move to being event based, it's currently run every frame on the file select screen -extern "C" void SoH_ProcessDroppedFiles() { - const char* droppedFile = CVarGetString("gDroppedFile", ""); - if (CVarGetInteger("gNewFileDropped", 0) && strcmp(droppedFile, "") != 0) { - try { - std::ifstream configStream(SohUtils::Sanitize(droppedFile)); - if (!configStream) { - return; - } - - nlohmann::json configJson; - configStream >> configJson; - - if (!configJson.contains("CVars")) { - return; - } - - clearCvars(enhancementsCvars); - clearCvars(cheatCvars); - clearCvars(randomizerCvars); - - // Flatten everything under CVars into a single array - auto cvars = configJson["CVars"].flatten(); - - for (auto& [key, value] : cvars.items()) { - // Replace slashes with dots in key, and remove leading dot - std::string path = key; - std::replace(path.begin(), path.end(), '/', '.'); - if (path[0] == '.') { - path.erase(0, 1); - } - if (value.is_string()) { - CVarSetString(path.c_str(), value.get().c_str()); - } else if (value.is_number_integer()) { - CVarSetInteger(path.c_str(), value.get()); - } else if (value.is_number_float()) { - CVarSetFloat(path.c_str(), value.get()); - } - } - - auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui(); - gui->GetGuiWindow("Console")->Hide(); - gui->GetGuiWindow("Actor Viewer")->Hide(); - gui->GetGuiWindow("Collision Viewer")->Hide(); - gui->GetGuiWindow("Save Editor")->Hide(); - gui->GetGuiWindow("Display List Viewer")->Hide(); - gui->GetGuiWindow("Stats")->Hide(); - std::dynamic_pointer_cast(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->ClearBindings(); - - gui->SaveConsoleVariablesOnNextTick(); - - uint32_t finalHash = boost::hash_32{}(configJson.dump()); - gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Configuration Loaded. Hash: %d", finalHash); - } catch (std::exception& e) { - SPDLOG_ERROR("Failed to load config file: {}", e.what()); - auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui(); - gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file"); - return; - } catch (...) { - SPDLOG_ERROR("Failed to load config file"); - auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui(); - gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file"); +void SoH_ProcessDroppedFiles(std::string filePath) { + try { + std::ifstream configStream(filePath); + if (!configStream) { return; } + + nlohmann::json configJson; + configStream >> configJson; + + // #region SOH [Randomizer] TODO: Refactor spoiler file handling for randomizer + if (configJson.contains("version") && configJson.contains("finalSeed")) { + CVarSetString("gRandomizerDroppedFile", filePath.c_str()); + CVarSetInteger("gRandomizerNewFileDropped", 1); + return; + } + // #endregion + + if (!configJson.contains("CVars")) { + return; + } + + clearCvars(enhancementsCvars); + clearCvars(cheatCvars); + clearCvars(randomizerCvars); + + // Flatten everything under CVars into a single array + auto cvars = configJson["CVars"].flatten(); + + for (auto& [key, value] : cvars.items()) { + // Replace slashes with dots in key, and remove leading dot + std::string path = key; + std::replace(path.begin(), path.end(), '/', '.'); + if (path[0] == '.') { + path.erase(0, 1); + } + if (value.is_string()) { + CVarSetString(path.c_str(), value.get().c_str()); + } else if (value.is_number_integer()) { + CVarSetInteger(path.c_str(), value.get()); + } else if (value.is_number_float()) { + CVarSetFloat(path.c_str(), value.get()); + } + } + + auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui(); + gui->GetGuiWindow("Console")->Hide(); + gui->GetGuiWindow("Actor Viewer")->Hide(); + gui->GetGuiWindow("Collision Viewer")->Hide(); + gui->GetGuiWindow("Save Editor")->Hide(); + gui->GetGuiWindow("Display List Viewer")->Hide(); + gui->GetGuiWindow("Stats")->Hide(); + std::dynamic_pointer_cast(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->ClearBindings(); + + gui->SaveConsoleVariablesOnNextTick(); + + uint32_t finalHash = boost::hash_32{}(configJson.dump()); + gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Configuration Loaded. Hash: %d", finalHash); + } catch (std::exception& e) { + SPDLOG_ERROR("Failed to load config file: {}", e.what()); + auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui(); + gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file"); + return; + } catch (...) { + SPDLOG_ERROR("Failed to load config file"); + auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui(); + gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file"); + return; } } // #endregion diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index 88c57ced3..9b42e6895 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -175,7 +175,6 @@ void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex); void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement); void SaveManager_ThreadPoolWait(); void CheckTracker_OnMessageClose(); -void SoH_ProcessDroppedFiles(); int32_t GetGIID(uint32_t itemID); #endif diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 9e6150593..bb8dd14d4 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -1030,18 +1030,18 @@ void FileChoose_UpdateRandomizer() { fileSelectSpoilerFileLoaded = false; } - if ((CVarGetInteger("gNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0) || + if ((CVarGetInteger("gRandomizerNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0) || (!fileSelectSpoilerFileLoaded && SpoilerFileExists(CVarGetString("gSpoilerLog", "")))) { - if (CVarGetInteger("gNewFileDropped", 0) != 0) { - CVarSetString("gSpoilerLog", CVarGetString("gDroppedFile", "None")); + if (CVarGetInteger("gRandomizerNewFileDropped", 0) != 0) { + CVarSetString("gSpoilerLog", CVarGetString("gRandomizerDroppedFile", "None")); } bool silent = true; - if ((CVarGetInteger("gNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0)) { + if ((CVarGetInteger("gRandomizerNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0)) { silent = false; } CVarSetInteger("gNewSeedGenerated", 0); - CVarSetInteger("gNewFileDropped", 0); - CVarSetString("gDroppedFile", ""); + CVarSetInteger("gRandomizerNewFileDropped", 0); + CVarSetString("gRandomizerDroppedFile", ""); fileSelectSpoilerFileLoaded = false; const char* fileLoc = CVarGetString("gSpoilerLog", ""); Randomizer_LoadSettings(fileLoc); @@ -1076,7 +1076,6 @@ void FileChoose_UpdateMainMenu(GameState* thisx) { Input* input = &this->state.input[0]; bool dpad = CVarGetInteger("gDpadText", 0); - SoH_ProcessDroppedFiles(); FileChoose_UpdateRandomizer(); if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) { @@ -1267,7 +1266,6 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) { s8 i = 0; bool dpad = CVarGetInteger("gDpadText", 0); - SoH_ProcessDroppedFiles(); FileChoose_UpdateRandomizer(); if (ABS(this->stickRelX) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT | BTN_DRIGHT))) { diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c index 377c23eb3..5c5cb3e6d 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c @@ -456,7 +456,6 @@ void FileChoose_DrawNameEntry(GameState* thisx) { this->prevConfigMode = CM_MAIN_MENU; this->configMode = CM_NAME_ENTRY_TO_MAIN; CVarSetInteger("gOnFileSelectNameEntry", 0); - CVarSetInteger("gNewFileDropped", 0); this->nameBoxAlpha[this->buttonIndex] = this->nameAlpha[this->buttonIndex] = 200; this->connectorAlpha[this->buttonIndex] = 255; func_800AA000(300.0f, 0xB4, 0x14, 0x64);