From e14439626d4da5bd94665fb9327d315470ef2d2b Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sun, 17 Nov 2024 15:55:17 +0000 Subject: [PATCH] Implemented accessing options menu via pause and title screen --- UnleashedRecomp/CMakeLists.txt | 2 + .../Base/Type/detail/hhStringHolder.inl | 2 +- UnleashedRecomp/api/SWA.h | 2 + .../Character/EvilSonic/Hud/EvilHudGuide.h | 2 +- .../api/SWA/System/GameMode/Title/TitleMenu.h | 13 ++ .../System/GameMode/Title/TitleStateBase.h | 22 +++ UnleashedRecomp/cfg/config.h | 4 +- UnleashedRecomp/cfg/config_locale.h | 15 +- .../patches/ui/CHudPause_patches.cpp | 133 ++++++++++++++++++ .../patches/ui/CTitleMenu_patches.cpp | 29 ++++ .../patches/ui/frontend_listener.h | 4 + UnleashedRecomp/ui/options_menu.cpp | 46 ++++-- UnleashedRecomp/ui/options_menu.h | 6 + UnleashedRecompLib/config/SWA.toml | 79 +++++++++++ 14 files changed, 336 insertions(+), 23 deletions(-) create mode 100644 UnleashedRecomp/api/SWA/System/GameMode/Title/TitleMenu.h create mode 100644 UnleashedRecomp/api/SWA/System/GameMode/Title/TitleStateBase.h create mode 100644 UnleashedRecomp/patches/ui/CHudPause_patches.cpp create mode 100644 UnleashedRecomp/patches/ui/CTitleMenu_patches.cpp diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index ba61cd96..ad2abc6f 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -65,6 +65,8 @@ set(SWA_HID_CXX_SOURCES ) set(SWA_PATCHES_CXX_SOURCES + "patches/ui/CHudPause_patches.cpp" + "patches/ui/CTitleMenu_patches.cpp" "patches/ui/frontend_listener.cpp" "patches/fps_patches.cpp" "patches/misc_patches.cpp" diff --git a/UnleashedRecomp/api/Hedgehog/Base/Type/detail/hhStringHolder.inl b/UnleashedRecomp/api/Hedgehog/Base/Type/detail/hhStringHolder.inl index 235b9426..5fede682 100644 --- a/UnleashedRecomp/api/Hedgehog/Base/Type/detail/hhStringHolder.inl +++ b/UnleashedRecomp/api/Hedgehog/Base/Type/detail/hhStringHolder.inl @@ -20,7 +20,7 @@ namespace Hedgehog::Base const size_t memSize = GetMemorySize(in_Length); const size_t memSizeAligned = GetMemorySizeAligned(in_Length); - SStringHolder* pHolder = (SStringHolder*)__HH_ALLOC(memSizeAligned); + auto pHolder = (SStringHolder*)__HH_ALLOC(memSizeAligned); pHolder->RefCount = 1; pHolder->Length = (uint16_t)in_Length; diff --git a/UnleashedRecomp/api/SWA.h b/UnleashedRecomp/api/SWA.h index 555c49c8..17baf177 100644 --- a/UnleashedRecomp/api/SWA.h +++ b/UnleashedRecomp/api/SWA.h @@ -39,6 +39,8 @@ #include "SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h" #include "SWA/Player/Character/EvilSonic/EvilSonic.h" #include "SWA/Player/Character/EvilSonic/EvilSonicContext.h" +#include "SWA/System/GameMode/Title/TitleMenu.h" +#include "SWA/System/GameMode/Title/TitleStateBase.h" #include "SWA/System/ApplicationDocument.h" #include "SWA/System/GameDocument.h" #include "SWA/System/InputState.h" diff --git a/UnleashedRecomp/api/SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h b/UnleashedRecomp/api/SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h index 090d5b09..8c4d0e10 100644 --- a/UnleashedRecomp/api/SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h +++ b/UnleashedRecomp/api/SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h @@ -24,6 +24,6 @@ namespace SWA::Player SWA_INSERT_PADDING(0x14D); bool m_IsShown; bool m_IsVisible; - EGuideType m_GuideType; + be m_GuideType; }; } diff --git a/UnleashedRecomp/api/SWA/System/GameMode/Title/TitleMenu.h b/UnleashedRecomp/api/SWA/System/GameMode/Title/TitleMenu.h new file mode 100644 index 00000000..a533e260 --- /dev/null +++ b/UnleashedRecomp/api/SWA/System/GameMode/Title/TitleMenu.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace SWA +{ + class CTitleMenu + { + public: + SWA_INSERT_PADDING(0x44); + be m_CursorIndex; + }; +} diff --git a/UnleashedRecomp/api/SWA/System/GameMode/Title/TitleStateBase.h b/UnleashedRecomp/api/SWA/System/GameMode/Title/TitleStateBase.h new file mode 100644 index 00000000..6782f3b5 --- /dev/null +++ b/UnleashedRecomp/api/SWA/System/GameMode/Title/TitleStateBase.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace SWA +{ + class CTitleStateBase // : Hedgehog::Universe::TStateMachine::TState + { + public: + class CMember + { + public: + SWA_INSERT_PADDING(0x1E8); + xpointer m_pTitleMenu; + }; + + SWA_INSERT_PADDING(0x08); + xpointer m_pMember; + SWA_INSERT_PADDING(0x5C); + be m_State; + }; +} diff --git a/UnleashedRecomp/cfg/config.h b/UnleashedRecomp/cfg/config.h index cce1bc84..82ee9dd9 100644 --- a/UnleashedRecomp/cfg/config.h +++ b/UnleashedRecomp/cfg/config.h @@ -30,8 +30,8 @@ public: CONFIG_DEFINE_ENUM("Video", EGraphicsAPI, GraphicsAPI, EGraphicsAPI::D3D12); CONFIG_DEFINE("Video", int32_t, WindowX, WINDOWPOS_CENTRED); CONFIG_DEFINE("Video", int32_t, WindowY, WINDOWPOS_CENTRED); - CONFIG_DEFINE_LOCALISED("Video", int32_t, WindowWidth, 1280); - CONFIG_DEFINE_LOCALISED("Video", int32_t, WindowHeight, 720); + CONFIG_DEFINE("Video", int32_t, WindowWidth, 1280); + CONFIG_DEFINE("Video", int32_t, WindowHeight, 720); CONFIG_DEFINE_ENUM("Video", EWindowState, WindowState, EWindowState::Normal); CONFIG_DEFINE_CALLBACK("Video", float, ResolutionScale, 1.0f, diff --git a/UnleashedRecomp/cfg/config_locale.h b/UnleashedRecomp/cfg/config_locale.h index cbef42d8..92369ca1 100644 --- a/UnleashedRecomp/cfg/config_locale.h +++ b/UnleashedRecomp/cfg/config_locale.h @@ -2,6 +2,12 @@ #include "config_detail.h" +#define CONFIG_DEFINE_LOCALE(name) \ + inline static std::unordered_map g_##name##_locale = + +#define CONFIG_DEFINE_ENUM_LOCALE(type) \ + inline static std::unordered_map> g_##type##_locale = + CONFIG_DEFINE_ENUM_LOCALE(bool) { { @@ -135,14 +141,9 @@ CONFIG_DEFINE_LOCALE(WerehogBattleMusic) { ELanguage::English, "Werehog Battle Theme" } }; -CONFIG_DEFINE_LOCALE(WindowWidth) +CONFIG_DEFINE_LOCALE(WindowSize) { - { ELanguage::English, "Window Width" } -}; - -CONFIG_DEFINE_LOCALE(WindowHeight) -{ - { ELanguage::English, "Window Height" } + { ELanguage::English, "Window Size" } }; CONFIG_DEFINE_LOCALE(ResolutionScale) diff --git a/UnleashedRecomp/patches/ui/CHudPause_patches.cpp b/UnleashedRecomp/patches/ui/CHudPause_patches.cpp new file mode 100644 index 00000000..6e6d6bc2 --- /dev/null +++ b/UnleashedRecomp/patches/ui/CHudPause_patches.cpp @@ -0,0 +1,133 @@ +#include +#include +#include +#include + +bool m_isOptionsFromPause = false; + +void CHudPauseAddOptionsItemMidAsmHook(PPCRegister& pThis) +{ + auto pStrMemory = __HH_ALLOC(8); + + auto menu = Hedgehog::Base::CSharedString("TopMenu"); + auto name = Hedgehog::Base::CSharedString("option"); + + // TODO: replace with wrapper to put these into guest memory. + memcpy(pStrMemory, &menu, 4); + memcpy((void*)((size_t)pStrMemory + 4), &name, 4); + + GuestToHostFunction(0x824AE690, pThis.u32, pStrMemory, (void*)((size_t)pStrMemory + 4)); + + __HH_FREE(pStrMemory); +} + +bool InjectOptionsBehaviour(uint32_t pThis, uint32_t count, uint32_t exitType = 2, uint32_t transitionType = 2) +{ + auto status = *(be*)g_memory.Translate(pThis + 0x190); + auto pauseType = *(be*)g_memory.Translate(pThis + 0x18C); + auto cursorIndex = *(be*)g_memory.Translate(4 * (*(be*)g_memory.Translate(pThis + 0x19C) + 0x68) + pThis); + + /* + 0 ---- Undefined + 1 ---- Status + 2 ---- Return to Previous Area + 3 ---- Inventory + 4 ---- Skills + 5 ---- Go to the Lab + 6 ---- Return to World Map + 7 ---- Undefined + 8 ---- Restart Stage + 9 ---- Continue Stage + <=10 - Undefined + */ + auto pExitType = (be*)g_memory.Translate(pThis + 0x188); + + /* + 0 --- Undefined + 1 --- Unknown menu + 2 --- Quit menu + 3 --- Pause menu? + 4 --- Undefined + 5 --- Make cursor small? + 6 --- Hide UI and ignore face buttons + 7 --- Stop updating pause menu + 8 --- Hide UI (apart from pause header) and ignore face buttons + <=9 - Stop updating pause menu + */ + auto pTransitionType = (be*)g_memory.Translate(pThis + 0x194); + + if (status == 1) + { + if (cursorIndex == count - 2) + { + OptionsMenu::Open(pauseType); + m_isOptionsFromPause = true; + + *pExitType = 0; + *pTransitionType = 6; + + return true; + } + else if (cursorIndex == count - 1) + { + *pExitType = exitType; + *pTransitionType = transitionType; + + return true; + } + } + + return false; +} + +bool CHudPauseItemCountMidAsmHook(PPCRegister& pThis, PPCRegister& count) +{ + count.u32 += 1; + + return InjectOptionsBehaviour(pThis.u32, count.u32); +} + +bool CHudPauseHubItemCountMidAsmHook(PPCRegister& pThis, PPCRegister& count) +{ + count.u32 += 1; + + return InjectOptionsBehaviour(pThis.u32, count.u32, 2, 6); +} + +bool CHudPauseMiscItemCountMidAsmHook(PPCRegister& count) +{ + if (count.u32 < 3) + return true; + + return false; +} + +bool CHudPauseMiscInjectOptionsMidAsmHook(PPCRegister& pThis) +{ + return InjectOptionsBehaviour(pThis.u32, 3); +} + +// SWA::CHudPause::Update +PPC_FUNC_IMPL(__imp__sub_824B0930); +PPC_FUNC(sub_824B0930) +{ + if (!OptionsMenu::s_isVisible || !m_isOptionsFromPause) + { + __imp__sub_824B0930(ctx, base); + return; + } + + auto pauseType = *(be*)g_memory.Translate(ctx.r3.u32 + 0x18C); + + if (auto pInputState = SWA::CInputState::GetInstance()) + { + // TODO: disable Start button closing menu. + if (pInputState->GetPadState().IsTapped(SWA::eKeyState_B)) + { + OptionsMenu::Close(pauseType); + m_isOptionsFromPause = false; + + GuestToHostFunction(0x824AFD28, ctx.r3.u32, 0, 0, 0, 1); + } + } +} diff --git a/UnleashedRecomp/patches/ui/CTitleMenu_patches.cpp b/UnleashedRecomp/patches/ui/CTitleMenu_patches.cpp new file mode 100644 index 00000000..4f45f36b --- /dev/null +++ b/UnleashedRecomp/patches/ui/CTitleMenu_patches.cpp @@ -0,0 +1,29 @@ +#include +#include +#include + +// SWA::CTitleStateMenu::Update +PPC_FUNC_IMPL(__imp__sub_825882B8); +PPC_FUNC(sub_825882B8) +{ + auto pTitleState = (SWA::CTitleStateBase*)g_memory.Translate(ctx.r3.u32); + auto pInputState = SWA::CInputState::GetInstance(); + auto isOptionsIndex = pTitleState->m_pMember->m_pTitleMenu->m_CursorIndex == 2; + + if (pInputState && isOptionsIndex) + { + // TODO: play sys_worldmap_decide. + if (pInputState->GetPadState().IsTapped(SWA::eKeyState_A)) + OptionsMenu::Open(); + } + + if (!OptionsMenu::s_isVisible) + __imp__sub_825882B8(ctx, base); + + if (pInputState && isOptionsIndex) + { + // TODO: play sys_worldmap_cancel (could be "cansel" instead). + if (pInputState->GetPadState().IsTapped(SWA::eKeyState_B)) + OptionsMenu::Close(); + } +} diff --git a/UnleashedRecomp/patches/ui/frontend_listener.h b/UnleashedRecomp/patches/ui/frontend_listener.h index da89c737..eb149dd6 100644 --- a/UnleashedRecomp/patches/ui/frontend_listener.h +++ b/UnleashedRecomp/patches/ui/frontend_listener.h @@ -2,6 +2,7 @@ #include "kernel/memory.h" #include "ui/sdl_listener.h" +#include "ui/options_menu.h" class FrontendListener : public SDLEventListener { @@ -10,6 +11,9 @@ class FrontendListener : public SDLEventListener public: void OnSDLEvent(SDL_Event* event) override { + if (OptionsMenu::s_isVisible) + return; + switch (event->type) { case SDL_KEYDOWN: diff --git a/UnleashedRecomp/ui/options_menu.cpp b/UnleashedRecomp/ui/options_menu.cpp index 86ff3fbb..61ff753a 100644 --- a/UnleashedRecomp/ui/options_menu.cpp +++ b/UnleashedRecomp/ui/options_menu.cpp @@ -414,32 +414,54 @@ static void DrawConfigOptions() void OptionsMenu::Draw() { - g_callbackDataIndex = 0; + if (!s_isVisible) + return; + g_callbackDataIndex = 0; + auto& res = ImGui::GetIO().DisplaySize; auto drawList = ImGui::GetForegroundDrawList(); - - //drawList->AddRectFilled({ 0.0f, 0.0f }, res, IM_COL32(0, 0, 0, 223)); - - //*(bool*)g_memory.Translate(0x8328BB26) = false; - + + if (s_isDimBackground) + drawList->AddRectFilled({ 0.0f, 0.0f }, res, IM_COL32(0, 0, 0, 127)); + DrawScanlineBars(); - + constexpr float CONTAINER_POS_X = 236.0f; constexpr float CONTAINER_POS_Y = 118.0f; ImVec2 min = { Scale(AlignToNextGrid(CONTAINER_POS_X)), Scale(AlignToNextGrid(CONTAINER_POS_Y)) }; ImVec2 max = { Scale(AlignToNextGrid(1280.0f - CONTAINER_POS_X)), Scale(AlignToNextGrid(720.0f - CONTAINER_POS_Y)) }; - + DrawContainer(min, max); - + DrawCategories(); - + DrawConfigOptions(); - + // Pop clip rect from DrawCategories drawList->PopClipRect(); - + // Pop clip rect from DrawContainer drawList->PopClipRect(); } + +void OptionsMenu::Open(bool stage) +{ + s_isVisible = true; + s_isDimBackground = stage; + + *(bool*)g_memory.Translate(0x8328BB26) = false; + + // TODO: animate Miles Electric in if we're in a stage. +} + +void OptionsMenu::Close(bool stage) +{ + s_isVisible = false; + s_isDimBackground = stage; + + *(bool*)g_memory.Translate(0x8328BB26) = true; + + // TODO: animate Miles Electric out if we're in a stage. +} diff --git a/UnleashedRecomp/ui/options_menu.h b/UnleashedRecomp/ui/options_menu.h index 80287da9..45463dad 100644 --- a/UnleashedRecomp/ui/options_menu.h +++ b/UnleashedRecomp/ui/options_menu.h @@ -2,6 +2,12 @@ struct OptionsMenu { +public: + inline static bool s_isVisible = false; + inline static bool s_isDimBackground = false; + static void Init(); static void Draw(); + static void Open(bool stage = false); + static void Close(bool stage = false); }; diff --git a/UnleashedRecompLib/config/SWA.toml b/UnleashedRecompLib/config/SWA.toml index 8d2fe1a3..c419ed55 100644 --- a/UnleashedRecompLib/config/SWA.toml +++ b/UnleashedRecompLib/config/SWA.toml @@ -422,3 +422,82 @@ registers = ["r7"] name = "LoadingScreenSpeedFixMidAsmHook" address = 0x824DAB60 registers = ["r4"] + +# World Map Pause Menu +[[midasm_hook]] +name = "CHudPauseAddOptionsItemMidAsmHook" +address = 0x824AF140 +registers = ["r31"] + +# Village Pause Menu +[[midasm_hook]] +name = "CHudPauseAddOptionsItemMidAsmHook" +address = 0x824AF430 +registers = ["r31"] + +# Stage Pause Menu +[[midasm_hook]] +name = "CHudPauseAddOptionsItemMidAsmHook" +address = 0x824AF988 +registers = ["r31"] + +# Hub Pause Menu +[[midasm_hook]] +name = "CHudPauseAddOptionsItemMidAsmHook" +address = 0x824AFB20 +registers = ["r31"] + +# Misc Pause Menu +[[midasm_hook]] +name = "CHudPauseAddOptionsItemMidAsmHook" +address = 0x824AFCC8 +registers = ["r31"] + +# World Map Pause Menu +[[midasm_hook]] +name = "CHudPauseItemCountMidAsmHook" +address = 0x824B02F8 +registers = ["r3", "r11"] +return_on_true = true + +# Village Pause Menu +[[midasm_hook]] +name = "CHudPauseItemCountMidAsmHook" +address = 0x824B04AC +registers = ["r31", "r10"] +return_on_true = true + +# Stage Pause Menu +[[midasm_hook]] +name = "CHudPauseItemCountMidAsmHook" +address = 0x824B061C +registers = ["r3", "r11"] +return_on_true = true + +# Hub Pause Menu +[[midasm_hook]] +name = "CHudPauseHubItemCountMidAsmHook" +address = 0x824B07C4 +registers = ["r3", "r10"] +return_on_true = true + +# Misc Pause Menu Index Wrap-around +[[midasm_hook]] +name = "CHudPauseItemCountMidAsmHook" +address = 0x824B08A8 +registers = ["r3", "r11"] +return_on_true = true + +# Misc Pause Menu Index Comparison +[[midasm_hook]] +name = "CHudPauseMiscItemCountMidAsmHook" +address = 0x824B08B0 +registers = ["r11"] +jump_address_on_true = 0x824B08C0 + +# Misc Pause Menu Inject Options Behaviour +[[midasm_hook]] +name = "CHudPauseMiscInjectOptionsMidAsmHook" +address = 0x824B08C0 +registers = ["r3"] +return_on_true = true