Implemented accessing options menu via pause and title screen

This commit is contained in:
Hyper 2024-11-17 15:55:17 +00:00
parent 91db1eae2b
commit e14439626d
14 changed files with 336 additions and 23 deletions

View file

@ -65,6 +65,8 @@ set(SWA_HID_CXX_SOURCES
) )
set(SWA_PATCHES_CXX_SOURCES set(SWA_PATCHES_CXX_SOURCES
"patches/ui/CHudPause_patches.cpp"
"patches/ui/CTitleMenu_patches.cpp"
"patches/ui/frontend_listener.cpp" "patches/ui/frontend_listener.cpp"
"patches/fps_patches.cpp" "patches/fps_patches.cpp"
"patches/misc_patches.cpp" "patches/misc_patches.cpp"

View file

@ -20,7 +20,7 @@ namespace Hedgehog::Base
const size_t memSize = GetMemorySize(in_Length); const size_t memSize = GetMemorySize(in_Length);
const size_t memSizeAligned = GetMemorySizeAligned(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->RefCount = 1;
pHolder->Length = (uint16_t)in_Length; pHolder->Length = (uint16_t)in_Length;

View file

@ -39,6 +39,8 @@
#include "SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h" #include "SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h"
#include "SWA/Player/Character/EvilSonic/EvilSonic.h" #include "SWA/Player/Character/EvilSonic/EvilSonic.h"
#include "SWA/Player/Character/EvilSonic/EvilSonicContext.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/ApplicationDocument.h"
#include "SWA/System/GameDocument.h" #include "SWA/System/GameDocument.h"
#include "SWA/System/InputState.h" #include "SWA/System/InputState.h"

View file

@ -24,6 +24,6 @@ namespace SWA::Player
SWA_INSERT_PADDING(0x14D); SWA_INSERT_PADDING(0x14D);
bool m_IsShown; bool m_IsShown;
bool m_IsVisible; bool m_IsVisible;
EGuideType m_GuideType; be<EGuideType> m_GuideType;
}; };
} }

View file

@ -0,0 +1,13 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CTitleMenu
{
public:
SWA_INSERT_PADDING(0x44);
be<uint32_t> m_CursorIndex;
};
}

View file

@ -0,0 +1,22 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CTitleStateBase // : Hedgehog::Universe::TStateMachine<SWA::CTitleManager>::TState
{
public:
class CMember
{
public:
SWA_INSERT_PADDING(0x1E8);
xpointer<CTitleMenu> m_pTitleMenu;
};
SWA_INSERT_PADDING(0x08);
xpointer<CMember> m_pMember;
SWA_INSERT_PADDING(0x5C);
be<uint32_t> m_State;
};
}

View file

@ -30,8 +30,8 @@ public:
CONFIG_DEFINE_ENUM("Video", EGraphicsAPI, GraphicsAPI, EGraphicsAPI::D3D12); CONFIG_DEFINE_ENUM("Video", EGraphicsAPI, GraphicsAPI, EGraphicsAPI::D3D12);
CONFIG_DEFINE("Video", int32_t, WindowX, WINDOWPOS_CENTRED); CONFIG_DEFINE("Video", int32_t, WindowX, WINDOWPOS_CENTRED);
CONFIG_DEFINE("Video", int32_t, WindowY, WINDOWPOS_CENTRED); CONFIG_DEFINE("Video", int32_t, WindowY, WINDOWPOS_CENTRED);
CONFIG_DEFINE_LOCALISED("Video", int32_t, WindowWidth, 1280); CONFIG_DEFINE("Video", int32_t, WindowWidth, 1280);
CONFIG_DEFINE_LOCALISED("Video", int32_t, WindowHeight, 720); CONFIG_DEFINE("Video", int32_t, WindowHeight, 720);
CONFIG_DEFINE_ENUM("Video", EWindowState, WindowState, EWindowState::Normal); CONFIG_DEFINE_ENUM("Video", EWindowState, WindowState, EWindowState::Normal);
CONFIG_DEFINE_CALLBACK("Video", float, ResolutionScale, 1.0f, CONFIG_DEFINE_CALLBACK("Video", float, ResolutionScale, 1.0f,

View file

@ -2,6 +2,12 @@
#include "config_detail.h" #include "config_detail.h"
#define CONFIG_DEFINE_LOCALE(name) \
inline static std::unordered_map<ELanguage, std::string> g_##name##_locale =
#define CONFIG_DEFINE_ENUM_LOCALE(type) \
inline static std::unordered_map<ELanguage, std::unordered_map<type, std::string>> g_##type##_locale =
CONFIG_DEFINE_ENUM_LOCALE(bool) CONFIG_DEFINE_ENUM_LOCALE(bool)
{ {
{ {
@ -135,14 +141,9 @@ CONFIG_DEFINE_LOCALE(WerehogBattleMusic)
{ ELanguage::English, "Werehog Battle Theme" } { ELanguage::English, "Werehog Battle Theme" }
}; };
CONFIG_DEFINE_LOCALE(WindowWidth) CONFIG_DEFINE_LOCALE(WindowSize)
{ {
{ ELanguage::English, "Window Width" } { ELanguage::English, "Window Size" }
};
CONFIG_DEFINE_LOCALE(WindowHeight)
{
{ ELanguage::English, "Window Height" }
}; };
CONFIG_DEFINE_LOCALE(ResolutionScale) CONFIG_DEFINE_LOCALE(ResolutionScale)

View file

@ -0,0 +1,133 @@
#include <cpu/guest_code.h>
#include <kernel/function.h>
#include <api/SWA.h>
#include <ui/options_menu.h>
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<int>(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<uint32_t>*)g_memory.Translate(pThis + 0x190);
auto pauseType = *(be<uint32_t>*)g_memory.Translate(pThis + 0x18C);
auto cursorIndex = *(be<uint32_t>*)g_memory.Translate(4 * (*(be<uint32_t>*)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<uint32_t>*)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<uint32_t>*)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<uint32_t>*)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<int>(0x824AFD28, ctx.r3.u32, 0, 0, 0, 1);
}
}
}

