diff --git a/ZAPDTR b/ZAPDTR index 684f21a475d..ee3397a365c 160000 --- a/ZAPDTR +++ b/ZAPDTR @@ -1 +1 @@ -Subproject commit 684f21a475dcfeee89938ae1f4afc42768a3e7ef +Subproject commit ee3397a365c5f350a60538c88f0643f155944836 diff --git a/libultraship b/libultraship index 3ef24c3d164..8c55f607f22 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 3ef24c3d1645fe1fc2a7065d1c2e132973ebc151 +Subproject commit 8c55f607f2249f3ac696fc0f7277553fe3ce75a6 diff --git a/soh/soh/Extractor/Extract.cpp b/soh/soh/Extractor/Extract.cpp index 558a3f74d79..94adce841df 100644 --- a/soh/soh/Extractor/Extract.cpp +++ b/soh/soh/Extractor/Extract.cpp @@ -401,7 +401,6 @@ bool Extractor::ManuallySearchForRom() { std::ifstream inFile; if (!GetRomPathFromBox()) { - ShowErrorBox("No rom selected", "No Rom selected. Exiting"); return false; } @@ -481,11 +480,15 @@ bool Extractor::RunFileStandalone(std::string rom) { return true; } +void Extractor::SetSearchPath(const std::string& path) { + mSearchPath = path; +} + bool Extractor::Run(std::string searchPath, RomSearchMode searchMode) { std::vector roms; std::ifstream inFile; - mSearchPath = searchPath; + SetSearchPath(searchPath); GetRoms(roms); FilterRoms(roms, searchMode); @@ -634,10 +637,11 @@ std::string Extractor::Mkdtemp() { return tmppath; } -extern "C" int zapd_main(int argc, char** argv); +extern "C" int zapd_report(int argc, char** argv, std::atomic* extractCount, std::atomic* totalExtract); static void MessageboxWorker(); -bool Extractor::CallZapd(std::string installPath, std::string exportdir) { +bool Extractor::CallZapd(std::string installPath, std::string exportdir, std::atomic* extractCount, + std::atomic* totalExtract) { constexpr int argc = 22; char xmlPath[1024]; char confPath[1024]; @@ -688,26 +692,7 @@ bool Extractor::CallZapd(std::string installPath, std::string exportdir) { argv[20] = "-osf"; argv[21] = "placeholder"; -#ifdef _WIN32 - // Grab a handle to the command window. - HWND cmdWindow = GetConsoleWindow(); - - // Normally the command window is hidden. We want the window to be shown here so the user can see the progess of the - // extraction. - ShowWindow(cmdWindow, SW_SHOW); - SetWindowPos(cmdWindow, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); -#else - // Show extraction in background message until linux/mac can have visual progress - std::thread mbThread(MessageboxWorker); - mbThread.detach(); -#endif - - zapd_main(argc, (char**)argv.data()); - -#ifdef _WIN32 - // Hide the command window again. - ShowWindow(cmdWindow, SW_HIDE); -#endif + zapd_report(argc, (char**)argv.data(), extractCount, totalExtract); std::filesystem::copy(otrFile, exportdir + "/" + otrFile, std::filesystem::copy_options::overwrite_existing); diff --git a/soh/soh/Extractor/Extract.h b/soh/soh/Extractor/Extract.h index 691233aba73..552d5bf4eb1 100644 --- a/soh/soh/Extractor/Extract.h +++ b/soh/soh/Extractor/Extract.h @@ -1,6 +1,7 @@ #ifndef EXTRACT_H #define EXTRACT_H +#include #include #include #include @@ -45,23 +46,25 @@ class Extractor { void SetRomInfo(const std::string& path); void FilterRoms(std::vector& roms, RomSearchMode searchMode); - void GetRoms(std::vector& roms); void ShowSizeErrorBox() const; void ShowCrcErrorBox() const; void ShowCompressedErrorBox() const; int ShowRomPickBox(uint32_t verCrc) const; bool ManuallySearchForRom(); - bool ManuallySearchForRomMatchingType(RomSearchMode searchMode); public: // TODO create some kind of abstraction for message boxes. static int ShowYesNoBox(const char* title, const char* text); static void ShowErrorBox(const char* title, const char* text); bool IsMasterQuest() const; + bool ManuallySearchForRomMatchingType(RomSearchMode searchMode); + void SetSearchPath(const std::string& path); + void GetRoms(std::vector& roms); bool RunFileStandalone(std::string file); bool Run(std::string searchPath, RomSearchMode searchMode = RomSearchMode::Both); - bool CallZapd(std::string installPath, std::string exportdir); + bool CallZapd(std::string installPath, std::string exportdir, std::atomic* extractCount, + std::atomic* totalExtract); const char* GetZapdStr(); std::string Mkdtemp(); }; diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 114435e9cef..33e33e4c44d 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2,8 +2,10 @@ #include "OTRAudio.h" #include #include +#include #include #include +#include #include #include "ResourceManagerHelpers.h" @@ -37,6 +39,8 @@ #include "Enhancements/gameplaystats.h" #include "ObjectExtension/ObjectExtension.h" #include "frame_interpolation.h" +#include "SohGui/SohMenu.h" +#include "SohGui/SohGui.hpp" #include "variables.h" #include "z64.h" #include "macros.h" @@ -145,6 +149,8 @@ Sail* Sail::Instance; Anchor* Anchor::Instance; extern "C" char** cameraStrings; + +extern "C" void PadMgr_ThreadEntry(PadMgr* padMgr); std::vector> cameraStdStrings; Color_RGB8 kokiriColor = { 0x1E, 0x69, 0x1B }; @@ -263,25 +269,520 @@ const char* constCameraStrings[] = { GFXP_KATAKANA "キ- / ", }; +typedef struct { + uint16_t major; + uint16_t minor; + uint16_t patch; +} OTRVersion; + +std::shared_ptr sohFast3dWindow; +static OTRVersion DetectOTRVersion(std::string path, bool isMq); +static bool VerifyArchiveVersion(OTRVersion version); +std::string portArchivePath = ""; +static bool sohArchiveVersionMatch = false; + OTRGlobals::OTRGlobals() { context = Ship::Context::CreateUninitializedInstance("Ship of Harkinian", appShortName, "shipofharkinian.json"); + + portArchivePath = Ship::Context::LocateFileAcrossAppDirs("soh.o2r"); + OTRVersion portArchiveVersion = DetectOTRVersion(portArchivePath, false); + sohArchiveVersionMatch = portArchiveVersion.major == gBuildVersionMajor && + portArchiveVersion.minor == gBuildVersionMinor && + portArchiveVersion.patch == gBuildVersionPatch; + + context->InitConfiguration(); + context->InitConsoleVariables(); + + auto controlDeck = std::make_shared(std::vector({ + BTN_CUSTOM_MODIFIER1, + BTN_CUSTOM_MODIFIER2, + BTN_CUSTOM_OCARINA_NOTE_D4, + BTN_CUSTOM_OCARINA_NOTE_F4, + BTN_CUSTOM_OCARINA_NOTE_A4, + BTN_CUSTOM_OCARINA_NOTE_B4, + BTN_CUSTOM_OCARINA_NOTE_D5, + BTN_CUSTOM_OCARINA_DISABLE_SONGS, + BTN_CUSTOM_OCARINA_PITCH_UP, + BTN_CUSTOM_OCARINA_PITCH_DOWN, + })); + context->InitControlDeck(controlDeck); + context->InitResourceManager({ portArchivePath }, {}, 3, true); + context->InitConsole(); + + auto sohInputEditorWindow = + std::make_shared(CVAR_WINDOW("ControllerConfiguration"), "Configure Controller"); + sohFast3dWindow = + std::make_shared(std::vector>({ sohInputEditorWindow })); + context->InitWindow(sohFast3dWindow); + + SohGui::SetupMenu(); + + if (sohArchiveVersionMatch) { + + auto overlay = context->GetInstance()->GetWindow()->GetGui()->GetGameOverlay(); + overlay->LoadFont("Press Start 2P", 12.0f, "fonts/PressStart2P-Regular.ttf"); + overlay->LoadFont("Fipps", 32.0f, "fonts/Fipps-Regular.otf"); + overlay->SetCurrentFont(CVarGetString(CVAR_GAME_OVERLAY_FONT, "Press Start 2P")); + + fontMonoSmall = CreateFontWithSize(14.0f, "fonts/Inconsolata-Regular.ttf"); + fontMono = CreateFontWithSize(16.0f, "fonts/Inconsolata-Regular.ttf"); + fontMonoLarger = CreateFontWithSize(20.0f, "fonts/Inconsolata-Regular.ttf"); + fontMonoLargest = CreateFontWithSize(24.0f, "fonts/Inconsolata-Regular.ttf"); + fontStandard = CreateFontWithSize(16.0f, "fonts/Montserrat-Regular.ttf"); + fontStandardLarger = CreateFontWithSize(20.0f, "fonts/Montserrat-Regular.ttf"); + fontStandardLargest = CreateFontWithSize(24.0f, "fonts/Montserrat-Regular.ttf"); + fontJapanese = CreateFontWithSize(24.0f, "fonts/NotoSansJP-Regular.ttf", true); + ImGui::GetIO().FontDefault = fontStandardLarger; + } + + previousImGuiScaleIndex = -1; + previousImGuiScale = defaultImGuiScale; + ScaleImGui(); +} + +typedef enum ExtractSteps { + ES_PORT_ARCHIVE, + ES_WINDOWS, + ES_EXTRACT_ARGS, + ES_EXTRACT, + ES_VERIFY, +} ExtractSteps; + +typedef enum PromptSteps { + PS_FILE_CHECK, + PS_LOCAL, + PS_FIRST, + PS_SECOND, + PS_DUPE, + PS_WAIT, + PS_NONE, +} PromptSteps; + +typedef enum WindowsSteps { + WS_TEMP, + WS_PERMS, + WS_ONEDRIVE, + WS_DONE, +} WindowsSteps; + +bool IsSubpath(const std::filesystem::path& path, const std::filesystem::path& base) { + auto rel = std::filesystem::relative(path, base); + return !rel.empty() && rel.native()[0] != '.'; +} + +bool PathTestCleanup(FILE* tfile) { + try { + if (std::filesystem::exists("./text.txt")) + std::filesystem::remove("./text.txt"); + if (std::filesystem::exists("./test/")) + std::filesystem::remove("./test/"); + } catch (std::filesystem::filesystem_error const& ex) { return false; } + return true; +} + +void CheckAndCreateModFolder() { + try { + std::string modsPath = Ship::Context::LocateFileAcrossAppDirs("mods", appShortName); + if (!std::filesystem::exists(modsPath)) { + // Create mods folder relative to app dir + modsPath = Ship::Context::GetPathRelativeToAppDirectory("mods", appShortName); + std::string filePath = modsPath + "/custom_mod_files_go_here.txt"; + if (std::filesystem::create_directories(modsPath)) { + std::ofstream(filePath).close(); + } + } + } catch (std::filesystem::filesystem_error const& ex) { + // Couldn't make the folder, continue silently + return; + } +} + +namespace SohGui { +extern std::shared_ptr mSohMenu; +} + +void OTRGlobals::RunExtract(int argc, char* argv[]) { + bool extractDone = false; + ExtractSteps extractStep = ES_PORT_ARCHIVE; + WindowsSteps windowsStep = WS_TEMP; + auto wnd = std::dynamic_pointer_cast(OTRGlobals::Instance->context->GetWindow()); + auto gui = wnd->GetGui(); + + OTRVersion vanillaVersion = DetectOTRVersion("oot.o2r", false); + OTRVersion mqVersion = DetectOTRVersion("oot-mq.o2r", true); + + bool shouldRegen = VerifyArchiveVersion(vanillaVersion) || VerifyArchiveVersion(mqVersion); + + std::filesystem::path ownPath; + std::vector args; + if (argc > 1) { + for (int i = 1; i < argc; i++) { + args.push_back(argv[argc]); + } + } + Extractor extract; + PromptSteps promptStep = PS_FILE_CHECK; + bool generatedIsMQ = false; + std::atomic extracting = false; + std::atomic extractCount = 0, totalExtract = 0; + + std::string installPath = Ship::Context::GetAppBundlePath(); + std::string file; + +#if defined(__SWITCH__) + SohGui::RegisterPopup("Outdated ROM Archives", + "\x1b[2;2HYou've launched the Ship with an old ROM O2R file." + "\x1b[4;2HPlease regenerate a new ROM O2R and relaunch." + "\x1b[6;2HPress the Home button to exit...", + "OK", "", [&]() { exit(1); }); +#elif defined(__WIIU__) + SohGui::RegisterPopup("Outdated ROM Archives", + "You've launched the Ship with an old a ROM O2R file.\n\n" + "Please generate a ROM O2R and relaunch.\n\n" + "Press and hold the Power button to shutdown...", + "OK", "", [&]() { exit(1); }); + OSFatal(); +#endif + + if (!std::filesystem::exists(installPath + "/assets")) { + SohGui::RegisterPopup("Extractor assets not found", + "No O2R files found. Missing 'assets/' folder needed to generate OTR file.\nPlease " + "re-extract them from the download or.\n\nExiting...", + "OK", "", [&]() { exit(1); }); + } else if (shouldRegen) { + SohGui::RegisterPopup("Outdated ROM Archives", + "Your oot.o2r or oot-mq.o2r were created with incompatible versions of SoH.\nYou will " + "now be redirected to re-extract them."); + std::filesystem::remove("oot.o2r"); + std::filesystem::remove("oot-mq.o2r"); + } + + std::shared_ptr threadPool = std::make_shared(1); + while (!extractDone) { + if (SohGui::PopupsQueued() > 0 || extracting) { + goto render; + } + switch (extractStep) { + case ES_PORT_ARCHIVE: { + if (sohArchiveVersionMatch) { +#ifdef _WIN32 + extractStep = ES_WINDOWS; +#elif (defined(__WIIU__) || defined(__SWITCH__)) + extractStep = ES_VERIFY; +#else + extractStep = ES_EXTRACT; +#endif + } else { + std::string msg; + +#if defined(__SWITCH__) + msg = "\x1b[4;2HPlease re-extract it from the download.\n" + "\x1b[6;2HPress the Home button to exit..."; +#elif defined(__WIIU__) + msg = "Please extract the soh.o2r from the Ship of Harkinian download\nto your folder.\n\nPress " + "and hold the power\n" + "button to shutdown..."; +#else + msg = + "Please extract the soh.o2r from the Ship of Harkinian download to your folder.\n\nExiting..."; +#endif + std::string title = + !std::filesystem::exists(portArchivePath) ? "Missing soh.o2r" : "soh.o2r is outdated"; + SohGui::RegisterPopup(title, msg, "OK", "", [&]() { exit(1); }); + } + continue; + } + case ES_WINDOWS: { + switch (windowsStep) { + case WS_TEMP: { +#ifdef _WIN32 + char* tempVar = getenv("TEMP"); + std::filesystem::path tempPath; + try { + tempPath = std::filesystem::canonical(tempVar); + } catch (std::filesystem::filesystem_error const& ex) { + std::string userPath = getenv("USERPROFILE"); + userPath.append("\\AppData\\Local\\Temp"); + tempPath = std::filesystem::canonical(userPath); + } + wchar_t buffer[MAX_PATH]; + GetModuleFileName(NULL, buffer, _countof(buffer)); + ownPath = std::filesystem::canonical(buffer).parent_path(); + if (IsSubpath(ownPath, tempPath)) { + SohGui::RegisterPopup("SoH Path Error", + "SoH is running in a temp folder.\nExtract the .zip and run again.", + "OK", "", [&]() { exit(0); }); + } else { + windowsStep = WS_PERMS; + } +#endif + continue; + } + case WS_PERMS: { + FILE* tfile = fopen("./text.txt", "w"); + std::filesystem::path tfolder = std::filesystem::path("./test/"); + bool error = false; + try { + create_directories(tfolder); + } catch (std::filesystem::filesystem_error const& ex) { error = true; } + if (tfile == NULL || error) { + SohGui::RegisterPopup("SoH Permissions Error", + "SoH does not have proper file permissions.\nPlease move it to a " + "folder that does and run again.", + "OK", "", [&]() { + fclose(tfile); + PathTestCleanup(tfile); + exit(0); + }); + } else { + fclose(tfile); + if (!PathTestCleanup(tfile)) { + SohGui::RegisterPopup("SoH Permissions Error", + "SoH does not have proper file permissions.\nPlease move it to a " + "folder that does and run again.", + "OK", "", [&]() { exit(0); }); + } + windowsStep = WS_ONEDRIVE; + } + continue; + } + case WS_ONEDRIVE: { + if (ownPath.string().find("OneDrive") != std::string::npos) { + SohGui::RegisterPopup("SoH Path Error", + "SoH appears to be in a OneDrive folder, which will cause issues.\n" + "Please move it to a folder outside of OneDrive, like the root of a\n" + "drive (e.g. \"C:\\Games\\SoH\").", + "OK", "", [&]() { exit(0); }); + } else { + windowsStep = WS_DONE; + if (args.size() > 0) { + extractStep = ES_EXTRACT_ARGS; + } else { + extractStep = ES_EXTRACT; + } + } + continue; + } + default: + continue; + } + break; + } + case ES_EXTRACT_ARGS: { +#if !defined(__SWITCH__) && !defined(__WIIU__) + if (args.size() == 0) { + SohGui::RegisterPopup( + "Run Ship of Harkinian", "All files have been processed. Run SoH?", "Yes", "No", + [&]() { + if (!std::filesystem::exists(Ship::Context::GetAppDirectoryPath(appShortName) + + "/oot.o2r") && + !std::filesystem::exists(Ship::Context::GetAppDirectoryPath(appShortName) + + "/oot-mq.o2r")) { + extractStep = ES_EXTRACT; + promptStep = PS_FILE_CHECK; + } else { + extractStep = ES_VERIFY; + } + }, + [&]() { exit(0); }); + break; + } + file = args.at(0); + args.erase(args.begin()); + extract = Extractor(); + if (extract.RunFileStandalone(file)) { + bool doExtract = true; + std::string archive = (extract.IsMasterQuest() ? "oot-mq.o2r" : "oot.o2r"); + if (std::filesystem::exists(Ship::Context::GetAppDirectoryPath(appShortName) + "/" + archive)) { + std::string msg = "Archive for current ROM, " + archive + ", already exists.\nExtract again?"; + SohGui::RegisterPopup("Confirm Re-extract", msg.c_str(), "Yes", "No", [&]() { + extracting = true; + threadPool->submit_task([&]() -> void { + extract.CallZapd(installPath, Ship::Context::GetAppDirectoryPath(appShortName), + &extractCount, &totalExtract); + extracting = false; + extractCount = totalExtract = 0; + }); + }); + } else { + extracting = true; + threadPool->submit_task([&]() -> void { + extract.CallZapd(installPath, Ship::Context::GetAppDirectoryPath(appShortName), + &extractCount, &totalExtract); + extracting = false; + extractCount = totalExtract = 0; + }); + } + } else { + bool open = true; + std::string msg = "File\n" + std::string(file) + "\nis not a ROM or does not match supported ROMs."; + SohGui::RegisterPopup("SoH ROM Error", msg.c_str()); + } +#else + extractStep = ES_VERIFY; +#endif + break; + } + case ES_EXTRACT: { + switch (promptStep) { + case PS_FILE_CHECK: { + const bool ootO2RExists = + std::filesystem::exists( + Ship::Context::LocateFileAcrossAppDirs("oot-mq.o2r", appShortName)) || + std::filesystem::exists(Ship::Context::LocateFileAcrossAppDirs("oot.o2r", appShortName)); + + if (!ootO2RExists) { + SohGui::RegisterPopup( + "No O2R Files", "No O2R files found. Generate one now?", "Yes", "No", + [&]() { promptStep = PS_LOCAL; }, [&]() { exit(0); }); + } else { + extractStep = ES_VERIFY; + } + continue; + } + case PS_LOCAL: { + extract = Extractor(); + extract.SetSearchPath(installPath); + extract.GetRoms(args); + if (!args.empty()) { + promptStep = PS_WAIT; + SohGui::RegisterPopup( + "ROMs found", "ROMs found in application directory. Would you like to process them?", + "Yes", "No", [&]() { extractStep = ES_EXTRACT_ARGS; }, + [&]() { promptStep = PS_FIRST; }); + } else { + promptStep = PS_FIRST; + } + continue; + } + case PS_FIRST: { + if (!extract.ManuallySearchForRomMatchingType(RomSearchMode::Both)) { + promptStep = PS_FILE_CHECK; + continue; + } + extracting = true; + threadPool->submit_task([&]() -> void { + extract.CallZapd(installPath, Ship::Context::GetAppDirectoryPath(appShortName), + &extractCount, &totalExtract); + generatedIsMQ = extract.IsMasterQuest(); + extracting = false; + promptStep = PS_SECOND; + extractCount = 0; + totalExtract = 0; + }); + continue; + } + case PS_SECOND: { + SohGui::RegisterPopup( + "Extraction Complete", "ROM Extracted. Extract another?", "Yes", "No", + [&]() { + if (!extract.ManuallySearchForRomMatchingType(generatedIsMQ ? RomSearchMode::Vanilla + : RomSearchMode::MQ)) { + extractStep = ES_VERIFY; + } else { + extracting = true; + threadPool->submit_task([&]() -> void { + extract.CallZapd(installPath, Ship::Context::GetAppDirectoryPath(appShortName), + &extractCount, &totalExtract); + extracting = false; + extractStep = ES_VERIFY; + extractCount = 0; + totalExtract = 0; + }); + } + }, + [&]() { extractStep = ES_VERIFY; }); + continue; + } + default: + break; + } + break; + } + case ES_VERIFY: { + const bool ootO2RExists = + std::filesystem::exists(Ship::Context::LocateFileAcrossAppDirs("oot-mq.o2r", appShortName)) || + std::filesystem::exists(Ship::Context::LocateFileAcrossAppDirs("oot.o2r", appShortName)); + + if (!ootO2RExists) { + SohGui::RegisterPopup("No ROM Archives", + "No ROM O2R files detected. Please generate a ROM O2R and relaunch.", "OK", + "", [&]() { exit(0); }); + } + extractDone = true; + continue; + } + default: + break; + } + + render: + if (!WindowIsRunning()) { + exit(0); + } + // Process window events for resize, mouse, keyboard events + wnd->HandleEvents(); + UIWidgets::Colors themeColor = + static_cast(CVarGetInteger(CVAR_SETTING("Menu.Theme"), UIWidgets::Colors::LightBlue)); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIWidgets::ColorValues.at(themeColor)); + ImGui::PushStyleColor(ImGuiCol_ModalWindowDimBg, UIWidgets::ColorValues.at(UIWidgets::Colors::DarkGray)); + + // Skip dropped frames + if (!wnd->IsFrameReady()) { + continue; + } + gui->StartDraw(); + sohFast3dWindow->StartFrame(); + sohFast3dWindow->RunGuiOnly(); + if (extracting && !ImGui::IsPopupOpen("ROM Extraction")) { + ImGui::OpenPopup("ROM Extraction"); + } + if (extracting) { + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f)); + auto color = UIWidgets::ColorValues.at(THEME_COLOR); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(color.x, color.y, color.z, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(color.x, color.y, color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.3f)); + if (ImGui::BeginPopupModal("ROM Extraction", NULL, + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoSavedSettings)) { + float progress = (totalExtract > 0.0f ? (float)extractCount / (float)totalExtract : 0) * 100.0f; + auto filename = std::filesystem::path(file).filename().string(); + ImGui::Text("Extracting %s...%s", filename.c_str(), + roundf(progress) == 100.0f ? " Done. Finishing up." : ""); + std::string overlay = extractCount > 0 ? fmt::format("{:.0f}%", progress) : "Starting Up"; + ImGui::ProgressBar(progress / 100.0f, ImVec2(600.0f, 50.0f), overlay.c_str()); + ImGui::EndPopup(); + } + ImGui::PopStyleColor(3); + ImGui::PopStyleVar(2); + } + gui->EndDraw(); + sohFast3dWindow->EndFrame(); + ImGui::PopStyleColor(2); + } + +#ifdef __SWITCH__ + Ship::Switch::Init(Ship::PreInitPhase); +#elif defined(__WIIU__) + Ship::WiiU::Init(appShortName); +#endif + +#if not defined(__SWITCH__) && not defined(__WIIU__) + CheckAndCreateModFolder(); +#endif } void OTRGlobals::Initialize() { - std::vector OTRFiles; std::string mqPath = Ship::Context::LocateFileAcrossAppDirs("oot-mq.o2r", appShortName); if (std::filesystem::exists(mqPath)) { - OTRFiles.push_back(mqPath); + context->GetResourceManager()->GetArchiveManager()->AddArchive(mqPath); } std::string ootPath = Ship::Context::LocateFileAcrossAppDirs("oot.o2r", appShortName); if (std::filesystem::exists(ootPath)) { - OTRFiles.push_back(ootPath); - } - - std::string sohOtrPath = Ship::Context::LocateFileAcrossAppDirs("soh.o2r"); - - if (std::filesystem::exists(sohOtrPath)) { - OTRFiles.push_back(sohOtrPath); + context->GetResourceManager()->GetArchiveManager()->AddArchive(ootPath); } std::unordered_set ValidHashes = { @@ -306,42 +807,15 @@ void OTRGlobals::Initialize() { context->InitFileDropMgr(); // tell LUS to reserve 3 SoH specific threads (Game, Audio, Save) - context->InitResourceManager(OTRFiles, {}, 3); prevAltAssets = CVarGetInteger(CVAR_SETTING("AltAssets"), 1); context->GetResourceManager()->SetAltAssetsEnabled(prevAltAssets); - auto controlDeck = std::make_shared(std::vector({ - BTN_CUSTOM_MODIFIER1, - BTN_CUSTOM_MODIFIER2, - BTN_CUSTOM_OCARINA_NOTE_D4, - BTN_CUSTOM_OCARINA_NOTE_F4, - BTN_CUSTOM_OCARINA_NOTE_A4, - BTN_CUSTOM_OCARINA_NOTE_B4, - BTN_CUSTOM_OCARINA_NOTE_D5, - BTN_CUSTOM_OCARINA_DISABLE_SONGS, - BTN_CUSTOM_OCARINA_PITCH_UP, - BTN_CUSTOM_OCARINA_PITCH_DOWN, - })); - context->InitControlDeck(controlDeck); - context->InitCrashHandler(); - context->InitConsole(); - - auto sohInputEditorWindow = - std::make_shared(CVAR_WINDOW("ControllerConfiguration"), "Configure Controller"); - auto sohFast3dWindow = - std::make_shared(std::vector>({ sohInputEditorWindow })); - context->InitWindow(sohFast3dWindow); context->GetWindow()->SetAutoCaptureMouse(CVarGetInteger(CVAR_SETTING("EnableMouse"), 0) && CVarGetInteger(CVAR_SETTING("AutoCaptureMouse"), 1)); context->GetWindow()->SetForceCursorVisibility(CVarGetInteger(CVAR_SETTING("CursorVisibility"), 0)); - auto overlay = context->GetInstance()->GetWindow()->GetGui()->GetGameOverlay(); - overlay->LoadFont("Press Start 2P", 12.0f, "fonts/PressStart2P-Regular.ttf"); - overlay->LoadFont("Fipps", 32.0f, "fonts/Fipps-Regular.otf"); - overlay->SetCurrentFont(CVarGetString(CVAR_GAME_OVERLAY_FONT, "Press Start 2P")); - context->InitAudio({ .SampleRate = 32000, .SampleLength = 1024, .DesiredBuffered = 1680 }); SPDLOG_INFO("Starting Ship of Harkinian version {} (Branch: {} | Commit: {})", (char*)gBuildVersion, @@ -430,20 +904,6 @@ void OTRGlobals::Initialize() { hasMasterQuest = hasOriginal = false; - previousImGuiScaleIndex = -1; - previousImGuiScale = defaultImGuiScale; - - fontMonoSmall = CreateFontWithSize(14.0f, "fonts/Inconsolata-Regular.ttf", false); - fontMono = CreateFontWithSize(16.0f, "fonts/Inconsolata-Regular.ttf", false); - fontMonoLarger = CreateFontWithSize(20.0f, "fonts/Inconsolata-Regular.ttf", false); - fontMonoLargest = CreateFontWithSize(24.0f, "fonts/Inconsolata-Regular.ttf", false); - fontStandard = CreateFontWithSize(16.0f, "fonts/Montserrat-Regular.ttf", false); - fontStandardLarger = CreateFontWithSize(20.0f, "fonts/Montserrat-Regular.ttf", false); - fontStandardLargest = CreateFontWithSize(24.0f, "fonts/Montserrat-Regular.ttf", false); - fontJapanese = CreateFontWithSize(24.0f, "fonts/NotoSansJP-Regular.ttf", true); - ImGui::GetIO().FontDefault = fontStandardLarger; - ScaleImGui(); - // Move the camera strings from read only memory onto the heap (writable memory) // This is in OTRGlobals right now because this is a place that will only ever be run once at the beginning of // startup. We should probably find some code in db_camera that does initialization and only run once, and then @@ -961,12 +1421,6 @@ extern "C" void OTRExtScanner() { } } -typedef struct { - uint16_t major; - uint16_t minor; - uint16_t patch; -} OTRVersion; - // Read the port version from an OTR file OTRVersion ReadPortVersionFromOTR(std::string otrPath) { OTRVersion version = {}; @@ -989,284 +1443,38 @@ OTRVersion ReadPortVersionFromOTR(std::string otrPath) { return version; } -// Check that a soh.o2r exists and matches the version of soh running -// Otherwise show a message and exit -void CheckSoHOTRVersion(std::string otrPath) { - std::string msg; - -#if defined(__SWITCH__) - msg = "\x1b[4;2HPlease re-extract it from the download." - "\x1b[6;2HPress the Home button to exit..."; -#elif defined(__WIIU__) - msg = "Please extract the soh.o2r from the Ship of Harkinian download\nto your folder.\n\nPress and hold the power " - "button to shutdown..."; -#else - msg = "Please extract the soh.o2r from the Ship of Harkinian download to your folder.\n\nExiting..."; -#endif - - if (!std::filesystem::exists(otrPath)) { -#if not defined(__SWITCH__) && not defined(__WIIU__) - Extractor::ShowErrorBox("soh.o2r file is missing", msg.c_str()); - exit(1); -#elif defined(__SWITCH__) - Ship::Switch::PrintErrorMessageToScreen(("\x1b[2;2HYou are missing the soh.o2r file." + msg).c_str()); -#elif defined(__WIIU__) - OSFatal(("You are missing the soh.o2r file\n\n" + msg).c_str()); -#endif - } - - OTRVersion otrVersion = ReadPortVersionFromOTR(otrPath); - - if (otrVersion.major != gBuildVersionMajor || otrVersion.minor != gBuildVersionMinor || - otrVersion.patch != gBuildVersionPatch) { -#if not defined(__SWITCH__) && not defined(__WIIU__) - Extractor::ShowErrorBox("soh.o2r file version does not match", msg.c_str()); - exit(1); -#elif defined(__SWITCH__) - Ship::Switch::PrintErrorMessageToScreen(("\x1b[2;2HYou have an old soh.o2r file." + msg).c_str()); -#elif defined(__WIIU__) - OSFatal(("You have an old soh.o2r file\n\n" + msg).c_str()); -#endif - } -} - // Checks the program version stored in the otr and compares the major value to soh // For Windows/Mac/Linux if the version doesn't match, offer to -void DetectOTRVersion(std::string fileName, bool isMQ) { +OTRVersion DetectOTRVersion(std::string fileName, bool isMQ) { bool isOtrOld = false; std::string otrPath = Ship::Context::LocateFileAcrossAppDirs(fileName, appShortName); // Doesn't exist so nothing to do here if (!std::filesystem::exists(otrPath)) { - return; + return { INT16_MAX, INT16_MAX, INT16_MAX }; } - OTRVersion otrVersion = ReadPortVersionFromOTR(otrPath); - - if (otrVersion.major != gBuildVersionMajor) { - isOtrOld = true; - } - - if (isOtrOld) { -#if not defined(__SWITCH__) && not defined(__WIIU__) - char msgBuf[250]; - char version[18]; // 5 digits for int16_max (x3) + separators + terminator - - if (otrVersion.major != 0 || otrVersion.minor != 0 || otrVersion.patch != 0) { - snprintf(version, 18, "%d.%d.%d", otrVersion.major, otrVersion.minor, otrVersion.patch); - } else { - snprintf(version, 18, "no version found"); - } - - snprintf(msgBuf, 250, - "The %s file was generated with a different version of Ship of Harkinian.\nOTR version: %s\n\n" - "You must regenerate to be able to play, otherwise the program will exit.\nWould you like to " - "regenerate it now?", - fileName.c_str(), version); - - if (Extractor::ShowYesNoBox("Old OTR File Found", msgBuf) == IDYES) { - std::string installPath = Ship::Context::GetAppBundlePath(); - if (!std::filesystem::exists(installPath + "/assets")) { - Extractor::ShowErrorBox( - "Extractor assets not found", - "Unable to regenerate. Missing assets/ folder needed to generate OTR file.\n\nExiting..."); - exit(1); - } - - Extractor extract; - if (!extract.Run(Ship::Context::GetAppDirectoryPath(appShortName), - isMQ ? RomSearchMode::MQ : RomSearchMode::Vanilla)) { - Extractor::ShowErrorBox("Error", "An error occured, no OTR file was generated.\n\nExiting..."); - exit(1); - } - extract.CallZapd(installPath, Ship::Context::GetAppDirectoryPath(appShortName)); - } else { - exit(1); - } - -#elif defined(__SWITCH__) - Ship::Switch::PrintErrorMessageToScreen("\x1b[2;2HYou've launched the Ship with an old game OTR file." - "\x1b[4;2HPlease regenerate a new game OTR and relaunch." - "\x1b[6;2HPress the Home button to exit..."); -#elif defined(__WIIU__) - OSFatal("You've launched the Ship with an old a game OTR file.\n\n" - "Please generate a game OTR and relaunch.\n\n" - "Press and hold the Power button to shutdown..."); -#endif - } + return ReadPortVersionFromOTR(otrPath); } extern "C" void Messagebox_ShowErrorBox(char* title, char* body) { Extractor::ShowErrorBox(title, body); } -bool IsSubpath(const std::filesystem::path& path, const std::filesystem::path& base) { - auto rel = std::filesystem::relative(path, base); - return !rel.empty() && rel.native()[0] != '.'; -} - -bool PathTestCleanup(FILE* tfile) { - try { - if (std::filesystem::exists("./text.txt")) - std::filesystem::remove("./text.txt"); - if (std::filesystem::exists("./test/")) - std::filesystem::remove("./test/"); - } catch (std::filesystem::filesystem_error const& ex) { return false; } - return true; -} - -void CheckAndCreateModFolder() { - try { - std::string modsPath = Ship::Context::LocateFileAcrossAppDirs("mods", appShortName); - if (!std::filesystem::exists(modsPath)) { - // Create mods folder relative to app dir - modsPath = Ship::Context::GetPathRelativeToAppDirectory("mods", appShortName); - std::string filePath = modsPath + "/custom_mod_files_go_here.txt"; - if (std::filesystem::create_directories(modsPath)) { - std::ofstream(filePath).close(); - } - } - } catch (std::filesystem::filesystem_error const& ex) { - // Couldn't make the folder, continue silently - return; +bool VerifyArchiveVersion(OTRVersion version) { + if (version.major != INT16_MAX && version.major != gBuildVersionMajor) { + return true; } + return false; } extern "C" void InitOTR(int argc, char* argv[]) { -#if !defined(__SWITCH__) && !defined(__WIIU__) - if (argc > 1) { - for (int i = 1; i < argc; i++) { - std::string installPath = Ship::Context::GetAppBundlePath(); - Extractor extract; - if (extract.RunFileStandalone(argv[i])) { - bool doExtract = true; - std::string archive = (extract.IsMasterQuest() ? "oot-mq.o2r" : "oot.o2r"); - if (std::filesystem::exists(Ship::Context::GetAppBundlePath() + "/" + archive)) { - std::string msg = "Archive for current ROM, " + archive + ", already exists. Extract again?"; - doExtract = extract.ShowYesNoBox("Confirm Re-extract", msg.c_str()) == IDYES; - } - if (doExtract) { - extract.CallZapd(installPath, Ship::Context::GetAppDirectoryPath(appShortName)); - } - } else { - std::string msg = "File " + std::string(argv[i]) + " is not a ROM or does not match supported ROMs."; - extract.ShowErrorBox("Incompatible File", msg.c_str()); - } - } - if (Extractor::ShowYesNoBox("Run Ship of Harkinian", "All files have been processed. Run SoH?") != IDYES) { - exit(0); - } - } -#endif OTRGlobals::Instance = new OTRGlobals(); -#ifdef __SWITCH__ - Ship::Switch::Init(Ship::PreInitPhase); -#elif defined(__WIIU__) - Ship::WiiU::Init(appShortName); -#endif - -#ifdef _WIN32 - char* tempVar = getenv("TEMP"); - std::filesystem::path tempPath; - try { - tempPath = std::filesystem::canonical(tempVar); - } catch (std::filesystem::filesystem_error const& ex) { - std::string userPath = getenv("USERPROFILE"); - userPath.append("\\AppData\\Local\\Temp"); - tempPath = std::filesystem::canonical(userPath); - } - wchar_t buffer[MAX_PATH]; - GetModuleFileName(NULL, buffer, _countof(buffer)); - auto ownPath = std::filesystem::canonical(buffer).parent_path(); - if (IsSubpath(ownPath, tempPath)) { - Extractor::ShowErrorBox("Error", "SoH is running in a temp folder. Extract the .zip and run again."); - exit(1); - } - FILE* tfile = fopen("./text.txt", "w"); - std::filesystem::path tfolder = std::filesystem::path("./test/"); - bool error = false; - try { - create_directories(tfolder); - } catch (std::filesystem::filesystem_error const& ex) { error = true; } - if (tfile == NULL || error) { - Extractor::ShowErrorBox( - "Error", "SoH does not have proper file permissions. Please move it to a folder that does and run again."); - PathTestCleanup(tfile); - exit(1); - } - fclose(tfile); - if (!PathTestCleanup(tfile)) { - Extractor::ShowErrorBox( - "Error", "SoH does not have proper file permissions. Please move it to a folder that does and run again."); - exit(1); - } - if (ownPath.string().find("OneDrive") != std::string::npos) { - Extractor::ShowErrorBox( - "Error", - "SoH appears to be in a OneDrive folder, which will cause issues. " - "Please move it to a folder outside of OneDrive, like the root of a drive (e.g. \"C:\\Games\\SoH\")."); - exit(1); - } -#endif - -#if not defined(__SWITCH__) && not defined(__WIIU__) - CheckAndCreateModFolder(); -#endif - const bool ootO2RExists = - std::filesystem::exists(Ship::Context::LocateFileAcrossAppDirs("oot-mq.o2r", appShortName)) || - std::filesystem::exists(Ship::Context::LocateFileAcrossAppDirs("oot.o2r", appShortName)); - - if (!ootO2RExists) { - -#if not defined(__SWITCH__) && not defined(__WIIU__) - std::string installPath = Ship::Context::GetAppBundlePath(); - if (!std::filesystem::exists(installPath + "/assets")) { - Extractor::ShowErrorBox( - "Extractor assets not found", - "No OTR files found. Missing assets/ folder needed to generate OTR file.\n\nExiting..."); - exit(1); - } - - bool generatedOtrIsMQ = false; - if (Extractor::ShowYesNoBox("No OTR Files", "No OTR files found. Generate one now?") == IDYES) { - Extractor extract; - if (!extract.Run(Ship::Context::GetAppDirectoryPath(appShortName))) { - Extractor::ShowErrorBox("Error", "An error occured, no OTR file was generated.\n\nExiting..."); - exit(1); - } - extract.CallZapd(installPath, Ship::Context::GetAppDirectoryPath(appShortName)); - generatedOtrIsMQ = extract.IsMasterQuest(); - } else { - exit(1); - } - if (Extractor::ShowYesNoBox("Extraction Complete", "ROM Extracted. Extract another?") == IDYES) { - Extractor extract; - if (!extract.Run(Ship::Context::GetAppDirectoryPath(appShortName), - generatedOtrIsMQ ? RomSearchMode::Vanilla : RomSearchMode::MQ)) { - Extractor::ShowErrorBox( - "Error", - "An error occured, an OTR file may have been generated by a different step.\n\nContinuing..."); - } else { - extract.CallZapd(installPath, Ship::Context::GetAppDirectoryPath(appShortName)); - } - } - -#elif defined(__SWITCH__) - Ship::Switch::PrintErrorMessageToScreen("\x1b[2;2HYou've launched the Ship without a game OTR file." - "\x1b[4;2HPlease generate a game OTR and relaunch." - "\x1b[6;2HPress the Home button to exit..."); -#elif defined(__WIIU__) - OSFatal("You've launched the Ship without a game OTR file.\n\n" - "Please generate a game OTR and relaunch.\n\n" - "Press and hold the Power button to shutdown..."); -#endif - } - - DetectOTRVersion("oot.o2r", false); - DetectOTRVersion("oot-mq.o2r", true); + OTRGlobals::Instance->RunExtract(argc, argv); OTRGlobals::Instance->Initialize(); CustomMessageManager::Instance = new CustomMessageManager(); + Randomizer::CreateCustomMessages(); ItemTableManager::Instance = new ItemTableManager(); GameInteractor::Instance = new GameInteractor(); SaveManager::Instance = new SaveManager(); @@ -1279,7 +1487,7 @@ extern "C" void InitOTR(int argc, char* argv[]) { conf->RunVersionUpdates(); SohGui::SetupGuiElements(); - ShipInit::InitAll(); + SohGui::SetupMenuElements(); Rando::StaticData::InitHashMaps(); OTRGlobals::Instance->gRandoContext->AddExcludedOptions(); @@ -1338,6 +1546,7 @@ extern "C" void InitOTR(int argc, char* argv[]) { if (CVarGetInteger(CVAR_REMOTE_ANCHOR("Enabled"), 0)) { Anchor::Instance->Enable(); } + ShipInit::InitAll(); } extern "C" void SaveManager_ThreadPoolWait() { diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index e6fbb418105..2d5ebf3ad7d 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -59,20 +59,21 @@ class OTRGlobals { ImFont* defaultFontLarger; ImFont* defaultFontLargest; - ImFont* fontMonoSmall; - ImFont* fontStandard; - ImFont* fontStandardLarger; - ImFont* fontStandardLargest; - ImFont* fontMono; - ImFont* fontMonoLarger; - ImFont* fontMonoLargest; - ImFont* fontJapanese; + ImFont* fontMonoSmall = nullptr; + ImFont* fontStandard = nullptr; + ImFont* fontStandardLarger = nullptr; + ImFont* fontStandardLargest = nullptr; + ImFont* fontMono = nullptr; + ImFont* fontMonoLarger = nullptr; + ImFont* fontMonoLargest = nullptr; + ImFont* fontJapanese = nullptr; OTRGlobals(); ~OTRGlobals(); void ScaleImGui(); void Initialize(); + void RunExtract(int argc, char* argv[]); bool HasMasterQuest(); bool HasOriginal(); uint32_t GetInterpolationFPS(); @@ -83,7 +84,7 @@ class OTRGlobals { bool hasMasterQuest; bool hasOriginal; ImFont* CreateDefaultFontWithSize(float size); - ImFont* CreateFontWithSize(float size, std::string fontPath, bool isJapaneseFont); + ImFont* CreateFontWithSize(float size, std::string fontPath, bool isJapaneseFont = false); }; #endif diff --git a/soh/soh/SohGui/Menu.cpp b/soh/soh/SohGui/Menu.cpp index 1cb28f6209e..cddb68877fc 100644 --- a/soh/soh/SohGui/Menu.cpp +++ b/soh/soh/SohGui/Menu.cpp @@ -559,6 +559,9 @@ void Menu::Draw() { static bool freshOpen = true; void Menu::DrawElement() { + if (OTRGlobals::Instance->fontStandardLargest == nullptr) { + return; + } for (auto& [reason, info] : disabledMap) { info.active = info.evaluation(info); } diff --git a/soh/soh/SohGui/SohGui.cpp b/soh/soh/SohGui/SohGui.cpp index 286beb180a8..de0b8be09e2 100644 --- a/soh/soh/SohGui/SohGui.cpp +++ b/soh/soh/SohGui/SohGui.cpp @@ -104,23 +104,22 @@ UIWidgets::Colors GetMenuThemeColor() { return mSohMenu->GetMenuThemeColor(); } -void SetupGuiElements() { +void SetupMenu() { auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui(); + mSohMenu = std::make_shared(CVAR_WINDOW("Menu"), "Port Menu"); + gui->SetMenu(mSohMenu); - /*mSohMenuBar = std::make_shared(CVAR_MENU_BAR_OPEN, CVarGetInteger(CVAR_MENU_BAR_OPEN, 0)); - gui->SetMenuBar(std::reinterpret_pointer_cast(mSohMenuBar)); + mModalWindow = std::make_shared(CVAR_WINDOW("ModalWindow"), "Modal Window"); + gui->AddGuiWindow(mModalWindow); + mModalWindow->Show(); +} - if (!gui->GetMenuBar() && !CVarGetInteger("gSettings.DisableMenuShortcutNotify", 0)) { -#if defined(__SWITCH__) || defined(__WIIU__) - gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Press - to access enhancements menu"); -#else - gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Press F1 to access enhancements menu"); - gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Press F2 to enable the mouse cursor"); -#endif - }*/ +void SetupMenuElements() { + mSohMenu->AddMenuElements(); +} - mSohMenu = std::make_shared(CVAR_WINDOW("Menu"), "Port Menu"); - gui->SetMenu(mSohMenu); +void SetupGuiElements() { + auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui(); mConsoleWindow = std::make_shared(CVAR_WINDOW("SohConsole"), "Console##SoH", ImVec2(820, 630)); gui->AddGuiWindow(mConsoleWindow); @@ -195,9 +194,6 @@ void SetupGuiElements() { mPlandomizerWindow = std::make_shared(CVAR_WINDOW("PlandomizerEditor"), "Plandomizer Editor", ImVec2(850, 760)); gui->AddGuiWindow(mPlandomizerWindow); - mModalWindow = std::make_shared(CVAR_WINDOW("ModalWindow"), "Modal Window"); - gui->AddGuiWindow(mModalWindow); - mModalWindow->Show(); mNotificationWindow = std::make_shared(CVAR_WINDOW("Notifications"), "Notifications Window"); gui->AddGuiWindow(mNotificationWindow); mNotificationWindow->Show(); @@ -247,6 +243,18 @@ void RegisterPopup(std::string title, std::string message, std::string button1, mModalWindow->RegisterPopup(title, message, button1, button2, button1callback, button2callback); } +size_t PopupsQueued() { + return mModalWindow->PopupsQueued(); +} + +bool DismissPopup(std::string title) { + if (mModalWindow->IsPopupOpen(title)) { + mModalWindow->DismissPopup(); + return true; + } + return false; +} + void ShowRandomizerSettingsMenu() { CVarSetString(CVAR_SETTING("Menu.ActiveHeader"), "Randomizer"); CVarSetString(CVAR_SETTING("Menu.RandomizerSidebarSection"), "General"); diff --git a/soh/soh/SohGui/SohGui.hpp b/soh/soh/SohGui/SohGui.hpp index 941772bba6b..2af5ef0fd5c 100644 --- a/soh/soh/SohGui/SohGui.hpp +++ b/soh/soh/SohGui/SohGui.hpp @@ -32,11 +32,15 @@ namespace SohGui { void SetupHooks(); +void SetupMenu(); +void SetupMenuElements(); void SetupGuiElements(); void Draw(); void Destroy(); void RegisterPopup(std::string title, std::string message, std::string button1 = "OK", std::string button2 = "", std::function button1callback = nullptr, std::function button2callback = nullptr); +size_t PopupsQueued(); +bool DismissPopup(std::string title); void ShowRandomizerSettingsMenu(); void ShowEscMenu(); UIWidgets::Colors GetMenuThemeColor(); diff --git a/soh/soh/SohGui/SohMenu.cpp b/soh/soh/SohGui/SohMenu.cpp index 0a78563db4a..7df4cda66f5 100644 --- a/soh/soh/SohGui/SohMenu.cpp +++ b/soh/soh/SohGui/SohMenu.cpp @@ -81,8 +81,7 @@ SohMenu::SohMenu(const std::string& consoleVariable, const std::string& name) : Menu(consoleVariable, name, 0, UIWidgets::Colors::LightBlue) { } -void SohMenu::InitElement() { - Ship::Menu::InitElement(); +void SohMenu::AddMenuElements() { AddMenuSettings(); AddMenuEnhancements(); AddMenuRandomizer(); @@ -97,6 +96,12 @@ void SohMenu::InitElement() { initFunc(); } + mMenuElementsInitialized = true; +} + +void SohMenu::InitElement() { + Ship::Menu::InitElement(); + disabledMap = { { DISABLE_FOR_NO_VSYNC, { [](disabledInfo& info) -> bool { @@ -172,6 +177,8 @@ void SohMenu::Draw() { } void SohMenu::DrawElement() { - Ship::Menu::DrawElement(); + if (mMenuElementsInitialized) { + Ship::Menu::DrawElement(); + } } } // namespace SohGui diff --git a/soh/soh/SohGui/SohMenu.h b/soh/soh/SohGui/SohMenu.h index 5d24dbcced8..3f147eddc59 100644 --- a/soh/soh/SohGui/SohMenu.h +++ b/soh/soh/SohGui/SohMenu.h @@ -38,6 +38,7 @@ class SohMenu : public Ship::Menu { void AddSidebarEntry(std::string sectionName, std::string sidbarName, uint32_t columnCount); WidgetInfo& AddWidget(WidgetPath& pathInfo, std::string widgetName, WidgetType widgetType); + void AddMenuElements(); void AddMenuSettings(); void AddMenuEnhancements(); void AddMenuDevTools(); @@ -48,6 +49,7 @@ class SohMenu : public Ship::Menu { private: char mGitCommitHashTruncated[8]; bool mIsTaggedVersion; + bool mMenuElementsInitialized = false; }; } // namespace SohGui diff --git a/soh/soh/SohGui/SohMenuRandomizer.cpp b/soh/soh/SohGui/SohMenuRandomizer.cpp index 18912459e30..e3bf45b5e3d 100644 --- a/soh/soh/SohGui/SohMenuRandomizer.cpp +++ b/soh/soh/SohGui/SohMenuRandomizer.cpp @@ -545,7 +545,6 @@ void DrawTricksMenu(WidgetInfo& info) { } void SohMenu::AddMenuRandomizer() { - Randomizer::CreateCustomMessages(); // Add Randomizer Menu AddMenuEntry("Randomizer", CVAR_SETTING("Menu.RandomizerSidebarSection")); diff --git a/soh/soh/SohGui/SohModals.cpp b/soh/soh/SohGui/SohModals.cpp index 339511e756b..8cdd1a09b9b 100644 --- a/soh/soh/SohGui/SohModals.cpp +++ b/soh/soh/SohGui/SohModals.cpp @@ -42,6 +42,7 @@ void SohModalWindow::DrawElement() { modals.erase(modals.begin()); closePopup = false; } + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal(curModal.title_.c_str(), NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | @@ -68,8 +69,8 @@ void SohModalWindow::DrawElement() { } UIWidgets::PopStyleButton(); } + ImGui::EndPopup(); } - ImGui::EndPopup(); } } @@ -78,6 +79,10 @@ void SohModalWindow::RegisterPopup(std::string title, std::string message, std:: modals.push_back({ title, message, button1, button2, button1callback, button2callback }); } +size_t SohModalWindow::PopupsQueued() { + return modals.size(); +} + bool SohModalWindow::IsPopupOpen(std::string title) { return !modals.empty() && modals.at(0).title_ == title; } diff --git a/soh/soh/SohGui/SohModals.h b/soh/soh/SohGui/SohModals.h index 68a9b85106c..fbb12373508 100644 --- a/soh/soh/SohGui/SohModals.h +++ b/soh/soh/SohGui/SohModals.h @@ -16,5 +16,6 @@ class SohModalWindow final : public Ship::GuiWindow { std::function button1callback = nullptr, std::function button2callback = nullptr); bool IsPopupOpen(std::string title); + size_t PopupsQueued(); void DismissPopup(); }; \ No newline at end of file