diff --git a/soh/soh/Enhancements/mod_menu.cpp b/soh/soh/Enhancements/mod_menu.cpp new file mode 100644 index 000000000..609edf4d4 --- /dev/null +++ b/soh/soh/Enhancements/mod_menu.cpp @@ -0,0 +1,135 @@ +#include "mod_menu.h" +#include "utils/StringHelper.h" +#include +#include "soh/OTRGlobals.h" +#include +#include + +std::shared_ptr GetArchiveManager() { + return Ship::Context::GetInstance()->GetResourceManager()->GetArchiveManager(); +} + +std::map modFiles; + +bool is_enabled(const std::pair& p) { + return p.second; +} + +std::vector GetEnabledModFiles() { + std::map enabledMods; + + std::copy_if(modFiles.begin(), modFiles.end(), std::inserter(enabledMods, enabledMods.begin()), is_enabled); + + auto ks = std::views::keys(enabledMods); + std::vector keys{ ks.begin(), ks.end() }; + + return keys; +} + +bool is_disabled(const std::pair& p) { + return !p.second; +} + +std::vector GetDisabledModFiles() { + std::map disabledMods; + + std::copy_if(modFiles.begin(), modFiles.end(), std::inserter(disabledMods, disabledMods.begin()), is_disabled); + + auto ks = std::views::keys(disabledMods); + std::vector keys{ ks.begin(), ks.end() }; + + return keys; +} + +void UpdateModFiles() { + modFiles.clear(); + std::string modsPath = Ship::Context::LocateFileAcrossAppDirs("mods", appShortName); + if (modsPath.length() > 0 && std::filesystem::exists(modsPath)) { + if (std::filesystem::is_directory(modsPath)) { + for (const std::filesystem::directory_entry& p : std::filesystem::recursive_directory_iterator(modsPath, std::filesystem::directory_options::follow_directory_symlink)) { + std::string extension = p.path().extension().string(); + if ( + StringHelper::IEquals(extension, ".otr") || + StringHelper::IEquals(extension, ".mpq") || + StringHelper::IEquals(extension, ".o2r") || + StringHelper::IEquals(extension, ".zip") + ) { + modFiles.emplace(p.path().generic_string(), false); + } + } + } + } + + /* + std::sort(modFiles.begin(), modFiles.end(), [](const std::string& a, const std::string& b) { + return std::lexicographical_compare( + a.begin(), a.end(), + b.begin(), b.end(), + [](char c1, char c2) { + return std::tolower(c1) < std::tolower(c2); + } + ); + }); + */ +} + +void ModMenuWindow::DrawElement() { + if (ImGui::Button("Update")) { + UpdateModFiles(); + } + + if (ImGui::BeginTable("tableMods", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { + ImGui::TableSetupColumn("Enabled Mods", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableSetupColumn("Disabled Mods", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::TableHeadersRow(); + ImGui::PopItemFlag(); + ImGui::TableNextRow(); + + ImGui::TableNextColumn(); + + if (ImGui::BeginChild("Enabled Mods", ImVec2(0, -8))) { + std::vector enabledMods = GetEnabledModFiles(); + if (!enabledMods.empty()) { + for (std::string file : enabledMods) { + if (ImGui::Button(("Disable##" + file).c_str())) { + modFiles[file] = false; + Ship::Context::GetInstance()->GetResourceManager()->GetArchiveManager()->RemoveArchive(file); + } + ImGui::SameLine(); + ImGui::Text(file.c_str()); + } + } else { + ImGui::Text(""); + } + + ImGui::EndChild(); + } + + ImGui::TableNextColumn(); + + if (ImGui::BeginChild("Disabled Mods", ImVec2(0, -8))) { + std::vector disabledMods = GetDisabledModFiles(); + if (!disabledMods.empty()) { + for (std::string file : disabledMods) { + if (ImGui::Button(("Enable##" + file).c_str())) { + modFiles[file] = true; + Ship::Context::GetInstance()->GetResourceManager()->GetArchiveManager()->AddArchive(file); + } + ImGui::SameLine(); + ImGui::Text(file.c_str()); + } + } else { + ImGui::Text(""); + } + + ImGui::EndChild(); + } + + ImGui::EndTable(); + } +} + +void ModMenuWindow::InitElement() { + UpdateModFiles(); +} \ No newline at end of file diff --git a/soh/soh/Enhancements/mod_menu.h b/soh/soh/Enhancements/mod_menu.h new file mode 100644 index 000000000..0a69e0769 --- /dev/null +++ b/soh/soh/Enhancements/mod_menu.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#ifdef __cplusplus +class ModMenuWindow : public Ship::GuiWindow { + public: + using GuiWindow::GuiWindow; + + void InitElement() override; + void DrawElement() override; + void UpdateElement() override {}; +}; +#endif \ No newline at end of file diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 7856de88f..867fdecc3 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -265,7 +265,7 @@ OTRGlobals::OTRGlobals() { std::string mqPath = Ship::Context::LocateFileAcrossAppDirs("oot-mq.otr", appShortName); if (std::filesystem::exists(mqPath)) { OTRFiles.push_back(mqPath); - } + } std::string ootPath = Ship::Context::LocateFileAcrossAppDirs("oot.otr", appShortName); if (std::filesystem::exists(ootPath)) { OTRFiles.push_back(ootPath); @@ -274,6 +274,7 @@ OTRGlobals::OTRGlobals() { if (std::filesystem::exists(sohOtrPath)) { OTRFiles.push_back(sohOtrPath); } + /* std::string patchesPath = Ship::Context::LocateFileAcrossAppDirs("mods", appShortName); std::vector patchOTRs = {}; if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) { @@ -297,7 +298,9 @@ OTRGlobals::OTRGlobals() { } ); }); + OTRFiles.insert(OTRFiles.end(), patchOTRs.begin(), patchOTRs.end()); + */ std::unordered_set ValidHashes = { OOT_PAL_MQ, OOT_NTSC_JP_MQ, diff --git a/soh/soh/SohGui.cpp b/soh/soh/SohGui.cpp index 6cc4e1de6..f4c483211 100644 --- a/soh/soh/SohGui.cpp +++ b/soh/soh/SohGui.cpp @@ -36,6 +36,7 @@ #include "Enhancements/debugger/MessageViewer.h" #include "soh/Notification/Notification.h" #include "soh/Enhancements/TimeDisplay/TimeDisplay.h" +#include "soh/Enhancements/mod_menu.h" bool isBetaQuestEnabled = false; @@ -110,6 +111,7 @@ namespace SohGui { std::shared_ptr mGfxDebuggerWindow; std::shared_ptr mInputEditorWindow; + std::shared_ptr mModMenuWindow; std::shared_ptr mAudioEditorWindow; std::shared_ptr mInputViewer; std::shared_ptr mInputViewerSettings; @@ -170,6 +172,8 @@ namespace SohGui { SPDLOG_ERROR("Could not find input editor window"); } + mModMenuWindow = std::make_shared(CVAR_WINDOW("ModMenu"), "Mod Menu", ImVec2(820, 630)); + gui->AddGuiWindow(mModMenuWindow); mAudioEditorWindow = std::make_shared(CVAR_WINDOW("AudioEditor"), "Audio Editor", ImVec2(820, 630)); gui->AddGuiWindow(mAudioEditorWindow); mInputViewer = std::make_shared(CVAR_WINDOW("InputViewer"), "Input Viewer"); @@ -227,8 +231,9 @@ namespace SohGui { void Destroy() { auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui(); gui->RemoveAllGuiWindows(); - + mNotificationWindow = nullptr; + mModMenuWindow = nullptr; mModalWindow = nullptr; mAdvancedResolutionSettingsWindow = nullptr; mRandomizerSettingsWindow = nullptr; diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index f3a9557c2..562660dbf 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -44,6 +44,7 @@ #include "Enhancements/timesplits/TimeSplits.h" #include "Enhancements/randomizer/Plandomizer.h" #include "Enhancements/TimeDisplay/TimeDisplay.h" +#include "Enhancements/mod_menu.h" // FA icons are kind of wonky, if they worked how I expected them to the "+ 2.0f" wouldn't be needed, but // they don't work how I expect them to so I added that because it looked good when I eyeballed it @@ -608,6 +609,7 @@ extern std::shared_ptr mCosmeticsEditorWindow; extern std::shared_ptr mGameplayStatsWindow; extern std::shared_ptr mTimeSplitWindow; extern std::shared_ptr mTimeDisplayWindow; +extern std::shared_ptr mModMenuWindow; void DrawEnhancementsMenu() { if (ImGui::BeginMenu("Enhancements")) @@ -1769,6 +1771,13 @@ void DrawEnhancementsMenu() { } } } + + if (mModMenuWindow) { + if (ImGui::Button(GetWindowButtonText("Mod Menu", CVarGetInteger(CVAR_WINDOW("ModMenu"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { + mModMenuWindow->ToggleVisibility(); + } + } + ImGui::PopStyleVar(3); ImGui::PopStyleColor(1);