From df03a64305273e3a54a5d9b036bd307369c98357 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 7 Dec 2024 01:00:46 +0000 Subject: [PATCH] Redirected Install button to custom implementation This currently fades out and closes the game upon accepting the message, it does not yet reboot into the installer menu using --install-dlc. --- UnleashedRecomp/CMakeLists.txt | 1 + UnleashedRecomp/app.cpp | 15 ++++- UnleashedRecomp/app.h | 4 +- UnleashedRecomp/gpu/imgui_snapshot.cpp | 2 +- UnleashedRecomp/gpu/video.cpp | 2 + UnleashedRecomp/install/installer.cpp | 15 ++++- UnleashedRecomp/install/installer.h | 1 + UnleashedRecomp/locale/locale.h | 24 +++++++ .../patches/ui/CTitleStateMenu_patches.cpp | 59 +++++++++++++++-- UnleashedRecomp/ui/fader.cpp | 63 +++++++++++++++++++ UnleashedRecomp/ui/fader.h | 12 ++++ UnleashedRecomp/ui/installer_wizard.cpp | 2 +- UnleashedRecomp/ui/message_window.cpp | 32 ++++++---- UnleashedRecomp/ui/message_window.h | 3 + UnleashedRecomp/user/paths.h | 5 ++ 15 files changed, 219 insertions(+), 21 deletions(-) create mode 100644 UnleashedRecomp/ui/fader.cpp create mode 100644 UnleashedRecomp/ui/fader.h diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index b405c55..d547896 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -114,6 +114,7 @@ set(SWA_UI_CXX_SOURCES "ui/achievement_overlay.cpp" "ui/installer_wizard.cpp" "ui/button_guide.cpp" + "ui/fader.cpp" "ui/message_window.cpp" "ui/options_menu.cpp" "ui/sdl_listener.cpp" diff --git a/UnleashedRecomp/app.cpp b/UnleashedRecomp/app.cpp index 8d5e8b3..479ab14 100644 --- a/UnleashedRecomp/app.cpp +++ b/UnleashedRecomp/app.cpp @@ -1,11 +1,24 @@ #include +#include #include #include #include -bool g_isGameLoaded = false; +bool g_isAppInit = false; +bool g_isMissingDLC = false; + double g_deltaTime; +// CApplication::Ctor +PPC_FUNC_IMPL(__imp__sub_824EB490); +PPC_FUNC(sub_824EB490) +{ + g_isAppInit = true; + g_isMissingDLC = !Installer::checkAllDLC(GetGamePath()); + + __imp__sub_824EB490(ctx, base); +} + // CApplication::Update PPC_FUNC_IMPL(__imp__sub_822C1130); PPC_FUNC(sub_822C1130) diff --git a/UnleashedRecomp/app.h b/UnleashedRecomp/app.h index b9b1bf5..4932a41 100644 --- a/UnleashedRecomp/app.h +++ b/UnleashedRecomp/app.h @@ -1,4 +1,6 @@ #pragma once -extern bool g_isGameLoaded; +extern bool g_isAppInit; +extern bool g_isMissingDLC; + extern double g_deltaTime; diff --git a/UnleashedRecomp/gpu/imgui_snapshot.cpp b/UnleashedRecomp/gpu/imgui_snapshot.cpp index 0b375a2..0c5f5f6 100644 --- a/UnleashedRecomp/gpu/imgui_snapshot.cpp +++ b/UnleashedRecomp/gpu/imgui_snapshot.cpp @@ -222,7 +222,7 @@ void ImFontAtlasSnapshot::GenerateGlyphRanges() } } - if (g_isGameLoaded) + if (g_isAppInit) { for (size_t i = XDBF_LANGUAGE_ENGLISH; i <= XDBF_LANGUAGE_ITALIAN; i++) { diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index 821923a..24b786f 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1796,6 +1797,7 @@ static void DrawImGui() OptionsMenu::Draw(); AchievementOverlay::Draw(); InstallerWizard::Draw(); + Fader::Draw(); MessageWindow::Draw(); ButtonGuide::Draw(); diff --git a/UnleashedRecomp/install/installer.cpp b/UnleashedRecomp/install/installer.cpp index c28783b..16950e0 100644 --- a/UnleashedRecomp/install/installer.cpp +++ b/UnleashedRecomp/install/installer.cpp @@ -18,7 +18,7 @@ static const std::string GameDirectory = "game"; static const std::string DLCDirectory = "dlc"; static const std::string ApotosShamarDirectory = DLCDirectory + "/Apotos & Shamar Adventure Pack"; -static const std::string ChunnanDirectory = DLCDirectory + "/Chunnan Adventure Pack"; +static const std::string ChunnanDirectory = DLCDirectory + "/Chun-nan Adventure Pack"; static const std::string EmpireCityAdabatDirectory = DLCDirectory + "/Empire City & Adabat Adventure Pack"; static const std::string HoloskaDirectory = DLCDirectory + "/Holoska Adventure Pack"; static const std::string MazuriDirectory = DLCDirectory + "/Mazuri Adventure Pack"; @@ -222,6 +222,19 @@ bool Installer::checkDLCInstall(const std::filesystem::path &baseDirectory, DLC } } +bool Installer::checkAllDLC(const std::filesystem::path& baseDirectory) +{ + bool result = true; + + for (int i = 1; i < (int)DLC::Count; i++) + { + if (!checkDLCInstall(baseDirectory, (DLC)i)) + result = false; + } + + return result; +} + bool Installer::computeTotalSize(std::span filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, Journal &journal, uint64_t &totalSize) { for (FilePair pair : filePairs) diff --git a/UnleashedRecomp/install/installer.h b/UnleashedRecomp/install/installer.h index fae798e..39cbf98 100644 --- a/UnleashedRecomp/install/installer.h +++ b/UnleashedRecomp/install/installer.h @@ -73,6 +73,7 @@ struct Installer static bool checkGameInstall(const std::filesystem::path &baseDirectory); static bool checkDLCInstall(const std::filesystem::path &baseDirectory, DLC dlc); + static bool checkAllDLC(const std::filesystem::path &baseDirectory); static bool computeTotalSize(std::span filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, Journal &journal, uint64_t &totalSize); static bool copyFiles(std::span filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function &progressCallback); static bool parseContent(const std::filesystem::path &sourcePath, std::unique_ptr &targetVfs, Journal &journal); diff --git a/UnleashedRecomp/locale/locale.h b/UnleashedRecomp/locale/locale.h index 90b3d36..7847b9b 100644 --- a/UnleashedRecomp/locale/locale.h +++ b/UnleashedRecomp/locale/locale.h @@ -226,6 +226,30 @@ inline static std::unordered_map #include +#include +#include +#include #include +#include +#include #include +static bool g_installMessageFaderBegun = false; +static bool g_installMessageOpen = false; +static int g_installMessageResult = -1; + +static bool ProcessInstallMessage() +{ + if (!g_installMessageOpen) + return false; + + if (g_installMessageFaderBegun) + return true; + + auto& str = g_isMissingDLC + ? Localise("Installer_Message_TitleMissingDLC") + : Localise("Installer_Message_Title"); + + std::array options = { Localise("Common_Yes"), Localise("Common_No") }; + + if (MessageWindow::Open(str, &g_installMessageResult, options, 1) == MSG_CLOSED) + { + switch (g_installMessageResult) + { + case 0: + // TODO: replace ExitProcess with restart method using --install-dlc argument. + Fader::FadeOut(1, []() { ExitProcess(0); }); + g_installMessageFaderBegun = true; + break; + + case 1: + g_installMessageOpen = false; + g_installMessageResult = -1; + break; + } + } + + return true; +} + // SWA::CTitleStateMenu::Update PPC_FUNC_IMPL(__imp__sub_825882B8); PPC_FUNC(sub_825882B8) @@ -10,11 +53,13 @@ PPC_FUNC(sub_825882B8) auto pTitleState = (SWA::CTitleStateBase*)g_memory.Translate(ctx.r3.u32); auto pInputState = SWA::CInputState::GetInstance(); auto& pPadState = pInputState->GetPadState(); + auto isAccepted = pPadState.IsTapped(SWA::eKeyState_A) || pPadState.IsTapped(SWA::eKeyState_Start); auto isOptionsIndex = pTitleState->m_pMember->m_pTitleMenu->m_CursorIndex == 2; + auto isInstallIndex = pTitleState->m_pMember->m_pTitleMenu->m_CursorIndex == 3; - if (!OptionsMenu::s_isVisible && pInputState && isOptionsIndex) + if (!OptionsMenu::s_isVisible && isOptionsIndex) { - if (pPadState.IsTapped(SWA::eKeyState_A) || pPadState.IsTapped(SWA::eKeyState_Start)) + if (isAccepted) { Game_PlaySound("sys_worldmap_window"); Game_PlaySound("sys_worldmap_decide"); @@ -22,11 +67,15 @@ PPC_FUNC(sub_825882B8) OptionsMenu::Open(); } } + else if (isInstallIndex && isAccepted) + { + g_installMessageOpen = true; + } - if (!OptionsMenu::s_isVisible) + if (!OptionsMenu::s_isVisible && !ProcessInstallMessage()) __imp__sub_825882B8(ctx, base); - if (pInputState && isOptionsIndex) + if (isOptionsIndex) { if (OptionsMenu::CanClose() && pPadState.IsTapped(SWA::eKeyState_B)) { @@ -45,4 +94,4 @@ void TitleMenuRemoveStorageDeviceOptionMidAsmHook(PPCRegister& r11) void TitleMenuAddInstallOptionMidAsmHook(PPCRegister& r3) { r3.u32 = 1; -} \ No newline at end of file +} diff --git a/UnleashedRecomp/ui/fader.cpp b/UnleashedRecomp/ui/fader.cpp new file mode 100644 index 0000000..bd3cd03 --- /dev/null +++ b/UnleashedRecomp/ui/fader.cpp @@ -0,0 +1,63 @@ +#include "fader.h" +#include "imgui_utils.h" +#include + +static bool g_isFadeIn; + +static float g_startTime; + +static float g_duration; +static ImU32 g_colour = IM_COL32(0, 0, 0, 255); +static std::function g_endCallback; +static float g_endCallbackDelay; + +void Fader::Draw() +{ + if (!s_isVisible) + return; + + auto time = (ImGui::GetTime() - g_startTime) / g_duration; + auto alpha = 1.0f; + + if (time >= g_duration) + { + if (g_endCallback && time >= g_duration + g_endCallbackDelay) + g_endCallback(); + } + else + { + alpha = g_isFadeIn + ? Lerp(1, 0, time) + : Lerp(0, 1, time); + } + + auto colour = IM_COL32(g_colour & 0xFF, (g_colour >> 8) & 0xFF, (g_colour >> 16) & 0xFF, 255 * alpha); + + ImGui::GetForegroundDrawList()->AddRectFilled({ 0, 0 }, ImGui::GetIO().DisplaySize, colour); +} + +static void DoFade(float duration, std::function endCallback, float endCallbackDelay) +{ + Fader::s_isVisible = true; + g_startTime = ImGui::GetTime(); + g_duration = duration; + g_endCallback = endCallback; + g_endCallbackDelay = endCallbackDelay; +} + +void Fader::SetFadeColour(ImU32 colour) +{ + g_colour = colour; +} + +void Fader::FadeIn(float duration, std::function endCallback, float endCallbackDelay) +{ + g_isFadeIn = true; + DoFade(duration, endCallback, endCallbackDelay); +} + +void Fader::FadeOut(float duration, std::function endCallback, float endCallbackDelay) +{ + g_isFadeIn = false; + DoFade(duration, endCallback, endCallbackDelay); +} diff --git a/UnleashedRecomp/ui/fader.h b/UnleashedRecomp/ui/fader.h new file mode 100644 index 0000000..d513e6a --- /dev/null +++ b/UnleashedRecomp/ui/fader.h @@ -0,0 +1,12 @@ +#pragma once + +class Fader +{ +public: + inline static bool s_isVisible = false; + + static void Draw(); + static void SetFadeColour(ImU32 colour); + static void FadeIn(float duration = 1, std::function endCallback = nullptr, float endCallbackDelay = 0.75f); + static void FadeOut(float duration = 1, std::function endCallback = nullptr, float endCallbackDelay = 0.75f); +}; diff --git a/UnleashedRecomp/ui/installer_wizard.cpp b/UnleashedRecomp/ui/installer_wizard.cpp index 6b0a371..5b5e649 100644 --- a/UnleashedRecomp/ui/installer_wizard.cpp +++ b/UnleashedRecomp/ui/installer_wizard.cpp @@ -1315,7 +1315,7 @@ static void DrawMessagePrompt() bool messageWindowReturned = false; if (g_currentMessagePromptConfirmation) { - std::array YesNoButtons = { "Yes", "No" }; + std::array YesNoButtons = { Localise("Common_Yes"), Localise("Common_No") }; messageWindowReturned = MessageWindow::Open(g_currentMessagePrompt, &g_currentMessageResult, YesNoButtons, 1); } else diff --git a/UnleashedRecomp/ui/message_window.cpp b/UnleashedRecomp/ui/message_window.cpp index b4f968e..988e334 100644 --- a/UnleashedRecomp/ui/message_window.cpp +++ b/UnleashedRecomp/ui/message_window.cpp @@ -65,6 +65,9 @@ public: { case SDL_KEYDOWN: { + if (g_isAppInit) + break; + switch (event->key.keysym.scancode) { case SDL_SCANCODE_UP: @@ -89,8 +92,14 @@ public: } case SDL_MOUSEBUTTONDOWN: + { + if (g_isAppInit) + break; + g_isAccepted = true; + break; + } case SDL_CONTROLLERBUTTONDOWN: { @@ -177,7 +186,7 @@ bool DrawContainer(float appearTime, ImVec2 centre, ImVec2 max, bool isForegroun g_foregroundCount++; if (isForeground) - drawList->AddRectFilled({ 0.0f, 0.0f }, ImGui::GetIO().DisplaySize, IM_COL32(0, 0, 0, 223 * (g_foregroundCount ? 1 : alpha))); + drawList->AddRectFilled({ 0.0f, 0.0f }, ImGui::GetIO().DisplaySize, IM_COL32(0, 0, 0, 190 * (g_foregroundCount ? 1 : alpha))); DrawPauseContainer(g_upWindow.get(), _min, _max, alpha); @@ -264,8 +273,8 @@ void MessageWindow::Draw() auto textMarginX = Scale(37); auto textMarginY = Scale(45); - bool isController = hid::detail::g_inputDevice == hid::detail::EInputDevice::Controller; - bool isKeyboard = hid::detail::g_inputDevice == hid::detail::EInputDevice::Keyboard; + bool isController = g_isAppInit ? true : hid::detail::g_inputDevice == hid::detail::EInputDevice::Controller; + bool isKeyboard = g_isAppInit ? false : hid::detail::g_inputDevice == hid::detail::EInputDevice::Keyboard; if (DrawContainer(g_appearTime, centre, { textSize.x / 2 + textMarginX, textSize.y / 2 + textMarginY }, !g_isControlsVisible)) { @@ -328,14 +337,6 @@ void MessageWindow::Draw() g_upWasHeld = upIsHeld; g_downWasHeld = downIsHeld; - if (g_isDeclined) - { - g_result = g_cancelButtonIndex; - - Game_PlaySound("sys_actstg_pausecansel"); - MessageWindow::Close(); - } - if (isController) { std::array buttons = @@ -350,6 +351,14 @@ void MessageWindow::Draw() { ButtonGuide::Open(Button(Localise("Common_Select"), EButtonIcon::Enter)); } + + if (g_isDeclined) + { + g_result = g_cancelButtonIndex; + + Game_PlaySound("sys_actstg_pausecansel"); + MessageWindow::Close(); + } } else { @@ -441,6 +450,7 @@ bool MessageWindow::Open(std::string text, int* result, std::span b *result = g_result; + // Returns true when the message window is closed. return !g_isAwaitingResult; } diff --git a/UnleashedRecomp/ui/message_window.h b/UnleashedRecomp/ui/message_window.h index 0ab32c0..5e46648 100644 --- a/UnleashedRecomp/ui/message_window.h +++ b/UnleashedRecomp/ui/message_window.h @@ -1,5 +1,8 @@ #pragma once +#define MSG_OPEN (false) +#define MSG_CLOSED (true) + class MessageWindow { public: diff --git a/UnleashedRecomp/user/paths.h b/UnleashedRecomp/user/paths.h index c10e045..c1a4bf4 100644 --- a/UnleashedRecomp/user/paths.h +++ b/UnleashedRecomp/user/paths.h @@ -2,6 +2,11 @@ #define USER_DIRECTORY "SWA" +static std::filesystem::path GetGamePath() +{ + return std::filesystem::current_path(); +} + static std::filesystem::path GetUserPath() { if (std::filesystem::exists("portable.txt"))