View file

@ -0,0 +1,29 @@
#include <cpu/guest_code.h>
#include <api/SWA.h>
#include <ui/options_menu.h>
// 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();
}
}

View file

@ -2,6 +2,7 @@
#include "kernel/memory.h" #include "kernel/memory.h"
#include "ui/sdl_listener.h" #include "ui/sdl_listener.h"
#include "ui/options_menu.h"
class FrontendListener : public SDLEventListener class FrontendListener : public SDLEventListener
{ {
@ -10,6 +11,9 @@ class FrontendListener : public SDLEventListener
public: public:
void OnSDLEvent(SDL_Event* event) override void OnSDLEvent(SDL_Event* event) override
{ {
if (OptionsMenu::s_isVisible)
return;
switch (event->type) switch (event->type)
{ {
case SDL_KEYDOWN: case SDL_KEYDOWN:

View file

@ -414,32 +414,54 @@ static void DrawConfigOptions()
void OptionsMenu::Draw() void OptionsMenu::Draw()
{ {
g_callbackDataIndex = 0; if (!s_isVisible)
return;
g_callbackDataIndex = 0;
auto& res = ImGui::GetIO().DisplaySize; auto& res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList(); auto drawList = ImGui::GetForegroundDrawList();
//drawList->AddRectFilled({ 0.0f, 0.0f }, res, IM_COL32(0, 0, 0, 223)); if (s_isDimBackground)
drawList->AddRectFilled({ 0.0f, 0.0f }, res, IM_COL32(0, 0, 0, 127));
//*(bool*)g_memory.Translate(0x8328BB26) = false;
DrawScanlineBars(); DrawScanlineBars();
constexpr float CONTAINER_POS_X = 236.0f; constexpr float CONTAINER_POS_X = 236.0f;
constexpr float CONTAINER_POS_Y = 118.0f; constexpr float CONTAINER_POS_Y = 118.0f;
ImVec2 min = { Scale(AlignToNextGrid(CONTAINER_POS_X)), Scale(AlignToNextGrid(CONTAINER_POS_Y)) }; 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)) }; ImVec2 max = { Scale(AlignToNextGrid(1280.0f - CONTAINER_POS_X)), Scale(AlignToNextGrid(720.0f - CONTAINER_POS_Y)) };
DrawContainer(min, max); DrawContainer(min, max);
DrawCategories(); DrawCategories();
DrawConfigOptions(); DrawConfigOptions();
// Pop clip rect from DrawCategories // Pop clip rect from DrawCategories
drawList->PopClipRect(); drawList->PopClipRect();
// Pop clip rect from DrawContainer // Pop clip rect from DrawContainer
drawList->PopClipRect(); 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.
}

View file

@ -2,6 +2,12 @@
struct OptionsMenu struct OptionsMenu
{ {
public:
inline static bool s_isVisible = false;
inline static bool s_isDimBackground = false;
static void Init(); static void Init();
static void Draw(); static void Draw();
static void Open(bool stage = false);
static void Close(bool stage = false);
}; };

View file

@ -422,3 +422,82 @@ registers = ["r7"]
name = "LoadingScreenSpeedFixMidAsmHook" name = "LoadingScreenSpeedFixMidAsmHook"
address = 0x824DAB60 address = 0x824DAB60
registers = ["r4"] 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