From b165c219a6c51e42a8ced96450cccfeba5a3293c Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 5 Aug 2025 06:30:58 +0100 Subject: [PATCH 01/52] Dynamic UI aspect ratio and controller icons support (WIP) Lots of stuff here, just pushing everything to keep my changes focused. Will take a cautionary glance at everything before a PR is considered. Unfinished work ahead!! --- MarathonRecomp/api/CSD/Manager/csdmProject.h | 15 +- MarathonRecomp/api/CSD/Manager/csdmScene.h | 83 +- MarathonRecomp/api/Marathon.h | 19 + MarathonRecomp/api/Marathon.inl | 72 + MarathonRecomp/api/Sonicteam/AppMarathon.h | 8 +- MarathonRecomp/api/Sonicteam/AppMarathon.inl | 7 + .../api/Sonicteam/Camera/CameraMode.h | 6 +- .../api/Sonicteam/Camera/Cameraman.h | 6 +- .../api/Sonicteam/Camera/SonicCamera.h | 9 +- MarathonRecomp/api/Sonicteam/CsdLink.h | 16 + MarathonRecomp/api/Sonicteam/CsdManager.h | 17 + MarathonRecomp/api/Sonicteam/CsdManager.inl | 7 + MarathonRecomp/api/Sonicteam/CsdObject.h | 15 + MarathonRecomp/api/Sonicteam/DocMarathonImp.h | 4 + MarathonRecomp/api/Sonicteam/GameImp.h | 4 +- .../api/Sonicteam/HUDButtonWindow.h | 16 + MarathonRecomp/api/Sonicteam/HUDPopupScreen.h | 24 + MarathonRecomp/api/Sonicteam/HUDRaderMap.h | 8 + MarathonRecomp/api/Sonicteam/HudTextParts.h | 29 + MarathonRecomp/api/Sonicteam/MyTexture.h | 13 + .../api/Sonicteam/SoX/ApplicationXenon.h | 17 + .../api/Sonicteam/SoX/ApplicationXenon.inl | 7 + .../api/Sonicteam/SoX/Engine/Application.h | 13 + .../SoX/Graphics/Xenon/TextureXenon.h | 16 + MarathonRecomp/api/Sonicteam/SoX/IResource.h | 12 + MarathonRecomp/api/Sonicteam/SoX/IResource2.h | 9 + .../api/Sonicteam/SoX/IResourceMgr.h | 13 + .../api/Sonicteam/SoX/RefCountObject.h | 11 + MarathonRecomp/api/Sonicteam/TextCard.h | 15 + MarathonRecomp/api/Sonicteam/TextEntity.h | 100 + .../api/Sonicteam/TextFontPicture.h | 19 + .../api/Sonicteam/TextFontPictureMgr.h | 8 + MarathonRecomp/api/stdx/string.h | 6 +- .../patches/aspect_ratio_patches.cpp | 1702 ++++------------- MarathonRecomp/patches/aspect_ratio_patches.h | 237 ++- MarathonRecomp/patches/misc_patches.cpp | 11 + MarathonRecomp/patches/video_patches.cpp | 93 + MarathonRecomp/ui/black_bar.cpp | 63 +- MarathonRecomp/ui/black_bar.h | 9 +- MarathonRecompLib/config/Marathon.toml | 5 + 40 files changed, 1377 insertions(+), 1367 deletions(-) create mode 100644 MarathonRecomp/api/Sonicteam/AppMarathon.inl create mode 100644 MarathonRecomp/api/Sonicteam/CsdLink.h create mode 100644 MarathonRecomp/api/Sonicteam/CsdManager.h create mode 100644 MarathonRecomp/api/Sonicteam/CsdManager.inl create mode 100644 MarathonRecomp/api/Sonicteam/CsdObject.h create mode 100644 MarathonRecomp/api/Sonicteam/HUDButtonWindow.h create mode 100644 MarathonRecomp/api/Sonicteam/HUDPopupScreen.h create mode 100644 MarathonRecomp/api/Sonicteam/HUDRaderMap.h create mode 100644 MarathonRecomp/api/Sonicteam/HudTextParts.h create mode 100644 MarathonRecomp/api/Sonicteam/MyTexture.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/ApplicationXenon.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/ApplicationXenon.inl create mode 100644 MarathonRecomp/api/Sonicteam/SoX/Engine/Application.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/Graphics/Xenon/TextureXenon.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/IResource.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/IResource2.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/IResourceMgr.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/RefCountObject.h create mode 100644 MarathonRecomp/api/Sonicteam/TextCard.h create mode 100644 MarathonRecomp/api/Sonicteam/TextEntity.h create mode 100644 MarathonRecomp/api/Sonicteam/TextFontPicture.h create mode 100644 MarathonRecomp/api/Sonicteam/TextFontPictureMgr.h diff --git a/MarathonRecomp/api/CSD/Manager/csdmProject.h b/MarathonRecomp/api/CSD/Manager/csdmProject.h index d1825f35d..1788eed92 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmProject.h +++ b/MarathonRecomp/api/CSD/Manager/csdmProject.h @@ -1,16 +1,21 @@ #pragma once -#include "Marathon.inl" -#include "CSD/Manager/csdmBase.h" -#include "CSD/Manager/csdmResourceBase.h" +#include +#include +#include namespace Chao::CSD { - struct Project; + class SceneNode; class CProject; class CScene; class CTexList; + struct Project + { + xpointer pRootNode; + }; + class CProject : public CResourceBase, CBase { public: @@ -28,4 +33,4 @@ namespace Chao::CSD }; } -#include "CSD/Manager/csdmProject.inl" +#include diff --git a/MarathonRecomp/api/CSD/Manager/csdmScene.h b/MarathonRecomp/api/CSD/Manager/csdmScene.h index c3904cf96..f6148315b 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmScene.h +++ b/MarathonRecomp/api/CSD/Manager/csdmScene.h @@ -1,17 +1,88 @@ #pragma once -#include "Marathon.inl" -#include "CSD/Manager/csdmBase.h" -#include "CSD/Manager/csdmResourceBase.h" -#include "CSD/Manager/csdmSceneObserver.h" -#include "CSD/Manager/csdmSubjectBase.h" +#include +#include +#include +#include +#include namespace Chao::CSD { - struct Scene; class CScene; class CNode; + struct Cast + { + MARATHON_INSERT_PADDING(0x144); // TODO: might be 0x130? + }; + + struct CastLink + { + be ChildCastIndex; + be SiblingCastIndex; + }; + + struct CastNode + { + be CastCount; + MARATHON_INSERT_PADDING(4); + xpointer> pCasts; + MARATHON_INSERT_PADDING(4); + be RootCastIndex; + MARATHON_INSERT_PADDING(4); + xpointer pCastLinks; + MARATHON_INSERT_PADDING(4); + }; + + struct CastIndex + { + xpointer pCastName; + MARATHON_INSERT_PADDING(4); + be CastNodeIndex; + be CastIndex; + }; + + struct Scene + { + MARATHON_INSERT_PADDING(0x30); + be CastNodeCount; + MARATHON_INSERT_PADDING(4); + xpointer pCastNodes; + MARATHON_INSERT_PADDING(4); + be CastCount; + MARATHON_INSERT_PADDING(4); + xpointer pCastIndices; + }; + + struct SceneIndex + { + xpointer pSceneName; + MARATHON_INSERT_PADDING(4); + be SceneIndex; + MARATHON_INSERT_PADDING(4); + }; + + struct SceneNodeIndex + { + xpointer pSceneNodeName; + be SceneNodeIndex; + }; + + struct SceneNode + { + be SceneCount; + MARATHON_INSERT_PADDING(4); + xpointer> pScenes; + MARATHON_INSERT_PADDING(4); + xpointer pSceneIndices; + MARATHON_INSERT_PADDING(4); + be SceneNodeCount; + MARATHON_INSERT_PADDING(4); + xpointer pSceneNodes; + MARATHON_INSERT_PADDING(4); + xpointer pSceneNodeIndices; + }; + enum EMotionRepeatType : uint32_t { eMotionRepeatType_PlayOnce = 0, diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index ebdf1f766..17387ea46 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -20,10 +20,18 @@ #include "Sonicteam/Camera/CameraMode.h" #include "Sonicteam/Camera/Cameraman.h" #include "Sonicteam/Camera/SonicCamera.h" +#include "Sonicteam/CsdLink.h" +#include "Sonicteam/CsdManager.h" +#include "Sonicteam/CsdObject.h" #include "Sonicteam/DocMarathonImp.h" #include "Sonicteam/DocMarathonState.h" #include "Sonicteam/GameImp.h" #include "Sonicteam/GameMode.h" +#include "Sonicteam/HUDButtonWindow.h" +#include "Sonicteam/HUDPopupScreen.h" +#include "Sonicteam/HUDRaderMap.h" +#include "Sonicteam/HudTextParts.h" +#include "Sonicteam/MyTexture.h" #include "Sonicteam/Player/IDynamicLink.h" #include "Sonicteam/Player/IExportPostureRequestFlag.h" #include "Sonicteam/Player/IExportWeaponRequestFlag.h" @@ -48,12 +56,23 @@ #include "Sonicteam/Player/State/Object2.h" #include "Sonicteam/Player/State/TailsContext.h" #include "Sonicteam/SoX/AI/StateMachine.h" +#include "Sonicteam/SoX/ApplicationXenon.h" #include "Sonicteam/SoX/Component.h" +#include "Sonicteam/SoX/Engine/Application.h" #include "Sonicteam/SoX/Engine/Doc.h" #include "Sonicteam/SoX/Engine/DocMode.h" #include "Sonicteam/SoX/Engine/Task.h" +#include "Sonicteam/SoX/Graphics/Xenon/TextureXenon.h" +#include "Sonicteam/SoX/IResource.h" +#include "Sonicteam/SoX/IResource2.h" +#include "Sonicteam/SoX/IResourceMgr.h" #include "Sonicteam/SoX/MessageReceiver.h" #include "Sonicteam/SoX/Object.h" +#include "Sonicteam/SoX/RefCountObject.h" +#include "Sonicteam/TextCard.h" +#include "Sonicteam/TextEntity.h" +#include "Sonicteam/TextFontPicture.h" +#include "Sonicteam/TextFontPictureMgr.h" #include "Sonicteam/TitleTask.h" #include "boost/smart_ptr/make_shared_object.h" #include "boost/smart_ptr/shared_ptr.h" diff --git a/MarathonRecomp/api/Marathon.inl b/MarathonRecomp/api/Marathon.inl index 134f99646..b56e04b03 100644 --- a/MarathonRecomp/api/Marathon.inl +++ b/MarathonRecomp/api/Marathon.inl @@ -3,6 +3,12 @@ #include #include +constexpr float RAD2DEGf = 57.2958f; +constexpr float DEG2RADf = 0.0174533f; + +constexpr double RAD2DEG = 57.29578018188477; +constexpr double DEG2RAD = 0.01745329238474369; + #define MARATHON_CONCAT2(x, y) x##y #define MARATHON_CONCAT(x, y) MARATHON_CONCAT2(x, y) @@ -19,3 +25,69 @@ GuestToHostFunction(*(be*)(g_memory.Translate(*(be*)(this) + (4 * virtualIndex))), __VA_ARGS__) struct marathon_null_ctor {}; + +inline std::vector ParseTextVariables(const char* pVariables) +{ + std::vector result{}; + + if (!pVariables) + return result; + + auto start = pVariables; + auto ptr = pVariables; + auto depth = 0; + + while (*ptr) + { + if (*ptr == '(') + { + depth++; + } + else if (*ptr == ')') + { + depth--; + } + else if (*ptr == ',' && !depth) + { + result.emplace_back(start, ptr - start); + start = ptr + 1; + } + + ++ptr; + } + + if (ptr != start) + result.emplace_back(start, ptr - start); + + return result; +} + +inline std::vector> MapTextVariables(const char* pVariables) +{ + std::vector> result{}; + + if (!pVariables) + return result; + + auto variables = ParseTextVariables(pVariables); + + for (auto& variable : variables) + { + auto open = variable.find('('); + auto close = variable.find(')'); + + if (open != std::string_view::npos && close != std::string_view::npos && close > open) + { + auto type = variable.substr(0, open); + auto value = variable.substr(open + 1, close - open - 1); + + result.emplace_back(type, value); + } + else + { + result.emplace_back(variable, std::string_view{}); + } + } + + return result; +} diff --git a/MarathonRecomp/api/Sonicteam/AppMarathon.h b/MarathonRecomp/api/Sonicteam/AppMarathon.h index 38ecab00a..ec42f1733 100644 --- a/MarathonRecomp/api/Sonicteam/AppMarathon.h +++ b/MarathonRecomp/api/Sonicteam/AppMarathon.h @@ -1,14 +1,18 @@ #pragma once #include +#include #include namespace Sonicteam { - class AppMarathon + class AppMarathon : public SoX::ApplicationXenon { public: - MARATHON_INSERT_PADDING(0x180); xpointer m_pDoc; + + static AppMarathon* GetInstance(); }; } + +#include diff --git a/MarathonRecomp/api/Sonicteam/AppMarathon.inl b/MarathonRecomp/api/Sonicteam/AppMarathon.inl new file mode 100644 index 000000000..866b481af --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/AppMarathon.inl @@ -0,0 +1,7 @@ +namespace Sonicteam +{ + inline AppMarathon* AppMarathon::GetInstance() + { + return *(xpointer*)MmGetHostAddress(0x82D3B348); + } +} diff --git a/MarathonRecomp/api/Sonicteam/Camera/CameraMode.h b/MarathonRecomp/api/Sonicteam/Camera/CameraMode.h index a2490fa75..5866a133d 100644 --- a/MarathonRecomp/api/Sonicteam/Camera/CameraMode.h +++ b/MarathonRecomp/api/Sonicteam/Camera/CameraMode.h @@ -1,13 +1,17 @@ #pragma once #include +#include #include +#include namespace Sonicteam::Camera { class CameraMode : public SoX::MessageReceiver { public: - MARATHON_INSERT_PADDING(0x24); + boost::shared_ptr m_spGameImp; + xpointer m_pTPCameraInputListener; + MARATHON_INSERT_PADDING(0x18); }; } diff --git a/MarathonRecomp/api/Sonicteam/Camera/Cameraman.h b/MarathonRecomp/api/Sonicteam/Camera/Cameraman.h index dc44a527e..e162e02fb 100644 --- a/MarathonRecomp/api/Sonicteam/Camera/Cameraman.h +++ b/MarathonRecomp/api/Sonicteam/Camera/Cameraman.h @@ -8,11 +8,7 @@ namespace Sonicteam::Camera class Cameraman : Actor { public: - MARATHON_INSERT_PADDING(0x28); - boost::anonymous_shared_ptr m_spMyInputObj; - MARATHON_INSERT_PADDING(0x34); + MARATHON_INSERT_PADDING(0x58); be m_FOV; - MARATHON_INSERT_PADDING(0x04); - boost::anonymous_shared_ptr m_spKynapseControl; }; } diff --git a/MarathonRecomp/api/Sonicteam/Camera/SonicCamera.h b/MarathonRecomp/api/Sonicteam/Camera/SonicCamera.h index ba4f99464..4aabd5de5 100644 --- a/MarathonRecomp/api/Sonicteam/Camera/SonicCamera.h +++ b/MarathonRecomp/api/Sonicteam/Camera/SonicCamera.h @@ -5,15 +5,20 @@ namespace Sonicteam::Camera class SonicCamera : public CameraMode { public: - MARATHON_INSERT_PADDING(0x1C); + MARATHON_INSERT_PADDING(4); + xpointer m_pDoc; + MARATHON_INSERT_PADDING(0x14); be m_SpringK; be m_DampingK; - MARATHON_INSERT_PADDING(0x08); + MARATHON_INSERT_PADDING(8); be m_AzDriveK; be m_AzDampingK; be m_AltDriveK; be m_AltDampingK; MARATHON_INSERT_PADDING(0x3C); be m_FovY; + MARATHON_INSERT_PADDING(0x3C); + be m_Distance; + MARATHON_INSERT_PADDING(0x0C); }; } diff --git a/MarathonRecomp/api/Sonicteam/CsdLink.h b/MarathonRecomp/api/Sonicteam/CsdLink.h new file mode 100644 index 000000000..8bc739976 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/CsdLink.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class CsdLink + { + public: + xpointer m_pVftable; + xpointer m_pNext; + xpointer m_pPrevious; + be m_Priority; + MARATHON_INSERT_PADDING(8); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/CsdManager.h b/MarathonRecomp/api/Sonicteam/CsdManager.h new file mode 100644 index 000000000..9c3a86059 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/CsdManager.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace Sonicteam +{ + class CsdManager : public SoX::IResourceMgr + { + public: + MARATHON_INSERT_PADDING(0x1C); + + static CsdManager* GetInstance(); + }; +} + +#include diff --git a/MarathonRecomp/api/Sonicteam/CsdManager.inl b/MarathonRecomp/api/Sonicteam/CsdManager.inl new file mode 100644 index 000000000..9145a048d --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/CsdManager.inl @@ -0,0 +1,7 @@ +namespace Sonicteam +{ + inline CsdManager* CsdManager::GetInstance() + { + return *(xpointer*)MmGetHostAddress(0x82D3BC58); + } +} diff --git a/MarathonRecomp/api/Sonicteam/CsdObject.h b/MarathonRecomp/api/Sonicteam/CsdObject.h new file mode 100644 index 000000000..9d4bacd1f --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/CsdObject.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace Sonicteam +{ + class CsdObject : public SoX::RefCountObject, public CsdLink + { + public: + MARATHON_INSERT_PADDING(4); + xpointer pCsdProject; + MARATHON_INSERT_PADDING(4); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/DocMarathonImp.h b/MarathonRecomp/api/Sonicteam/DocMarathonImp.h index 75b0b6e73..8228c09af 100644 --- a/MarathonRecomp/api/Sonicteam/DocMarathonImp.h +++ b/MarathonRecomp/api/Sonicteam/DocMarathonImp.h @@ -5,10 +5,14 @@ namespace Sonicteam { + class RenderTargetContainer; + class DocMarathonImp : public SoX::Engine::Doc { public: MARATHON_INSERT_PADDING(0x74); bool m_VFrame; + MARATHON_INSERT_PADDING(4); + xpointer m_pRenderTargetContainer; }; } diff --git a/MarathonRecomp/api/Sonicteam/GameImp.h b/MarathonRecomp/api/Sonicteam/GameImp.h index 2dc4855a1..8f34d1e32 100644 --- a/MarathonRecomp/api/Sonicteam/GameImp.h +++ b/MarathonRecomp/api/Sonicteam/GameImp.h @@ -27,7 +27,9 @@ namespace Sonicteam MARATHON_INSERT_PADDING(0x10); }; - MARATHON_INSERT_PADDING(0xE3C); + MARATHON_INSERT_PADDING(0x658); + bool m_IsPlayingStage; + MARATHON_INSERT_PADDING(0x7E0); PlayerData m_PlayerData[4]; }; } diff --git a/MarathonRecomp/api/Sonicteam/HUDButtonWindow.h b/MarathonRecomp/api/Sonicteam/HUDButtonWindow.h new file mode 100644 index 000000000..d100581ce --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HUDButtonWindow.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include + +namespace Sonicteam +{ + class HUDButtonWindow : public SoX::RefCountObject, public SoX::Engine::Task + { + public: + MARATHON_INSERT_PADDING(0x38); + xpointer m_pHudTextParts; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/HUDPopupScreen.h b/MarathonRecomp/api/Sonicteam/HUDPopupScreen.h new file mode 100644 index 000000000..c213bf8de --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HUDPopupScreen.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include + +namespace Sonicteam +{ + class HUDPopupScreen : public SoX::Engine::Task + { + public: + xpointer m_pCsdObject; + xpointer m_pMainTexture; + xpointer m_pMaskTexture; + xpointer m_pTechnique; + MARATHON_INSERT_PADDING(8); + stdx::string m_SceneName; + stdx::string m_SpriteName; + MARATHON_INSERT_PADDING(4); + be m_X; + be m_Y; + MARATHON_INSERT_PADDING(8); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/HUDRaderMap.h b/MarathonRecomp/api/Sonicteam/HUDRaderMap.h new file mode 100644 index 000000000..e600efe2c --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HUDRaderMap.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class HUDRaderMap : public HUDPopupScreen {}; +} diff --git a/MarathonRecomp/api/Sonicteam/HudTextParts.h b/MarathonRecomp/api/Sonicteam/HudTextParts.h new file mode 100644 index 000000000..bbb7a4a8a --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HudTextParts.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include + +namespace Sonicteam +{ + class HudTextParts : public SoX::RefCountObject + { + public: + MARATHON_INSERT_PADDING(8); + boost::shared_ptr m_spTextCard; + boost::shared_ptr m_spTextEntity; + xpointer m_pCsdObject; + MARATHON_INSERT_PADDING(0x38); + be m_Priority; + be m_OffsetX; + be m_OffsetY; + MARATHON_INSERT_PADDING(4); + be m_AlignmentFlags; + MARATHON_INSERT_PADDING(0x0C); + be m_Field7C; + be m_Field80; + }; + + MARATHON_ASSERT_OFFSETOF(HudTextParts, m_Field80, 0x80); +} diff --git a/MarathonRecomp/api/Sonicteam/MyTexture.h b/MarathonRecomp/api/Sonicteam/MyTexture.h new file mode 100644 index 000000000..e3832c61c --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/MyTexture.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace Sonicteam +{ + class MyTexture : public SoX::Graphics::Xenon::TextureXenon + { + public: + MARATHON_INSERT_PADDING(4); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/ApplicationXenon.h b/MarathonRecomp/api/Sonicteam/SoX/ApplicationXenon.h new file mode 100644 index 000000000..714059dbe --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/ApplicationXenon.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace Sonicteam::SoX +{ + class ApplicationXenon : public Engine::Application + { + public: + MARATHON_INSERT_PADDING(0x174); + + static ApplicationXenon* GetInstance(); + }; +} + +#include diff --git a/MarathonRecomp/api/Sonicteam/SoX/ApplicationXenon.inl b/MarathonRecomp/api/Sonicteam/SoX/ApplicationXenon.inl new file mode 100644 index 000000000..46ba52de0 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/ApplicationXenon.inl @@ -0,0 +1,7 @@ +namespace Sonicteam::SoX +{ + inline ApplicationXenon* ApplicationXenon::GetInstance() + { + return *(xpointer*)MmGetHostAddress(0x82D3B348); + } +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/Engine/Application.h b/MarathonRecomp/api/Sonicteam/SoX/Engine/Application.h new file mode 100644 index 000000000..8f5473e37 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Engine/Application.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace Sonicteam::SoX::Engine +{ + class Application + { + public: + xpointer m_pVftable; + MARATHON_INSERT_PADDING(8); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/Graphics/Xenon/TextureXenon.h b/MarathonRecomp/api/Sonicteam/SoX/Graphics/Xenon/TextureXenon.h new file mode 100644 index 000000000..d0a49b190 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/Xenon/TextureXenon.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +namespace Sonicteam::SoX::Graphics::Xenon +{ + class TextureXenon : public IResource + { + public: + MARATHON_INSERT_PADDING(0x1C); + be m_Width; + be m_Height; + MARATHON_INSERT_PADDING(4); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/IResource.h b/MarathonRecomp/api/Sonicteam/SoX/IResource.h new file mode 100644 index 000000000..259a33f0b --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/IResource.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace Sonicteam::SoX +{ + class IResource : public RefCountObject + { + public: + MARATHON_INSERT_PADDING(0x5C); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/IResource2.h b/MarathonRecomp/api/Sonicteam/SoX/IResource2.h new file mode 100644 index 000000000..9c1fa55de --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/IResource2.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace Sonicteam::SoX +{ + template + class IResource2 : public IResource {}; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/IResourceMgr.h b/MarathonRecomp/api/Sonicteam/SoX/IResourceMgr.h new file mode 100644 index 000000000..63853e831 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/IResourceMgr.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace Sonicteam::SoX +{ + class IResourceMgr + { + public: + xpointer m_pVftable; + MARATHON_INSERT_PADDING(0x0C); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/RefCountObject.h b/MarathonRecomp/api/Sonicteam/SoX/RefCountObject.h new file mode 100644 index 000000000..4f006d9b7 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/RefCountObject.h @@ -0,0 +1,11 @@ +#pragma once + +namespace Sonicteam::SoX +{ + class RefCountObject + { + public: + xpointer m_pVftable; + be m_ReferenceCount; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/TextCard.h b/MarathonRecomp/api/Sonicteam/TextCard.h new file mode 100644 index 000000000..7ac63a6b4 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/TextCard.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class TextCard + { + public: + boost::shared_ptr m_spResource; + xpointer m_pText; + xpointer m_pVariables; + MARATHON_INSERT_PADDING(0x30); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/TextEntity.h b/MarathonRecomp/api/Sonicteam/TextEntity.h new file mode 100644 index 000000000..422a51a2f --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/TextEntity.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include + +namespace Sonicteam +{ + class TextFont; + class TextBoard; + + struct TextEntityVertex + { + be X; + be Y; + be Field08; + be Field0C; + be Field10; + be Field14; + be Colour; // ARGB8888 + be U; + be V; + be Field24; + be Field28; + be Field2C; + be Field30; + be Field34; + be Field38; + }; + + class TextEntity : public CsdLink + { + public: + MARATHON_INSERT_PADDING(4); + boost::shared_ptr m_spTextFont; + boost::shared_ptr m_spTextBoard; + MARATHON_INSERT_PADDING(4); + be m_X; + be m_Y; + MARATHON_INSERT_PADDING(0x0C); + xpointer m_pText; + MARATHON_INSERT_PADDING(0x0C); + be m_TextLength; + MARATHON_INSERT_PADDING(0x24); + xpointer m_pVariables; + MARATHON_INSERT_PADDING(0x4C); + be m_ScaleX; + be m_ScaleY; + MARATHON_INSERT_PADDING(0x0C); + xpointer m_FieldE0; // Only present when there's character vertices. + xpointer m_FieldE4; // Only present when there's image vertices. + xpointer m_pCharacterVertices; // BL/TL/TR BL/TR/BR (two triangles per character) + xpointer m_pImageVertices; // BL/TL/TR BL/TR/BR (two triangles per image) + be m_CharacterVertexCount; + be m_ImageVertexCount; + + std::pair Measure() const + { + float w{}; + float h{}; + + if (m_TextLength <= 0) + return { w, h }; + + if (m_FieldE0) + { + for (int i = 0; i < m_CharacterVertexCount; i += 6) + { + auto& tl = m_pCharacterVertices[i + 0]; + auto& bl = m_pCharacterVertices[i + 1]; + auto& tr = m_pCharacterVertices[i + 2]; + + w += tr.X - tl.X; + h = std::max(h, bl.Y - tl.Y); + } + } + + if (m_FieldE4) + { + for (int i = 0; i < m_ImageVertexCount; i += 6) + { + auto& tl = m_pImageVertices[i + 0]; + auto& bl = m_pImageVertices[i + 1]; + auto& tr = m_pImageVertices[i + 2]; + + w += tr.X - tl.X; + h = std::max(h, bl.Y - tl.Y); + } + } + + return { w, h }; + } + }; + + MARATHON_ASSERT_OFFSETOF(TextEntity, m_FieldE0, 0xE0); + MARATHON_ASSERT_OFFSETOF(TextEntity, m_FieldE4, 0xE4); + MARATHON_ASSERT_OFFSETOF(TextEntity, m_pCharacterVertices, 0xE8); + MARATHON_ASSERT_OFFSETOF(TextEntity, m_pImageVertices, 0xEC); + MARATHON_ASSERT_OFFSETOF(TextEntity, m_CharacterVertexCount, 0xF0); + MARATHON_ASSERT_OFFSETOF(TextEntity, m_ImageVertexCount, 0xF4); +} diff --git a/MarathonRecomp/api/Sonicteam/TextFontPicture.h b/MarathonRecomp/api/Sonicteam/TextFontPicture.h new file mode 100644 index 000000000..cb58c4068 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/TextFontPicture.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +namespace Sonicteam +{ + class TextFontPicture : public SoX::IResource2 + { + public: + xpointer m_pResource; + xpointer m_pTexture; + be m_TextureWidth; + be m_TextureHeight; + be m_Field74; + xpointer m_Field78; + be m_CropCount; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/TextFontPictureMgr.h b/MarathonRecomp/api/Sonicteam/TextFontPictureMgr.h new file mode 100644 index 000000000..bc122642b --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/TextFontPictureMgr.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class TextFontPictureMgr : public SoX::IResourceMgr {}; +} diff --git a/MarathonRecomp/api/stdx/string.h b/MarathonRecomp/api/stdx/string.h index d3d940755..c1d8ffad5 100644 --- a/MarathonRecomp/api/stdx/string.h +++ b/MarathonRecomp/api/stdx/string.h @@ -23,7 +23,7 @@ namespace stdx bool is_short() const { - return _Mysize <= 0x10; + return _Mysize.get() < 0x10; } public: @@ -34,12 +34,12 @@ namespace stdx size_t size() const { - return _Mysize; + return _Mysize.get(); } size_t capacity() const { - return _Myres; + return _Myres.get(); } string() diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index e1de67ece..2cad2963a 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1,84 +1,76 @@ #include "aspect_ratio_patches.h" -#include #include -#include -#include -#include #include -#include +#include +#include +#include +#include +#include +#include -// These are here for now to not recompile basically all of the project. -namespace Chao::CSD -{ - struct Cast - { - MARATHON_INSERT_PADDING(0x144); - }; +// #define CORNER_DEBUG - struct CastLink - { - be ChildCastIndex; - be SiblingCastIndex; - }; +static Mutex g_pathMutex; +static std::map g_paths; - struct CastNode - { - be CastCount; - xpointer> pCasts; - be RootCastIndex; - xpointer pCastLinks; - }; +static std::optional g_sceneModifier; +static std::optional g_castNodeModifier; +static std::optional g_castModifier; - struct CastIndex - { - xpointer pCastName; - be CastNodeIndex; - be CastIndex; - }; +static Sonicteam::TextFontPicture* g_pTextFontPicture; - struct Scene - { - MARATHON_INSERT_PADDING(0x24); - be CastNodeCount; - xpointer pCastNodes; - be CastCount; - xpointer pCastIndices; - }; +static float g_corners[8]; +static bool g_cornerExtract; - struct SceneIndex - { - xpointer pSceneName; - be SceneIndex; - }; +static float g_radarMapX; +static float g_radarMapY; +static float g_radarMapCoverWidth; +static float g_radarMapCoverHeight; - struct SceneNodeIndex - { - xpointer pSceneNodeName; - be SceneNodeIndex; - }; +// Explicit translations don't get affected by gameplay UI downscaling. +static float g_scenePositionX; +static float g_scenePositionY; - struct SceneNode - { - be SceneCount; - xpointer> pScenes; - xpointer pSceneIndices; - be SceneNodeCount; - xpointer pSceneNodes; - xpointer pSceneNodeIndices; - }; +static float ComputeScale(float aspectRatio) +{ + return ((aspectRatio * 720.0f) / 1280.0f) / sqrt((aspectRatio * 720.0f) / 1280.0f); +} - struct Project +void AspectRatioPatches::ComputeOffsets() +{ + float width = Video::s_viewportWidth; + float height = Video::s_viewportHeight; + + g_aspectRatio = width / height; + g_aspectRatioGameplayScale = 1.0f; + + if (g_aspectRatio >= NARROW_ASPECT_RATIO) { - xpointer pRootNode; - }; -} + g_aspectRatioOffsetX = (width - height * WIDE_ASPECT_RATIO) / 2.0f; + g_aspectRatioOffsetY = 0.0f; + g_aspectRatioScale = height / 720.0f; -static Mutex g_pathMutex; -static std::map g_paths; + // keep same scale above Steam Deck aspect ratio + if (g_aspectRatio < WIDE_ASPECT_RATIO) + { + // interpolate to original 4:3 scale + float steamDeckScale = g_aspectRatio / WIDE_ASPECT_RATIO; + float narrowScale = ComputeScale(NARROW_ASPECT_RATIO); -static XXH64_hash_t HashStr(const std::string_view& value) -{ - return XXH3_64bits(value.data(), value.size()); + float lerpFactor = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (STEAM_DECK_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f); + g_aspectRatioGameplayScale = narrowScale + (steamDeckScale - narrowScale) * lerpFactor; + } + } + else + { + // 4:3 crop + g_aspectRatioOffsetX = (width - width * NARROW_ASPECT_RATIO) / 2.0f; + g_aspectRatioOffsetY = (height - width / NARROW_ASPECT_RATIO) / 2.0f; + g_aspectRatioScale = width / 960.0f; + g_aspectRatioGameplayScale = ComputeScale(NARROW_ASPECT_RATIO); + } + + g_aspectRatioNarrowScale = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (WIDE_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f); } static void EmplacePath(const void* key, const std::string_view& value) @@ -114,6 +106,8 @@ static void TraverseCast(Chao::CSD::Scene* scene, uint32_t castNodeIndex, Chao:: path += "/"; TraverseCast(scene, castNodeIndex, castNode, castNode->pCastLinks[castIndex].ChildCastIndex, path); + + // LOGFN_UTILITY("CSD hierarchy: {}", path); } static void TraverseScene(Chao::CSD::Scene* scene, std::string path) @@ -146,771 +140,147 @@ static void TraverseSceneNode(Chao::CSD::SceneNode* sceneNode, std::string path) } } -void MakeCsdProjectMidAsmHook(PPCRegister& r3, PPCRegister& r29) +static std::optional FindCsdModifier(uint32_t data) { - uint8_t* base = g_memory.base; - auto csdProject = reinterpret_cast(base + PPC_LOAD_U32(PPC_LOAD_U32(r3.u32 + 16) + 4)); - auto name = reinterpret_cast(base + PPC_LOAD_U32(r29.u32)); - TraverseSceneNode(csdProject->m_pResource->pRootNode, name); -} - -// Chao::CSD::CMemoryAlloc::Free -// PPC_FUNC_IMPL(__imp__sub_825E2E60); -// PPC_FUNC(sub_825E2E60) -// { -// if (ctx.r4.u32 != NULL && PPC_LOAD_U32(ctx.r4.u32) == 0x4E594946 && PPC_LOAD_U32(ctx.r4.u32 + 0x20) == 0x6E43504A) // NYIF, nCPJ -// { -// uint32_t fileSize = PPC_LOAD_U32(ctx.r4.u32 + 0x14); - -// std::lock_guard lock(g_pathMutex); -// const uint8_t* key = base + ctx.r4.u32; - -// auto lower = g_paths.lower_bound(key); -// auto upper = g_paths.lower_bound(key + fileSize); - -// g_paths.erase(lower, upper); -// } - -// __imp__sub_825E2E60(ctx, base); -// } - -static float ComputeScale(float aspectRatio) -{ - return ((aspectRatio * 720.0f) / 1280.0f) / sqrt((aspectRatio * 720.0f) / 1280.0f); -} - -void AspectRatioPatches::ComputeOffsets() -{ - float width = Video::s_viewportWidth; - float height = Video::s_viewportHeight; - - g_aspectRatio = width / height; - g_aspectRatioGameplayScale = 1.0f; - - if (g_aspectRatio >= NARROW_ASPECT_RATIO) + XXH64_hash_t path; { - g_aspectRatioOffsetX = (width - height * WIDE_ASPECT_RATIO) / 2.0f; - g_aspectRatioOffsetY = 0.0f; - g_aspectRatioScale = height / 720.0f; + std::lock_guard lock(g_pathMutex); - // keep same scale above Steam Deck aspect ratio - if (g_aspectRatio < WIDE_ASPECT_RATIO) - { - // interpolate to original 4:3 scale - float steamDeckScale = g_aspectRatio / WIDE_ASPECT_RATIO; - float narrowScale = ComputeScale(NARROW_ASPECT_RATIO); + auto findResult = g_paths.find(g_memory.Translate(data)); + if (findResult == g_paths.end()) + return {}; - float lerpFactor = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (STEAM_DECK_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f); - g_aspectRatioGameplayScale = narrowScale + (steamDeckScale - narrowScale) * lerpFactor; - } + path = findResult->second; } - else - { - // 4:3 crop - g_aspectRatioOffsetX = (width - width * NARROW_ASPECT_RATIO) / 2.0f; - g_aspectRatioOffsetY = (height - width / NARROW_ASPECT_RATIO) / 2.0f; - g_aspectRatioScale = width / 960.0f; - g_aspectRatioGameplayScale = ComputeScale(NARROW_ASPECT_RATIO); - } - - g_aspectRatioNarrowScale = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (WIDE_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f); -} - -static void GetViewport(void* application, be* width, be* height) -{ - *width = 1280; - *height = 720; -} - -GUEST_FUNCTION_HOOK(sub_82E169B8, GetViewport); - -// SWA::CGameDocument::ComputeScreenPosition -// PPC_FUNC_IMPL(__imp__sub_8250FC70); -// PPC_FUNC(sub_8250FC70) -// { -// __imp__sub_8250FC70(ctx, base); - -// auto position = reinterpret_cast*>(base + ctx.r3.u32); -// position[0] = (position[0] / 1280.0f * Video::s_viewportWidth - g_aspectRatioOffsetX) / g_aspectRatioScale; -// position[1] = (position[1] / 720.0f * Video::s_viewportHeight - g_aspectRatioOffsetY) / g_aspectRatioScale; -// } - -void ComputeScreenPositionMidAsmHook(PPCRegister& f1, PPCRegister& f2) -{ - f1.f64 = (f1.f64 / 1280.0 * Video::s_viewportWidth - g_aspectRatioOffsetX) / g_aspectRatioScale; - f2.f64 = (f2.f64 / 720.0 * Video::s_viewportHeight - g_aspectRatioOffsetY) / g_aspectRatioScale; -} + auto findResult = g_modifiers.find(path); + if (findResult != g_modifiers.end()) + return findResult->second; -void WorldMapInfoMidAsmHook(PPCRegister& r4) -{ - // Prevent the game from snapping "cts_parts_sun_moon" - // to "cts_guide_icon" automatically, we will do this ourselves. - r4.u32 = 0x8200A621; + return {}; } -// SWA::CTitleStateWorldMap::Update -// PPC_FUNC_IMPL(__imp__sub_8258B558); -// PPC_FUNC(sub_8258B558) -// { -// auto r3 = ctx.r3; -// __imp__sub_8258B558(ctx, base); - -// uint32_t worldMapSimpleInfo = PPC_LOAD_U32(r3.u32 + 0x70); -// if (worldMapSimpleInfo != NULL) -// { -// auto setPosition = [&](uint32_t rcPtr, float offsetX = 0.0f, float offsetY = 0.0f) -// { -// uint32_t scene = PPC_LOAD_U32(rcPtr + 0x4); -// if (scene != NULL) -// { -// scene = PPC_LOAD_U32(scene + 0x4); -// if (scene != NULL) -// { -// ctx.r3.u32 = scene; -// ctx.f1.f64 = offsetX + g_aspectRatioNarrowScale * 140.0f; -// ctx.f2.f64 = offsetY; - -// if (Config::UIAlignmentMode == EUIAlignmentMode::Edge && g_aspectRatioNarrowScale >= 1.0f) -// ctx.f1.f64 += g_aspectRatioOffsetX / g_aspectRatioScale; - -// sub_830BB3D0(ctx, base); -// } -// } -// }; - -// setPosition(worldMapSimpleInfo + 0x2C, 299.0f, -178.0f); -// setPosition(worldMapSimpleInfo + 0x34); -// setPosition(worldMapSimpleInfo + 0x4C); - -// for (uint32_t it = PPC_LOAD_U32(worldMapSimpleInfo + 0x20); it != PPC_LOAD_U32(worldMapSimpleInfo + 0x24); it += 8) -// setPosition(it); - -// uint32_t menuTextBox = PPC_LOAD_U32(worldMapSimpleInfo + 0x5C); -// if (menuTextBox != NULL) -// { -// uint32_t textBox = PPC_LOAD_U32(menuTextBox + 0x4); -// if (textBox != NULL) -// { -// float value = 708.0f + g_aspectRatioNarrowScale * 140.0f; -// if (Config::UIAlignmentMode == EUIAlignmentMode::Edge && g_aspectRatioNarrowScale >= 1.0f) -// value += g_aspectRatioOffsetX / g_aspectRatioScale; - -// PPC_STORE_U32(textBox + 0x38, reinterpret_cast(value)); -// } -// } -// } -// } - -enum +// Sonicteam::CsdResource::MakeResource +PPC_FUNC_IMPL(__imp__sub_82617570); +PPC_FUNC(sub_82617570) { - ALIGN_CENTER = 0 << 0, - - ALIGN_TOP = 1 << 0, - ALIGN_LEFT = 1 << 1, - ALIGN_BOTTOM = 1 << 2, - ALIGN_RIGHT = 1 << 3, - - ALIGN_TOP_LEFT = ALIGN_TOP | ALIGN_LEFT, - ALIGN_TOP_RIGHT = ALIGN_TOP | ALIGN_RIGHT, - ALIGN_BOTTOM_LEFT = ALIGN_BOTTOM | ALIGN_LEFT, - ALIGN_BOTTOM_RIGHT = ALIGN_BOTTOM | ALIGN_RIGHT, - - STRETCH_HORIZONTAL = 1 << 4, - STRETCH_VERTICAL = 1 << 5, + auto pName = reinterpret_cast(base + PPC_LOAD_U32(ctx.r4.u32 + 4)); - STRETCH = STRETCH_HORIZONTAL | STRETCH_VERTICAL, + __imp__sub_82617570(ctx, base); - SCALE = 1 << 6, - - WORLD_MAP = 1 << 7, - - EXTEND_LEFT = 1 << 8, - EXTEND_RIGHT = 1 << 9, - - STORE_LEFT_CORNER = 1 << 10, - STORE_RIGHT_CORNER = 1 << 11, - - SKIP = 1 << 12, - - OFFSET_SCALE_LEFT = 1 << 13, - OFFSET_SCALE_RIGHT = 1 << 14, + if (!ctx.r3.u32) + return; - REPEAT_LEFT = 1 << 15, + auto ppCsdObject = PPC_LOAD_U32(ctx.r3.u32); - TORNADO_DEFENSE = 1 << 16, + if (!ppCsdObject) + return; - LOADING_BLACK_BAR_MIN = 1 << 17, - LOADING_BLACK_BAR_MAX = 1 << 18, + auto pCsdObject = reinterpret_cast(base + ppCsdObject); - UNSTRETCH_HORIZONTAL = 1 << 19, + if (!pCsdObject || !pCsdObject->pCsdProject) + return; - CORNER_EXTRACT = 1 << 20, + LOGFN_UTILITY("CSD loaded: {} (0x{:08X})", pName, (uint64_t)pCsdObject->pCsdProject.get()); - SKIP_INSPIRE = 1 << 21, + TraverseSceneNode(pCsdObject->pCsdProject->m_pResource->pRootNode, pName); +} - CONTROL_TUTORIAL = 1 << 22, +// Chao::CSD::CMemoryAlloc::Free +PPC_FUNC_IMPL(__imp__sub_82656650); +PPC_FUNC(sub_82656650) +{ + if (ctx.r4.u32 != NULL && PPC_LOAD_U32(ctx.r4.u32) == 0x4649584E && PPC_LOAD_U32(ctx.r4.u32 + 0x20) == 0x6E43504A) // NXIF, nCPJ + { + uint32_t fileSize = PPC_LOAD_U32(ctx.r4.u32 + 0x14); + + std::lock_guard lock(g_pathMutex); + const uint8_t* key = base + ctx.r4.u32; + + auto lower = g_paths.lower_bound(key); + auto upper = g_paths.lower_bound(key + fileSize); + + g_paths.erase(lower, upper); + + LOGFN_UTILITY("CSD freed: 0x{:08X}", (uint64_t)key); + } - LOADING_ARROW = 1 << 23, -}; + __imp__sub_82656650(ctx, base); +} -struct CsdModifier +// Chao::CSD::CScene::Render +PPC_FUNC_IMPL(__imp__sub_825CB378); +PPC_FUNC(sub_825CB378) { - uint32_t flags{}; - float cornerMax{}; - uint32_t cornerIndex{}; -}; + g_scenePositionX = 0.0f; + g_scenePositionY = 0.0f; -static const xxHashMap g_modifiers = -{ - // ui_balloon - { HashStr("ui_balloon/window/bg"), { STRETCH } }, - { HashStr("ui_balloon/window/footer"), { ALIGN_BOTTOM } }, - - // ui_boss_gauge - { HashStr("ui_boss_gauge/gauge_bg"), { ALIGN_TOP_RIGHT | SCALE | SKIP_INSPIRE} }, - { HashStr("ui_boss_gauge/gauge_2"), { ALIGN_TOP_RIGHT | SCALE | SKIP_INSPIRE} }, - { HashStr("ui_boss_gauge/gauge_1"), { ALIGN_TOP_RIGHT | SCALE | SKIP_INSPIRE} }, - { HashStr("ui_boss_gauge/gauge_breakpoint"), { ALIGN_TOP_RIGHT | SCALE | SKIP_INSPIRE} }, - - // ui_boss_name - { HashStr("ui_boss_name/name_so/bg"), { UNSTRETCH_HORIZONTAL } }, - { HashStr("ui_boss_name/name_so/pale"), { UNSTRETCH_HORIZONTAL } }, - - // ui_exstage - { HashStr("ui_exstage/shield/L_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_exstage/shield/L_gauge_effect"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_exstage/shield/L_gauge_effect_2"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_exstage/energy/R_gauge"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("ui_exstage/energy/R_gauge_effect"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("ui_exstage/energy/R_gauge_effect_2"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("ui_exstage/hit/hit_counter_bg"), { ALIGN_RIGHT | SCALE | OFFSET_SCALE_RIGHT, 986.0f } }, - { HashStr("ui_exstage/hit/hit_counter_bg/C"), { ALIGN_RIGHT | SCALE | EXTEND_RIGHT } }, - { HashStr("ui_exstage/hit/hit_counter_bg/C/L"), { SKIP } }, // L/R are mixed up - { HashStr("ui_exstage/hit/hit_counter_bg/C/R"), { ALIGN_RIGHT | SCALE | STORE_LEFT_CORNER } }, - { HashStr("ui_exstage/hit/hit_counter_num"), { ALIGN_RIGHT | SCALE } }, - - // ui_gate - { HashStr("ui_gate/footer/status_footer"), { ALIGN_BOTTOM } }, - { HashStr("ui_gate/header/status_title"), { ALIGN_TOP | OFFSET_SCALE_LEFT, 652.0f } }, - { HashStr("ui_gate/header/status_title/title_bg/center"), { ALIGN_TOP | EXTEND_LEFT } }, - { HashStr("ui_gate/header/status_title/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT } }, - { HashStr("ui_gate/header/status_title/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } }, - { HashStr("ui_gate/window/window_bg"), { STRETCH } }, - - // ui_general - { HashStr("ui_general/bg"), { STRETCH } }, - { HashStr("ui_general/footer"), { ALIGN_BOTTOM } }, - - // ui_itemresult - { HashStr("ui_itemresult/footer/result_footer"), { ALIGN_BOTTOM } }, - { HashStr("ui_itemresult/main/iresult_title"), { ALIGN_TOP | OFFSET_SCALE_LEFT, 688.0f } }, - { HashStr("ui_itemresult/main/iresult_title/title_bg/center"), { ALIGN_TOP | EXTEND_LEFT } }, - { HashStr("ui_itemresult/main/iresult_title/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT } }, - { HashStr("ui_itemresult/main/iresult_title/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } }, - { HashStr("ui_itemresult/main/iresult_title/title_brilliance1"), { ALIGN_TOP | STORE_RIGHT_CORNER | OFFSET_SCALE_LEFT, 632.25775f, 1 } }, - { HashStr("ui_itemresult/main/iresult_title/title_brilliance2"), { ALIGN_TOP | STORE_RIGHT_CORNER | OFFSET_SCALE_LEFT, 830.0f, 2 } }, - { HashStr("ui_itemresult/main/iresult_title/title_brilliance3"), { ALIGN_TOP | STORE_RIGHT_CORNER | OFFSET_SCALE_LEFT, 640.0f, 3 } }, - - // ui_loading - { HashStr("ui_loading/bg_1"), { STRETCH } }, - { HashStr("ui_loading/bg_1/arrow"), { STRETCH | LOADING_ARROW } }, - { HashStr("ui_loading/bg_2"), { STRETCH } }, - { HashStr("ui_loading/bg_2/arrow"), { STRETCH | LOADING_ARROW } }, - { HashStr("ui_loading/n_2_d/bg/sky"), { STRETCH } }, - { HashStr("ui_loading/n_2_d/bg/under"), { STRETCH } }, - { HashStr("ui_loading/n_2_d/letterbox/letterbox_under"), { STRETCH } }, - { HashStr("ui_loading/n_2_d/letterbox/letterbox_top"), { STRETCH } }, - { HashStr("ui_loading/n_2_d/letterbox/black_l"), { EXTEND_LEFT | STRETCH_VERTICAL } }, - { HashStr("ui_loading/n_2_d/letterbox/black_r"), { EXTEND_RIGHT | STRETCH_VERTICAL } }, - { HashStr("ui_loading/event_viewer/black/black_top"), { LOADING_BLACK_BAR_MIN } }, - { HashStr("ui_loading/event_viewer/black/black_under"), { LOADING_BLACK_BAR_MAX } }, - { HashStr("ui_loading/pda/pda_frame/L"), { LOADING_BLACK_BAR_MIN } }, - { HashStr("ui_loading/pda/pda_frame/R"), { LOADING_BLACK_BAR_MAX } }, - - // ui_mediaroom - { HashStr("ui_mediaroom/header/bg/img_1"), { EXTEND_LEFT } }, - { HashStr("ui_mediaroom/header/bg/img_10"), { EXTEND_RIGHT } }, - { HashStr("ui_mediaroom/header/frame/img_1"), { EXTEND_LEFT } }, - { HashStr("ui_mediaroom/header/frame/img_5"), { EXTEND_RIGHT } }, - - // ui_missionscreen - { HashStr("ui_missionscreen/player_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_missionscreen/time_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_missionscreen/time_count/position_S/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/time_count/position_S/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/time_count/position_S/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/time_count/position_S/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/time_count/position_L/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/time_count/position_L/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/time_count/position_L/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/time_count/position_L/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/laptime_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_missionscreen/laptime_count/position_S/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/laptime_count/position_S/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/laptime_count/position_S/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/laptime_count/position_S/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/laptime_count/position_L/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/laptime_count/position_L/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/laptime_count/position_L/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/laptime_count/position_L/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/score_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_missionscreen/score_count/position_S/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/score_count/position_S/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/score_count/position_S/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/score_count/position_S/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/score_count/position_L/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/score_count/position_L/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/score_count/position_L/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/score_count/position_L/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/item_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_missionscreen/item_count/position_L/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/item_count/position_L/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/item_count/position_L/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/item_count/position_L/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_missionscreen/lap_count"), { ALIGN_TOP_RIGHT | SCALE } }, - { HashStr("ui_missionscreen/lap_count/position/bar"), { ALIGN_RIGHT | SCALE | EXTEND_RIGHT } }, - { HashStr("ui_missionscreen/lap_count/position/bar/R"), { SKIP } }, - - // ui_misson - { HashStr("ui_misson/bg"), { STRETCH } }, - { HashStr("ui_misson/footer/footer_B"), { ALIGN_BOTTOM } }, - { HashStr("ui_misson/header/misson_title_B"), { ALIGN_TOP | OFFSET_SCALE_LEFT, 638.0f } }, - { HashStr("ui_misson/header/misson_title_B/title_bg/center"), { ALIGN_TOP | EXTEND_LEFT } }, - { HashStr("ui_misson/header/misson_title_B/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT} }, - { HashStr("ui_misson/header/misson_title_B/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } }, - { HashStr("ui_misson/window/bg_B2/position/bg"), { STRETCH } }, - - // ui_pause - { HashStr("ui_pause/bg"), { STRETCH } }, - { HashStr("ui_pause/footer/footer_A"), { ALIGN_BOTTOM } }, - { HashStr("ui_pause/footer/footer_B"), { ALIGN_BOTTOM } }, - { HashStr("ui_pause/header/status_title"), { ALIGN_TOP | OFFSET_SCALE_LEFT, 585.0f } }, - { HashStr("ui_pause/header/status_title/title_bg/center"), { ALIGN_TOP | EXTEND_LEFT } }, - { HashStr("ui_pause/header/status_title/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT} }, - { HashStr("ui_pause/header/status_title/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } }, - - // ui_playscreen - { HashStr("ui_playscreen/player_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_playscreen/time_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_playscreen/time_count/position_s/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen/time_count/position_s/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen/time_count/position_s/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen/time_count/position_s/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen/score_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_playscreen/score_count/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen/score_count/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen/score_count/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen/score_count/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen/exp_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_playscreen/exp_count/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen/exp_count/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen/exp_count/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen/exp_count/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen/so_speed_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen/so_ringenagy_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen/gauge_frame"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen/ring_count"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen/ring_get"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen/add/speed_count"), { ALIGN_RIGHT | SCALE } }, - { HashStr("ui_playscreen/add/speed_count/position/bar"), { ALIGN_RIGHT | SCALE | EXTEND_RIGHT } }, - { HashStr("ui_playscreen/add/speed_count/position/bar/R"), { SKIP } }, - { HashStr("ui_playscreen/add/speed_count/position/bar_pale"), { ALIGN_RIGHT | SCALE | EXTEND_RIGHT } }, - { HashStr("ui_playscreen/add/speed_count/position/bar_pale/R"), { SKIP } }, - { HashStr("ui_playscreen/add/u_info"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("ui_playscreen/add/u_info/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } }, - { HashStr("ui_playscreen/add/u_info/position/bar/R"), { SKIP } }, - { HashStr("ui_playscreen/add/medal_get_s"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("ui_playscreen/add/medal_get_s/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } }, - { HashStr("ui_playscreen/add/medal_get_s/position/bar/R"), { SKIP } }, - { HashStr("ui_playscreen/add/medal_get_m"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("ui_playscreen/add/medal_get_m/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } }, - { HashStr("ui_playscreen/add/medal_get_m/position/bar/R"), { SKIP } }, - - // ui_playscreen_ev - { HashStr("ui_playscreen_ev/player_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/score_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/score_count/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen_ev/score_count/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen_ev/score_count/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen_ev/score_count/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen_ev/ring_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/ring_count/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen_ev/ring_count/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen_ev/ring_count/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen_ev/ring_count/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen_ev/ring_get"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/exp_count"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/exp_count/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen_ev/exp_count/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen_ev/exp_count/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen_ev/exp_count/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | EXTEND_LEFT } }, - { HashStr("ui_playscreen_ev/add/u_info"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("ui_playscreen_ev/add/u_info/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } }, - { HashStr("ui_playscreen_ev/add/u_info/position/bar/R"), { SKIP } }, - { HashStr("ui_playscreen_ev/add/medal_get_s"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("ui_playscreen_ev/add/medal_get_s/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } }, - { HashStr("ui_playscreen_ev/add/medal_get_s/position/bar/R"), { SKIP } }, - { HashStr("ui_playscreen_ev/add/medal_get_m"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("ui_playscreen_ev/add/medal_get_m/position/bar"), { ALIGN_BOTTOM_RIGHT | SCALE | EXTEND_RIGHT } }, - { HashStr("ui_playscreen_ev/add/medal_get_m/position/bar/R"), { SKIP } }, - { HashStr("ui_playscreen_ev/gauge/unleash_bg"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/life_bg"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/unleash_body"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/unleash_bar_1"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/unleash_gauge"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/unleash_gauge_effect_2"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/unleash_gauge_effect"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/unleash_bar_2"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/life"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield_position"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_01"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_02"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_03"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_04"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_05"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_06"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_07"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_08"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_09"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_10"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_11"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_12"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_13"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_14"), { ALIGN_BOTTOM_LEFT | SCALE } }, - { HashStr("ui_playscreen_ev/gauge/shield/shield_15"), { ALIGN_BOTTOM_LEFT | SCALE } }, - - // ui_playscreen_ev_hit - { HashStr("ui_playscreen_ev_hit/hit_counter_bg"), { ALIGN_RIGHT | SCALE } }, - { HashStr("ui_playscreen_ev_hit/hit_counter_bg/C"), { ALIGN_RIGHT | SCALE | EXTEND_RIGHT } }, - { HashStr("ui_playscreen_ev_hit/hit_counter_bg/C/L"), { ALIGN_RIGHT | SCALE } }, - { HashStr("ui_playscreen_ev_hit/hit_counter_bg/C/R"), { SKIP } }, - { HashStr("ui_playscreen_ev_hit/hit_counter_num"), { ALIGN_RIGHT | SCALE } }, - { HashStr("ui_playscreen_ev_hit/hit_counter_txt_1"), { ALIGN_RIGHT | SCALE } }, - { HashStr("ui_playscreen_ev_hit/hit_counter_txt_2"), { ALIGN_RIGHT | SCALE } }, - { HashStr("ui_playscreen_ev_hit/chance_attack"), { ALIGN_RIGHT | SCALE } }, - - // ui_playscreen_su - { HashStr("ui_playscreen_su/su_sonic_gauge"), { ALIGN_BOTTOM_LEFT | SCALE | OFFSET_SCALE_LEFT, 632.0f } }, - { HashStr("ui_playscreen_su/su_sonic_gauge/position/C/R"), { ALIGN_BOTTOM_LEFT | SCALE | STORE_RIGHT_CORNER } }, - { HashStr("ui_playscreen_su/gaia_gauge"), { ALIGN_BOTTOM_LEFT | SCALE | OFFSET_SCALE_LEFT, 632.0f } }, - { HashStr("ui_playscreen_su/gaia_gauge/position/C/R"), { ALIGN_BOTTOM_LEFT | SCALE | STORE_RIGHT_CORNER } }, - { HashStr("ui_playscreen_su/footer"), { ALIGN_BOTTOM_RIGHT | SCALE | CONTROL_TUTORIAL } }, - - // ui_prov_playscreen - { HashStr("ui_prov_playscreen/so_speed_gauge"), { ALIGN_BOTTOM_LEFT | SCALE | TORNADO_DEFENSE } }, - { HashStr("ui_prov_playscreen/so_ringenagy_gauge"), { ALIGN_BOTTOM_LEFT | SCALE | TORNADO_DEFENSE } }, - { HashStr("ui_prov_playscreen/bg"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE } }, - { HashStr("ui_prov_playscreen/info_1"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE } }, - { HashStr("ui_prov_playscreen/info_1/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } }, - { HashStr("ui_prov_playscreen/info_1/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } }, - { HashStr("ui_prov_playscreen/info_1/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } }, - { HashStr("ui_prov_playscreen/info_1/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } }, - { HashStr("ui_prov_playscreen/info_2"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE } }, - { HashStr("ui_prov_playscreen/info_2/position/bg_1"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } }, - { HashStr("ui_prov_playscreen/info_2/position/bg_1/C_h"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } }, - { HashStr("ui_prov_playscreen/info_2/position/bg_2"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } }, - { HashStr("ui_prov_playscreen/info_2/position/bg_2/C_h"), { ALIGN_TOP_LEFT | SCALE | TORNADO_DEFENSE | EXTEND_LEFT } }, - { HashStr("ui_prov_playscreen/ring_get_effect"), { ALIGN_BOTTOM_LEFT | SCALE | TORNADO_DEFENSE } }, - - // ui_result - { HashStr("ui_result/footer/result_footer"), { ALIGN_BOTTOM } }, - { HashStr("ui_result/main/result_title"), { ALIGN_TOP | OFFSET_SCALE_LEFT, 688.0f } }, - { HashStr("ui_result/main/result_title/title_bg/center"), { ALIGN_TOP | EXTEND_LEFT } }, - { HashStr("ui_result/main/result_title/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT} }, - { HashStr("ui_result/main/result_title/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } }, - { HashStr("ui_result/main/result_num_1"), { CORNER_EXTRACT } }, - { HashStr("ui_result/main/result_num_1/num_bg"), { OFFSET_SCALE_RIGHT, 669.0f } }, - { HashStr("ui_result/main/result_num_1/num_bg/position_1/center_1"), { EXTEND_RIGHT } }, - { HashStr("ui_result/main/result_num_1/num_bg/position_1/center_1/h_light"), { EXTEND_RIGHT } }, - { HashStr("ui_result/main/result_num_1/num_bg/position_1/center_1/left"), { STORE_LEFT_CORNER } }, - { HashStr("ui_result/main/result_num_1/num_bg/position_1/center_1/right"), { SKIP } }, - { HashStr("ui_result/main/result_num_1/num_bg/position_1/center_1/right/h_light"), { SKIP } }, - { HashStr("ui_result/main/result_num_2"), { CORNER_EXTRACT } }, - { HashStr("ui_result/main/result_num_2/num_bg"), { OFFSET_SCALE_RIGHT, 669.0f } }, - { HashStr("ui_result/main/result_num_2/num_bg/position_2/center_1"), { EXTEND_RIGHT } }, - { HashStr("ui_result/main/result_num_2/num_bg/position_2/center_1/h_light"), { EXTEND_RIGHT } }, - { HashStr("ui_result/main/result_num_2/num_bg/position_2/center_1/left"), { STORE_LEFT_CORNER } }, - { HashStr("ui_result/main/result_num_2/num_bg/position_2/center_1/right"), { SKIP } }, - { HashStr("ui_result/main/result_num_2/num_bg/position_2/center_1/right/h_light"), { SKIP } }, - { HashStr("ui_result/main/result_num_3"), { CORNER_EXTRACT } }, - { HashStr("ui_result/main/result_num_3/num_bg"), { OFFSET_SCALE_RIGHT, 669.0f } }, - { HashStr("ui_result/main/result_num_3/num_bg/position_3/center_1"), { EXTEND_RIGHT } }, - { HashStr("ui_result/main/result_num_3/num_bg/position_3/center_1/h_light"), { EXTEND_RIGHT } }, - { HashStr("ui_result/main/result_num_3/num_bg/position_3/center_1/left"), { STORE_LEFT_CORNER } }, - { HashStr("ui_result/main/result_num_3/num_bg/position_3/center_1/right"), { SKIP } }, - { HashStr("ui_result/main/result_num_3/num_bg/position_3/center_1/right/h_light"), { SKIP } }, - { HashStr("ui_result/main/result_num_4"), { CORNER_EXTRACT } }, - { HashStr("ui_result/main/result_num_4/num_bg"), { OFFSET_SCALE_RIGHT, 669.0f } }, - { HashStr("ui_result/main/result_num_4/num_bg/position_4/center_1"), { EXTEND_RIGHT } }, - { HashStr("ui_result/main/result_num_4/num_bg/position_4/center_1/h_light"), { EXTEND_RIGHT } }, - { HashStr("ui_result/main/result_num_4/num_bg/position_4/center_1/left"), { STORE_LEFT_CORNER } }, - { HashStr("ui_result/main/result_num_4/num_bg/position_4/center_1/right"), { SKIP } }, - { HashStr("ui_result/main/result_num_4/num_bg/position_4/center_1/right/h_light"), { SKIP } }, - { HashStr("ui_result/main/result_num_5"), { CORNER_EXTRACT } }, - { HashStr("ui_result/main/result_num_5/num_bg"), { OFFSET_SCALE_RIGHT, 669.0f } }, - { HashStr("ui_result/main/result_num_5/num_bg/position_5/center_1"), { EXTEND_RIGHT } }, - { HashStr("ui_result/main/result_num_5/num_bg/position_5/center_1/h_light"), { EXTEND_RIGHT } }, - { HashStr("ui_result/main/result_num_5/num_bg/position_5/center_1/left"), { STORE_LEFT_CORNER } }, - { HashStr("ui_result/main/result_num_5/num_bg/position_5/center_1/right"), { SKIP } }, - { HashStr("ui_result/main/result_num_5/num_bg/position_5/center_1/right/h_light"), { SKIP } }, - { HashStr("ui_result/main/result_num_6"), { CORNER_EXTRACT } }, - { HashStr("ui_result/main/result_num_6/num_bg"), { OFFSET_SCALE_LEFT, 1094.0f } }, - { HashStr("ui_result/main/result_num_6/num_bg/position_6/center"), { EXTEND_LEFT } }, - { HashStr("ui_result/main/result_num_6/num_bg/position_6/center/h_light"), { EXTEND_LEFT } }, - { HashStr("ui_result/main/result_num_6/num_bg/position_6/center/right"), { STORE_RIGHT_CORNER } }, - { HashStr("ui_result/main/result_num_6/num_bg/position_6/center/left"), { SKIP } }, - { HashStr("ui_result/main/result_num_6/num_bg/position_6/center/left/h_light"), { SKIP } }, - { HashStr("ui_result/newRecode/result_newR/position/newR_brilliance1"), { OFFSET_SCALE_RIGHT | STORE_LEFT_CORNER, 458.4f } }, - { HashStr("ui_result/newRecode/result_newR/position/newR_brilliance2"), { OFFSET_SCALE_RIGHT | STORE_LEFT_CORNER, 458.4f } }, - { HashStr("ui_result/newRecode/result_newR/position/newR_brilliance3"), { OFFSET_SCALE_RIGHT | STORE_LEFT_CORNER, 458.4f } }, - - // ui_result_ex - { HashStr("ui_result_ex/footer/result_footer"), { ALIGN_BOTTOM } }, - { HashStr("ui_result_ex/main/result_title"), { ALIGN_TOP | OFFSET_SCALE_LEFT, 688.0f } }, - { HashStr("ui_result_ex/main/result_title/title_bg/center"), { ALIGN_TOP | EXTEND_LEFT } }, - { HashStr("ui_result_ex/main/result_title/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT} }, - { HashStr("ui_result_ex/main/result_title/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } }, - { HashStr("ui_result_ex/main/number/result_num_1"), { OFFSET_SCALE_LEFT | OFFSET_SCALE_RIGHT, 669.0f } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_1"), { OFFSET_SCALE_RIGHT, 669.0f, 0 } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_1/center_1"), { EXTEND_RIGHT } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_1/center_1/h_light"), { EXTEND_RIGHT } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_1/center_1/left"), { STORE_LEFT_CORNER, 0, 0 } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_1/center_1/right"), { SKIP } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_1/center_1/right/h_light"), { SKIP } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_2"), { OFFSET_SCALE_RIGHT, 669.0f, 1 } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_2/center_1"), { EXTEND_RIGHT } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_2/center_1/h_light"), { EXTEND_RIGHT } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_2/center_1/left"), { STORE_LEFT_CORNER, 0, 1 } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_2/center_1/right"), { SKIP } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_2/center_1/right/h_light"), { SKIP } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_3"), { OFFSET_SCALE_RIGHT, 669.0f, 2 } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_3/center_1"), { EXTEND_RIGHT } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_3/center_1/h_light"), { EXTEND_RIGHT } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_3/center_1/left"), { STORE_LEFT_CORNER, 0, 2 } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_3/center_1/right"), { SKIP } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_3/center_1/right/h_light"), { SKIP } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_4"), { OFFSET_SCALE_RIGHT, 669.0f, 3 } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_4/center_1"), { EXTEND_RIGHT } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_4/center_1/h_light"), { EXTEND_RIGHT } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_4/center_1/left"), { STORE_LEFT_CORNER, 0, 3 } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_4/center_1/right"), { SKIP } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_4/center_1/right/h_light"), { SKIP } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_6"), { OFFSET_SCALE_LEFT, 1094.0f, 4 } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_6/center"), { EXTEND_LEFT } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_6/center/right"), { STORE_RIGHT_CORNER, 0, 4 } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_6/center/h_light"), { EXTEND_LEFT } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_6/center/left"), { SKIP } }, - { HashStr("ui_result_ex/main/number/result_num_1/position_6/center/left/h_light"), { SKIP } }, - { HashStr("ui_result_ex/newRecode/result_newR/position/newR_brilliance1"), { OFFSET_SCALE_RIGHT | STORE_LEFT_CORNER, 458.4f } }, - { HashStr("ui_result_ex/newRecode/result_newR/position/newR_brilliance2"), { OFFSET_SCALE_RIGHT | STORE_LEFT_CORNER, 458.4f } }, - { HashStr("ui_result_ex/newRecode/result_newR/position/newR_brilliance3"), { OFFSET_SCALE_RIGHT | STORE_LEFT_CORNER, 458.4f } }, - - // ui_shop - { HashStr("ui_shop/footer/shop_footer"), { ALIGN_BOTTOM } }, - { HashStr("ui_shop/header/ring"), { ALIGN_TOP } }, - { HashStr("ui_shop/header/shop_nametag"), { ALIGN_TOP } }, - - // ui_start - { HashStr("ui_start/Clear/position/bg/bg_1"), { STRETCH } }, - { HashStr("ui_start/Clear/position/bg/bg_2"), { STRETCH } }, - { HashStr("ui_start/Start/img/bg/bg_1"), { STRETCH } }, - { HashStr("ui_start/Start/img/bg/bg_2"), { STRETCH } }, - - // ui_status - { HashStr("ui_status/footer/status_footer"), { ALIGN_BOTTOM } }, - { HashStr("ui_status/header/status_title"), { ALIGN_TOP | OFFSET_SCALE_LEFT, 617.0f } }, - { HashStr("ui_status/header/status_title/title_bg/center"), { ALIGN_TOP | EXTEND_LEFT } }, - { HashStr("ui_status/header/status_title/title_bg/center/h_light"), { ALIGN_TOP | EXTEND_LEFT } }, - { HashStr("ui_status/header/status_title/title_bg/right"), { ALIGN_TOP | STORE_RIGHT_CORNER } }, - { HashStr("ui_status/logo/logo/bg_position/c_1"), { STRETCH_HORIZONTAL } }, - { HashStr("ui_status/logo/logo/bg_position/c_2"), { STRETCH_HORIZONTAL } }, - { HashStr("ui_status/main/arrow_effect/a_efc_1"), { OFFSET_SCALE_LEFT, 866.0f } }, - { HashStr("ui_status/main/arrow_effect/a_efc_1/position/img_01"), { STORE_RIGHT_CORNER } }, - { HashStr("ui_status/main/progless/bg/prgs_bg_1"), { OFFSET_SCALE_LEFT, 714.0f } }, - { HashStr("ui_status/main/progless/bg/prgs_bg_1/position/center/right"), { STORE_RIGHT_CORNER } }, - { HashStr("ui_status/main/progless/prgs/prgs_bar_1"), { OFFSET_SCALE_LEFT, 586.0f } }, - { HashStr("ui_status/main/progless/prgs/prgs_bar_1/position/bg/center/right"), { STORE_RIGHT_CORNER } }, - { HashStr("ui_status/main/tag/bg/tag_bg_1"), { OFFSET_SCALE_LEFT, 413.0f } }, - { HashStr("ui_status/main/tag/bg/tag_bg_1/total_1_bg/center"), { EXTEND_LEFT } }, - { HashStr("ui_status/main/tag/bg/tag_bg_1/total_1_bg/center/h_light"), { EXTEND_LEFT } }, - { HashStr("ui_status/main/tag/bg/tag_bg_1/total_1_bg/center/right"), { STORE_RIGHT_CORNER } }, - { HashStr("ui_status/main/tag/bg/tag_bg_1/total_1_bg/center/left"), { SKIP } }, - { HashStr("ui_status/main/tag/bg/tag_bg_1/total_1_bg/center/left/h_light"), { SKIP } }, - { HashStr("ui_status/main/tag/txt/tag_txt_1"), { OFFSET_SCALE_LEFT, 352.0f } }, - { HashStr("ui_status/main/tag/txt/tag_txt_1/position/img"), { STORE_RIGHT_CORNER } }, - { HashStr("ui_status/window/bg"), { STRETCH } }, - - // ui_title - { HashStr("ui_title/bg/bg"), { STRETCH } }, - { HashStr("ui_title/bg/headr"), { ALIGN_TOP | STRETCH_HORIZONTAL } }, - { HashStr("ui_title/bg/footer"), { ALIGN_BOTTOM | STRETCH_HORIZONTAL } }, - { HashStr("ui_title/bg/position"), { ALIGN_BOTTOM } }, - - // ui_townscreen - { HashStr("ui_townscreen/time"), { ALIGN_TOP_RIGHT | SCALE } }, - { HashStr("ui_townscreen/time_effect"), { ALIGN_TOP_RIGHT | SCALE } }, - { HashStr("ui_townscreen/info"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("ui_townscreen/cam"), { ALIGN_TOP_RIGHT | SCALE } }, - { HashStr("ui_townscreen/footer"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - - // ui_worldmap - { HashStr("ui_worldmap/contents/choices/cts_choices_bg"), { STRETCH } }, - { HashStr("ui_worldmap/contents/info/bg/cts_info_bg"), { ALIGN_TOP_LEFT | WORLD_MAP } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1"), { ALIGN_TOP_LEFT | WORLD_MAP } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_01/row_01/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_01/row_02/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_01/row_03/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_01/row_04/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_02/row_01/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_02/row_02/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_02/row_03/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_02/row_04/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_03/row_01/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_03/row_02/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_03/row_03/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_03/row_04/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_04/row_01/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_04/row_02/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_04/row_03/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/bg/info_bg_1/position_04/row_04/img_12"), { ALIGN_TOP_LEFT | WORLD_MAP | REPEAT_LEFT } }, - { HashStr("ui_worldmap/contents/info/img/info_img_1"), { ALIGN_TOP_LEFT | WORLD_MAP } }, - { HashStr("ui_worldmap/contents/info/img/info_img_2"), { ALIGN_TOP_LEFT | WORLD_MAP } }, - { HashStr("ui_worldmap/contents/info/img/info_img_3"), { ALIGN_TOP_LEFT | WORLD_MAP } }, - { HashStr("ui_worldmap/contents/info/img/info_img_4"), { ALIGN_TOP_LEFT | WORLD_MAP } }, - { HashStr("ui_worldmap/footer/worldmap_footer_bg"), { ALIGN_BOTTOM } }, - { HashStr("ui_worldmap/footer/worldmap_footer_img_A"), { ALIGN_BOTTOM } }, - { HashStr("ui_worldmap/header/worldmap_header_bg"), { ALIGN_TOP } }, - { HashStr("ui_worldmap/header/worldmap_header_img"), { ALIGN_TOP_LEFT | WORLD_MAP } }, - { HashStr("ui_worldmap/header/worldmap_header_img/head_icon"), { SKIP } }, - - // ui_worldmap_help - { HashStr("ui_worldmap_help/balloon/help_window/position/msg_bg_l"), { EXTEND_LEFT } }, - { HashStr("ui_worldmap_help/balloon/help_window/position/msg_bg_r"), { EXTEND_RIGHT } }, -}; - -static std::optional FindModifier(uint32_t data) -{ - XXH64_hash_t path; + uint32_t motionPattern = PPC_LOAD_U32(ctx.r3.u32 + 0x70); + if (motionPattern != NULL) { - std::lock_guard lock(g_pathMutex); - - auto findResult = g_paths.find(g_memory.Translate(data)); - if (findResult == g_paths.end()) - return {}; + uint32_t member = PPC_LOAD_U32(motionPattern + 8); + if (member != NULL) + { + uint32_t x = PPC_LOAD_U32(member + 0x2C); + uint32_t y = PPC_LOAD_U32(member + 0x30); - path = findResult->second; + g_scenePositionX = 1280.0f * reinterpret_cast(x); + g_scenePositionY = 720.0f * reinterpret_cast(y); + } } - auto findResult = g_modifiers.find(path); - if (findResult != g_modifiers.end()) - return findResult->second; - - return {}; + __imp__sub_825CB378(ctx, base); } -static std::optional g_sceneModifier; -static float g_corners[8]; -static bool g_cornerExtract; +// Chao::CSD::Scene::Render +PPC_FUNC_IMPL(__imp__sub_828C8F60); +PPC_FUNC(sub_828C8F60) +{ + g_sceneModifier = FindCsdModifier(ctx.r3.u32); -//#define CORNER_DEBUG + if (g_sceneModifier.has_value()) + { + if (g_aspectRatio > WIDE_ASPECT_RATIO && (g_sceneModifier->Flags & (OFFSET_SCALE_LEFT | OFFSET_SCALE_RIGHT | CORNER_EXTRACT)) != 0) + { + auto r3 = ctx.r3; + auto r4 = ctx.r4; + auto r5 = ctx.r5; + auto r6 = ctx.r6; -// Explicit translations don't get affected by gameplay UI downscaling. -// This is required for the medal info in pause menu. -static float g_scenePositionX; -static float g_scenePositionY; + // Queue draw calls, but don't actually draw anything. We just want to extract the corner. + g_cornerExtract = true; + __imp__sub_828C8F60(ctx, base); + g_cornerExtract = false; -// Chao::CSD::CScene::Render -// PPC_FUNC_IMPL(__imp__sub_830BC640); -// PPC_FUNC(sub_830BC640) -// { -// g_scenePositionX = 0.0f; -// g_scenePositionY = 0.0f; - -// uint32_t motionPattern = PPC_LOAD_U32(ctx.r3.u32 + 0x98); -// if (motionPattern != NULL) -// { -// uint32_t member = PPC_LOAD_U32(motionPattern + 0xC); -// if (member != NULL) -// { -// uint32_t x = PPC_LOAD_U32(member + 0x2C); -// uint32_t y = PPC_LOAD_U32(member + 0x30); - -// g_scenePositionX = 1280.0f * reinterpret_cast(x); -// g_scenePositionY = 720.0f * reinterpret_cast(y); -// } -// } - -// __imp__sub_830BC640(ctx, base); -// } +#ifdef CORNER_DEBUG + if (g_sceneModifier->cornerMax == FLT_MAX) + { + fmt::print("Corners: "); + for (auto corner : g_corners) + fmt::print("{} ", corner); -// Chao::CSD::Scene::Render -// PPC_FUNC_IMPL(__imp__sub_830C6A00); -// PPC_FUNC(sub_830C6A00) -// { -// g_sceneModifier = FindModifier(ctx.r3.u32); - -// if (g_sceneModifier.has_value()) -// { -// if (!Config::ControlTutorial && (g_sceneModifier->flags & CONTROL_TUTORIAL) != 0) -// { -// return; -// } - -// // Tornado Defense bugs out when applying gameplay UI scaling. -// // This seems consistent with base game behavior, because the UI -// // is normally squashed, which was probably done to work around this. -// if ((g_sceneModifier->flags & TORNADO_DEFENSE) != 0) -// { -// g_scenePositionX = 0.0f; -// g_scenePositionY = 0.0f; -// } - -// if (g_aspectRatio > WIDE_ASPECT_RATIO && (g_sceneModifier->flags & (OFFSET_SCALE_LEFT | OFFSET_SCALE_RIGHT | CORNER_EXTRACT)) != 0) -// { -// auto r3 = ctx.r3; -// auto r4 = ctx.r4; -// auto r5 = ctx.r5; -// auto r6 = ctx.r6; - -// // Queue draw calls, but don't actually draw anything. We just want to extract the corner. -// g_cornerExtract = true; -// __imp__sub_830C6A00(ctx, base); -// g_cornerExtract = false; - -// #ifdef CORNER_DEBUG -// if (g_sceneModifier->cornerMax == FLT_MAX) -// { -// fmt::print("Corners: "); -// for (auto corner : g_corners) -// fmt::print("{} ", corner); - -// fmt::println(""); -// } -// #endif - -// ctx.r3 = r3; -// ctx.r4 = r4; -// ctx.r5 = r5; -// ctx.r6 = r6; -// } -// } - -// __imp__sub_830C6A00(ctx, base); -// } + fmt::println(""); + } +#endif -static std::optional g_castNodeModifier; + ctx.r3 = r3; + ctx.r4 = r4; + ctx.r5 = r5; + ctx.r6 = r6; + } + } + + __imp__sub_828C8F60(ctx, base); +} void RenderCsdCastNodeMidAsmHook(PPCRegister& r10, PPCRegister& r27) { - g_castNodeModifier = FindModifier(r10.u32 + r27.u32); + g_castNodeModifier = FindCsdModifier(r10.u32 + r27.u32); } -static std::optional g_castModifier; - void RenderCsdCastMidAsmHook(PPCRegister& r4) { - g_castModifier = FindModifier(r4.u32); + g_castModifier = FindCsdModifier(r4.u32); } static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) @@ -930,17 +300,17 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str modifier = g_sceneModifier.value(); } - if ((modifier.flags & SKIP) != 0) + if ((modifier.Flags & SKIP) != 0) { return; } if (g_cornerExtract) { - if ((modifier.flags & (STORE_LEFT_CORNER | STORE_RIGHT_CORNER)) != 0) + if ((modifier.Flags & (STORE_LEFT_CORNER | STORE_RIGHT_CORNER)) != 0) { - uint32_t vertexIndex = ((modifier.flags & STORE_LEFT_CORNER) != 0) ? 0 : 3; - g_corners[modifier.cornerIndex] = *reinterpret_cast*>(base + ctx.r4.u32 + vertexIndex * stride); + uint32_t vertexIndex = ((modifier.Flags & STORE_LEFT_CORNER) != 0) ? 0 : 3; + g_corners[modifier.CornerIndex] = *reinterpret_cast*>(base + ctx.r4.u32 + vertexIndex * stride); } return; @@ -949,12 +319,9 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str if (Config::UIAlignmentMode == EUIAlignmentMode::Centre) { if (g_aspectRatio >= WIDE_ASPECT_RATIO) - modifier.flags &= ~(ALIGN_LEFT | ALIGN_RIGHT); + modifier.Flags &= ~(ALIGN_LEFT | ALIGN_RIGHT); } - // Tornado Defense UI is squashed at 4:3, presumably to work around an explicit translation issue. - bool squash = Config::AspectRatio == EAspectRatio::OriginalNarrow && (modifier.flags & TORNADO_DEFENSE) != 0; - uint32_t size = ctx.r5.u32 * stride; ctx.r1.u32 -= size; @@ -962,25 +329,9 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str memcpy(stack, base + ctx.r4.u32, size); auto getPosition = [&](size_t index) - { - return reinterpret_cast*>(stack + index * stride); - }; - - // Subtract half a pixel from loading arrows to prevent transparent pixels surrounding them from leaking into the texture filtering. - if (Video::s_viewportHeight > 720 && (modifier.flags & LOADING_ARROW) != 0 && stride == 0x14) { - for (size_t i = 0; i < 4; i++) - { - auto texCoord = getPosition(i) + 4; - - constexpr float OFFSET = 0.5f / 720.0f; - - if (i == 0 || i == 2) // Top - *texCoord = (*texCoord + OFFSET); - else // Bottom - *texCoord = (*texCoord - OFFSET); - } - } + return reinterpret_cast*>(stack + index * stride); + }; float offsetX = 0.0f; float offsetY = 0.0f; @@ -991,7 +342,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str bool needsStretch = g_aspectRatio >= WIDE_ASPECT_RATIO; - if (squash || (needsStretch && (modifier.flags & STRETCH_HORIZONTAL) != 0)) + if (needsStretch && (modifier.Flags & STRETCH_HORIZONTAL) != 0) { scaleX = Video::s_viewportWidth / 1280.0f; } @@ -999,40 +350,34 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str { scaleX = g_aspectRatioScale; - if (needsStretch && (modifier.flags & UNSTRETCH_HORIZONTAL) != 0) + if (needsStretch && (modifier.Flags & UNSTRETCH_HORIZONTAL) != 0) { pivotX = *getPosition(0); offsetX = pivotX * Video::s_viewportWidth / 1280.0f; } else { - if ((modifier.flags & ALIGN_RIGHT) != 0) + if ((modifier.Flags & ALIGN_RIGHT) != 0) offsetX = g_aspectRatioOffsetX * 2.0f; - else if ((modifier.flags & ALIGN_LEFT) == 0) + else if ((modifier.Flags & ALIGN_LEFT) == 0) offsetX = g_aspectRatioOffsetX; - if ((modifier.flags & SCALE) != 0) + if ((modifier.Flags & SCALE) != 0) { scaleX *= g_aspectRatioGameplayScale; pivotX = g_scenePositionX; - if ((modifier.flags & ALIGN_RIGHT) != 0) + if ((modifier.Flags & ALIGN_RIGHT) != 0) offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; - else if ((modifier.flags & ALIGN_LEFT) == 0) + else if ((modifier.Flags & ALIGN_LEFT) == 0) offsetX += 640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; offsetX += pivotX * g_aspectRatioScale; } - - if ((modifier.flags & WORLD_MAP) != 0) - { - if ((modifier.flags & ALIGN_LEFT) != 0) - offsetX += (1.0f - g_aspectRatioNarrowScale) * g_aspectRatioScale * -20.0f; - } } } - if (squash || ((modifier.flags & STRETCH_VERTICAL) != 0)) + if ((modifier.Flags & STRETCH_VERTICAL) != 0) { scaleY = Video::s_viewportHeight / 720.0f; } @@ -1040,19 +385,19 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str { scaleY = g_aspectRatioScale; - if ((modifier.flags & ALIGN_BOTTOM) != 0) + if ((modifier.Flags & ALIGN_BOTTOM) != 0) offsetY = g_aspectRatioOffsetY * 2.0f; - else if ((modifier.flags & ALIGN_TOP) == 0) + else if ((modifier.Flags & ALIGN_TOP) == 0) offsetY = g_aspectRatioOffsetY; - if ((modifier.flags & SCALE) != 0) + if ((modifier.Flags & SCALE) != 0) { scaleY *= g_aspectRatioGameplayScale; pivotY = g_scenePositionY; - if ((modifier.flags & ALIGN_BOTTOM) != 0) + if ((modifier.Flags & ALIGN_BOTTOM) != 0) offsetY += 720.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; - else if ((modifier.flags & ALIGN_TOP) == 0) + else if ((modifier.Flags & ALIGN_TOP) == 0) offsetY += 360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; offsetY += pivotY * g_aspectRatioScale; @@ -1068,83 +413,99 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str { offsetScaleModifier = g_castModifier.value(); - uint32_t vertexIndex = ((offsetScaleModifier.flags & STORE_LEFT_CORNER) != 0) ? 0 : 3; + uint32_t vertexIndex = ((offsetScaleModifier.Flags & STORE_LEFT_CORNER) != 0) ? 0 : 3; corner = *getPosition(vertexIndex); } - if (offsetScaleModifier.cornerMax == 0.0f && g_castNodeModifier.has_value()) + if (offsetScaleModifier.CornerMax == 0.0f && g_castNodeModifier.has_value()) { offsetScaleModifier = g_castNodeModifier.value(); - corner = g_corners[offsetScaleModifier.cornerIndex]; + corner = g_corners[offsetScaleModifier.CornerIndex]; } - if (offsetScaleModifier.cornerMax == 0.0f && g_sceneModifier.has_value()) + if (offsetScaleModifier.CornerMax == 0.0f && g_sceneModifier.has_value()) { offsetScaleModifier = g_sceneModifier.value(); - corner = g_corners[offsetScaleModifier.cornerIndex]; + corner = g_corners[offsetScaleModifier.CornerIndex]; } #ifdef CORNER_DEBUG - if ((offsetScaleModifier.flags & (OFFSET_SCALE_LEFT | OFFSET_SCALE_RIGHT)) != 0 && offsetScaleModifier.cornerMax == FLT_MAX) + if ((offsetScaleModifier.Flags & (OFFSET_SCALE_LEFT | OFFSET_SCALE_RIGHT)) != 0 && offsetScaleModifier.cornerMax == FLT_MAX) fmt::println("Corner: {}", corner); #endif - if ((offsetScaleModifier.flags & OFFSET_SCALE_LEFT) != 0) - offsetX *= corner / offsetScaleModifier.cornerMax; - else if ((offsetScaleModifier.flags & OFFSET_SCALE_RIGHT) != 0) - offsetX = Video::s_viewportWidth - (Video::s_viewportWidth - offsetX) * (1280.0f - corner) / (1280.0f - offsetScaleModifier.cornerMax); + if ((offsetScaleModifier.Flags & OFFSET_SCALE_LEFT) != 0) + offsetX *= corner / offsetScaleModifier.CornerMax; + else if ((offsetScaleModifier.Flags & OFFSET_SCALE_RIGHT) != 0) + offsetX = Video::s_viewportWidth - (Video::s_viewportWidth - offsetX) * (1280.0f - corner) / (1280.0f - offsetScaleModifier.CornerMax); } + float firstX = 0.0f; + float firstY = 0.0f; + float lastX = 0.0f; + float lastY = 0.0f; + float width = 0.0f; + float height = 0.0f; + for (size_t i = 0; i < ctx.r5.u32; i++) { auto position = getPosition(i); - float x = offsetX + (position[0] - pivotX) * scaleX; - float y = offsetY + (position[1] - pivotY) * scaleY; + float x = offsetX + (position[0] - pivotX) * (scaleX * (1280.0f / Video::s_viewportWidth)); + float y = offsetY + (position[1] - pivotY) * (scaleY * (720.0f / Video::s_viewportHeight)); - if ((modifier.flags & EXTEND_LEFT) != 0 && (i == 0 || i == 1)) + if ((modifier.Flags & EXTEND_LEFT) != 0 && (i == 0 || i == 1)) { x = std::min(x, 0.0f); } - else if ((modifier.flags & EXTEND_RIGHT) != 0 && (i == 2 || i == 3)) + else if ((modifier.Flags & EXTEND_RIGHT) != 0 && (i == 2 || i == 3)) { x = std::max(x, float(Video::s_viewportWidth)); } + switch (i) + { + case 0: firstX = x; break; + case 1: lastY = y; break; + case 2: firstY = y; break; + case 3: lastX = x; break; + } + + if ((modifier.Flags & RADARMAP) != 0) + { + g_radarMapX = x; + g_radarMapY = y; + } + position[0] = round(x); position[1] = round(y); } - if ((modifier.flags & LOADING_BLACK_BAR_MIN) != 0) - { - auto position = getPosition(0); - BlackBar::g_loadingBlackBarMin = ImVec2{ position[0], position[1] }; - BlackBar::g_loadingBlackBarAlpha = *(base + ctx.r1.u32 + 0xB); - } - else if ((modifier.flags & LOADING_BLACK_BAR_MAX) != 0) + width = lastX - firstX; + height = lastY - firstY; + + if ((modifier.Flags & RADARMAP) != 0) { - auto position = getPosition(3); - BlackBar::g_loadingBlackBarMax = ImVec2{ position[0], position[1] }; + g_radarMapCoverWidth = width; + g_radarMapCoverHeight = height; } - if ((modifier.flags & REPEAT_LEFT) != 0) + if ((modifier.Flags & PILLARBOX) != 0) + BlackBar::g_isPillarbox = true; + + // FIXME: currently causes a crash, may need stack alignment. + if ((modifier.Flags & REPEAT_LEFT) != 0) { float width = *getPosition(2) - *getPosition(0); auto r3 = ctx.r3; auto r5 = ctx.r5; - auto r6 = ctx.r6; - auto r7 = ctx.r7; - auto r8 = ctx.r8; while (*getPosition(2) > 0.0f) { ctx.r3 = r3; ctx.r4 = ctx.r1; ctx.r5 = r5; - ctx.r6 = r6; - ctx.r7 = r7; - ctx.r8 = r8; original(ctx, base); for (size_t i = 0; i < ctx.r5.u32; i++) @@ -1161,485 +522,250 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str } } -// SWA::CCsdPlatformMirage::Draw -// PPC_FUNC_IMPL(__imp__sub_825E2E70); -// PPC_FUNC(sub_825E2E70) -// { -// Draw(ctx, base, __imp__sub_825E2E70, 0x14); -// } +// Sonicteam::CPlatformMarathon::Draw +PPC_FUNC_IMPL(__imp__sub_826315C8); +PPC_FUNC(sub_826315C8) +{ + // r3 = Sonicteam::CPlatformMarathon* + // r4 = pointer to vertices + // r5 = vertex count -// // SWA::CCsdPlatformMirage::DrawNoTex -// PPC_FUNC_IMPL(__imp__sub_825E2E88); -// PPC_FUNC(sub_825E2E88) -// { -// Draw(ctx, base, __imp__sub_825E2E88, 0xC); -// } + Draw(ctx, base, __imp__sub_826315C8, 0x14); +} -// // Hedgehog::MirageDebug::SetScissorRect -// PPC_FUNC_IMPL(__imp__sub_82E16C70); -// PPC_FUNC(sub_82E16C70) -// { -// auto scissorRect = reinterpret_cast(base + ctx.r4.u32); +// Sonicteam::CPlatformMarathon::DrawNoTex +PPC_FUNC_IMPL(__imp__sub_82631718); +PPC_FUNC(sub_82631718) +{ + // r3 = Sonicteam::CPlatformMarathon* + // r4 = pointer to vertices + // r5 = vertex count + + Draw(ctx, base, __imp__sub_82631718, 0x0C); +} -// scissorRect->left = scissorRect->left * g_aspectRatioScale + g_aspectRatioOffsetX; -// scissorRect->top = scissorRect->top * g_aspectRatioScale + g_aspectRatioOffsetY; -// scissorRect->right = scissorRect->right * g_aspectRatioScale + g_aspectRatioOffsetX; -// scissorRect->bottom = scissorRect->bottom * g_aspectRatioScale + g_aspectRatioOffsetY; +// Sonicteam::HUDRaderMap::Update +PPC_FUNC_IMPL(__imp__sub_824F1538); +PPC_FUNC(sub_824F1538) +{ + auto pHUDRaderMap = (Sonicteam::HUDRaderMap*)(base + ctx.r3.u32); -// __imp__sub_82E16C70(ctx, base); -// } + __imp__sub_824F1538(ctx, base); -// Store whether the primitive should be stretched in available padding space. -static constexpr size_t PRIMITIVE_2D_PADDING_OFFSET = 0x29; -static constexpr size_t PRIMITIVE_2D_PADDING_SIZE = 0x3; + auto radermapScale = 256 * g_aspectRatioScale; + + pHUDRaderMap->m_pMaskTexture->m_Width = radermapScale; + pHUDRaderMap->m_pMaskTexture->m_Height = radermapScale; + pHUDRaderMap->m_X = g_radarMapX - g_radarMapCoverWidth / 2; + pHUDRaderMap->m_Y = g_radarMapY - g_radarMapCoverHeight / 2; +} -// Hedgehog::MirageDebug::CPrimitive2D::CPrimitive2D -// PPC_FUNC_IMPL(__imp__sub_822D0328); -// PPC_FUNC(sub_822D0328) +// Sonicteam::HUDLoading::HUDLoading +// PPC_FUNC_IMPL(__imp__sub_824D7BC8); +// PPC_FUNC(sub_824D7BC8) // { -// memset(base + ctx.r3.u32 + PRIMITIVE_2D_PADDING_OFFSET, 0, PRIMITIVE_2D_PADDING_SIZE); -// __imp__sub_822D0328(ctx, base); +// App::s_isLoading = true; +// BlackBar::g_isPillarbox = true; +// +// __imp__sub_824D7BC8(ctx, base); // } - -// // Hedgehog::MirageDebug::CPrimitive2D::CPrimitive2D(const Hedgehog::MirageDebug::CPrimitive2D&) -// PPC_FUNC_IMPL(__imp__sub_830D2328); -// PPC_FUNC(sub_830D2328) +// +// // Sonicteam::HUDLoading::~HUDLoading +// PPC_FUNC_IMPL(__imp__sub_824D7520); +// PPC_FUNC(sub_824D7520) // { -// memcpy(base + ctx.r3.u32 + PRIMITIVE_2D_PADDING_OFFSET, base + ctx.r4.u32 + PRIMITIVE_2D_PADDING_OFFSET, PRIMITIVE_2D_PADDING_SIZE); -// __imp__sub_830D2328(ctx, base); +// App::s_isLoading = false; +// BlackBar::g_isPillarbox = false; +// +// __imp__sub_824D7520(ctx, base); // } -void AddPrimitive2DMidAsmHook(PPCRegister& r3) -{ - *(g_memory.base + r3.u32 + PRIMITIVE_2D_PADDING_OFFSET) = 0x01; -} - -// Hedgehog::MirageDebug::CPrimitive2D::Draw -// PPC_FUNC_IMPL(__imp__sub_830D1EF0); -// PPC_FUNC(sub_830D1EF0) +// PPC_FUNC_IMPL(__imp__sub_82167D70); +// PPC_FUNC(sub_82167D70) // { -// auto r3 = ctx.r3; - -// __imp__sub_830D1EF0(ctx, base); - -// struct Vertex -// { -// be x; -// be y; -// be z; -// be w; -// be color; -// be u; -// be v; -// }; - -// auto vertex = reinterpret_cast(base + ctx.r4.u32); - -// for (size_t i = 0; i < 4; i++) -// { -// float x = vertex[i].x * 640.0f + 640.0f; -// float y = vertex[i].y * -360.0f + 360.0f; - -// if (PPC_LOAD_U8(r3.u32 + PRIMITIVE_2D_PADDING_OFFSET)) -// { -// // Stretch -// x = ((x + 0.5f) / 1280.0f) * Video::s_viewportWidth; -// y = ((y + 0.5f) / 720.0f) * Video::s_viewportHeight; -// } -// else -// { -// // Center -// x = g_aspectRatioOffsetX + (x + 0.5f) * g_aspectRatioScale; -// y = g_aspectRatioOffsetY + (y + 0.5f) * g_aspectRatioScale; -// } - -// vertex[i].x = ((x - 0.5f) / Video::s_viewportWidth) * 2.0f - 1.0f; -// vertex[i].y = ((y - 0.5f) / Video::s_viewportHeight) * -2.0f + 1.0f; -// } - -// bool letterboxTop = PPC_LOAD_U8(r3.u32 + PRIMITIVE_2D_PADDING_OFFSET + 0x1); -// bool letterboxBottom = PPC_LOAD_U8(r3.u32 + PRIMITIVE_2D_PADDING_OFFSET + 0x2); - -// if (letterboxTop || letterboxBottom) -// { -// float halfPixelX = 1.0f / Video::s_viewportWidth; -// float halfPixelY = 1.0f / Video::s_viewportHeight; - -// if (letterboxTop) -// { -// vertex[0].x = -1.0f - halfPixelX; -// vertex[0].y = 1.0f + halfPixelY; - -// vertex[1].x = 1.0f - halfPixelX; -// vertex[1].y = 1.0f + halfPixelY; - -// vertex[2].x = -1.0f - halfPixelX; -// // vertex[2].y untouched - -// vertex[3].x = 1.0f - halfPixelX; -// // vertex[3].y untouched -// } -// else if (letterboxBottom) -// { -// vertex[0].x = -1.0f - halfPixelX; -// // vertex[0].y untouched - -// vertex[1].x = 1.0f - halfPixelX; -// // vertex[1].y untouched - -// vertex[2].x = -1.0f - halfPixelX; -// vertex[2].y = -1.0f + halfPixelY; - -// vertex[3].x = 1.0f - halfPixelX; -// vertex[3].y = -1.0f + halfPixelY; -// } -// } +// __imp__sub_82167D70(ctx, base); // } -// Objects are pushed forward by 1m, so the coordinates need to compensate for it. -static const double OBJ_GET_ITEM_TANGENT = tan(M_PI / 8.0); - -// Coordinates are in [-1, 1] range. Automatically fit and centered. -// The tangent is calculated incorrectly in game, causing distortion. -// The hook makes them move to the correct position regardless of FOV. -//static float ComputeObjGetItemTangent(float fieldOfView, float aspectRatio) -//{ -// return tan(AdjustFieldOfView(fieldOfView, aspectRatio) / 2.0) / OBJ_GET_ITEM_TANGENT; -//} -// -//void ObjGetItemFieldOfViewMidAsmHook(PPCRegister& r1, PPCRegister& f1) -//{ -// if (Config::AspectRatio != EAspectRatio::OriginalNarrow) -// *reinterpret_cast*>(g_memory.base + r1.u32 + 0x58) = ComputeObjGetItemTangent(f1.f64, g_aspectRatio); -//} - -static double ComputeObjGetItemX(uint32_t type) +void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity, xxHashMap pftParams) { - if (type >= 47) // Ring, Moon Medal, Sun Medal + if (!pTextEntity || !pTextEntity->m_ImageVertexCount) + return; + + auto variables = MapTextVariables(pTextEntity->m_pVariables); + auto variablesIndex = 0; + auto pictureIndex = 0; + + for (int i = 0; i < pTextEntity->m_TextLength * 2; i++) { - double x; + auto c = ByteSwap(pTextEntity->m_pText[i]); - if (type == 47) // Ring - x = 142.0; - else // Medal - x = 1058.0; + if (c != L'$') + continue; - x *= g_aspectRatioScale; - x *= g_aspectRatioGameplayScale; + // Some strings may have more placeholder + // characters than variables in the map. + if (variablesIndex >= variables.size()) + break; - double scaleOffset = (1280.0 * (1.0 - g_aspectRatioGameplayScale)) * g_aspectRatioScale; + auto& variable = variables[variablesIndex]; - if (Config::UIAlignmentMode == EUIAlignmentMode::Edge) - { - if (type != 47) // Medal - x += g_aspectRatioOffsetX * 2.0 + scaleOffset; - } - else if (Config::UIAlignmentMode == EUIAlignmentMode::Centre) + if (variable.first == "picture") { - x += g_aspectRatioOffsetX + scaleOffset; + auto w = g_pTextFontPicture->m_TextureWidth; + auto h = g_pTextFontPicture->m_TextureHeight; + + auto baseParams = FindFontPictureParams(g_pftParamsXenon, variable.second); + auto newParams = FindFontPictureParams(pftParams, variable.second); + + auto uv = PIXELS_TO_UV_COORDS(w, h, newParams.X, newParams.Y, newParams.Width, newParams.Height); + auto& min = std::get<0>(uv); + auto& max = std::get<1>(uv); + + auto vi = pictureIndex * 6; + auto& v0 = pTextEntity->m_pImageVertices[vi + 0]; + auto& v1 = pTextEntity->m_pImageVertices[vi + 1]; + auto& v2 = pTextEntity->m_pImageVertices[vi + 2]; + auto& v3 = pTextEntity->m_pImageVertices[vi + 3]; + auto& v4 = pTextEntity->m_pImageVertices[vi + 4]; + auto& v5 = pTextEntity->m_pImageVertices[vi + 5]; + + auto centreX = (v0.X + v2.X) / 2.0f; + auto widthAdjust = (float)newParams.Width / (float)baseParams.Width; + auto adjust = [&](auto& vertex) + { + vertex.X = centreX + (vertex.X - centreX) * widthAdjust; + }; + + // Bottom Left (Triangle 1) + adjust(v0); + v0.U = min.x; + v0.V = min.y; + + // Top Left (Triangle 1) + adjust(v1); + v1.U = min.x; + v1.V = max.y; + + // Top Right (Triangle 1) + adjust(v2); + v2.U = max.x; + v2.V = max.y; + + // Bottom Left (Triangle 2) + adjust(v3); + v3.U = min.x; + v3.V = min.y; + + // Top Right (Triangle 2) + adjust(v4); + v4.U = max.x; + v4.V = max.y; + + // Bottom Right (Triangle 2) + adjust(v5); + v5.U = max.x; + v5.V = min.y; + + pictureIndex++; } - return (x - (0.5 * Video::s_viewportWidth)) / (0.5 * Video::s_viewportHeight) * OBJ_GET_ITEM_TANGENT; + variablesIndex++; } - - return 0.0; } -// SWA::CObjGetItem::GetX -// PPC_FUNC_IMPL(__imp__sub_82690660); -// PPC_FUNC(sub_82690660) -// { -// if (Config::AspectRatio == EAspectRatio::OriginalNarrow) -// { -// __imp__sub_82690660(ctx, base); -// return; -// } - -// uint32_t type = PPC_LOAD_U32(ctx.r3.u32 + 0xE8); -// ctx.f1.f64 = ComputeObjGetItemX(type); -// } +static float g_textScaleY; +static float g_textHeight; -static double ComputeObjGetItemY(uint32_t type) +// Sonicteam::TextEntity::Draw +PPC_FUNC_IMPL(__imp__sub_8262D868); +PPC_FUNC(sub_8262D868) { - if (type >= 47) // Ring, Moon Medal, Sun Medal - { - double y; + auto pTextEntity = (Sonicteam::TextEntity*)(base + ctx.r3.u32); - if (type == 47) // Ring - y = 642.0; - else if (type == 48) // Moon Medal - y = 632.0; - else if (type == 49) // Sun Medal - y = 582.0; + LOGFN_UTILITY("TextEntity: 0x{:08X}", (uint64_t)pTextEntity); - y *= g_aspectRatioScale; - y *= g_aspectRatioGameplayScale; - y += g_aspectRatioOffsetY * 2.0 + 720.0 * (1.0 - g_aspectRatioGameplayScale) * g_aspectRatioScale; + constexpr auto baseWidth = 1280.0f; + constexpr auto baseHeight = 720.0f; - return ((0.5 * Video::s_viewportHeight) - y) / (0.5 * Video::s_viewportHeight) * OBJ_GET_ITEM_TANGENT; - } - - return 0.25; -} - -// SWA::CObjGetItem::GetY -// PPC_FUNC_IMPL(__imp__sub_826906A8); -// PPC_FUNC(sub_826906A8) -// { -// if (Config::AspectRatio == EAspectRatio::OriginalNarrow) -// { -// __imp__sub_826906A8(ctx, base); -// // Game scales Y by 1.4 at 4:3 aspect ratio. -// ctx.f1.f64 *= 1.4; -// return; -// } - -// uint32_t type = PPC_LOAD_U32(ctx.r3.u32 + 0xE8); -// ctx.f1.f64 = ComputeObjGetItemY(type); -// } + auto scaleFactor = 1.0f; + auto scaleY = 1.0f; + auto offsetY = (baseHeight - (baseHeight * scaleY)) / 2.0f; -void WorldMapProjectionMidAsmHook(PPCVRegister& v63, PPCVRegister& v62) -{ - // The world map icons are actually broken at 4:3 in the original game!!! - if (Config::AspectRatio != EAspectRatio::OriginalNarrow) + if (g_aspectRatio < WIDE_ASPECT_RATIO) { - v63.f32[3] *= std::max(NARROW_ASPECT_RATIO, g_aspectRatio) / WIDE_ASPECT_RATIO; - v62.f32[2] *= NARROW_ASPECT_RATIO / std::min(NARROW_ASPECT_RATIO, g_aspectRatio); - } -} - -// CViewRing has the same exact incorrect math as CObjGetItem. -//void ViewRingFieldOfViewMidAsmHook(PPCRegister& r1, PPCRegister& f1) -//{ -// if (Config::AspectRatio != EAspectRatio::OriginalNarrow) -// *reinterpret_cast*>(g_memory.base + r1.u32 + 0x54) = ComputeObjGetItemTangent(f1.f64, g_aspectRatio); -//} + scaleFactor = 1.125f; + scaleY = g_aspectRatio / WIDE_ASPECT_RATIO; -void ViewRingYMidAsmHook(PPCRegister& f1) -{ - if (Config::AspectRatio != EAspectRatio::OriginalNarrow) - f1.f64 = -ComputeObjGetItemY(47); // Ring -} + pTextEntity->m_Y = pTextEntity->m_Y * scaleY + offsetY; + pTextEntity->m_ScaleY = scaleY * scaleFactor; -void ViewRingXMidAsmHook(PPCRegister& f1, PPCVRegister& v62) -{ - if (Config::AspectRatio == EAspectRatio::OriginalNarrow) - { - // Game scales Y by 1.4 at 4:3 aspect ratio. - for (size_t i = 0; i < 4; i++) - v62.f32[i] *= 1.4f; - } - else - { - f1.f64 = -ComputeObjGetItemX(47); // Ring + g_textScaleY = pTextEntity->m_ScaleY; } -} - -// SWA::Inspire::CLetterbox::CLetterbox -// PPC_FUNC_IMPL(__imp__sub_82B8A8F8); -// PPC_FUNC(sub_82B8A8F8) -// { -// // Permanently store the letterbox bool instead of letting the game set it to false from widescreen check. -// bool letterbox = PPC_LOAD_U8(ctx.r4.u32); -// __imp__sub_82B8A8F8(ctx, base); -// PPC_STORE_U8(ctx.r3.u32, letterbox); -// } -// // SWA::Inspire::CLetterbox::Draw -// PPC_FUNC_IMPL(__imp__sub_82B8AA40); -// PPC_FUNC(sub_82B8AA40) -// { -// bool letterbox = PPC_LOAD_U8(ctx.r3.u32); -// bool shouldDrawLetterbox = letterbox && Config::CutsceneAspectRatio != ECutsceneAspectRatio::Unlocked && g_aspectRatio < WIDE_ASPECT_RATIO; - -// PPC_STORE_U8(ctx.r3.u32, shouldDrawLetterbox); -// if (shouldDrawLetterbox) -// { -// float aspectRatio = std::max(NARROW_ASPECT_RATIO, g_aspectRatio); -// uint32_t width = aspectRatio * 720; - -// PPC_STORE_U32(ctx.r3.u32 + 0xC, width); -// PPC_STORE_U32(ctx.r3.u32 + 0x10, 720); -// PPC_STORE_U32(ctx.r3.u32 + 0x14, (720 - width * 9 / 16) / 2); -// } - -// auto r3 = ctx.r3; -// __imp__sub_82B8AA40(ctx, base); - -// // Restore the original letterbox value. -// PPC_STORE_U8(r3.u32, letterbox); - -// if (letterbox) -// { -// // Would be nice to also push this as a 2D primitive but I really cannot be bothered right now... -// BlackBar::g_inspirePillarbox = Config::CutsceneAspectRatio != ECutsceneAspectRatio::Unlocked && g_aspectRatio > WIDE_ASPECT_RATIO; -// } -// } + auto scaleX = 1.0f * (WIDE_ASPECT_RATIO / g_aspectRatio) * scaleY; + auto scaledWidth = baseWidth * scaleX; + auto offsetX = (baseWidth - scaledWidth) / 2.0f; -void InspireLetterboxTopMidAsmHook(PPCRegister& r3) -{ - *(g_memory.base + r3.u32 + PRIMITIVE_2D_PADDING_OFFSET + 0x1) = 0x01; // Letterbox Top - *(g_memory.base + r3.u32 + PRIMITIVE_2D_PADDING_OFFSET + 0x2) = 0x00; // Letterbox Bottom -} + pTextEntity->m_X = pTextEntity->m_X * scaleX + offsetX; + pTextEntity->m_ScaleX = scaleX * scaleFactor; -void InspireLetterboxBottomMidAsmHook(PPCRegister& r3) -{ - *(g_memory.base + r3.u32 + PRIMITIVE_2D_PADDING_OFFSET + 0x1) = 0x00; // Letterbox Top - *(g_memory.base + r3.u32 + PRIMITIVE_2D_PADDING_OFFSET + 0x2) = 0x01; // Letterbox Bottom -} + __imp__sub_8262D868(ctx, base); -void InspireSubtitleMidAsmHook(PPCRegister& r3) -{ - constexpr float NARROW_OFFSET = 485.0f; - constexpr float WIDE_OFFSET = 560.0f; + auto& pftParams = hid::g_inputDeviceController == hid::EInputDevice::PlayStation + ? g_pftParamsPS3 + : g_pftParamsXenon; - *reinterpret_cast*>(g_memory.base + r3.u32 + 0x3C) = NARROW_OFFSET + (WIDE_OFFSET - NARROW_OFFSET) * g_aspectRatioNarrowScale; + ReplaceTextVariables(pTextEntity, pftParams); } -enum class FadeTextureMode +// Sonicteam::HUDButtonWindow::Draw +PPC_FUNC_IMPL(__imp__sub_824D4628); +PPC_FUNC(sub_824D4628) { - Unknown, - Letterbox, - SideCrop -}; + auto pHUDButtonWindow = (Sonicteam::HUDButtonWindow*)g_memory.Translate(ctx.r3.u32); -static FadeTextureMode g_fadeTextureMode; + auto width = Video::s_viewportWidth; + auto height = Video::s_viewportHeight; + auto baseWidth = height * WIDE_ASPECT_RATIO; + auto baseHeight = width / WIDE_ASPECT_RATIO; -void FxFadePreRenderQuadMidAsmHook(PPCRegister& r31) -{ - g_fadeTextureMode = *(g_memory.base + r31.u32 + 0x44) ? FadeTextureMode::Letterbox : FadeTextureMode::SideCrop; -} + auto horizontalBar = 0.0f; + auto verticalBar = 0.0f; -void FxFadePostRenderQuadMidAsmHook() -{ - g_fadeTextureMode = FadeTextureMode::Unknown; -} + if (width < baseWidth) + horizontalBar = (baseWidth - width) / 2.0f; -void YggdrasillRenderQuadMidAsmHook(PPCRegister& r3, PPCRegister& r6) -{ - if (g_fadeTextureMode != FadeTextureMode::Unknown) - { - float scaleX = 1.0f; - float scaleY = 1.0f; - - // Fade textures are slightly squashed in the original game at 4:3. - if (Config::AspectRatio == EAspectRatio::OriginalNarrow) - { - if (g_fadeTextureMode == FadeTextureMode::Letterbox) - scaleY = NARROW_ASPECT_RATIO; - else - scaleX = 0.8f; - } - else - { - if (g_fadeTextureMode == FadeTextureMode::Letterbox && g_aspectRatio < WIDE_ASPECT_RATIO) - scaleY = WIDE_ASPECT_RATIO / g_aspectRatio; - else - scaleX = g_aspectRatio / WIDE_ASPECT_RATIO; - } + if (height < baseHeight) + verticalBar = (baseHeight - height) / 2.0f; - struct Vertex - { - be x; - be y; - be z; - be u; - be v; - }; + // LOGFN_UTILITY("vert: {}", verticalBar); + // LOGFN_UTILITY("horz: {}", horizontalBar); - auto vertex = reinterpret_cast(g_memory.base + r6.u32); + // pHUDButtonWindow->m_pHudTextParts->m_OffsetX = -55.0f; - for (size_t i = 0; i < 6; i++) - { - vertex[i].u = (vertex[i].u - 0.5f) * scaleX + 0.5f; - vertex[i].v = (vertex[i].v - 0.5f) * scaleY + 0.5f; - } - } -} + if (horizontalBar > 0) + pHUDButtonWindow->m_pHudTextParts->m_OffsetY = horizontalBar; -// Explicit CSD set position calls don't seem to care about the -// viewport size. This causes them to appear shifted by 1.5x, -// as the backbuffer resolution is 640x480 at 4:3. We need to account -// for this manually to make the positioning match with the original game. -static constexpr uint32_t EVIL_HUD_GUIDE_BYTE_SIZE = 0x154; + __imp__sub_824D4628(ctx, base); +} -void EvilHudGuideAllocMidAsmHook(PPCRegister& r3) +// Sonicteam::HudTextParts::HudTextParts +PPC_FUNC_IMPL(__imp__sub_824D04C8); +PPC_FUNC(sub_824D04C8) { - r3.u32 += sizeof(float); -} + auto pTextBookName = (const char*)g_memory.Translate(ctx.r7.u32); + auto pTextCardName = (const char*)g_memory.Translate(ctx.r8.u32); -// SWA::Player::CEvilHudGuide::CEvilHudGuide -// PPC_FUNC_IMPL(__imp__sub_82448CF0); -// PPC_FUNC(sub_82448CF0) -// { -// *reinterpret_cast(base + ctx.r3.u32 + EVIL_HUD_GUIDE_BYTE_SIZE) = 0.0f; -// __imp__sub_82448CF0(ctx, base); -// } + __imp__sub_824D04C8(ctx, base); -void EvilHudGuideUpdateMidAsmHook(PPCRegister& r30, PPCRegister& f30) -{ - *reinterpret_cast(g_memory.base + r30.u32 + EVIL_HUD_GUIDE_BYTE_SIZE) = f30.f64; + auto pHudTextParts = (Sonicteam::HudTextParts*)g_memory.Translate(ctx.r3.u32); } -// SWA::Player::CEvilHudGuide::Update -// PPC_FUNC_IMPL(__imp__sub_82449088); -// PPC_FUNC(sub_82449088) -// { -// auto r3 = ctx.r3; -// __imp__sub_82449088(ctx, base); - -// float positionX = *reinterpret_cast(base + r3.u32 + EVIL_HUD_GUIDE_BYTE_SIZE); -// constexpr uint32_t OFFSETS[] = { 312, 320 }; - -// for (const auto offset : OFFSETS) -// { -// uint32_t scene = PPC_LOAD_U32(r3.u32 + offset + 0x4); -// if (scene != NULL) -// { -// scene = PPC_LOAD_U32(scene + 0x4); -// if (scene != NULL) -// { -// ctx.r3.u32 = scene; -// ctx.f1.f64 = (1.5 - 0.5 * g_aspectRatioNarrowScale) * positionX; -// ctx.f2.f64 = 0.0; - -// sub_830BB3D0(ctx, base); -// } -// } -// } -// } +// Sonicteam::TextFontPicture::LoadResource +PPC_FUNC_IMPL(__imp__sub_8263CC40); +PPC_FUNC(sub_8263CC40) +{ + g_pTextFontPicture = (Sonicteam::TextFontPicture*)(base + ctx.r3.u32); -// The shadow offseting is buggy for FCO text just like Werehog button guide, -// making them appear thicker than they actually are. -// PPC_FUNC_IMPL(__imp__sub_82E54950); -// PPC_FUNC(sub_82E54950) -// { -// if (Config::AspectRatio == EAspectRatio::OriginalNarrow) -// { -// // Luckily, they have shadow offset scale values that are only used in this function. -// uint32_t x = PPC_LOAD_U32(0x8332B7B8); -// uint32_t y = PPC_LOAD_U32(0x8332B7BC); - -// PPCRegister scaled; - -// // X -// scaled.u32 = x; -// scaled.f32 *= 1.5f; -// PPC_STORE_U32(0x8332B7B8, scaled.u32); - -// // Y -// scaled.u32 = y; -// scaled.f32 *= 1.5f; -// PPC_STORE_U32(0x8332B7BC, scaled.u32); - -// __imp__sub_82E54950(ctx, base); - -// // Restore old values. -// PPC_STORE_U32(0x8332B7B8, x); -// PPC_STORE_U32(0x8332B7BC, y); -// } -// else -// { -// __imp__sub_82E54950(ctx, base); -// } -// } + __imp__sub_8263CC40(ctx, base); +} diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index 3cf6f0f97..86d467c75 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -1,5 +1,7 @@ #pragma once +#include + inline constexpr float NARROW_ASPECT_RATIO = 4.0f / 3.0f; inline constexpr float WIDE_ASPECT_RATIO = 16.0f / 9.0f; inline constexpr float STEAM_DECK_ASPECT_RATIO = 16.0f / 10.0f; @@ -11,7 +13,240 @@ inline float g_aspectRatioScale; inline float g_aspectRatioGameplayScale; inline float g_aspectRatioNarrowScale; -struct AspectRatioPatches +class AspectRatioPatches { +public: static void ComputeOffsets(); }; + +inline XXH64_hash_t HashStr(const std::string_view& value) +{ + return XXH3_64bits(value.data(), value.size()); +} + +struct CsdModifier +{ + uint32_t Flags{}; + float CornerMax{}; + uint32_t CornerIndex{}; +}; + +enum +{ + ALIGN_CENTER = 0 << 0, + + ALIGN_TOP = 1 << 0, + ALIGN_LEFT = 1 << 1, + ALIGN_BOTTOM = 1 << 2, + ALIGN_RIGHT = 1 << 3, + + ALIGN_TOP_LEFT = ALIGN_TOP | ALIGN_LEFT, + ALIGN_TOP_RIGHT = ALIGN_TOP | ALIGN_RIGHT, + ALIGN_BOTTOM_LEFT = ALIGN_BOTTOM | ALIGN_LEFT, + ALIGN_BOTTOM_RIGHT = ALIGN_BOTTOM | ALIGN_RIGHT, + + STRETCH_HORIZONTAL = 1 << 4, + STRETCH_VERTICAL = 1 << 5, + + STRETCH = STRETCH_HORIZONTAL | STRETCH_VERTICAL, + + SCALE = 1 << 6, + + EXTEND_LEFT = 1 << 7, + EXTEND_RIGHT = 1 << 8, + + STORE_LEFT_CORNER = 1 << 9, + STORE_RIGHT_CORNER = 1 << 10, + + SKIP = 1 << 11, + + OFFSET_SCALE_LEFT = 1 << 12, + OFFSET_SCALE_RIGHT = 1 << 13, + + REPEAT_LEFT = 1 << 14, + + PILLARBOX = 1 << 15, + + UNSTRETCH_HORIZONTAL = 1 << 16, + + CORNER_EXTRACT = 1 << 17, + + RADARMAP = 1 << 18 +}; + +inline const xxHashMap g_modifiers = +{ + // audio + // TODO: extend main box right (implement REPEAT_RIGHT). + { HashStr("sprite/audio/audio/pod/pod/Cast_1084"), { EXTEND_RIGHT } }, + { HashStr("sprite/audio/audio/pod/pod/Cast_1088"), { EXTEND_RIGHT } }, + { HashStr("sprite/audio/audio/genre"), { ALIGN_TOP_LEFT } }, + { HashStr("sprite/audio/audio/genre_cursor"), { ALIGN_TOP_LEFT } }, + + // background + { HashStr("sprite/background/background/mainmenu_back"), { STRETCH } }, + + // black_out + { HashStr("sprite/black_out/black_out"), { STRETCH } }, + + // button_window + // { HashStr("sprite/button_window/button_window/Scene_0000"), { ALIGN_BOTTOM_RIGHT } }, + + // gadget_ber + { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar_anime"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar_ue"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/gadget_ber/gadget_bar/icon_text"), { ALIGN_BOTTOM_RIGHT } }, + + // goldmedal + // TODO: reimplement this menu entirely to add achievements. + { HashStr("sprite/goldmedal/goldmedal/goldmedal"), { ALIGN_RIGHT } }, + { HashStr("sprite/goldmedal/goldmedal/charaselect_cursor"), { ALIGN_LEFT } }, + { HashStr("sprite/goldmedal/goldmedal/charaselect_cursor2"), { ALIGN_LEFT } }, + { HashStr("sprite/goldmedal/goldmedal/total_medal"), { ALIGN_RIGHT } }, + { HashStr("sprite/goldmedal/goldmedal/sonic"), { ALIGN_LEFT } }, + { HashStr("sprite/goldmedal/goldmedal/shadow"), { ALIGN_LEFT } }, + { HashStr("sprite/goldmedal/goldmedal/silver"), { ALIGN_LEFT } }, + { HashStr("sprite/goldmedal/goldmedal/last"), { ALIGN_LEFT } }, + + // cri_logo + { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/bg"), { STRETCH } }, + { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/criware"), { SCALE } }, + + // loading_english + // TODO: draw pillarbox. + { HashStr("sprite/loading/loading_english/Scene_0000/Loading"), { ALIGN_BOTTOM } }, + { HashStr("sprite/loading/loading_english/Scene_0000/Loading_02"), { ALIGN_BOTTOM } }, + { HashStr("sprite/loading/loading_english/Scene_0000/arrow_01"), { ALIGN_BOTTOM } }, + { HashStr("sprite/loading/loading_english/Scene_0000/arrow_02"), { ALIGN_BOTTOM } }, + { HashStr("sprite/loading/loading_english/Scene_0000/arrow_03"), { ALIGN_BOTTOM } }, + + // main_menu + // TODO: use ImGui to stitch together the bottom metal panels (step through CSD for textures). + // { HashStr("sprite/main_menu/main_menu_cursor"), { ALIGN_TOP_LEFT | SCALE } }, + // { HashStr("sprite/main_menu/main_menu_cursor2"), { ALIGN_TOP_LEFT | SCALE } }, + // { HashStr("sprite/main_menu/main_menu_cursor3"), { ALIGN_TOP_LEFT | SCALE } }, + // { HashStr("sprite/main_menu/eposodeselect"), { ALIGN_LEFT | SCALE } }, + // { HashStr("sprite/main_menu/episodeselect_cursor1"), { ALIGN_LEFT | SCALE } }, + // { HashStr("sprite/main_menu/episodeselect_cursor2"), { ALIGN_LEFT | SCALE } }, + // { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita3"), { EXTEND_RIGHT } }, + // { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita5"), { EXTEND_RIGHT } }, + // { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita8"), { EXTEND_RIGHT } }, + // { HashStr("sprite/main_menu/mission_plate/mission_plate/mission_plate2"), { EXTEND_RIGHT } }, + // { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1332"), { EXTEND_RIGHT } }, + // { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1336"), { EXTEND_RIGHT } }, + // { HashStr("sprite/main_menu/stage_cursor"), { ALIGN_TOP_LEFT | SCALE } }, + // { HashStr("sprite/main_menu/mission_cursor"), { ALIGN_TOP_LEFT | SCALE } }, + // { HashStr("sprite/main_menu/stage_cursor2"), { ALIGN_TOP_LEFT | SCALE } }, + // { HashStr("sprite/main_menu/text"), { ALIGN_BOTTOM | STRETCH_HORIZONTAL | SCALE } }, + // { HashStr("sprite/main_menu/text_cover/Null_0290"), { ALIGN_BOTTOM | STRETCH_HORIZONTAL | SCALE } }, + // { HashStr("sprite/main_menu/mission_next"), { ALIGN_BOTTOM_LEFT } }, + // { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { ALIGN_TOP_LEFT | SCALE } }, + // { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { ALIGN_TOP_LEFT | EXTEND_RIGHT | SCALE } }, + // { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966"), { ALIGN_TOP_LEFT | SCALE } }, + // { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { ALIGN_TOP_LEFT | EXTEND_RIGHT | SCALE } }, + // { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { ALIGN_TOP_LEFT | SCALE } }, + // { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { ALIGN_BOTTOM_LEFT | STRETCH_HORIZONTAL | SCALE } }, + // { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { ALIGN_BOTTOM_RIGHT | STRETCH_HORIZONTAL | SCALE } }, + + // maindisplay + { HashStr("sprite/maindisplay/power"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/maindisplay/custom_bar_anime"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/maindisplay/power_a"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/maindisplay/power_bar_anime"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/maindisplay/score"), { ALIGN_TOP_LEFT } }, + { HashStr("sprite/maindisplay/life_ber_anime"), { ALIGN_TOP_LEFT } }, + { HashStr("sprite/maindisplay/life"), { ALIGN_TOP_LEFT } }, + { HashStr("sprite/maindisplay/time"), { ALIGN_TOP_LEFT } }, + { HashStr("sprite/maindisplay/ring"), { ALIGN_TOP_LEFT } }, + { HashStr("sprite/maindisplay/ring_anime"), { ALIGN_TOP_LEFT } }, + { HashStr("sprite/maindisplay/bar_ue"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/maindisplay/power_bar_effect"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/maindisplay/ring_000_effect"), { ALIGN_TOP_LEFT } }, + { HashStr("sprite/maindisplay/boss_gauge"), { ALIGN_TOP_RIGHT } }, + { HashStr("sprite/maindisplay/boss_gauge_anime"), { ALIGN_TOP_RIGHT } }, + { HashStr("sprite/maindisplay/item_ber_anime"), { ALIGN_TOP_LEFT } }, + { HashStr("sprite/maindisplay/item"), { ALIGN_TOP_LEFT } }, + { HashStr("sprite/maindisplay/custom_gem"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/maindisplay/custom_level"), { ALIGN_BOTTOM_RIGHT } }, + + // radarmap_cover + { HashStr("sprite/radarmap_cover/radarmap_cover/Scene_0000"), { ALIGN_TOP_RIGHT | RADARMAP } }, + + // result_English + { HashStr("sprite/result/result_English/title_plate/plate_ue"), { STRETCH } }, // TODO: use REPEAT modes. + { HashStr("sprite/result/result_English/title_plate/plate_sita"), { STRETCH } }, // TODO: use REPEAT modes. + + // sonicteam_logo + { HashStr("sprite/logo/sonicteam_logo/sonicteam"), { SCALE } }, + + // tag_character + { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug1"), { EXTEND_LEFT } }, + { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug1"), { EXTEND_RIGHT } }, + + // talkwindow + { HashStr("sprite/talkwindow/talkwindow/window"), { SCALE } }, + { HashStr("sprite/talkwindow/talkwindow/nafuda"), { SCALE } }, + { HashStr("sprite/talkwindow/talkwindow/Scene_0021"), { SCALE } }, + + // title_English + { HashStr("sprite/title/title_English/Scene_Title/Logo_add"), { SCALE } }, + { HashStr("sprite/title/title_English/Scene_Title/Logo"), { SCALE } }, + { HashStr("sprite/title/title_English/Scene_Title/copyright"), { ALIGN_BOTTOM_RIGHT } }, + + // towndisplay + { HashStr("sprite/towndisplay/ring"), { ALIGN_TOP_LEFT } }, + { HashStr("sprite/towndisplay/ring_anime"), { ALIGN_TOP_LEFT } }, + + // trickpoint_English + // TODO: offset score text properly. + { HashStr("sprite/trickpoint/trickpoint_English/Scene_0000"), { ALIGN_TOP_RIGHT } }, + { HashStr("sprite/trickpoint/trickpoint_English/Scene_0001"), { ALIGN_TOP_RIGHT } }, + { HashStr("sprite/trickpoint/trickpoint_English/score"), { ALIGN_TOP_RIGHT } }, +}; + +struct TextFontPictureParams +{ + uint16_t X{}; + uint16_t Y{}; + uint16_t Width{}; + uint16_t Height{}; +}; + +inline const xxHashMap g_pftParamsXenon = +{ + { HashStr("button_a"), { 0, 0, 28, 28 } }, + { HashStr("button_b"), { 28, 0, 28, 28 } }, + { HashStr("button_x"), { 56, 0, 28, 28 } }, + { HashStr("button_y"), { 84, 0, 28, 28 } }, + { HashStr("button_lb"), { 112, 0, 53, 28 } }, + { HashStr("button_lt"), { 56, 28, 55, 28 } }, + { HashStr("button_rb"), { 168, 0, 53, 28 } }, + { HashStr("button_rt"), { 0, 28, 55, 28 } }, + { HashStr("button_start"), { 112, 28, 28, 28 } }, + { HashStr("button_back"), { 140, 28, 28, 28 } } +}; + +inline const xxHashMap g_pftParamsPS3 = +{ + { HashStr("button_a"), { 0, 56, 28, 28 } }, + { HashStr("button_b"), { 28, 56, 28, 28 } }, + { HashStr("button_x"), { 56, 56, 28, 28 } }, + { HashStr("button_y"), { 84, 56, 28, 28 } }, + { HashStr("button_lb"), { 112, 56, 48, 28 } }, + { HashStr("button_lt"), { 168, 56, 48, 28 } }, + { HashStr("button_rb"), { 0, 84, 48, 28 } }, + { HashStr("button_rt"), { 56, 84, 48, 28 } }, + { HashStr("button_start"), { 140, 84, 28, 28 } }, + { HashStr("button_back"), { 112, 84, 28, 28 } } +}; + +inline TextFontPictureParams FindFontPictureParams(xxHashMap pftParams, std::string_view& name) +{ + auto findResult = pftParams.find(HashStr(name)); + + if (findResult != pftParams.end()) + return findResult->second; + + return {}; +} diff --git a/MarathonRecomp/patches/misc_patches.cpp b/MarathonRecomp/patches/misc_patches.cpp index 88cd0253b..14d61abf1 100644 --- a/MarathonRecomp/patches/misc_patches.cpp +++ b/MarathonRecomp/patches/misc_patches.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -93,3 +94,13 @@ void PostureDisableEdgeGrabLeftover(PPCRegister& posture) { auto base = g_memory.base; *(volatile uint8_t*)(base + (posture.u32 + 0x3C0)) = 1; } + +// Sonicteam::EventEntityTask::Update +PPC_FUNC_IMPL(__imp__sub_8264AC48); +PPC_FUNC(sub_8264AC48) +{ + if (Config::CutsceneAspectRatio == ECutsceneAspectRatio::Original) + BlackBar::g_isPillarbox = true; + + __imp__sub_8264AC48(ctx, base); +} diff --git a/MarathonRecomp/patches/video_patches.cpp b/MarathonRecomp/patches/video_patches.cpp index 5d23e795d..f5c12822b 100644 --- a/MarathonRecomp/patches/video_patches.cpp +++ b/MarathonRecomp/patches/video_patches.cpp @@ -1,4 +1,97 @@ +#include +#include +#include +#include + +const char* g_pBlockName{}; + void SetMSAALevel(PPCRegister& val) { val.u32 = 0; } + +void BeginBlockGetName(PPCRegister& r3) +{ + g_pBlockName = (const char*)g_memory.Translate(r3.u32); + +#if _DEBUG + if (g_pBlockName) + LOGFN_UTILITY("Block Begin: {}", g_pBlockName); +#endif +} + +// EndBlock +PPC_FUNC_IMPL(__imp__sub_826078D8); +PPC_FUNC(sub_826078D8) +{ +#if _DEBUG + if (g_pBlockName) + LOGFN_UTILITY("Block End: {}", g_pBlockName); +#endif + + g_pBlockName = nullptr; + + __imp__sub_826078D8(ctx, base); +} + +// CreateTexture +PPC_FUNC_IMPL(__imp__sub_82619D00); +PPC_FUNC(sub_82619D00) +{ + auto pName = (stdx::string*)g_memory.Translate(ctx.r4.u32); + + if (*pName == "radermap") + { + auto radermapScale = 256 * g_aspectRatioScale; + + ctx.r5.u32 = radermapScale; + ctx.r6.u32 = radermapScale; + } + +#if _DEBUG + auto width = ctx.r5.u32; + auto height = ctx.r6.u32; +#endif + + __imp__sub_82619D00(ctx, base); + +#if _DEBUG + LOGFN_UTILITY("Created texture: {} ({}x{})", pName->c_str(), width, height); +#endif +} + +// CreateDepthStencilSurface +PPC_FUNC_IMPL(__imp__sub_82619B88); +PPC_FUNC(sub_82619B88) +{ + auto pName = (stdx::string*)g_memory.Translate(ctx.r4.u32); + + if (g_pBlockName) + { + if (strcmp(g_pBlockName, "radermap0") == 0) + { + auto radermapScale = 256 * g_aspectRatioScale; + + ctx.r5.u32 = radermapScale; + ctx.r6.u32 = radermapScale; + } + } + +#if _DEBUG + auto width = ctx.r5.u32; + auto height = ctx.r6.u32; +#endif + + __imp__sub_82619B88(ctx, base); + +#if _DEBUG + if (g_pBlockName) + { + LOGFN_UTILITY("Created texture for {}: {} ({}x{})", g_pBlockName, pName->c_str(), width, height); + } + else + { + LOGFN_UTILITY("Created texture: {} ({}x{})", pName->c_str(), width, height); + } +#endif +} diff --git a/MarathonRecomp/ui/black_bar.cpp b/MarathonRecomp/ui/black_bar.cpp index ebe029493..a7dd3bd58 100644 --- a/MarathonRecomp/ui/black_bar.cpp +++ b/MarathonRecomp/ui/black_bar.cpp @@ -1,58 +1,27 @@ #include "black_bar.h" #include +#include void BlackBar::Draw() { - if (g_inspirePillarbox) - { - auto drawList = ImGui::GetBackgroundDrawList(); - auto& res = ImGui::GetIO().DisplaySize; + if (!g_isPillarbox) + return; - float width = (res.x - (res.y * 16.0f / 9.0f)) / 2.0f; + auto drawList = ImGui::GetBackgroundDrawList(); + auto& res = ImGui::GetIO().DisplaySize; - drawList->AddRectFilled( - { 0.0f, 0.0f }, - { width, res.y }, - IM_COL32_BLACK); + float width = (res.x - (res.y * 16.0f / 9.0f)) / 2.0f; - drawList->AddRectFilled( - { res.x - width, 0.0f }, - res, - IM_COL32_BLACK); + drawList->AddRectFilled( + { 0.0f, 0.0f }, + { width, res.y }, + IM_COL32_BLACK); - g_inspirePillarbox = false; - } + drawList->AddRectFilled( + { res.x - width, 0.0f }, + res, + IM_COL32_BLACK); - if (g_loadingBlackBarAlpha != 0) - { - auto drawList = ImGui::GetBackgroundDrawList(); - auto& res = ImGui::GetIO().DisplaySize; - - if (g_aspectRatio > WIDE_ASPECT_RATIO) - { - drawList->AddRectFilled( - { 0.0f, 0.0f }, - { g_loadingBlackBarMin.x, g_loadingBlackBarMax.y }, - IM_COL32(0, 0, 0, g_loadingBlackBarAlpha)); - - drawList->AddRectFilled( - { g_loadingBlackBarMax.x, g_loadingBlackBarMin.y }, - res, - IM_COL32(0, 0, 0, g_loadingBlackBarAlpha)); - } - else if (g_aspectRatio < NARROW_ASPECT_RATIO) - { - drawList->AddRectFilled( - { 0.0f, 0.0f }, - { g_loadingBlackBarMax.x, g_loadingBlackBarMin.y }, - IM_COL32(0, 0, 0, g_loadingBlackBarAlpha)); - - drawList->AddRectFilled( - { g_loadingBlackBarMin.x, g_loadingBlackBarMax.y }, - res, - IM_COL32(0, 0, 0, g_loadingBlackBarAlpha)); - } - - g_loadingBlackBarAlpha = 0; - } + if (!App::s_isLoading) + g_isPillarbox = false; } diff --git a/MarathonRecomp/ui/black_bar.h b/MarathonRecomp/ui/black_bar.h index 7cb076802..a5a3ebe00 100644 --- a/MarathonRecomp/ui/black_bar.h +++ b/MarathonRecomp/ui/black_bar.h @@ -1,12 +1,9 @@ #pragma once -struct BlackBar +class BlackBar { - static inline bool g_inspirePillarbox; - - static inline ImVec2 g_loadingBlackBarMin; - static inline ImVec2 g_loadingBlackBarMax; - static inline uint8_t g_loadingBlackBarAlpha; +public: + static inline bool g_isPillarbox; static void Draw(); }; diff --git a/MarathonRecompLib/config/Marathon.toml b/MarathonRecompLib/config/Marathon.toml index f7f57503d..65b869679 100644 --- a/MarathonRecompLib/config/Marathon.toml +++ b/MarathonRecompLib/config/Marathon.toml @@ -153,3 +153,8 @@ registers = ["r5", "r31"] name = "PostureDisableEdgeGrabLeftover" address = 0x82200568 registers = ["r31"] + +[[midasm_hook]] +name = "BeginBlockGetName" +address = 0x82607850 +registers = ["r3"] From 6ed120bea23cabc16b561ce3dc20ddb80e0d4b8e Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 5 Aug 2025 07:04:12 +0100 Subject: [PATCH 02/52] Apply config controller icons first before auto --- MarathonRecomp/patches/aspect_ratio_patches.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 2cad2963a..0afe1eb74 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -711,10 +711,16 @@ PPC_FUNC(sub_8262D868) __imp__sub_8262D868(ctx, base); - auto& pftParams = hid::g_inputDeviceController == hid::EInputDevice::PlayStation + auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation; + + if (Config::ControllerIcons == EControllerIcons::Auto) + isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation; + + auto& pftParams = isPlayStation ? g_pftParamsPS3 : g_pftParamsXenon; + // TODO (Hyper): why does this not apply to the B button in Audio Room? ReplaceTextVariables(pTextEntity, pftParams); } From a9b239dab4ef01841d4d774cbd7fb7431855455c Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Mon, 11 Aug 2025 07:45:35 +0100 Subject: [PATCH 03/52] CsdManager: use Sonicteam::System::Singleton --- MarathonRecomp/api/Sonicteam/CsdManager.h | 8 +++----- MarathonRecomp/api/Sonicteam/CsdManager.inl | 7 ------- 2 files changed, 3 insertions(+), 12 deletions(-) delete mode 100644 MarathonRecomp/api/Sonicteam/CsdManager.inl diff --git a/MarathonRecomp/api/Sonicteam/CsdManager.h b/MarathonRecomp/api/Sonicteam/CsdManager.h index 9c3a86059..88f0c1d17 100644 --- a/MarathonRecomp/api/Sonicteam/CsdManager.h +++ b/MarathonRecomp/api/Sonicteam/CsdManager.h @@ -2,16 +2,14 @@ #include #include +#include +#include namespace Sonicteam { - class CsdManager : public SoX::IResourceMgr + class CsdManager : public SoX::IResourceMgr, public System::Singleton> { public: MARATHON_INSERT_PADDING(0x1C); - - static CsdManager* GetInstance(); }; } - -#include diff --git a/MarathonRecomp/api/Sonicteam/CsdManager.inl b/MarathonRecomp/api/Sonicteam/CsdManager.inl deleted file mode 100644 index 9145a048d..000000000 --- a/MarathonRecomp/api/Sonicteam/CsdManager.inl +++ /dev/null @@ -1,7 +0,0 @@ -namespace Sonicteam -{ - inline CsdManager* CsdManager::GetInstance() - { - return *(xpointer*)MmGetHostAddress(0x82D3BC58); - } -} From ec5dfd63bfaacb719b923d1cc4f9ca476f43bda0 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 19 Aug 2025 16:17:55 +0100 Subject: [PATCH 04/52] Implement pillarboxing on loading screens and CSD repeat modifiers --- MarathonRecomp/CMakeLists.txt | 1 + MarathonRecomp/api/Marathon.h | 1 + MarathonRecomp/api/Sonicteam/GameImp.h | 21 ++- MarathonRecomp/api/Sonicteam/HUDLoading.h | 18 +++ MarathonRecomp/app.cpp | 10 ++ .../patches/aspect_ratio_patches.cpp | 146 ++++++++++++------ MarathonRecomp/patches/aspect_ratio_patches.h | 112 ++++++++------ MarathonRecomp/patches/hook_event.h | 18 +++ MarathonRecomp/patches/loading_patches.cpp | 17 ++ MarathonRecomp/patches/loading_patches.h | 7 + 10 files changed, 254 insertions(+), 97 deletions(-) create mode 100644 MarathonRecomp/api/Sonicteam/HUDLoading.h create mode 100644 MarathonRecomp/patches/hook_event.h create mode 100644 MarathonRecomp/patches/loading_patches.cpp create mode 100644 MarathonRecomp/patches/loading_patches.h diff --git a/MarathonRecomp/CMakeLists.txt b/MarathonRecomp/CMakeLists.txt index c7aa1825c..748321c8f 100644 --- a/MarathonRecomp/CMakeLists.txt +++ b/MarathonRecomp/CMakeLists.txt @@ -139,6 +139,7 @@ set(MARATHON_RECOMP_PATCHES_CXX_SOURCES "patches/audio_patches.cpp" "patches/camera_patches.cpp" "patches/frontend_listener.cpp" + "patches/loading_patches.cpp" "patches/misc_patches.cpp" "patches/player_patches.cpp" "patches/TitleTask_patches.cpp" diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index 3e46e9fdb..965f16977 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -29,6 +29,7 @@ #include "Sonicteam/GameImp.h" #include "Sonicteam/GameMode.h" #include "Sonicteam/HUDButtonWindow.h" +#include "Sonicteam/HUDLoading.h" #include "Sonicteam/HUDPopupScreen.h" #include "Sonicteam/HUDRaderMap.h" #include "Sonicteam/HudTextParts.h" diff --git a/MarathonRecomp/api/Sonicteam/GameImp.h b/MarathonRecomp/api/Sonicteam/GameImp.h index 8f34d1e32..8e1f755d0 100644 --- a/MarathonRecomp/api/Sonicteam/GameImp.h +++ b/MarathonRecomp/api/Sonicteam/GameImp.h @@ -7,6 +7,19 @@ namespace Sonicteam class GameImp : public SoX::MessageReceiver { public: + enum GameState : uint32_t + { + eGameState_MainMenu, + eGameState_Stage, + eGameState_Event, + eGameState_Movie, + eGameState_Result, + eGameState_Message, + eGameState_6, + eGameState_Save, + eGameState_ReturnToMainMenu + }; + struct PlayerData { be ActorID; @@ -27,9 +40,11 @@ namespace Sonicteam MARATHON_INSERT_PADDING(0x10); }; - MARATHON_INSERT_PADDING(0x658); - bool m_IsPlayingStage; - MARATHON_INSERT_PADDING(0x7E0); + MARATHON_INSERT_PADDING(4); + be m_GameState; + xpointer m_pDoc; + be m_Flags; + MARATHON_INSERT_PADDING(0xE2C); PlayerData m_PlayerData[4]; }; } diff --git a/MarathonRecomp/api/Sonicteam/HUDLoading.h b/MarathonRecomp/api/Sonicteam/HUDLoading.h new file mode 100644 index 000000000..b13349bc2 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HUDLoading.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class HUDLoading : public SoX::RefCountObject, public SoX::Engine::Task + { + public: + enum HUDLoadingFlags + { + eHUDLoadingFlags_Finished = 6 + }; + + MARATHON_INSERT_PADDING(0x5C); + be m_Flags; + }; +} diff --git a/MarathonRecomp/app.cpp b/MarathonRecomp/app.cpp index 857359be1..0984f9ba1 100644 --- a/MarathonRecomp/app.cpp +++ b/MarathonRecomp/app.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,8 @@ PPC_FUNC(sub_8262A568) __imp__sub_8262A568(ctx, base); App::s_pApp = (Sonicteam::AppMarathon*)g_memory.Translate(ctx.r3.u32); + + AspectRatioPatches::Init(); } // Sonicteam::DocMarathonState::Update @@ -98,6 +101,13 @@ PPC_FUNC(sub_825EA610) // Allow variable FPS when config is not 60 FPS. App::s_pApp->m_pDoc->m_VFrame = Config::FPS != 60; + // TODO: REMOVE THIS!!! + if (auto pGameMode = App::s_pApp->m_pDoc->GetDocMode()) + { + if (GetAsyncKeyState(VK_F5) & 1) + pGameMode->m_pGameImp->m_GameState = Sonicteam::GameImp::eGameState_Result; + } + __imp__sub_825EA610(ctx, base); } diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 0afe1eb74..92e2a105f 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include #include @@ -31,11 +33,26 @@ static float g_radarMapCoverHeight; static float g_scenePositionX; static float g_scenePositionY; +static class LoadingPillarboxEvent : public HookEvent +{ +public: + void Update(float deltaTime) override + { + BlackBar::g_isPillarbox = true; + } +} +g_loadingPillarboxEvent{}; + static float ComputeScale(float aspectRatio) { return ((aspectRatio * 720.0f) / 1280.0f) / sqrt((aspectRatio * 720.0f) / 1280.0f); } +void AspectRatioPatches::Init() +{ + LoadingPatches::Events.push_back(&g_loadingPillarboxEvent); +} + void AspectRatioPatches::ComputeOffsets() { float width = Video::s_viewportWidth; @@ -328,9 +345,18 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str uint8_t* stack = base + ctx.r1.u32; memcpy(stack, base + ctx.r4.u32, size); - auto getPosition = [&](size_t index) + struct CSDVertex { - return reinterpret_cast*>(stack + index * stride); + be X; + be Y; + be Colour; + be U; + be V; + }; + + auto getVertex = [&](size_t index) + { + return reinterpret_cast(stack + (index * stride)); }; float offsetX = 0.0f; @@ -352,7 +378,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str if (needsStretch && (modifier.Flags & UNSTRETCH_HORIZONTAL) != 0) { - pivotX = *getPosition(0); + pivotX = getVertex(0)->X; offsetX = pivotX * Video::s_viewportWidth / 1280.0f; } else @@ -414,7 +440,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str offsetScaleModifier = g_castModifier.value(); uint32_t vertexIndex = ((offsetScaleModifier.Flags & STORE_LEFT_CORNER) != 0) ? 0 : 3; - corner = *getPosition(vertexIndex); + corner = getVertex(vertexIndex)->X; } if (offsetScaleModifier.CornerMax == 0.0f && g_castNodeModifier.has_value()) @@ -449,10 +475,10 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str for (size_t i = 0; i < ctx.r5.u32; i++) { - auto position = getPosition(i); + auto vertex = getVertex(i); - float x = offsetX + (position[0] - pivotX) * (scaleX * (1280.0f / Video::s_viewportWidth)); - float y = offsetY + (position[1] - pivotY) * (scaleY * (720.0f / Video::s_viewportHeight)); + float x = offsetX + (vertex->X - pivotX) * (scaleX * (1280.0f / Video::s_viewportWidth)); + float y = offsetY + (vertex->Y - pivotY) * (scaleY * (720.0f / Video::s_viewportHeight)); if ((modifier.Flags & EXTEND_LEFT) != 0 && (i == 0 || i == 1)) { @@ -477,8 +503,8 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str g_radarMapY = y; } - position[0] = round(x); - position[1] = round(y); + vertex->X = round(x); + vertex->Y = round(y); } width = lastX - firstX; @@ -493,23 +519,83 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str if ((modifier.Flags & PILLARBOX) != 0) BlackBar::g_isPillarbox = true; - // FIXME: currently causes a crash, may need stack alignment. - if ((modifier.Flags & REPEAT_LEFT) != 0) - { - float width = *getPosition(2) - *getPosition(0); + if ((modifier.Flags & PROHIBIT_BLACK_BAR) != 0) + BlackBar::g_isPillarbox = false; + auto isRepeatLeft = (modifier.Flags & REPEAT_LEFT) != 0; + auto isRepeatRight = (modifier.Flags & REPEAT_RIGHT) != 0; + + if (isRepeatLeft || isRepeatRight) + { auto r3 = ctx.r3; auto r5 = ctx.r5; + auto vertexIndex = isRepeatLeft ? 2 : 0; + auto x = getVertex(vertexIndex)->X; - while (*getPosition(2) > 0.0f) + while (isRepeatLeft ? x > 0.0f : x < float(Video::s_viewportWidth)) { ctx.r3 = r3; ctx.r4 = ctx.r1; ctx.r5 = r5; original(ctx, base); - for (size_t i = 0; i < ctx.r5.u32; i++) - *getPosition(i) = *getPosition(i) - width; + if (isRepeatLeft) + { + for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->X = getVertex(i)->X - width; + } + else if (isRepeatRight) + { + for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->X = getVertex(i)->X + width; + } + + x = getVertex(vertexIndex)->X; + + auto isFlipHorz = (modifier.Flags & REPEAT_FLIP_HORIZONTAL) != 0; + + if (isFlipHorz) + { + getVertex(0)->X = getVertex(0)->X + width; + getVertex(1)->X = getVertex(1)->X + width; + getVertex(2)->X = getVertex(2)->X - width; + getVertex(3)->X = getVertex(3)->X - width; + } + + if ((modifier.Flags & REPEAT_UV_MODIFIER) != 0) + { + getVertex(0)->U = getVertex(0)->U + modifier.UVs.U0; + getVertex(0)->V = getVertex(0)->V + modifier.UVs.V0; + getVertex(1)->U = getVertex(1)->U + modifier.UVs.U1; + getVertex(1)->V = getVertex(1)->V + modifier.UVs.V1; + getVertex(2)->U = getVertex(2)->U + modifier.UVs.U2; + getVertex(2)->V = getVertex(2)->V + modifier.UVs.V2; + getVertex(3)->U = getVertex(3)->U + modifier.UVs.U3; + getVertex(3)->V = getVertex(3)->V + modifier.UVs.V3; + } + + if ((modifier.Flags & REPEAT_COLOUR_MODIFIER) != 0) + { + getVertex(0)->Colour = modifier.Colours.C0; + getVertex(1)->Colour = modifier.Colours.C1; + getVertex(2)->Colour = modifier.Colours.C2; + getVertex(3)->Colour = modifier.Colours.C3; + } + + if ((modifier.Flags & REPEAT_EXTEND) != 0) + { + for (size_t i = 0; i < r5.u32; i++) + { + if (isRepeatLeft && (i == (isFlipHorz ? 2 : 0) || i == (isFlipHorz ? 3 : 1))) + { + getVertex(i)->X = std::min(getVertex(i)->X.get(), 0.0f); + } + else if (isRepeatRight && (i == (isFlipHorz ? 0 : 2) || i == (isFlipHorz ? 1 : 3))) + { + getVertex(i)->X = std::max(getVertex(i)->X.get(), float(Video::s_viewportWidth)); + } + } + } } ctx.r1.u32 += size; @@ -560,32 +646,6 @@ PPC_FUNC(sub_824F1538) pHUDRaderMap->m_Y = g_radarMapY - g_radarMapCoverHeight / 2; } -// Sonicteam::HUDLoading::HUDLoading -// PPC_FUNC_IMPL(__imp__sub_824D7BC8); -// PPC_FUNC(sub_824D7BC8) -// { -// App::s_isLoading = true; -// BlackBar::g_isPillarbox = true; -// -// __imp__sub_824D7BC8(ctx, base); -// } -// -// // Sonicteam::HUDLoading::~HUDLoading -// PPC_FUNC_IMPL(__imp__sub_824D7520); -// PPC_FUNC(sub_824D7520) -// { -// App::s_isLoading = false; -// BlackBar::g_isPillarbox = false; -// -// __imp__sub_824D7520(ctx, base); -// } - -// PPC_FUNC_IMPL(__imp__sub_82167D70); -// PPC_FUNC(sub_82167D70) -// { -// __imp__sub_82167D70(ctx, base); -// } - void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity, xxHashMap pftParams) { if (!pTextEntity || !pTextEntity->m_ImageVertexCount) @@ -682,7 +742,7 @@ PPC_FUNC(sub_8262D868) { auto pTextEntity = (Sonicteam::TextEntity*)(base + ctx.r3.u32); - LOGFN_UTILITY("TextEntity: 0x{:08X}", (uint64_t)pTextEntity); + // LOGFN_UTILITY("TextEntity: 0x{:08X}", (uint64_t)pTextEntity); constexpr auto baseWidth = 1280.0f; constexpr auto baseHeight = 720.0f; diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index 86d467c75..e083754bf 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -16,6 +16,7 @@ inline float g_aspectRatioNarrowScale; class AspectRatioPatches { public: + static void Init(); static void ComputeOffsets(); }; @@ -24,9 +25,31 @@ inline XXH64_hash_t HashStr(const std::string_view& value) return XXH3_64bits(value.data(), value.size()); } +struct CsdRepeatUVs +{ + float U0{}; + float V0{}; + float U1{}; + float V1{}; + float U2{}; + float V2{}; + float U3{}; + float V3{}; +}; + +struct CsdRepeatColours +{ + uint32_t C0{}; + uint32_t C1{}; + uint32_t C2{}; + uint32_t C3{}; +}; + struct CsdModifier { uint32_t Flags{}; + CsdRepeatUVs UVs{}; + CsdRepeatColours Colours{}; float CornerMax{}; uint32_t CornerIndex{}; }; @@ -64,33 +87,39 @@ enum OFFSET_SCALE_RIGHT = 1 << 13, REPEAT_LEFT = 1 << 14, + REPEAT_RIGHT = 1 << 15, + REPEAT_FLIP_HORIZONTAL = 1 << 16, + REPEAT_FLIP_VERTICAL = 1 << 17, + REPEAT_EXTEND = 1 << 18, + REPEAT_UV_MODIFIER = 1 << 19, + REPEAT_COLOUR_MODIFIER = 1 << 20, - PILLARBOX = 1 << 15, + PILLARBOX = 1 << 21, + PROHIBIT_BLACK_BAR = 1 << 22, - UNSTRETCH_HORIZONTAL = 1 << 16, + UNSTRETCH_HORIZONTAL = 1 << 23, - CORNER_EXTRACT = 1 << 17, + CORNER_EXTRACT = 1 << 24, - RADARMAP = 1 << 18 + RADARMAP = 1 << 25, + POD = 1 << 26 }; inline const xxHashMap g_modifiers = { // audio - // TODO: extend main box right (implement REPEAT_RIGHT). + { HashStr("sprite/audio/audio/pod/pod"), { POD } }, { HashStr("sprite/audio/audio/pod/pod/Cast_1084"), { EXTEND_RIGHT } }, { HashStr("sprite/audio/audio/pod/pod/Cast_1088"), { EXTEND_RIGHT } }, - { HashStr("sprite/audio/audio/genre"), { ALIGN_TOP_LEFT } }, - { HashStr("sprite/audio/audio/genre_cursor"), { ALIGN_TOP_LEFT } }, // background - { HashStr("sprite/background/background/mainmenu_back"), { STRETCH } }, + { HashStr("sprite/background/background/mainmenu_back"), { STRETCH | PROHIBIT_BLACK_BAR } }, // black_out { HashStr("sprite/black_out/black_out"), { STRETCH } }, // button_window - // { HashStr("sprite/button_window/button_window/Scene_0000"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/button_window/button_window/Scene_0000"), { ALIGN_BOTTOM | SCALE } }, // gadget_ber { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar"), { ALIGN_BOTTOM_RIGHT } }, @@ -98,17 +127,6 @@ inline const xxHashMap g_modifiers = { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar_ue"), { ALIGN_BOTTOM_RIGHT } }, { HashStr("sprite/gadget_ber/gadget_bar/icon_text"), { ALIGN_BOTTOM_RIGHT } }, - // goldmedal - // TODO: reimplement this menu entirely to add achievements. - { HashStr("sprite/goldmedal/goldmedal/goldmedal"), { ALIGN_RIGHT } }, - { HashStr("sprite/goldmedal/goldmedal/charaselect_cursor"), { ALIGN_LEFT } }, - { HashStr("sprite/goldmedal/goldmedal/charaselect_cursor2"), { ALIGN_LEFT } }, - { HashStr("sprite/goldmedal/goldmedal/total_medal"), { ALIGN_RIGHT } }, - { HashStr("sprite/goldmedal/goldmedal/sonic"), { ALIGN_LEFT } }, - { HashStr("sprite/goldmedal/goldmedal/shadow"), { ALIGN_LEFT } }, - { HashStr("sprite/goldmedal/goldmedal/silver"), { ALIGN_LEFT } }, - { HashStr("sprite/goldmedal/goldmedal/last"), { ALIGN_LEFT } }, - // cri_logo { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/bg"), { STRETCH } }, { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/criware"), { SCALE } }, @@ -122,32 +140,25 @@ inline const xxHashMap g_modifiers = { HashStr("sprite/loading/loading_english/Scene_0000/arrow_03"), { ALIGN_BOTTOM } }, // main_menu - // TODO: use ImGui to stitch together the bottom metal panels (step through CSD for textures). - // { HashStr("sprite/main_menu/main_menu_cursor"), { ALIGN_TOP_LEFT | SCALE } }, - // { HashStr("sprite/main_menu/main_menu_cursor2"), { ALIGN_TOP_LEFT | SCALE } }, - // { HashStr("sprite/main_menu/main_menu_cursor3"), { ALIGN_TOP_LEFT | SCALE } }, - // { HashStr("sprite/main_menu/eposodeselect"), { ALIGN_LEFT | SCALE } }, - // { HashStr("sprite/main_menu/episodeselect_cursor1"), { ALIGN_LEFT | SCALE } }, - // { HashStr("sprite/main_menu/episodeselect_cursor2"), { ALIGN_LEFT | SCALE } }, - // { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita3"), { EXTEND_RIGHT } }, - // { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita5"), { EXTEND_RIGHT } }, - // { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita8"), { EXTEND_RIGHT } }, - // { HashStr("sprite/main_menu/mission_plate/mission_plate/mission_plate2"), { EXTEND_RIGHT } }, - // { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1332"), { EXTEND_RIGHT } }, - // { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1336"), { EXTEND_RIGHT } }, - // { HashStr("sprite/main_menu/stage_cursor"), { ALIGN_TOP_LEFT | SCALE } }, - // { HashStr("sprite/main_menu/mission_cursor"), { ALIGN_TOP_LEFT | SCALE } }, - // { HashStr("sprite/main_menu/stage_cursor2"), { ALIGN_TOP_LEFT | SCALE } }, - // { HashStr("sprite/main_menu/text"), { ALIGN_BOTTOM | STRETCH_HORIZONTAL | SCALE } }, - // { HashStr("sprite/main_menu/text_cover/Null_0290"), { ALIGN_BOTTOM | STRETCH_HORIZONTAL | SCALE } }, - // { HashStr("sprite/main_menu/mission_next"), { ALIGN_BOTTOM_LEFT } }, - // { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { ALIGN_TOP_LEFT | SCALE } }, - // { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { ALIGN_TOP_LEFT | EXTEND_RIGHT | SCALE } }, - // { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966"), { ALIGN_TOP_LEFT | SCALE } }, - // { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { ALIGN_TOP_LEFT | EXTEND_RIGHT | SCALE } }, - // { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { ALIGN_TOP_LEFT | SCALE } }, - // { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { ALIGN_BOTTOM_LEFT | STRETCH_HORIZONTAL | SCALE } }, - // { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { ALIGN_BOTTOM_RIGHT | STRETCH_HORIZONTAL | SCALE } }, + { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita3"), { EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita5"), { EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita8"), { EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/stage_plate/stage_plate/stage_plate2"), { EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1337"), { EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1340"), { EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/mission_plate/mission_plate/mission_plate2"), { EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1332"), { EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1336"), { EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/text"), { ALIGN_BOTTOM | SCALE } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/cover_l"), { ALIGN_BOTTOM | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.05f, 0, -0.05f, 0 } } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/cover_c"), { ALIGN_BOTTOM | SCALE } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/vocer_r"), { ALIGN_BOTTOM | SCALE | REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { -0.05f, 0, -0.05f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { ALIGN_TOP | SCALE | EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { ALIGN_TOP | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { ALIGN_TOP | SCALE | EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { ALIGN_TOP | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { SCALE | REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { -0.5f, 0, -0.5f, 0, 0, 0, 0, 0 } } }, // maindisplay { HashStr("sprite/maindisplay/power"), { ALIGN_BOTTOM_RIGHT } }, @@ -174,15 +185,14 @@ inline const xxHashMap g_modifiers = { HashStr("sprite/radarmap_cover/radarmap_cover/Scene_0000"), { ALIGN_TOP_RIGHT | RADARMAP } }, // result_English - { HashStr("sprite/result/result_English/title_plate/plate_ue"), { STRETCH } }, // TODO: use REPEAT modes. - { HashStr("sprite/result/result_English/title_plate/plate_sita"), { STRETCH } }, // TODO: use REPEAT modes. + { HashStr("sprite/result/result_English/title_plate"), { PILLARBOX } }, // TODO // sonicteam_logo { HashStr("sprite/logo/sonicteam_logo/sonicteam"), { SCALE } }, // tag_character - { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug1"), { EXTEND_LEFT } }, - { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug1"), { EXTEND_RIGHT } }, + { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug1"), { REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER | REPEAT_COLOUR_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, + { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug1"), { REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER | REPEAT_COLOUR_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, // talkwindow { HashStr("sprite/talkwindow/talkwindow/window"), { SCALE } }, @@ -192,7 +202,7 @@ inline const xxHashMap g_modifiers = // title_English { HashStr("sprite/title/title_English/Scene_Title/Logo_add"), { SCALE } }, { HashStr("sprite/title/title_English/Scene_Title/Logo"), { SCALE } }, - { HashStr("sprite/title/title_English/Scene_Title/copyright"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/title/title_English/Scene_Title/copyright"), { ALIGN_BOTTOM } }, // towndisplay { HashStr("sprite/towndisplay/ring"), { ALIGN_TOP_LEFT } }, diff --git a/MarathonRecomp/patches/hook_event.h b/MarathonRecomp/patches/hook_event.h new file mode 100644 index 000000000..c7f7fbca4 --- /dev/null +++ b/MarathonRecomp/patches/hook_event.h @@ -0,0 +1,18 @@ +#pragma once + +class IHookEvent +{ +public: + virtual ~IHookEvent() = default; + virtual void Prefix(); + virtual void Update(float deltaTime); + virtual void Postfix(); +}; + +class HookEvent : public IHookEvent +{ +public: + void Prefix() override {} + void Update(float deltaTime) override {} + void Postfix() override {} +}; diff --git a/MarathonRecomp/patches/loading_patches.cpp b/MarathonRecomp/patches/loading_patches.cpp new file mode 100644 index 000000000..143c6a456 --- /dev/null +++ b/MarathonRecomp/patches/loading_patches.cpp @@ -0,0 +1,17 @@ +#include "loading_patches.h" +#include + +// Sonicteam::HUDLoading::Update +PPC_FUNC_IMPL(__imp__sub_824D7340); +PPC_FUNC(sub_824D7340) +{ + auto pHUDLoading = (Sonicteam::HUDLoading*)(base + ctx.r3.u32); + + if ((pHUDLoading->m_Flags.get() & Sonicteam::HUDLoading::eHUDLoadingFlags_Finished) == 0) + { + for (auto& event : LoadingPatches::Events) + event->Update(ctx.f1.f64); + } + + __imp__sub_824D7340(ctx, base); +} diff --git a/MarathonRecomp/patches/loading_patches.h b/MarathonRecomp/patches/loading_patches.h new file mode 100644 index 000000000..aba89e152 --- /dev/null +++ b/MarathonRecomp/patches/loading_patches.h @@ -0,0 +1,7 @@ +#include "hook_event.h" + +class LoadingPatches +{ +public: + static inline std::vector Events{}; +}; From 31ff1a0e5c8630b21b5426436008785a242d050f Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 19 Aug 2025 16:32:47 +0100 Subject: [PATCH 05/52] Fix macOS build --- MarathonRecomp/patches/hook_event.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MarathonRecomp/patches/hook_event.h b/MarathonRecomp/patches/hook_event.h index c7f7fbca4..b230e4a4b 100644 --- a/MarathonRecomp/patches/hook_event.h +++ b/MarathonRecomp/patches/hook_event.h @@ -4,9 +4,9 @@ class IHookEvent { public: virtual ~IHookEvent() = default; - virtual void Prefix(); - virtual void Update(float deltaTime); - virtual void Postfix(); + virtual void Prefix() = 0; + virtual void Update(float deltaTime) = 0; + virtual void Postfix() = 0; }; class HookEvent : public IHookEvent From 7f020739b81c9e3c7cb4bc3319dcf95c59ec5489 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 19 Aug 2025 16:33:08 +0100 Subject: [PATCH 06/52] Align main menu plates to bottom --- MarathonRecomp/patches/aspect_ratio_patches.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index e083754bf..53dce5ad3 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -157,8 +157,8 @@ inline const xxHashMap g_modifiers = { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { ALIGN_TOP | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { ALIGN_TOP | SCALE | EXTEND_RIGHT } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { ALIGN_TOP | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { SCALE | REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { -0.5f, 0, -0.5f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { ALIGN_BOTTOM | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { ALIGN_BOTTOM | SCALE | REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { -0.5f, 0, -0.5f, 0, 0, 0, 0, 0 } } }, // maindisplay { HashStr("sprite/maindisplay/power"), { ALIGN_BOTTOM_RIGHT } }, From 9f0f70ecc430e8ccd40f64ef99a20091a9e86303 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:27:26 +0100 Subject: [PATCH 07/52] Apply scaling to main HUD and radar --- .../patches/aspect_ratio_patches.cpp | 2 +- MarathonRecomp/patches/aspect_ratio_patches.h | 81 ++++++++++--------- MarathonRecomp/patches/video_patches.cpp | 2 +- 3 files changed, 44 insertions(+), 41 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 92e2a105f..e8f55c5f0 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -638,7 +638,7 @@ PPC_FUNC(sub_824F1538) __imp__sub_824F1538(ctx, base); - auto radermapScale = 256 * g_aspectRatioScale; + auto radermapScale = 256 * g_aspectRatioScale * g_aspectRatioGameplayScale; pHUDRaderMap->m_pMaskTexture->m_Width = radermapScale; pHUDRaderMap->m_pMaskTexture->m_Height = radermapScale; diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index 53dce5ad3..d3d418206 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -25,7 +25,7 @@ inline XXH64_hash_t HashStr(const std::string_view& value) return XXH3_64bits(value.data(), value.size()); } -struct CsdRepeatUVs +struct CsdRepeatUV { float U0{}; float V0{}; @@ -37,7 +37,7 @@ struct CsdRepeatUVs float V3{}; }; -struct CsdRepeatColours +struct CsdRepeatColour { uint32_t C0{}; uint32_t C1{}; @@ -48,8 +48,8 @@ struct CsdRepeatColours struct CsdModifier { uint32_t Flags{}; - CsdRepeatUVs UVs{}; - CsdRepeatColours Colours{}; + CsdRepeatUV UVs{}; + CsdRepeatColour Colours{}; float CornerMax{}; uint32_t CornerIndex{}; }; @@ -91,18 +91,21 @@ enum REPEAT_FLIP_HORIZONTAL = 1 << 16, REPEAT_FLIP_VERTICAL = 1 << 17, REPEAT_EXTEND = 1 << 18, - REPEAT_UV_MODIFIER = 1 << 19, - REPEAT_COLOUR_MODIFIER = 1 << 20, - PILLARBOX = 1 << 21, - PROHIBIT_BLACK_BAR = 1 << 22, + UV_MODIFIER = 1 << 19, + COLOUR_MODIFIER = 1 << 20, + REPEAT_UV_MODIFIER = 1 << 21, + REPEAT_COLOUR_MODIFIER = 1 << 22, - UNSTRETCH_HORIZONTAL = 1 << 23, + PILLARBOX = 1 << 23, + PROHIBIT_BLACK_BAR = 1 << 24, - CORNER_EXTRACT = 1 << 24, + UNSTRETCH_HORIZONTAL = 1 << 25, - RADARMAP = 1 << 25, - POD = 1 << 26 + CORNER_EXTRACT = 1 << 26, + + RADARMAP = 1 << 27, + POD = 1 << 28 }; inline const xxHashMap g_modifiers = @@ -150,39 +153,39 @@ inline const xxHashMap g_modifiers = { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1332"), { EXTEND_RIGHT } }, { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1336"), { EXTEND_RIGHT } }, { HashStr("sprite/main_menu/text"), { ALIGN_BOTTOM | SCALE } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/cover_l"), { ALIGN_BOTTOM | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.05f, 0, -0.05f, 0 } } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/cover_l"), { ALIGN_BOTTOM | SCALE | REPEAT_LEFT | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.05f, 0, -0.05f, 0 } } }, { HashStr("sprite/main_menu/text_cover/Null_0290/cover_c"), { ALIGN_BOTTOM | SCALE } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/vocer_r"), { ALIGN_BOTTOM | SCALE | REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { -0.05f, 0, -0.05f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/vocer_r"), { ALIGN_BOTTOM | SCALE | REPEAT_RIGHT | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { -0.05f, 0, -0.05f, 0, 0, 0, 0, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { ALIGN_TOP | SCALE | EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { ALIGN_TOP | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { ALIGN_TOP | SCALE | REPEAT_LEFT | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.1f, 0, -0.1f, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { ALIGN_TOP | SCALE | EXTEND_RIGHT } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { ALIGN_TOP | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { ALIGN_BOTTOM | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { ALIGN_BOTTOM | SCALE | REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { -0.5f, 0, -0.5f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { ALIGN_BOTTOM | SCALE | REPEAT_LEFT | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.8f, 0, -0.8f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { ALIGN_BOTTOM | SCALE | REPEAT_RIGHT | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, // maindisplay - { HashStr("sprite/maindisplay/power"), { ALIGN_BOTTOM_RIGHT } }, - { HashStr("sprite/maindisplay/custom_bar_anime"), { ALIGN_BOTTOM_RIGHT } }, - { HashStr("sprite/maindisplay/power_a"), { ALIGN_BOTTOM_RIGHT } }, - { HashStr("sprite/maindisplay/power_bar_anime"), { ALIGN_BOTTOM_RIGHT } }, - { HashStr("sprite/maindisplay/score"), { ALIGN_TOP_LEFT } }, - { HashStr("sprite/maindisplay/life_ber_anime"), { ALIGN_TOP_LEFT } }, - { HashStr("sprite/maindisplay/life"), { ALIGN_TOP_LEFT } }, - { HashStr("sprite/maindisplay/time"), { ALIGN_TOP_LEFT } }, - { HashStr("sprite/maindisplay/ring"), { ALIGN_TOP_LEFT } }, - { HashStr("sprite/maindisplay/ring_anime"), { ALIGN_TOP_LEFT } }, - { HashStr("sprite/maindisplay/bar_ue"), { ALIGN_BOTTOM_RIGHT } }, - { HashStr("sprite/maindisplay/power_bar_effect"), { ALIGN_BOTTOM_RIGHT } }, - { HashStr("sprite/maindisplay/ring_000_effect"), { ALIGN_TOP_LEFT } }, - { HashStr("sprite/maindisplay/boss_gauge"), { ALIGN_TOP_RIGHT } }, - { HashStr("sprite/maindisplay/boss_gauge_anime"), { ALIGN_TOP_RIGHT } }, - { HashStr("sprite/maindisplay/item_ber_anime"), { ALIGN_TOP_LEFT } }, - { HashStr("sprite/maindisplay/item"), { ALIGN_TOP_LEFT } }, - { HashStr("sprite/maindisplay/custom_gem"), { ALIGN_BOTTOM_RIGHT } }, - { HashStr("sprite/maindisplay/custom_level"), { ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/maindisplay/power"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/maindisplay/custom_bar_anime"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/maindisplay/power_a"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/maindisplay/power_bar_anime"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/maindisplay/score"), { ALIGN_TOP_LEFT | SCALE } }, + { HashStr("sprite/maindisplay/life_ber_anime"), { ALIGN_TOP_LEFT | SCALE } }, + { HashStr("sprite/maindisplay/life"), { ALIGN_TOP_LEFT | SCALE } }, + { HashStr("sprite/maindisplay/time"), { ALIGN_TOP_LEFT | SCALE } }, + { HashStr("sprite/maindisplay/ring"), { ALIGN_TOP_LEFT | SCALE } }, + { HashStr("sprite/maindisplay/ring_anime"), { ALIGN_TOP_LEFT | SCALE } }, + { HashStr("sprite/maindisplay/bar_ue"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/maindisplay/power_bar_effect"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/maindisplay/ring_000_effect"), { ALIGN_TOP_LEFT | SCALE } }, + { HashStr("sprite/maindisplay/boss_gauge"), { ALIGN_TOP_RIGHT | SCALE } }, + { HashStr("sprite/maindisplay/boss_gauge_anime"), { ALIGN_TOP_RIGHT | SCALE } }, + { HashStr("sprite/maindisplay/item_ber_anime"), { ALIGN_TOP_LEFT | SCALE } }, + { HashStr("sprite/maindisplay/item"), { ALIGN_TOP_LEFT | SCALE } }, + { HashStr("sprite/maindisplay/custom_gem"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/maindisplay/custom_level"), { ALIGN_BOTTOM_RIGHT | SCALE } }, // radarmap_cover - { HashStr("sprite/radarmap_cover/radarmap_cover/Scene_0000"), { ALIGN_TOP_RIGHT | RADARMAP } }, + { HashStr("sprite/radarmap_cover/radarmap_cover/Scene_0000"), { ALIGN_TOP_RIGHT | SCALE | RADARMAP } }, // result_English { HashStr("sprite/result/result_English/title_plate"), { PILLARBOX } }, // TODO @@ -205,8 +208,8 @@ inline const xxHashMap g_modifiers = { HashStr("sprite/title/title_English/Scene_Title/copyright"), { ALIGN_BOTTOM } }, // towndisplay - { HashStr("sprite/towndisplay/ring"), { ALIGN_TOP_LEFT } }, - { HashStr("sprite/towndisplay/ring_anime"), { ALIGN_TOP_LEFT } }, + { HashStr("sprite/towndisplay/ring"), { ALIGN_TOP_LEFT | SCALE } }, + { HashStr("sprite/towndisplay/ring_anime"), { ALIGN_TOP_LEFT | SCALE } }, // trickpoint_English // TODO: offset score text properly. diff --git a/MarathonRecomp/patches/video_patches.cpp b/MarathonRecomp/patches/video_patches.cpp index c5922c18e..4af572bb2 100644 --- a/MarathonRecomp/patches/video_patches.cpp +++ b/MarathonRecomp/patches/video_patches.cpp @@ -58,7 +58,7 @@ PPC_FUNC(sub_82619D00) if (*pName == "radermap") { - auto radermapScale = 256 * g_aspectRatioScale; + auto radermapScale = 256 * g_aspectRatioScale * g_aspectRatioGameplayScale; ctx.r5.u32 = radermapScale; ctx.r6.u32 = radermapScale; From d4b8adfb2b48a2bca41f130aa6db81bdd0940332 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Wed, 20 Aug 2025 02:02:56 +0100 Subject: [PATCH 08/52] Fix main menu seams and boss name alignment --- .../patches/aspect_ratio_patches.cpp | 56 +++++++++++-------- MarathonRecomp/patches/aspect_ratio_patches.h | 55 +++++++++++++----- 2 files changed, 76 insertions(+), 35 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index e8f55c5f0..d2c05a392 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -333,7 +333,13 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str return; } - if (Config::UIAlignmentMode == EUIAlignmentMode::Centre) + if ((modifier.Flags & PILLARBOX) != 0) + BlackBar::g_isPillarbox = true; + + if ((modifier.Flags & PROHIBIT_BLACK_BAR) != 0) + BlackBar::g_isPillarbox = false; + + if (Config::UIAlignmentMode == EUIAlignmentMode::Centre || BlackBar::g_isPillarbox) { if (g_aspectRatio >= WIDE_ASPECT_RATIO) modifier.Flags &= ~(ALIGN_LEFT | ALIGN_RIGHT); @@ -516,11 +522,31 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str g_radarMapCoverHeight = height; } - if ((modifier.Flags & PILLARBOX) != 0) - BlackBar::g_isPillarbox = true; + auto applyUVModifier = [&](CsdUVs uvModifier) + { + getVertex(0)->U = getVertex(0)->U + uvModifier.U0; + getVertex(0)->V = getVertex(0)->V + uvModifier.V0; + getVertex(1)->U = getVertex(1)->U + uvModifier.U1; + getVertex(1)->V = getVertex(1)->V + uvModifier.V1; + getVertex(2)->U = getVertex(2)->U + uvModifier.U2; + getVertex(2)->V = getVertex(2)->V + uvModifier.V2; + getVertex(3)->U = getVertex(3)->U + uvModifier.U3; + getVertex(3)->V = getVertex(3)->V + uvModifier.V3; + }; - if ((modifier.Flags & PROHIBIT_BLACK_BAR) != 0) - BlackBar::g_isPillarbox = false; + auto applyColourModifier = [&](CsdColours colourModifier) + { + getVertex(0)->Colour = colourModifier.C0; + getVertex(1)->Colour = colourModifier.C1; + getVertex(2)->Colour = colourModifier.C2; + getVertex(3)->Colour = colourModifier.C3; + }; + + if ((modifier.Flags & UV_MODIFIER) != 0) + applyUVModifier(modifier.UVs); + + if ((modifier.Flags & COLOUR_MODIFIER) != 0) + applyColourModifier(modifier.Colours); auto isRepeatLeft = (modifier.Flags & REPEAT_LEFT) != 0; auto isRepeatRight = (modifier.Flags & REPEAT_RIGHT) != 0; @@ -563,24 +589,10 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str } if ((modifier.Flags & REPEAT_UV_MODIFIER) != 0) - { - getVertex(0)->U = getVertex(0)->U + modifier.UVs.U0; - getVertex(0)->V = getVertex(0)->V + modifier.UVs.V0; - getVertex(1)->U = getVertex(1)->U + modifier.UVs.U1; - getVertex(1)->V = getVertex(1)->V + modifier.UVs.V1; - getVertex(2)->U = getVertex(2)->U + modifier.UVs.U2; - getVertex(2)->V = getVertex(2)->V + modifier.UVs.V2; - getVertex(3)->U = getVertex(3)->U + modifier.UVs.U3; - getVertex(3)->V = getVertex(3)->V + modifier.UVs.V3; - } + applyUVModifier(modifier.RepeatUVs); if ((modifier.Flags & REPEAT_COLOUR_MODIFIER) != 0) - { - getVertex(0)->Colour = modifier.Colours.C0; - getVertex(1)->Colour = modifier.Colours.C1; - getVertex(2)->Colour = modifier.Colours.C2; - getVertex(3)->Colour = modifier.Colours.C3; - } + applyColourModifier(modifier.RepeatColours); if ((modifier.Flags & REPEAT_EXTEND) != 0) { @@ -602,7 +614,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str } else { - ctx.r4.u32 = ctx.r1.u32; + ctx.r4 = ctx.r1; original(ctx, base); ctx.r1.u32 += size; } diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index d3d418206..4bae15fb4 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -25,7 +25,7 @@ inline XXH64_hash_t HashStr(const std::string_view& value) return XXH3_64bits(value.data(), value.size()); } -struct CsdRepeatUV +struct CsdUVs { float U0{}; float V0{}; @@ -37,7 +37,7 @@ struct CsdRepeatUV float V3{}; }; -struct CsdRepeatColour +struct CsdColours { uint32_t C0{}; uint32_t C1{}; @@ -48,8 +48,10 @@ struct CsdRepeatColour struct CsdModifier { uint32_t Flags{}; - CsdRepeatUV UVs{}; - CsdRepeatColour Colours{}; + CsdUVs UVs{}; + CsdColours Colours{}; + CsdUVs RepeatUVs{}; + CsdColours RepeatColours{}; float CornerMax{}; uint32_t CornerIndex{}; }; @@ -121,8 +123,35 @@ inline const xxHashMap g_modifiers = // black_out { HashStr("sprite/black_out/black_out"), { STRETCH } }, + // bossname + { HashStr("sprite/bossname/egg_cerberus/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1071/egg_cerberus/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1081/egg_cerberus/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/bossname/egg_genesis/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1091/egg_genesis/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/bossname/egg_wyvern/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1111/egg_wyvern/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/bossname/egg_wyvern/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1111/egg_wyvern/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/bossname/iblis/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1001/iblis/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1011/iblis/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1031/iblis/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/bossname/mephiles/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1041/mephiles/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1061/mephiles/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/bossname/shadow_the_hedgehog/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1161/shadow_the_hedgehog/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/bossname/silver_the_hedgehog/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1151/silver_the_hedgehog/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/bossname/solaris/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1121/solaris/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("sprite/bossname/sonic_the_hedgehog/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + { HashStr("event/e1141/sonic_the_hedgehog/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, + // button_window { HashStr("sprite/button_window/button_window/Scene_0000"), { ALIGN_BOTTOM | SCALE } }, + { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0002"), { EXTEND_RIGHT } }, // gadget_ber { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar"), { ALIGN_BOTTOM_RIGHT } }, @@ -153,15 +182,15 @@ inline const xxHashMap g_modifiers = { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1332"), { EXTEND_RIGHT } }, { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1336"), { EXTEND_RIGHT } }, { HashStr("sprite/main_menu/text"), { ALIGN_BOTTOM | SCALE } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/cover_l"), { ALIGN_BOTTOM | SCALE | REPEAT_LEFT | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.05f, 0, -0.05f, 0 } } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/cover_l"), { ALIGN_BOTTOM | SCALE | UV_MODIFIER | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.05f, 0, -0.05f, 0 } } }, { HashStr("sprite/main_menu/text_cover/Null_0290/cover_c"), { ALIGN_BOTTOM | SCALE } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/vocer_r"), { ALIGN_BOTTOM | SCALE | REPEAT_RIGHT | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { -0.05f, 0, -0.05f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/vocer_r"), { ALIGN_BOTTOM | SCALE | UV_MODIFIER | REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, 0.0015f, 0, 0.0015f, 0 }, {}, { -0.05f, 0, -0.05f, 0, 0, 0, 0, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { ALIGN_TOP | SCALE | EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { ALIGN_TOP | SCALE | REPEAT_LEFT | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.1f, 0, -0.1f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { ALIGN_TOP | SCALE | UV_MODIFIER | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0.1f, 0, 0.1f, 0, -0.1f, 0, -0.1f, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { ALIGN_TOP | SCALE | EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { ALIGN_TOP | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { ALIGN_BOTTOM | SCALE | REPEAT_LEFT | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.8f, 0, -0.8f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { ALIGN_BOTTOM | SCALE | REPEAT_RIGHT | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { ALIGN_TOP | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { ALIGN_BOTTOM | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.8f, 0, -0.8f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { ALIGN_BOTTOM | SCALE | REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, {}, {}, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, // maindisplay { HashStr("sprite/maindisplay/power"), { ALIGN_BOTTOM_RIGHT | SCALE } }, @@ -185,7 +214,7 @@ inline const xxHashMap g_modifiers = { HashStr("sprite/maindisplay/custom_level"), { ALIGN_BOTTOM_RIGHT | SCALE } }, // radarmap_cover - { HashStr("sprite/radarmap_cover/radarmap_cover/Scene_0000"), { ALIGN_TOP_RIGHT | SCALE | RADARMAP } }, + { HashStr("sprite/radarmap_cover/radarmap_cover/Scene_0000"), { RADARMAP | ALIGN_TOP_RIGHT | SCALE } }, // result_English { HashStr("sprite/result/result_English/title_plate"), { PILLARBOX } }, // TODO @@ -194,8 +223,8 @@ inline const xxHashMap g_modifiers = { HashStr("sprite/logo/sonicteam_logo/sonicteam"), { SCALE } }, // tag_character - { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug1"), { REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER | REPEAT_COLOUR_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, - { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug1"), { REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER | REPEAT_COLOUR_MODIFIER, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, + { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug1"), { REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER | REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, + { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug1"), { REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER | REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, // talkwindow { HashStr("sprite/talkwindow/talkwindow/window"), { SCALE } }, From c78290c613e45ec263d61254558663aa90499f74 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 23 Aug 2025 22:57:03 +0100 Subject: [PATCH 09/52] Clean-up, added CSD alignment for multiplayer HUDs --- MarathonRecomp/api/Marathon.h | 13 +- MarathonRecomp/api/Sonicteam/CsdObject.h | 5 +- MarathonRecomp/api/Sonicteam/CsdResource.h | 13 + MarathonRecomp/api/Sonicteam/Globals.h | 30 + MarathonRecomp/api/Sonicteam/HUDMainDisplay.h | 13 + MarathonRecomp/api/Sonicteam/SoX/IResource.h | 5 +- MarathonRecomp/api/Sonicteam/SoX/IResource2.h | 1 + MarathonRecomp/app.cpp | 3 +- .../patches/aspect_ratio_patches.cpp | 620 +++++++++++++----- MarathonRecomp/patches/aspect_ratio_patches.h | 311 +++------ MarathonRecomp/patches/loading_patches.cpp | 2 +- MarathonRecomp/patches/misc_patches.cpp | 118 ++-- MarathonRecompLib/config/Marathon.toml | 44 +- 13 files changed, 697 insertions(+), 481 deletions(-) create mode 100644 MarathonRecomp/api/Sonicteam/CsdResource.h create mode 100644 MarathonRecomp/api/Sonicteam/Globals.h create mode 100644 MarathonRecomp/api/Sonicteam/HUDMainDisplay.h diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index 4997bc0b1..d7a4693e0 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -25,17 +25,19 @@ #include "Sonicteam/CsdLink.h" #include "Sonicteam/CsdManager.h" #include "Sonicteam/CsdObject.h" +#include "Sonicteam/CsdResource.h" #include "Sonicteam/DocMarathonImp.h" #include "Sonicteam/DocMarathonState.h" #include "Sonicteam/GameImp.h" #include "Sonicteam/GameMode.h" +#include "Sonicteam/Globals.h" #include "Sonicteam/HUDButtonWindow.h" +#include "Sonicteam/HUDCALLBACK.h" #include "Sonicteam/HUDLoading.h" +#include "Sonicteam/HUDMainDisplay.h" #include "Sonicteam/HUDPopupScreen.h" #include "Sonicteam/HUDRaderMap.h" #include "Sonicteam/HudTextParts.h" -#include "Sonicteam/HUDCALLBACK.h" -#include "Sonicteam/HUDLoading.h" #include "Sonicteam/ImageFilter.h" #include "Sonicteam/MovieObject.h" #include "Sonicteam/MovieObjectWmv.h" @@ -76,15 +78,14 @@ #include "Sonicteam/SoX/Engine/Doc.h" #include "Sonicteam/SoX/Engine/DocMode.h" #include "Sonicteam/SoX/Engine/Task.h" -#include "Sonicteam/SoX/Graphics/Xenon/TextureXenon.h" -#include "Sonicteam/SoX/IResource2.h" -#include "Sonicteam/SoX/IResourceMgr.h" +#include "Sonicteam/SoX/Graphics/Device.h" #include "Sonicteam/SoX/Graphics/Technique.h" #include "Sonicteam/SoX/Graphics/TechniqueFXL.h" -#include "Sonicteam/SoX/Graphics/Device.h" #include "Sonicteam/SoX/Graphics/Xenon/DeviceXenon.h" #include "Sonicteam/SoX/Graphics/Xenon/TextureXenon.h" #include "Sonicteam/SoX/IResource.h" +#include "Sonicteam/SoX/IResource2.h" +#include "Sonicteam/SoX/IResourceMgr.h" #include "Sonicteam/SoX/Math/Matrix.h" #include "Sonicteam/SoX/Math/Vector.h" #include "Sonicteam/SoX/MessageReceiver.h" diff --git a/MarathonRecomp/api/Sonicteam/CsdObject.h b/MarathonRecomp/api/Sonicteam/CsdObject.h index 9d4bacd1f..34423349e 100644 --- a/MarathonRecomp/api/Sonicteam/CsdObject.h +++ b/MarathonRecomp/api/Sonicteam/CsdObject.h @@ -2,6 +2,7 @@ #include #include +#include namespace Sonicteam { @@ -9,7 +10,7 @@ namespace Sonicteam { public: MARATHON_INSERT_PADDING(4); - xpointer pCsdProject; - MARATHON_INSERT_PADDING(4); + xpointer m_pCsdProject; + xpointer m_pCsdResource; }; } diff --git a/MarathonRecomp/api/Sonicteam/CsdResource.h b/MarathonRecomp/api/Sonicteam/CsdResource.h new file mode 100644 index 000000000..7388fddda --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/CsdResource.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace Sonicteam +{ + class CsdResource : public SoX::IResource2 + { + public: + MARATHON_INSERT_PADDING(4); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/Globals.h b/MarathonRecomp/api/Sonicteam/Globals.h new file mode 100644 index 000000000..896ecddf8 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/Globals.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace Sonicteam +{ + enum Character : uint32_t + { + Character_Sonic, + Character_Shadow, + Character_Silver, + Character_Tails, + Character_Amy, + Character_Knuckles, + Character_Omega, + Character_Rouge, + Character_Blaze + }; + + struct Globals + { + static inline be* ms_MainDisplayColours[9]; + + static void Init() + { + for (int i = 0; i < 9; i++) + ms_MainDisplayColours[i] = (be*)MmGetHostAddress(0x82036BE4 + (i * 4)); + } + }; +} diff --git a/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h b/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h new file mode 100644 index 000000000..dab9d73d6 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class HUDMainDisplay : public SoX::RefCountObject, public SoX::Engine::Task + { + public: + MARATHON_INSERT_PADDING(0x24); + be m_Character; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/IResource.h b/MarathonRecomp/api/Sonicteam/SoX/IResource.h index 259a33f0b..b3812b9f9 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/IResource.h +++ b/MarathonRecomp/api/Sonicteam/SoX/IResource.h @@ -1,12 +1,15 @@ #pragma once #include +#include namespace Sonicteam::SoX { class IResource : public RefCountObject { public: - MARATHON_INSERT_PADDING(0x5C); + MARATHON_INSERT_PADDING(4); + stdx::string m_FilePath; + MARATHON_INSERT_PADDING(0x38); }; } diff --git a/MarathonRecomp/api/Sonicteam/SoX/IResource2.h b/MarathonRecomp/api/Sonicteam/SoX/IResource2.h index 9c1fa55de..044a61501 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/IResource2.h +++ b/MarathonRecomp/api/Sonicteam/SoX/IResource2.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace Sonicteam::SoX { diff --git a/MarathonRecomp/app.cpp b/MarathonRecomp/app.cpp index 056c23b20..219074a0f 100644 --- a/MarathonRecomp/app.cpp +++ b/MarathonRecomp/app.cpp @@ -38,6 +38,7 @@ PPC_FUNC(sub_8262A568) App::s_isMissingDLC = true; App::s_language = Config::Language; + Sonicteam::Globals::Init(); Registry::Save(); struct RenderConfig @@ -101,7 +102,7 @@ PPC_FUNC(sub_825EA610) if (auto pGameMode = App::s_pApp->m_pDoc->GetDocMode()) { if (GetAsyncKeyState(VK_F5) & 1) - pGameMode->m_pGameImp->m_GameState = Sonicteam::GameImp::eGameState_Result; + pGameMode->m_pGameImp->m_GameState = Sonicteam::GameImp::GameState_Result; } __imp__sub_825EA610(ctx, base); diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index fb7d66694..62439e6d2 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -85,13 +85,13 @@ void AspectRatioPatches::ComputeOffsets() g_aspectRatioNarrowScale = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (WIDE_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f); } -static void EmplacePath(const void* key, const std::string_view& value) +void EmplacePath(const void* key, const std::string_view& value) { std::lock_guard lock(g_pathMutex); g_paths.emplace(key, HashStr(value)); } -static void TraverseCast(Chao::CSD::Scene* scene, uint32_t castNodeIndex, Chao::CSD::CastNode* castNode, uint32_t castIndex, const std::string& parentPath) +void TraverseCast(Chao::CSD::Scene* scene, uint32_t castNodeIndex, Chao::CSD::CastNode* castNode, uint32_t castIndex, const std::string& parentPath) { if (castIndex == ~0) return; @@ -122,7 +122,7 @@ static void TraverseCast(Chao::CSD::Scene* scene, uint32_t castNodeIndex, Chao:: // LOGFN_UTILITY("CSD hierarchy: {}", path); } -static void TraverseScene(Chao::CSD::Scene* scene, std::string path) +void TraverseScene(Chao::CSD::Scene* scene, std::string path) { EmplacePath(scene, path); path += "/"; @@ -134,7 +134,7 @@ static void TraverseScene(Chao::CSD::Scene* scene, std::string path) } } -static void TraverseSceneNode(Chao::CSD::SceneNode* sceneNode, std::string path) +void TraverseSceneNode(Chao::CSD::SceneNode* sceneNode, std::string path) { EmplacePath(sceneNode, path); path += "/"; @@ -152,116 +152,6 @@ static void TraverseSceneNode(Chao::CSD::SceneNode* sceneNode, std::string path) } } -// Sonicteam::MovieObjectWmv::Draw -PPC_FUNC_IMPL(__imp__sub_8264CC90); -PPC_FUNC(sub_8264CC90) -{ - auto pMovieObjectWmv = (Sonicteam::MovieObjectWmv*)(base + ctx.r3.u32); - - static XXH64_hash_t s_movieNameHash{}; - static bool s_movieUsesCustomDimensions{}; - static float s_movieLeft{}; - static float s_movieRight{}; - static float s_movieTop{}; - static float s_movieBottom{}; - - auto movieNameHash = HashStr(pMovieObjectWmv->m_FilePath.c_str()); - - if (movieNameHash != s_movieNameHash) - { - s_movieNameHash = movieNameHash; - s_movieUsesCustomDimensions = pMovieObjectWmv->m_UseCustomDimensions; - s_movieLeft = pMovieObjectWmv->m_Left; - s_movieRight = pMovieObjectWmv->m_Right; - s_movieTop = pMovieObjectWmv->m_Top; - s_movieBottom = pMovieObjectWmv->m_Bottom; - - LOGFN_UTILITY("Movie: {} - {}x{}", pMovieObjectWmv->m_FilePath.c_str(), pMovieObjectWmv->m_Width.get(), pMovieObjectWmv->m_Height.get()); - } - - auto movieModifier = FindMovieModifier(movieNameHash); - auto movieAspectRatio = (float)pMovieObjectWmv->m_Width / (float)pMovieObjectWmv->m_Height; - - float width, height, left, right, top, bottom; - - if (s_movieUsesCustomDimensions) - { - auto movieRectLeft = s_movieLeft; - auto movieRectRight = s_movieRight; - auto movieRectTop = s_movieTop; - auto movieRectBottom = s_movieBottom; - - if (g_aspectRatio > movieAspectRatio) - { - // Scale width at wide aspect ratios. - movieRectLeft = s_movieLeft / (g_aspectRatio / movieAspectRatio); - movieRectRight = s_movieRight / (g_aspectRatio / movieAspectRatio); - } - else - { - // Scale height at narrow aspect ratios. - movieRectTop = s_movieTop / (movieAspectRatio / g_aspectRatio); - movieRectBottom = s_movieBottom / (movieAspectRatio / g_aspectRatio); - } - - auto movieRectCentreX = (movieRectLeft + movieRectRight) / 2.0f; - auto movieRectCentreY = (movieRectTop + movieRectBottom) / 2.0f; - - width = movieRectRight - movieRectLeft; - height = movieRectTop - movieRectBottom; - - left = movieRectCentreX - (width / 2.0f); - right = movieRectCentreX + (width / 2.0f); - top = movieRectCentreY + (height / 2.0f); - bottom = movieRectCentreY - (height / 2.0f); - } - else - { - width = 2.0f; - height = 2.0f; - - if (g_aspectRatio > movieAspectRatio) - { - if ((movieModifier.Flags & MovieFlags::CROP_WIDE) != 0) - { - // Crop vertically at wide aspect ratios. - height = 2.0f * (g_aspectRatio / movieAspectRatio); - } - else - { - // Pillarbox at wide aspect ratios. - width = 2.0f * (movieAspectRatio / g_aspectRatio); - } - } - else - { - if ((movieModifier.Flags & MovieFlags::CROP_NARROW) != 0) - { - // Crop horizontally at narrow aspect ratios. - width = 2.0f * (movieAspectRatio / g_aspectRatio); - } - else - { - // Letterbox at narrow aspect ratios. - height = 2.0f * (g_aspectRatio / movieAspectRatio); - } - } - - left = -width / 2.0f; - right = (width / 2.0f) * 2.0f; - top = height / 2.0f; - bottom = (-height / 2.0f) * 2.0f; - } - - pMovieObjectWmv->m_UseCustomDimensions = true; - pMovieObjectWmv->m_Left = left; - pMovieObjectWmv->m_Right = right; - pMovieObjectWmv->m_Top = top; - pMovieObjectWmv->m_Bottom = bottom; - - __imp__sub_8264CC90(ctx, base); -} - // Sonicteam::CsdResource::MakeResource PPC_FUNC_IMPL(__imp__sub_82617570); PPC_FUNC(sub_82617570) @@ -280,12 +170,12 @@ PPC_FUNC(sub_82617570) auto pCsdObject = reinterpret_cast(base + ppCsdObject); - if (!pCsdObject || !pCsdObject->pCsdProject) + if (!pCsdObject || !pCsdObject->m_pCsdProject) return; - LOGFN_UTILITY("CSD loaded: {} (0x{:08X})", pName, (uint64_t)pCsdObject->pCsdProject.get()); + LOGFN_UTILITY("CSD loaded: {} (0x{:08X})", pName, (uint64_t)pCsdObject->m_pCsdProject.get()); - TraverseSceneNode(pCsdObject->pCsdProject->m_pResource->pRootNode, pName); + TraverseSceneNode(pCsdObject->m_pCsdProject->m_pResource->pRootNode, pName); } // Chao::CSD::CMemoryAlloc::Free @@ -342,7 +232,7 @@ PPC_FUNC(sub_828C8F60) if (g_sceneModifier.has_value()) { - if (g_aspectRatio > WIDE_ASPECT_RATIO && (g_sceneModifier->Flags & (OFFSET_SCALE_LEFT | OFFSET_SCALE_RIGHT | CORNER_EXTRACT)) != 0) + if (g_aspectRatio > WIDE_ASPECT_RATIO && (g_sceneModifier->Flags & (CSD_OFFSET_SCALE_LEFT | CSD_OFFSET_SCALE_RIGHT | CSD_CORNER_EXTRACT)) != 0) { auto r3 = ctx.r3; auto r4 = ctx.r4; @@ -385,7 +275,7 @@ void RenderCsdCastMidAsmHook(PPCRegister& r4) g_castModifier = FindCsdModifier(r4.u32); } -static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) +void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) { CsdModifier modifier{}; @@ -402,32 +292,30 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str modifier = g_sceneModifier.value(); } - if ((modifier.Flags & SKIP) != 0) - { + if ((modifier.Flags & CSD_SKIP) != 0) return; - } if (g_cornerExtract) { - if ((modifier.Flags & (STORE_LEFT_CORNER | STORE_RIGHT_CORNER)) != 0) + if ((modifier.Flags & (CSD_STORE_LEFT_CORNER | CSD_STORE_RIGHT_CORNER)) != 0) { - uint32_t vertexIndex = ((modifier.Flags & STORE_LEFT_CORNER) != 0) ? 0 : 3; + uint32_t vertexIndex = ((modifier.Flags & CSD_STORE_LEFT_CORNER) != 0) ? 0 : 3; g_corners[modifier.CornerIndex] = *reinterpret_cast*>(base + ctx.r4.u32 + vertexIndex * stride); } return; } - if ((modifier.Flags & PILLARBOX) != 0) + if ((modifier.Flags & CSD_PILLARBOX) != 0) BlackBar::g_isPillarbox = true; - if ((modifier.Flags & PROHIBIT_BLACK_BAR) != 0) + if ((modifier.Flags & CSD_PROHIBIT_BLACK_BAR) != 0) BlackBar::g_isPillarbox = false; if (Config::UIAlignmentMode == EUIAlignmentMode::Centre || BlackBar::g_isPillarbox) { if (g_aspectRatio >= WIDE_ASPECT_RATIO) - modifier.Flags &= ~(ALIGN_LEFT | ALIGN_RIGHT); + modifier.Flags &= ~(CSD_ALIGN_LEFT | CSD_ALIGN_RIGHT); } uint32_t size = ctx.r5.u32 * stride; @@ -459,7 +347,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str bool needsStretch = g_aspectRatio >= WIDE_ASPECT_RATIO; - if (needsStretch && (modifier.Flags & STRETCH_HORIZONTAL) != 0) + if (needsStretch && (modifier.Flags & CSD_STRETCH_HORIZONTAL) != 0) { scaleX = Video::s_viewportWidth / 1280.0f; } @@ -467,26 +355,36 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str { scaleX = g_aspectRatioScale; - if (needsStretch && (modifier.Flags & UNSTRETCH_HORIZONTAL) != 0) + if (needsStretch && (modifier.Flags & CSD_UNSTRETCH_HORIZONTAL) != 0) { pivotX = getVertex(0)->X; offsetX = pivotX * Video::s_viewportWidth / 1280.0f; } else { - if ((modifier.Flags & ALIGN_RIGHT) != 0) + if ((modifier.Flags & CSD_ALIGN_RIGHT) != 0) + { offsetX = g_aspectRatioOffsetX * 2.0f; - else if ((modifier.Flags & ALIGN_LEFT) == 0) + + if ((modifier.Flags & CSD_MULTIPLAYER) != 0) + offsetX /= 2.0f; + } + else if ((modifier.Flags & CSD_ALIGN_LEFT) == 0) + { offsetX = g_aspectRatioOffsetX; - if ((modifier.Flags & SCALE) != 0) + if ((modifier.Flags & CSD_MULTIPLAYER_CENTER) != 0) + offsetX /= 1.5f; + } + + if ((modifier.Flags & CSD_SCALE) != 0) { scaleX *= g_aspectRatioGameplayScale; pivotX = g_scenePositionX; - if ((modifier.Flags & ALIGN_RIGHT) != 0) + if ((modifier.Flags & CSD_ALIGN_RIGHT) != 0) offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; - else if ((modifier.Flags & ALIGN_LEFT) == 0) + else if ((modifier.Flags & CSD_ALIGN_LEFT) == 0) offsetX += 640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; offsetX += pivotX * g_aspectRatioScale; @@ -494,7 +392,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str } } - if ((modifier.Flags & STRETCH_VERTICAL) != 0) + if ((modifier.Flags & CSD_STRETCH_VERTICAL) != 0) { scaleY = Video::s_viewportHeight / 720.0f; } @@ -502,19 +400,19 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str { scaleY = g_aspectRatioScale; - if ((modifier.Flags & ALIGN_BOTTOM) != 0) + if ((modifier.Flags & CSD_ALIGN_BOTTOM) != 0) offsetY = g_aspectRatioOffsetY * 2.0f; - else if ((modifier.Flags & ALIGN_TOP) == 0) + else if ((modifier.Flags & CSD_ALIGN_TOP) == 0) offsetY = g_aspectRatioOffsetY; - if ((modifier.Flags & SCALE) != 0) + if ((modifier.Flags & CSD_SCALE) != 0) { scaleY *= g_aspectRatioGameplayScale; pivotY = g_scenePositionY; - if ((modifier.Flags & ALIGN_BOTTOM) != 0) + if ((modifier.Flags & CSD_ALIGN_BOTTOM) != 0) offsetY += 720.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; - else if ((modifier.Flags & ALIGN_TOP) == 0) + else if ((modifier.Flags & CSD_ALIGN_TOP) == 0) offsetY += 360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; offsetY += pivotY * g_aspectRatioScale; @@ -530,7 +428,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str { offsetScaleModifier = g_castModifier.value(); - uint32_t vertexIndex = ((offsetScaleModifier.Flags & STORE_LEFT_CORNER) != 0) ? 0 : 3; + uint32_t vertexIndex = ((offsetScaleModifier.Flags & CSD_STORE_LEFT_CORNER) != 0) ? 0 : 3; corner = getVertex(vertexIndex)->X; } @@ -551,9 +449,9 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str fmt::println("Corner: {}", corner); #endif - if ((offsetScaleModifier.Flags & OFFSET_SCALE_LEFT) != 0) + if ((offsetScaleModifier.Flags & CSD_OFFSET_SCALE_LEFT) != 0) offsetX *= corner / offsetScaleModifier.CornerMax; - else if ((offsetScaleModifier.Flags & OFFSET_SCALE_RIGHT) != 0) + else if ((offsetScaleModifier.Flags & CSD_OFFSET_SCALE_RIGHT) != 0) offsetX = Video::s_viewportWidth - (Video::s_viewportWidth - offsetX) * (1280.0f - corner) / (1280.0f - offsetScaleModifier.CornerMax); } @@ -571,11 +469,11 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str float x = offsetX + (vertex->X - pivotX) * (scaleX * (1280.0f / Video::s_viewportWidth)); float y = offsetY + (vertex->Y - pivotY) * (scaleY * (720.0f / Video::s_viewportHeight)); - if ((modifier.Flags & EXTEND_LEFT) != 0 && (i == 0 || i == 1)) + if ((modifier.Flags & CSD_EXTEND_LEFT) != 0 && (i == 0 || i == 1)) { x = std::min(x, 0.0f); } - else if ((modifier.Flags & EXTEND_RIGHT) != 0 && (i == 2 || i == 3)) + else if ((modifier.Flags & CSD_EXTEND_RIGHT) != 0 && (i == 2 || i == 3)) { x = std::max(x, float(Video::s_viewportWidth)); } @@ -588,7 +486,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str case 3: lastX = x; break; } - if ((modifier.Flags & RADARMAP) != 0) + if ((modifier.Flags & CSD_RADARMAP) != 0) { g_radarMapX = x; g_radarMapY = y; @@ -601,7 +499,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str width = lastX - firstX; height = lastY - firstY; - if ((modifier.Flags & RADARMAP) != 0) + if ((modifier.Flags & CSD_RADARMAP) != 0) { g_radarMapCoverWidth = width; g_radarMapCoverHeight = height; @@ -627,14 +525,14 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str getVertex(3)->Colour = colourModifier.C3; }; - if ((modifier.Flags & UV_MODIFIER) != 0) + if ((modifier.Flags & CSD_UV_MODIFIER) != 0) applyUVModifier(modifier.UVs); - if ((modifier.Flags & COLOUR_MODIFIER) != 0) + if ((modifier.Flags & CSD_COLOUR_MODIFIER) != 0) applyColourModifier(modifier.Colours); - auto isRepeatLeft = (modifier.Flags & REPEAT_LEFT) != 0; - auto isRepeatRight = (modifier.Flags & REPEAT_RIGHT) != 0; + auto isRepeatLeft = (modifier.Flags & CSD_REPEAT_LEFT) != 0; + auto isRepeatRight = (modifier.Flags & CSD_REPEAT_RIGHT) != 0; if (isRepeatLeft || isRepeatRight) { @@ -663,7 +561,7 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str x = getVertex(vertexIndex)->X; - auto isFlipHorz = (modifier.Flags & REPEAT_FLIP_HORIZONTAL) != 0; + auto isFlipHorz = (modifier.Flags & CSD_REPEAT_FLIP_HORIZONTAL) != 0; if (isFlipHorz) { @@ -673,13 +571,13 @@ static void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t str getVertex(3)->X = getVertex(3)->X - width; } - if ((modifier.Flags & REPEAT_UV_MODIFIER) != 0) + if ((modifier.Flags & CSD_REPEAT_UV_MODIFIER) != 0) applyUVModifier(modifier.RepeatUVs); - if ((modifier.Flags & REPEAT_COLOUR_MODIFIER) != 0) + if ((modifier.Flags & CSD_REPEAT_COLOUR_MODIFIER) != 0) applyColourModifier(modifier.RepeatColours); - if ((modifier.Flags & REPEAT_EXTEND) != 0) + if ((modifier.Flags & CSD_REPEAT_EXTEND) != 0) { for (size_t i = 0; i < r5.u32; i++) { @@ -743,7 +641,117 @@ PPC_FUNC(sub_824F1538) pHUDRaderMap->m_Y = g_radarMapY - g_radarMapCoverHeight / 2; } -void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity, xxHashMap pftParams) +// Sonicteam::MovieObjectWmv::Draw +PPC_FUNC_IMPL(__imp__sub_8264CC90); +PPC_FUNC(sub_8264CC90) +{ + auto pMovieObjectWmv = (Sonicteam::MovieObjectWmv*)(base + ctx.r3.u32); + + static XXH64_hash_t s_movieNameHash{}; + static bool s_movieUsesCustomDimensions{}; + static float s_movieLeft{}; + static float s_movieRight{}; + static float s_movieTop{}; + static float s_movieBottom{}; + + auto movieNameHash = HashStr(pMovieObjectWmv->m_FilePath.c_str()); + + if (movieNameHash != s_movieNameHash) + { + s_movieNameHash = movieNameHash; + s_movieUsesCustomDimensions = pMovieObjectWmv->m_UseCustomDimensions; + s_movieLeft = pMovieObjectWmv->m_Left; + s_movieRight = pMovieObjectWmv->m_Right; + s_movieTop = pMovieObjectWmv->m_Top; + s_movieBottom = pMovieObjectWmv->m_Bottom; + + LOGFN_UTILITY("Movie: {} - {}x{}", pMovieObjectWmv->m_FilePath.c_str(), pMovieObjectWmv->m_Width.get(), pMovieObjectWmv->m_Height.get()); + } + + auto movieModifier = FindMovieModifier(movieNameHash); + auto movieAspectRatio = (float)pMovieObjectWmv->m_Width / (float)pMovieObjectWmv->m_Height; + + float width, height, left, right, top, bottom; + + if (s_movieUsesCustomDimensions) + { + auto movieRectLeft = s_movieLeft; + auto movieRectRight = s_movieRight; + auto movieRectTop = s_movieTop; + auto movieRectBottom = s_movieBottom; + + if (g_aspectRatio > movieAspectRatio) + { + // Scale width at wide aspect ratios. + movieRectLeft = s_movieLeft / (g_aspectRatio / movieAspectRatio); + movieRectRight = s_movieRight / (g_aspectRatio / movieAspectRatio); + } + else + { + // Scale height at narrow aspect ratios. + movieRectTop = s_movieTop / (movieAspectRatio / g_aspectRatio); + movieRectBottom = s_movieBottom / (movieAspectRatio / g_aspectRatio); + } + + auto movieRectCentreX = (movieRectLeft + movieRectRight) / 2.0f; + auto movieRectCentreY = (movieRectTop + movieRectBottom) / 2.0f; + + width = movieRectRight - movieRectLeft; + height = movieRectTop - movieRectBottom; + + left = movieRectCentreX - (width / 2.0f); + right = movieRectCentreX + (width / 2.0f); + top = movieRectCentreY + (height / 2.0f); + bottom = movieRectCentreY - (height / 2.0f); + } + else + { + width = 2.0f; + height = 2.0f; + + if (g_aspectRatio > movieAspectRatio) + { + if ((movieModifier.Flags & MOVIE_CROP_WIDE) != 0) + { + // Crop vertically at wide aspect ratios. + height = 2.0f * (g_aspectRatio / movieAspectRatio); + } + else + { + // Pillarbox at wide aspect ratios. + width = 2.0f * (movieAspectRatio / g_aspectRatio); + } + } + else + { + if ((movieModifier.Flags & MOVIE_CROP_NARROW) != 0) + { + // Crop horizontally at narrow aspect ratios. + width = 2.0f * (movieAspectRatio / g_aspectRatio); + } + else + { + // Letterbox at narrow aspect ratios. + height = 2.0f * (g_aspectRatio / movieAspectRatio); + } + } + + left = -width / 2.0f; + right = (width / 2.0f) * 2.0f; + top = height / 2.0f; + bottom = (-height / 2.0f) * 2.0f; + } + + pMovieObjectWmv->m_UseCustomDimensions = true; + pMovieObjectWmv->m_Left = left; + pMovieObjectWmv->m_Right = right; + pMovieObjectWmv->m_Top = top; + pMovieObjectWmv->m_Bottom = bottom; + + __imp__sub_8264CC90(ctx, base); +} + +void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity, xxHashMap pftModifier) { if (!pTextEntity || !pTextEntity->m_ImageVertexCount) return; @@ -771,8 +779,8 @@ void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity, xxHashMapm_TextureWidth; auto h = g_pTextFontPicture->m_TextureHeight; - auto baseParams = FindFontPictureParams(g_pftParamsXenon, variable.second); - auto newParams = FindFontPictureParams(pftParams, variable.second); + auto baseParams = FindFontPictureModifier(g_pftModifierXenon, variable.second); + auto newParams = FindFontPictureModifier(pftModifier, variable.second); auto uv = PIXELS_TO_UV_COORDS(w, h, newParams.X, newParams.Y, newParams.Width, newParams.Height); auto& min = std::get<0>(uv); @@ -873,12 +881,12 @@ PPC_FUNC(sub_8262D868) if (Config::ControllerIcons == EControllerIcons::Auto) isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation; - auto& pftParams = isPlayStation - ? g_pftParamsPS3 - : g_pftParamsXenon; + auto& pftModifier = isPlayStation + ? g_pftModifierPS3 + : g_pftModifierXenon; // TODO (Hyper): why does this not apply to the B button in Audio Room? - ReplaceTextVariables(pTextEntity, pftParams); + ReplaceTextVariables(pTextEntity, pftModifier); } // Sonicteam::HUDButtonWindow::Draw @@ -935,7 +943,293 @@ PPC_FUNC(sub_8263CC40) // -------------- MODIFIERS --------------- // +const xxHashMap g_csdModifiers = +{ + // audio + { HashStr("sprite/audio/audio/pod/pod"), { CSD_POD } }, + { HashStr("sprite/audio/audio/pod/pod/Cast_1084"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/audio/audio/pod/pod/Cast_1088"), { CSD_EXTEND_RIGHT } }, + + // background + { HashStr("sprite/background/background/mainmenu_back"), { CSD_STRETCH | CSD_PROHIBIT_BLACK_BAR } }, + + // battledisplay_1p + { HashStr("sprite/battledisplay_1p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/power_a"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/power_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/itembox_01"), { CSD_MULTIPLAYER | CSD_MULTIPLAYER_CENTER | CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/ring"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/ring_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/enemy"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/bronze_medal"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/gold_medal"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/bar_ue"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/power_bar_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/ring_000_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/custom_gem"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/custom_level1"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/custom_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + + // battledisplay_2p + { HashStr("sprite/battledisplay_2p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/power_a"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/power_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/itembox_01"), { CSD_MULTIPLAYER | CSD_MULTIPLAYER_CENTER | CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/ring"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/ring_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/enemy"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/bronze_medal"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/gold_medal"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/bar_ue"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/power_bar_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/ring_000_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/custom_gem"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/custom_level1"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/custom_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + + // black_out + { HashStr("sprite/black_out/black_out"), { CSD_STRETCH } }, + + // bossname + { HashStr("sprite/bossname/egg_cerberus/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1071/egg_cerberus/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1081/egg_cerberus/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/bossname/egg_genesis/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1091/egg_genesis/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/bossname/egg_wyvern/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1111/egg_wyvern/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/bossname/egg_wyvern/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1111/egg_wyvern/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/bossname/iblis/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1001/iblis/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1011/iblis/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1031/iblis/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/bossname/mephiles/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1041/mephiles/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1061/mephiles/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/bossname/shadow_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1161/shadow_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/bossname/silver_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1151/silver_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/bossname/solaris/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1121/solaris/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/bossname/sonic_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("event/e1141/sonic_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + + // button_window + { HashStr("sprite/button_window/button_window/Scene_0000"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0002"), { CSD_EXTEND_RIGHT } }, + + // gadget_ber + { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar"), { CSD_ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar_anime"), { CSD_ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar_ue"), { CSD_ALIGN_BOTTOM_RIGHT } }, + { HashStr("sprite/gadget_ber/gadget_bar/icon_text"), { CSD_ALIGN_BOTTOM_RIGHT } }, + + // cri_logo + { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/bg"), { CSD_STRETCH } }, + { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/criware"), { CSD_SCALE } }, + + // loading_english + // TODO: draw pillarbox. + { HashStr("sprite/loading/loading_english/Scene_0000/Loading"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/loading/loading_english/Scene_0000/Loading_02"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/loading/loading_english/Scene_0000/arrow_01"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/loading/loading_english/Scene_0000/arrow_02"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/loading/loading_english/Scene_0000/arrow_03"), { CSD_ALIGN_BOTTOM } }, + + // main_menu + { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita3"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita5"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita8"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/stage_plate/stage_plate/stage_plate2"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1337"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1340"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/mission_plate/mission_plate/mission_plate2"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1332"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1336"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/text"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/cover_l"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.05f, 0, -0.05f, 0 } } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/cover_c"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/vocer_r"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, 0.0015f, 0, 0.0015f, 0 }, {}, { -0.05f, 0, -0.05f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0.1f, 0, 0.1f, 0, -0.1f, 0, -0.1f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.8f, 0, -0.8f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, + + // maindisplay + { HashStr("sprite/maindisplay/power"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/custom_bar_anime"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/power_a"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/power_bar_anime"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/itembox_01"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/maindisplay/score"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/life_ber_anime"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/life"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/time"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/ring"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/ring_anime"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/bar_ue"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/power_bar_effect"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/ring_000_effect"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/boss_gauge"), { CSD_ALIGN_TOP_RIGHT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/boss_gauge_anime"), { CSD_ALIGN_TOP_RIGHT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/item_ber_anime"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/item"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/custom_gem"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/maindisplay/custom_level"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + + // radarmap_cover + { HashStr("sprite/radarmap_cover/radarmap_cover/Scene_0000"), { CSD_RADARMAP | CSD_ALIGN_TOP_RIGHT | CSD_SCALE } }, + + // result_English + { HashStr("sprite/result/result_English/title_plate"), { CSD_PILLARBOX } }, // TODO + + // sonicteam_logo + { HashStr("sprite/logo/sonicteam_logo/sonicteam"), { CSD_SCALE } }, + + // tag_character + { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug1"), { CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER | CSD_REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, + { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug1"), { CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER | CSD_REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, + + // tagdisplay_1p + { HashStr("sprite/tagdisplay_1p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/power_a"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/power_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/itembox_01"), { CSD_MULTIPLAYER | CSD_MULTIPLAYER_CENTER | CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/score"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/time"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/ring"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/ring_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/bar_ue"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/life_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/life"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/item_ber_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/item"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/power_bar_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/ring_000_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/custom_gem"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/custom_level1"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/custom_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + + // tagdisplay_2p + { HashStr("sprite/tagdisplay_2p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/power_a"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/power_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/itembox_01"), { CSD_MULTIPLAYER | CSD_MULTIPLAYER_CENTER | CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/score"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/time"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/ring"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/ring_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/bar_ue"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/life_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/life"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/item_ber_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/item"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/power_bar_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/ring_000_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/custom_gem"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/custom_level1"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/custom_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + + // talkwindow + { HashStr("sprite/talkwindow/talkwindow/window"), { CSD_SCALE } }, + { HashStr("sprite/talkwindow/talkwindow/nafuda"), { CSD_SCALE } }, + { HashStr("sprite/talkwindow/talkwindow/Scene_0021"), { CSD_SCALE } }, + + // title_English + { HashStr("sprite/title/title_English/Scene_Title/Logo_add"), { CSD_SCALE } }, + { HashStr("sprite/title/title_English/Scene_Title/Logo"), { CSD_SCALE } }, + { HashStr("sprite/title/title_English/Scene_Title/copyright"), { CSD_ALIGN_BOTTOM } }, + + // titleloop_sth + // TODO: adjust this if letterboxed. + { HashStr("sprite/logo/titleloop_sth/Scene_0000"), { CSD_ALIGN_BOTTOM } }, + + // towndisplay + { HashStr("sprite/towndisplay/ring"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + { HashStr("sprite/towndisplay/ring_anime"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, + + // trickpoint_English + // TODO: offset score text properly. + { HashStr("sprite/trickpoint/trickpoint_English/Scene_0000"), { CSD_ALIGN_TOP_RIGHT } }, + { HashStr("sprite/trickpoint/trickpoint_English/Scene_0001"), { CSD_ALIGN_TOP_RIGHT } }, + { HashStr("sprite/trickpoint/trickpoint_English/score"), { CSD_ALIGN_TOP_RIGHT } }, +}; + +std::optional FindCsdModifier(uint32_t data) +{ + XXH64_hash_t path; + { + std::lock_guard lock(g_pathMutex); + + auto findResult = g_paths.find(g_memory.Translate(data)); + + if (findResult == g_paths.end()) + return {}; + + path = findResult->second; + } + + auto findResult = g_csdModifiers.find(path); + + if (findResult != g_csdModifiers.end()) + return findResult->second; + + return {}; +} + +const xxHashMap g_pftModifierXenon = +{ + { HashStr("button_a"), { 0, 0, 28, 28 } }, + { HashStr("button_b"), { 28, 0, 28, 28 } }, + { HashStr("button_x"), { 56, 0, 28, 28 } }, + { HashStr("button_y"), { 84, 0, 28, 28 } }, + { HashStr("button_lb"), { 112, 0, 53, 28 } }, + { HashStr("button_lt"), { 56, 28, 55, 28 } }, + { HashStr("button_rb"), { 168, 0, 53, 28 } }, + { HashStr("button_rt"), { 0, 28, 55, 28 } }, + { HashStr("button_start"), { 112, 28, 28, 28 } }, + { HashStr("button_back"), { 140, 28, 28, 28 } } +}; + +const xxHashMap g_pftModifierPS3 = +{ + { HashStr("button_a"), { 0, 56, 28, 28 } }, + { HashStr("button_b"), { 28, 56, 28, 28 } }, + { HashStr("button_x"), { 56, 56, 28, 28 } }, + { HashStr("button_y"), { 84, 56, 28, 28 } }, + { HashStr("button_lb"), { 112, 56, 48, 28 } }, + { HashStr("button_lt"), { 168, 56, 48, 28 } }, + { HashStr("button_rb"), { 0, 84, 48, 28 } }, + { HashStr("button_rt"), { 56, 84, 48, 28 } }, + { HashStr("button_start"), { 140, 84, 28, 28 } }, + { HashStr("button_back"), { 112, 84, 28, 28 } } +}; + +TextFontPictureModifier FindFontPictureModifier(xxHashMap pftModifier, std::string_view& name) +{ + auto findResult = pftModifier.find(HashStr(name)); + + if (findResult != pftModifier.end()) + return findResult->second; + + return {}; +} + const xxHashMap g_movieModifiers = { - { HashStr("sound\\title_loop_GBn.wmv"), { CROP_NARROW } } + { HashStr("sound\\title_loop_GBn.wmv"), { MOVIE_CROP_NARROW } } }; + +MovieModifier FindMovieModifier(XXH64_hash_t nameHash) +{ + auto findResult = g_movieModifiers.find(nameHash); + + if (findResult != g_movieModifiers.end()) + return findResult->second; + + return {}; +} diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index 3b69075cf..8f7e0f6d7 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -25,6 +25,65 @@ inline float ComputeScale(float aspectRatio) return ((aspectRatio * 720.0f) / 1280.0f) / sqrt((aspectRatio * 720.0f) / 1280.0f); } +// -------------- CSD --------------- // + +enum CsdFlags +{ + CSD_ALIGN_CENTER = 0 << 0, + + CSD_ALIGN_TOP = 1 << 0, + CSD_ALIGN_LEFT = 1 << 1, + CSD_ALIGN_BOTTOM = 1 << 2, + CSD_ALIGN_RIGHT = 1 << 3, + + CSD_ALIGN_TOP_LEFT = CSD_ALIGN_TOP | CSD_ALIGN_LEFT, + CSD_ALIGN_TOP_RIGHT = CSD_ALIGN_TOP | CSD_ALIGN_RIGHT, + CSD_ALIGN_BOTTOM_LEFT = CSD_ALIGN_BOTTOM | CSD_ALIGN_LEFT, + CSD_ALIGN_BOTTOM_RIGHT = CSD_ALIGN_BOTTOM | CSD_ALIGN_RIGHT, + + CSD_STRETCH_HORIZONTAL = 1 << 4, + CSD_STRETCH_VERTICAL = 1 << 5, + + CSD_STRETCH = CSD_STRETCH_HORIZONTAL | CSD_STRETCH_VERTICAL, + + CSD_SCALE = 1 << 6, + + CSD_EXTEND_LEFT = 1 << 7, + CSD_EXTEND_RIGHT = 1 << 8, + + CSD_STORE_LEFT_CORNER = 1 << 9, + CSD_STORE_RIGHT_CORNER = 1 << 10, + + CSD_SKIP = 1 << 11, + + CSD_OFFSET_SCALE_LEFT = 1 << 12, + CSD_OFFSET_SCALE_RIGHT = 1 << 13, + + CSD_REPEAT_LEFT = 1 << 14, + CSD_REPEAT_RIGHT = 1 << 15, + CSD_REPEAT_FLIP_HORIZONTAL = 1 << 16, + CSD_REPEAT_FLIP_VERTICAL = 1 << 17, + CSD_REPEAT_EXTEND = 1 << 18, + + CSD_UV_MODIFIER = 1 << 19, + CSD_COLOUR_MODIFIER = 1 << 20, + CSD_REPEAT_UV_MODIFIER = 1 << 21, + CSD_REPEAT_COLOUR_MODIFIER = 1 << 22, + + CSD_PILLARBOX = 1 << 23, + CSD_PROHIBIT_BLACK_BAR = 1 << 24, + + CSD_UNSTRETCH_HORIZONTAL = 1 << 25, + + CSD_CORNER_EXTRACT = 1 << 26, + + CSD_RADARMAP = 1 << 27, + CSD_POD = 1 << 28, + + CSD_MULTIPLAYER = 1 << 29, + CSD_MULTIPLAYER_CENTER = 1 << 30 +}; + struct CsdUVs { float U0{}; @@ -56,198 +115,13 @@ struct CsdModifier uint32_t CornerIndex{}; }; -enum -{ - ALIGN_CENTER = 0 << 0, - - ALIGN_TOP = 1 << 0, - ALIGN_LEFT = 1 << 1, - ALIGN_BOTTOM = 1 << 2, - ALIGN_RIGHT = 1 << 3, - - ALIGN_TOP_LEFT = ALIGN_TOP | ALIGN_LEFT, - ALIGN_TOP_RIGHT = ALIGN_TOP | ALIGN_RIGHT, - ALIGN_BOTTOM_LEFT = ALIGN_BOTTOM | ALIGN_LEFT, - ALIGN_BOTTOM_RIGHT = ALIGN_BOTTOM | ALIGN_RIGHT, - - STRETCH_HORIZONTAL = 1 << 4, - STRETCH_VERTICAL = 1 << 5, - - STRETCH = STRETCH_HORIZONTAL | STRETCH_VERTICAL, - - SCALE = 1 << 6, +extern const xxHashMap g_csdModifiers; - EXTEND_LEFT = 1 << 7, - EXTEND_RIGHT = 1 << 8, +std::optional FindCsdModifier(uint32_t data); - STORE_LEFT_CORNER = 1 << 9, - STORE_RIGHT_CORNER = 1 << 10, +// -------------- TEXT -------------- // - SKIP = 1 << 11, - - OFFSET_SCALE_LEFT = 1 << 12, - OFFSET_SCALE_RIGHT = 1 << 13, - - REPEAT_LEFT = 1 << 14, - REPEAT_RIGHT = 1 << 15, - REPEAT_FLIP_HORIZONTAL = 1 << 16, - REPEAT_FLIP_VERTICAL = 1 << 17, - REPEAT_EXTEND = 1 << 18, - - UV_MODIFIER = 1 << 19, - COLOUR_MODIFIER = 1 << 20, - REPEAT_UV_MODIFIER = 1 << 21, - REPEAT_COLOUR_MODIFIER = 1 << 22, - - PILLARBOX = 1 << 23, - PROHIBIT_BLACK_BAR = 1 << 24, - - UNSTRETCH_HORIZONTAL = 1 << 25, - - CORNER_EXTRACT = 1 << 26, - - RADARMAP = 1 << 27, - POD = 1 << 28 -}; - -inline const xxHashMap g_modifiers = -{ - // audio - { HashStr("sprite/audio/audio/pod/pod"), { POD } }, - { HashStr("sprite/audio/audio/pod/pod/Cast_1084"), { EXTEND_RIGHT } }, - { HashStr("sprite/audio/audio/pod/pod/Cast_1088"), { EXTEND_RIGHT } }, - - // background - { HashStr("sprite/background/background/mainmenu_back"), { STRETCH | PROHIBIT_BLACK_BAR } }, - - // black_out - { HashStr("sprite/black_out/black_out"), { STRETCH } }, - - // bossname - { HashStr("sprite/bossname/egg_cerberus/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1071/egg_cerberus/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1081/egg_cerberus/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/bossname/egg_genesis/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1091/egg_genesis/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/bossname/egg_wyvern/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1111/egg_wyvern/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/bossname/egg_wyvern/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1111/egg_wyvern/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/bossname/iblis/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1001/iblis/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1011/iblis/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1031/iblis/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/bossname/mephiles/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1041/mephiles/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1061/mephiles/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/bossname/shadow_the_hedgehog/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1161/shadow_the_hedgehog/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/bossname/silver_the_hedgehog/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1151/silver_the_hedgehog/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/bossname/solaris/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1121/solaris/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/bossname/sonic_the_hedgehog/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("event/e1141/sonic_the_hedgehog/Scene_0000"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - - // button_window - { HashStr("sprite/button_window/button_window/Scene_0000"), { ALIGN_BOTTOM | SCALE } }, - { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0002"), { EXTEND_RIGHT } }, - - // gadget_ber - { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar"), { ALIGN_BOTTOM_RIGHT } }, - { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar_anime"), { ALIGN_BOTTOM_RIGHT } }, - { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar_ue"), { ALIGN_BOTTOM_RIGHT } }, - { HashStr("sprite/gadget_ber/gadget_bar/icon_text"), { ALIGN_BOTTOM_RIGHT } }, - - // cri_logo - { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/bg"), { STRETCH } }, - { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/criware"), { SCALE } }, - - // loading_english - // TODO: draw pillarbox. - { HashStr("sprite/loading/loading_english/Scene_0000/Loading"), { ALIGN_BOTTOM } }, - { HashStr("sprite/loading/loading_english/Scene_0000/Loading_02"), { ALIGN_BOTTOM } }, - { HashStr("sprite/loading/loading_english/Scene_0000/arrow_01"), { ALIGN_BOTTOM } }, - { HashStr("sprite/loading/loading_english/Scene_0000/arrow_02"), { ALIGN_BOTTOM } }, - { HashStr("sprite/loading/loading_english/Scene_0000/arrow_03"), { ALIGN_BOTTOM } }, - - // main_menu - { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita3"), { EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita5"), { EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita8"), { EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/stage_plate/stage_plate/stage_plate2"), { EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1337"), { EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1340"), { EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/mission_plate/mission_plate/mission_plate2"), { EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1332"), { EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1336"), { EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/text"), { ALIGN_BOTTOM | SCALE } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/cover_l"), { ALIGN_BOTTOM | SCALE | UV_MODIFIER | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.05f, 0, -0.05f, 0 } } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/cover_c"), { ALIGN_BOTTOM | SCALE } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/vocer_r"), { ALIGN_BOTTOM | SCALE | UV_MODIFIER | REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0, 0, 0, 0, 0.0015f, 0, 0.0015f, 0 }, {}, { -0.05f, 0, -0.05f, 0, 0, 0, 0, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { ALIGN_TOP | SCALE | EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { ALIGN_TOP | SCALE | UV_MODIFIER | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0.1f, 0, 0.1f, 0, -0.1f, 0, -0.1f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { ALIGN_TOP | SCALE | EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { ALIGN_TOP | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { ALIGN_BOTTOM | SCALE | REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.8f, 0, -0.8f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { ALIGN_BOTTOM | SCALE | REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER, {}, {}, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, - - // maindisplay - { HashStr("sprite/maindisplay/power"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/maindisplay/custom_bar_anime"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/maindisplay/power_a"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/maindisplay/power_bar_anime"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/maindisplay/score"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("sprite/maindisplay/life_ber_anime"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("sprite/maindisplay/life"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("sprite/maindisplay/time"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("sprite/maindisplay/ring"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("sprite/maindisplay/ring_anime"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("sprite/maindisplay/bar_ue"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/maindisplay/power_bar_effect"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/maindisplay/ring_000_effect"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("sprite/maindisplay/boss_gauge"), { ALIGN_TOP_RIGHT | SCALE } }, - { HashStr("sprite/maindisplay/boss_gauge_anime"), { ALIGN_TOP_RIGHT | SCALE } }, - { HashStr("sprite/maindisplay/item_ber_anime"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("sprite/maindisplay/item"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("sprite/maindisplay/custom_gem"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - { HashStr("sprite/maindisplay/custom_level"), { ALIGN_BOTTOM_RIGHT | SCALE } }, - - // radarmap_cover - { HashStr("sprite/radarmap_cover/radarmap_cover/Scene_0000"), { RADARMAP | ALIGN_TOP_RIGHT | SCALE } }, - - // result_English - { HashStr("sprite/result/result_English/title_plate"), { PILLARBOX } }, // TODO - - // sonicteam_logo - { HashStr("sprite/logo/sonicteam_logo/sonicteam"), { SCALE } }, - - // tag_character - { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug1"), { REPEAT_LEFT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER | REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, - { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug1"), { REPEAT_RIGHT | REPEAT_FLIP_HORIZONTAL | REPEAT_EXTEND | REPEAT_UV_MODIFIER | REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, - - // talkwindow - { HashStr("sprite/talkwindow/talkwindow/window"), { SCALE } }, - { HashStr("sprite/talkwindow/talkwindow/nafuda"), { SCALE } }, - { HashStr("sprite/talkwindow/talkwindow/Scene_0021"), { SCALE } }, - - // title_English - { HashStr("sprite/title/title_English/Scene_Title/Logo_add"), { SCALE } }, - { HashStr("sprite/title/title_English/Scene_Title/Logo"), { SCALE } }, - { HashStr("sprite/title/title_English/Scene_Title/copyright"), { ALIGN_BOTTOM } }, - - // towndisplay - { HashStr("sprite/towndisplay/ring"), { ALIGN_TOP_LEFT | SCALE } }, - { HashStr("sprite/towndisplay/ring_anime"), { ALIGN_TOP_LEFT | SCALE } }, - - // trickpoint_English - // TODO: offset score text properly. - { HashStr("sprite/trickpoint/trickpoint_English/Scene_0000"), { ALIGN_TOP_RIGHT } }, - { HashStr("sprite/trickpoint/trickpoint_English/Scene_0001"), { ALIGN_TOP_RIGHT } }, - { HashStr("sprite/trickpoint/trickpoint_English/score"), { ALIGN_TOP_RIGHT } }, -}; - -struct TextFontPictureParams +struct TextFontPictureModifier { uint16_t X{}; uint16_t Y{}; @@ -255,64 +129,25 @@ struct TextFontPictureParams uint16_t Height{}; }; -inline const xxHashMap g_pftParamsXenon = -{ - { HashStr("button_a"), { 0, 0, 28, 28 } }, - { HashStr("button_b"), { 28, 0, 28, 28 } }, - { HashStr("button_x"), { 56, 0, 28, 28 } }, - { HashStr("button_y"), { 84, 0, 28, 28 } }, - { HashStr("button_lb"), { 112, 0, 53, 28 } }, - { HashStr("button_lt"), { 56, 28, 55, 28 } }, - { HashStr("button_rb"), { 168, 0, 53, 28 } }, - { HashStr("button_rt"), { 0, 28, 55, 28 } }, - { HashStr("button_start"), { 112, 28, 28, 28 } }, - { HashStr("button_back"), { 140, 28, 28, 28 } } -}; - -inline const xxHashMap g_pftParamsPS3 = -{ - { HashStr("button_a"), { 0, 56, 28, 28 } }, - { HashStr("button_b"), { 28, 56, 28, 28 } }, - { HashStr("button_x"), { 56, 56, 28, 28 } }, - { HashStr("button_y"), { 84, 56, 28, 28 } }, - { HashStr("button_lb"), { 112, 56, 48, 28 } }, - { HashStr("button_lt"), { 168, 56, 48, 28 } }, - { HashStr("button_rb"), { 0, 84, 48, 28 } }, - { HashStr("button_rt"), { 56, 84, 48, 28 } }, - { HashStr("button_start"), { 140, 84, 28, 28 } }, - { HashStr("button_back"), { 112, 84, 28, 28 } } -}; - -inline TextFontPictureParams FindFontPictureParams(xxHashMap pftParams, std::string_view& name) -{ - auto findResult = pftParams.find(HashStr(name)); +extern const xxHashMap g_pftModifierXenon; +extern const xxHashMap g_pftModifierPS3; - if (findResult != pftParams.end()) - return findResult->second; +TextFontPictureModifier FindFontPictureModifier(xxHashMap pftModifier, std::string_view& name); - return {}; -} +// ------------- MOVIE -------------- // -enum MovieFlags : uint32_t +enum MovieFlags { - CROP_NARROW = 1 << 0, - CROP_WIDE = 1 << 1, - CROP = CROP_NARROW | CROP_WIDE + MOVIE_CROP_NARROW = 1 << 0, + MOVIE_CROP_WIDE = 1 << 1, + MOVIE_CROP = MOVIE_CROP_NARROW | MOVIE_CROP_WIDE }; struct MovieModifier { - MovieFlags Flags{}; + uint32_t Flags{}; }; extern const xxHashMap g_movieModifiers; -inline MovieModifier FindMovieModifier(XXH64_hash_t nameHash) -{ - auto findResult = g_movieModifiers.find(nameHash); - - if (findResult != g_movieModifiers.end()) - return findResult->second; - - return {}; -} +MovieModifier FindMovieModifier(XXH64_hash_t nameHash); diff --git a/MarathonRecomp/patches/loading_patches.cpp b/MarathonRecomp/patches/loading_patches.cpp index 143c6a456..d4873967d 100644 --- a/MarathonRecomp/patches/loading_patches.cpp +++ b/MarathonRecomp/patches/loading_patches.cpp @@ -7,7 +7,7 @@ PPC_FUNC(sub_824D7340) { auto pHUDLoading = (Sonicteam::HUDLoading*)(base + ctx.r3.u32); - if ((pHUDLoading->m_Flags.get() & Sonicteam::HUDLoading::eHUDLoadingFlags_Finished) == 0) + if ((pHUDLoading->m_Flags.get() & Sonicteam::HUDLoading::HUDLoadingFlags_Finished) == 0) { for (auto& event : LoadingPatches::Events) event->Update(ctx.f1.f64); diff --git a/MarathonRecomp/patches/misc_patches.cpp b/MarathonRecomp/patches/misc_patches.cpp index 45c4c10b0..9acb86771 100644 --- a/MarathonRecomp/patches/misc_patches.cpp +++ b/MarathonRecomp/patches/misc_patches.cpp @@ -9,90 +9,92 @@ void AchievementManagerUnlockMidAsmHook(PPCRegister& id) AchievementManager::Unlock(id.u32); } -void ContextualHUD_Init() +void SetLifeBarAnimation(PPCRegister& r3, PPCRegister& r4, PPCRegister& r5, PPCRegister& r31) { - auto base = g_memory.base; - - static bool Contextual_init; - if (Contextual_init) return; + static bool g_initContextualHUD{}; + static char* g_lifeBarSceneName{}; - PPC_STORE_U32(0x82036BE4, 0x0); // Sonic - PPC_STORE_U32(0x82036BE8, 0x3F800000); // Shadow - PPC_STORE_U32(0x82036BFC, 0x3F800000); // E-123 Omega - PPC_STORE_U32(0x82036C00, 0x3F800000); // Rouge - PPC_STORE_U32(0x82036BEC, 0x40000000); // Silver - PPC_STORE_U32(0x82036BF4, 0x40000000); // Amy - PPC_STORE_U32(0x82036C04, 0x40000000); // Blaze + if (!g_initContextualHUD) + { + constexpr const char* LIFE_BAR_ANIME = "life_bar_anime"; - Contextual_init = true; -} + g_lifeBarSceneName = (char*)g_userHeap.Alloc(strlen(LIFE_BAR_ANIME) + 1); -void ContextualHUD_LIFE_BER_ANIME_1(PPCRegister& str, PPCRegister& hud) -{ - if (!Config::RestoreContextualHUDColours) { - return; - } + strcpy(g_lifeBarSceneName, LIFE_BAR_ANIME); - ContextualHUD_Init(); + if (Config::RestoreContextualHUDColours) + { + *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Shadow] = 1.0f; + *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Omega] = 1.0f; + *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Rouge] = 1.0f; + *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Silver] = 2.0f; + *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Amy] = 2.0f; + *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Blaze] = 2.0f; + } - enum { - Sonic, - Shadow, - Silver, - - Tails, - Amy, - Knuckles, - - Omega, - Rouge, - Blaze - }; + g_initContextualHUD = true; + } auto base = g_memory.base; - auto chr_index = PPC_LOAD_U32(hud.u32 + 0x78); + auto pCsdObject = (Sonicteam::CsdObject*)(base + PPC_LOAD_U32(r3.u32)); + auto pSceneName = (const char*)(base + r4.u32); + + // Redirect "life_ber_anime" to "life_bar_anime", as they + // actually spelt "bar" correctly in the tag XNCP scene names... + if ((pCsdObject->m_pCsdResource->m_FilePath == "sprite/tagdisplay_1p" || + pCsdObject->m_pCsdResource->m_FilePath == "sprite/tagdisplay_2p") && + strcmp(pSceneName, "life_ber_anime") == 0) + { + r4.u32 = g_memory.MapVirtual(g_lifeBarSceneName); + } - uint32_t chr_in = str.u32; + if (!Config::RestoreContextualHUDColours) + return; - switch (chr_index) { - case Sonic: - case Tails: - case Knuckles: - chr_in = 0x82036778; // sonic_in + auto pHUDMainDisplay = (Sonicteam::HUDMainDisplay*)g_memory.Translate(r31.u32); + + switch (pHUDMainDisplay->m_Character) + { + case Sonicteam::Character_Sonic: + case Sonicteam::Character_Tails: + case Sonicteam::Character_Knuckles: + r5.u32 = 0x82036778; // "sonic_in" break; - case Shadow: - case Omega: - case Rouge: - chr_in = 0x8203676C; // shadow_in + + case Sonicteam::Character_Shadow: + case Sonicteam::Character_Omega: + case Sonicteam::Character_Rouge: + r5.u32 = 0x8203676C; // "shadow_in" break; - case Silver: - case Amy: - case Blaze: - chr_in = 0x82036760; // silver_in + + case Sonicteam::Character_Silver: + case Sonicteam::Character_Amy: + case Sonicteam::Character_Blaze: + r5.u32 = 0x82036760; // "silver_in" break; } - - str.u32 = chr_in; } -void ContextualHUD_RING_1(PPCRegister& index, PPCRegister& hud) +void SetRingBarIndex(PPCRegister& r5, PPCRegister& r31) { - if (!Config::RestoreContextualHUDColours) { + if (!Config::RestoreContextualHUDColours) return; - } - auto base = g_memory.base; - auto chr_index = PPC_LOAD_U32(hud.u32 + 0x78); - index.u32 = chr_index; + auto pHUDMainDisplay = (Sonicteam::HUDMainDisplay*)g_memory.Translate(r31.u32); + + r5.u32 = pHUDMainDisplay->m_Character; } +// Redirects 2P HUD to 1P HUD to remove overscan compensation. +void Load2PDisplayMidAsmHook() {} + void PostureDisableEdgeGrabLeftover(PPCRegister& posture) { - if (!Config::DisableEdgeGrabLeftover) { + if (!Config::DisableEdgeGrabLeftover) return; - } auto base = g_memory.base; + *(volatile uint8_t*)(base + (posture.u32 + 0x3C0)) = 1; } diff --git a/MarathonRecompLib/config/Marathon.toml b/MarathonRecompLib/config/Marathon.toml index 3ebc85787..9fa37b9b8 100644 --- a/MarathonRecompLib/config/Marathon.toml +++ b/MarathonRecompLib/config/Marathon.toml @@ -120,23 +120,23 @@ address = 0x82512B10 registers = ["r3", "r4"] [[midasm_hook]] -name = "ContextualHUD_LIFE_BER_ANIME_1" -address = 0x824D8044 -registers = ["r5", "r31"] +name = "SetLifeBarAnimation" +address = 0x824D804C +registers = ["r3", "r4", "r5", "r31"] [[midasm_hook]] -name = "ContextualHUD_LIFE_BER_ANIME_1" -address = 0x824D80A0 -registers = ["r5", "r31"] +name = "SetLifeBarAnimation" +address = 0x824D80A8 +registers = ["r3", "r4", "r5", "r31"] [[midasm_hook]] -name = "ContextualHUD_LIFE_BER_ANIME_1" -address = 0x824D8F2C -registers = ["r5", "r31"] +name = "SetLifeBarAnimation" +address = 0x824D8F34 +registers = ["r3", "r4", "r5", "r31"] [[midasm_hook]] -name = "ContextualHUD_RING_1" -address = 0x824DEB38 +name = "SetRingBarIndex" +address = 0x824DEB40 registers = ["r5", "r31"] [[midasm_hook]] @@ -256,3 +256,25 @@ registers = ["r8"] name = "MovieVoiceLang" address = 0x8264B4E4 registers = ["r19"] + +[[midasm_hook]] +name = "RenderCsdCastNodeMidAsmHook" +address = 0x828C8FBC +registers = ["r10", "r24"] + +[[midasm_hook]] +name = "RenderCsdCastMidAsmHook" +address = 0x828C8FFC +registers = ["r4"] + +# Redirect tagdisplay_2p to tagdisplay_1p +[[midasm_hook]] +name = "Load2PDisplayMidAsmHook" +address = 0x824DAE90 +jump_address = 0x824DAE94 + +# Redirect battledisplay_2p to battledisplay_1p +[[midasm_hook]] +name = "Load2PDisplayMidAsmHook" +address = 0x824DADE8 +jump_address = 0x824DADEC From a6a94a0aada58d559cbf4cbec7c8f24749dc36f8 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 23 Aug 2025 23:58:24 +0100 Subject: [PATCH 10/52] Fix radar and controller icons, aligned main menu titlebar effect --- MarathonRecomp/api/Sonicteam/SoX/IResource.h | 2 +- .../patches/aspect_ratio_patches.cpp | 44 +++++++++++-------- MarathonRecomp/patches/aspect_ratio_patches.h | 6 +-- MarathonRecomp/patches/video_patches.cpp | 12 ++--- 4 files changed, 31 insertions(+), 33 deletions(-) diff --git a/MarathonRecomp/api/Sonicteam/SoX/IResource.h b/MarathonRecomp/api/Sonicteam/SoX/IResource.h index b3812b9f9..52cfe31ff 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/IResource.h +++ b/MarathonRecomp/api/Sonicteam/SoX/IResource.h @@ -10,6 +10,6 @@ namespace Sonicteam::SoX public: MARATHON_INSERT_PADDING(4); stdx::string m_FilePath; - MARATHON_INSERT_PADDING(0x38); + MARATHON_INSERT_PADDING(0x3C); }; } diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 62439e6d2..ac58bb410 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -13,25 +13,25 @@ // #define CORNER_DEBUG static Mutex g_pathMutex; -static std::map g_paths; +static std::map g_paths{}; -static std::optional g_sceneModifier; -static std::optional g_castNodeModifier; -static std::optional g_castModifier; +static std::optional g_sceneModifier{}; +static std::optional g_castNodeModifier{}; +static std::optional g_castModifier{}; -static Sonicteam::TextFontPicture* g_pTextFontPicture; +static Sonicteam::TextFontPicture* g_pTextFontPicture{}; -static float g_corners[8]; -static bool g_cornerExtract; +static float g_corners[8]{}; +static bool g_cornerExtract{}; -static float g_radarMapX; -static float g_radarMapY; -static float g_radarMapCoverWidth; -static float g_radarMapCoverHeight; +static float g_radarMapX{}; +static float g_radarMapY{}; +static float g_radarMapCoverWidth{}; +static float g_radarMapCoverHeight{}; // Explicit translations don't get affected by gameplay UI downscaling. -static float g_scenePositionX; -static float g_scenePositionY; +static float g_scenePositionX{}; +static float g_scenePositionY{}; static class LoadingPillarboxEvent : public HookEvent { @@ -43,6 +43,11 @@ static class LoadingPillarboxEvent : public HookEvent } g_loadingPillarboxEvent{}; +float ComputeScale(float aspectRatio) +{ + return ((aspectRatio * 720.0f) / 1280.0f) / sqrt((aspectRatio * 720.0f) / 1280.0f); +} + void AspectRatioPatches::Init() { LoadingPatches::Events.push_back(&g_loadingPillarboxEvent); @@ -83,6 +88,7 @@ void AspectRatioPatches::ComputeOffsets() } g_aspectRatioNarrowScale = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (WIDE_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f); + g_radarMapScale = 256 * g_aspectRatioScale * g_aspectRatioGameplayScale; } void EmplacePath(const void* key, const std::string_view& value) @@ -633,10 +639,8 @@ PPC_FUNC(sub_824F1538) __imp__sub_824F1538(ctx, base); - auto radermapScale = 256 * g_aspectRatioScale * g_aspectRatioGameplayScale; - - pHUDRaderMap->m_pMaskTexture->m_Width = radermapScale; - pHUDRaderMap->m_pMaskTexture->m_Height = radermapScale; + pHUDRaderMap->m_pMaskTexture->m_Width = g_radarMapScale; + pHUDRaderMap->m_pMaskTexture->m_Height = g_radarMapScale; pHUDRaderMap->m_X = g_radarMapX - g_radarMapCoverWidth / 2; pHUDRaderMap->m_Y = g_radarMapY - g_radarMapCoverHeight / 2; } @@ -1017,8 +1021,8 @@ const xxHashMap g_csdModifiers = { HashStr("event/e1141/sonic_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, // button_window - { HashStr("sprite/button_window/button_window/Scene_0000"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, - { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0002"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0001"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0002"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_EXTEND_RIGHT } }, // gadget_ber { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar"), { CSD_ALIGN_BOTTOM_RIGHT } }, @@ -1056,8 +1060,10 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0.1f, 0, 0.1f, 0, -0.1f, 0, -0.1f, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966"), { CSD_ALIGN_TOP | CSD_SCALE } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.8f, 0, -0.8f, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/titlebar_effect"), { CSD_ALIGN_TOP | CSD_SCALE } }, // maindisplay { HashStr("sprite/maindisplay/power"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index 8f7e0f6d7..d908b95f3 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -12,6 +12,7 @@ inline float g_aspectRatioOffsetY; inline float g_aspectRatioScale; inline float g_aspectRatioGameplayScale; inline float g_aspectRatioNarrowScale; +inline float g_radarMapScale; class AspectRatioPatches { @@ -20,11 +21,6 @@ class AspectRatioPatches static void ComputeOffsets(); }; -inline float ComputeScale(float aspectRatio) -{ - return ((aspectRatio * 720.0f) / 1280.0f) / sqrt((aspectRatio * 720.0f) / 1280.0f); -} - // -------------- CSD --------------- // enum CsdFlags diff --git a/MarathonRecomp/patches/video_patches.cpp b/MarathonRecomp/patches/video_patches.cpp index 4af572bb2..8ca348af8 100644 --- a/MarathonRecomp/patches/video_patches.cpp +++ b/MarathonRecomp/patches/video_patches.cpp @@ -58,10 +58,8 @@ PPC_FUNC(sub_82619D00) if (*pName == "radermap") { - auto radermapScale = 256 * g_aspectRatioScale * g_aspectRatioGameplayScale; - - ctx.r5.u32 = radermapScale; - ctx.r6.u32 = radermapScale; + ctx.r5.u32 = g_radarMapScale; + ctx.r6.u32 = g_radarMapScale; } if (*pName == "reflection0") @@ -94,10 +92,8 @@ PPC_FUNC(sub_82619B88) { if (strcmp(g_pBlockName, "radermap0") == 0) { - auto radermapScale = 256 * g_aspectRatioScale; - - ctx.r5.u32 = radermapScale; - ctx.r6.u32 = radermapScale; + ctx.r5.u32 = g_radarMapScale; + ctx.r6.u32 = g_radarMapScale; } } From 92775f402effc3a40ee27ceab7d833860ea6b79c Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sun, 24 Aug 2025 03:48:29 +0100 Subject: [PATCH 11/52] Truncate XNCPs with language names to redirect CSD modifiers --- .../patches/aspect_ratio_patches.cpp | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index ac58bb410..61840e820 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -162,7 +162,7 @@ void TraverseSceneNode(Chao::CSD::SceneNode* sceneNode, std::string path) PPC_FUNC_IMPL(__imp__sub_82617570); PPC_FUNC(sub_82617570) { - auto pName = reinterpret_cast(base + PPC_LOAD_U32(ctx.r4.u32 + 4)); + auto pName = reinterpret_cast(base + PPC_LOAD_U32(ctx.r4.u32 + 4)); __imp__sub_82617570(ctx, base); @@ -181,6 +181,33 @@ PPC_FUNC(sub_82617570) LOGFN_UTILITY("CSD loaded: {} (0x{:08X})", pName, (uint64_t)pCsdObject->m_pCsdProject.get()); + static const char* s_languages[7] = + { + nullptr, // Maps to ELanguages enum. + "_English", + "_Japanese", + "_German", + "_French", + "_Spanish", + "_Italian" + }; + + auto pSuffix = s_languages[(int)Config::Language.Value]; + + auto nameLen = strlen(pName); + auto suffixLen = strlen(pSuffix); + + // Truncate language names to redirect CSD modifiers. + if (suffixLen < nameLen) + { + if (strcmp(pName + nameLen - suffixLen, pSuffix) == 0) + { + pName[nameLen - suffixLen] = '\0'; + + LOGFN_UTILITY("CSD modifier(s) redirected: {}", pName); + } + } + TraverseSceneNode(pCsdObject->m_pCsdProject->m_pResource->pRootNode, pName); } @@ -1034,13 +1061,12 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/bg"), { CSD_STRETCH } }, { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/criware"), { CSD_SCALE } }, - // loading_english - // TODO: draw pillarbox. - { HashStr("sprite/loading/loading_english/Scene_0000/Loading"), { CSD_ALIGN_BOTTOM } }, - { HashStr("sprite/loading/loading_english/Scene_0000/Loading_02"), { CSD_ALIGN_BOTTOM } }, - { HashStr("sprite/loading/loading_english/Scene_0000/arrow_01"), { CSD_ALIGN_BOTTOM } }, - { HashStr("sprite/loading/loading_english/Scene_0000/arrow_02"), { CSD_ALIGN_BOTTOM } }, - { HashStr("sprite/loading/loading_english/Scene_0000/arrow_03"), { CSD_ALIGN_BOTTOM } }, + // loading + { HashStr("sprite/loading/loading/Scene_0000/Loading"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/loading/loading/Scene_0000/Loading_02"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/loading/loading/Scene_0000/arrow_01"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/loading/loading/Scene_0000/arrow_02"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/loading/loading/Scene_0000/arrow_03"), { CSD_ALIGN_BOTTOM } }, // main_menu { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita3"), { CSD_EXTEND_RIGHT } }, @@ -1145,10 +1171,10 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/talkwindow/talkwindow/nafuda"), { CSD_SCALE } }, { HashStr("sprite/talkwindow/talkwindow/Scene_0021"), { CSD_SCALE } }, - // title_English - { HashStr("sprite/title/title_English/Scene_Title/Logo_add"), { CSD_SCALE } }, - { HashStr("sprite/title/title_English/Scene_Title/Logo"), { CSD_SCALE } }, - { HashStr("sprite/title/title_English/Scene_Title/copyright"), { CSD_ALIGN_BOTTOM } }, + // title + { HashStr("sprite/title/title/Scene_Title/Logo_add"), { CSD_SCALE } }, + { HashStr("sprite/title/title/Scene_Title/Logo"), { CSD_SCALE } }, + { HashStr("sprite/title/title/Scene_Title/copyright"), { CSD_ALIGN_BOTTOM } }, // titleloop_sth // TODO: adjust this if letterboxed. @@ -1158,11 +1184,11 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/towndisplay/ring"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/towndisplay/ring_anime"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, - // trickpoint_English + // trickpoint // TODO: offset score text properly. - { HashStr("sprite/trickpoint/trickpoint_English/Scene_0000"), { CSD_ALIGN_TOP_RIGHT } }, - { HashStr("sprite/trickpoint/trickpoint_English/Scene_0001"), { CSD_ALIGN_TOP_RIGHT } }, - { HashStr("sprite/trickpoint/trickpoint_English/score"), { CSD_ALIGN_TOP_RIGHT } }, + { HashStr("sprite/trickpoint/trickpoint/Scene_0000"), { CSD_ALIGN_TOP_RIGHT } }, + { HashStr("sprite/trickpoint/trickpoint/Scene_0001"), { CSD_ALIGN_TOP_RIGHT } }, + { HashStr("sprite/trickpoint/trickpoint/score"), { CSD_ALIGN_TOP_RIGHT } }, }; std::optional FindCsdModifier(uint32_t data) From 0d657726be1a65b2c391ef5aeb54ed06874fa87d Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sun, 24 Aug 2025 03:53:31 +0100 Subject: [PATCH 12/52] Results CSD adjustments --- .../patches/aspect_ratio_patches.cpp | 193 +++++++++++++++++- MarathonRecomp/patches/misc_patches.cpp | 24 +-- 2 files changed, 198 insertions(+), 19 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 61840e820..095eaf6c8 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -604,6 +604,16 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) getVertex(3)->X = getVertex(3)->X - width; } + auto isFlipVert = (modifier.Flags & CSD_REPEAT_FLIP_VERTICAL) != 0; + + if (isFlipVert) + { + getVertex(0)->Y = getVertex(0)->Y + height; + getVertex(1)->Y = getVertex(1)->Y - height; + getVertex(2)->Y = getVertex(2)->Y + height; + getVertex(3)->Y = getVertex(3)->Y - height; + } + if ((modifier.Flags & CSD_REPEAT_UV_MODIFIER) != 0) applyUVModifier(modifier.RepeatUVs); @@ -658,6 +668,34 @@ PPC_FUNC(sub_82631718) Draw(ctx, base, __imp__sub_82631718, 0x0C); } +// Sonicteam::EventEntityTask::Update +PPC_FUNC_IMPL(__imp__sub_8264AC48); +PPC_FUNC(sub_8264AC48) +{ + if (Config::CutsceneAspectRatio == ECutsceneAspectRatio::Original) + BlackBar::g_isPillarbox = true; + + __imp__sub_8264AC48(ctx, base); +} + +// Sonicteam::HUDResult::Update +PPC_FUNC_IMPL(__imp__sub_824F4D80); +PPC_FUNC(sub_824F4D80) +{ + BlackBar::g_isPillarbox = true; + + __imp__sub_824F4D80(ctx, base); +} + +// Sonicteam::HUDBattleResult::Update +PPC_FUNC_IMPL(__imp__sub_824D32C8); +PPC_FUNC(sub_824D32C8) +{ + BlackBar::g_isPillarbox = true; + + __imp__sub_824D32C8(ctx, base); +} + // Sonicteam::HUDRaderMap::Update PPC_FUNC_IMPL(__imp__sub_824F1538); PPC_FUNC(sub_824F1538) @@ -1018,6 +1056,17 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/battledisplay_2p/custom_level1"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/custom_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + // battle_result + { HashStr("sprite/battle_result/battle_result_English/congratulations"), { CSD_ALIGN_TOP } }, + { HashStr("sprite/battle_result/battle_result_English/plate/plate_ue/plate1"), { CSD_ALIGN_TOP | CSD_EXTEND_LEFT } }, + { HashStr("sprite/battle_result/battle_result_English/plate/plate_ue/plate2"), { CSD_ALIGN_TOP } }, + { HashStr("sprite/battle_result/battle_result_English/plate/plate_ue/plate3"), { CSD_ALIGN_TOP } }, + { HashStr("sprite/battle_result/battle_result_English/plate/plate_ue/plate4"), { CSD_ALIGN_TOP | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/battle_result/battle_result_English/plate/plate_sita/plate5"), { CSD_ALIGN_BOTTOM | CSD_EXTEND_LEFT } }, + { HashStr("sprite/battle_result/battle_result_English/plate/plate_sita/plate6"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/battle_result/battle_result_English/plate/plate_sita/plate7"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/battle_result/battle_result_English/plate/plate_sita/plate8"), { CSD_ALIGN_BOTTOM | CSD_EXTEND_RIGHT } }, + // black_out { HashStr("sprite/black_out/black_out"), { CSD_STRETCH } }, @@ -1116,8 +1165,148 @@ const xxHashMap g_csdModifiers = // radarmap_cover { HashStr("sprite/radarmap_cover/radarmap_cover/Scene_0000"), { CSD_RADARMAP | CSD_ALIGN_TOP_RIGHT | CSD_SCALE } }, - // result_English - { HashStr("sprite/result/result_English/title_plate"), { CSD_PILLARBOX } }, // TODO + // result + { HashStr("sprite/result/result/title_plate/plate_ue"), { CSD_ALIGN_TOP | CSD_SCALE } }, + { HashStr("sprite/result/result/title_plate/plate_sita"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/result/result/title_plate/result_title/result_title_ob"), { CSD_ALIGN_TOP | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/score/score_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/score/score_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/score/score_plate/score_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/score/score_plate/score_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/time/time_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/time/time_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/time/time_plate/time_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/time/time_plate/time_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/ring/ring_plare_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/ring/ring_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/ring/ring_plate/ring_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/ring/ring_plate/ring_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/time_bonus/time_bonus_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/time_bonus/time_bonus_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/time_bonus/time_bonus_plate/timebonus_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/time_bonus/time_bonus_plate/time_bonus_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/ring_bonus/ring_bonus_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/ring_bonus/ring_bonus_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/ring_bonus/ring_bonus_plate/ringbonus_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/ring_bonus/ring_bonus_plate/ring_bonus_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/amiplate/ami_plate_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/result/parts_anime_result/amiplate/ami_plate"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/town_score_t/score_plate_kage_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/town_score_t/score_plate_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/town_score_t/score_plate_t/score_text_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/town_score_t/score_plate_t/score_plate_title_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_t/time_plate_kage_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_t/time_plate_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_t/time_plate_t/time_text_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_t/time_plate_t/time_plate_title_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_t/ring_plare_kage_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_t/ring_plate_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_t/ring_plate_t/ring_text_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_t/ring_plate_t/ring_title_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_bonus_t/time_bonus_kage_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_bonus_t/time_bonus_plate_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_bonus_t/time_bonus_plate_t/timebonus_text_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_bonus_t/time_bonus_plate_t/time_bonus_plate_title_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_bonus_t/ring_bonus_plate_kage_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_bonus_t/ring_bonus_plate_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_bonus_t/ring_bonus_plate_t/ringbonus_text_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_bonus_t/ring_bonus_plate_t/ring_bonus_plate_title_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/record_t/record_plate_kage_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/record_t/record_bonus_plate_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/record_t/record_bonus_plate_t/record_text_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/record_t/record_bonus_plate_t/record_plate_title_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/record_t/item_icon"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/town_mission/parts_anime_result_t/amiplate_t"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate_kage01"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/list_text01"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/list_text01/nyoro01"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/ring01"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/ring01/ring01_b"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/bonus_text01"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/rank_list_s_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/rank_list_s"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate_kage02"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/list_text02"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/list_text02/nyoro02"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/ring02"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/ring02/ring02_b"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/bonus_text02"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/rank_list_a_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/rank_list_a"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate_kage03"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/list_text03"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/list_text03/nyoro03"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/ring03"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/ring03/ring03_b"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/bonus_text03"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/rank_list_b_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/rank_list_b"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate_kage04"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate04"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate04/list_text04"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate04/list_text04/nyoro04"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate04/ring04"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate04/ring04/ring04_b"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate04/bonus_text04"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/rank_list_c_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/rank_list_c"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate_kage05"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/list_text05"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/list_text05/nyoro05"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/ring05"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/ring05/ring05_b"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/bonus_text05"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/rank_list_d_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/rank_list_d"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/amiplate/ami_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/amiplate/ami_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/rank_bonus/rank_bonus_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/rank_bonus/rank_bonus_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/rank_bonus/rank_bonus_plate/rank_bonus_plate_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/rank_bonus/rank_bonus_plate/rank_bonus_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/rank_bonus/rank_bonus_ring"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/rank_bonus/rank_bonus_ring/rank_bonus_ring_b"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/total_ring/total_ring_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/total_ring/total_ring_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/total_ring/total_ring_plate/total_ring_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/TotalRing_Goldfont/total_ring_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/TotalRing_Goldfont/ring_l"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/bonus/parts_anime_bonus/TotalRing_Goldfont/ring_l_b"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/totalscore_rank/parts_anime/total_score/total_score_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/totalscore_rank/parts_anime/total_score/total_score_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/totalscore_rank/parts_anime/total_score/total_score_plate/totalscore_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/totalscore_rank/parts_anime/total_score/total_score_plate/total_score_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/totalscore_rank/parts_anime/rank/rank_plate_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/totalscore_rank/parts_anime/rank/rank_plate"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/totalscore_rank/parts_anime/rank/rank_plate/rank_plate_title"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/rank_anime"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/result/result/score_newrecord"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/time_newrecord"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/ring_newrecord"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/timebonus_newrecord"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + { HashStr("sprite/result/result/ringbonus_newrecord"), { CSD_ALIGN_LEFT | CSD_SCALE } }, + + // result WIP ultrawide adjustments + // { HashStr("sprite/result/result/title_plate/plate_ue/plate_ue_kage_l"), { CSD_ALIGN_TOP | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.015f, 0, 0.015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.6f, 0, -0.6f, 0 } } }, + // { HashStr("sprite/result/result/title_plate/plate_ue/plate_ue_kage_r"), { CSD_SKIP | CSD_ALIGN_TOP | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.08f, 0, -0.08f, 0 }, {}, { 0.6f, 0, 0.6f, 0, 0, 0, 0, 0 } } }, + // { HashStr("sprite/result/result/title_plate/plate_ue/plate_ue_plate_l"), { CSD_ALIGN_TOP | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.015f, 0, 0.015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.6f, 0, -0.6f, 0 } } }, + // { HashStr("sprite/result/result/title_plate/plate_ue/plate_ue_plate_r"), { CSD_ALIGN_TOP | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.015f, 0, -0.015f, 0 }, {}, { 0.6f, 0, 0.6f, 0, 0, 0, 0, 0 } } }, + // { HashStr("sprite/result/result/title_plate/plate_sita/plate_sita_kage_l"), { CSD_ALIGN_BOTTOM | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.08f, 0, -0.08f, 0 }, {}, { 0.6f, 0, 0.6f, 0, 0, 0, 0, 0 } } }, // TODO: this crashes the game. + // { HashStr("sprite/result/result/title_plate/plate_sita/plate_sita_kage_r"), { CSD_ALIGN_BOTTOM | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.015f, 0, 0.015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.6f, 0, -0.6f, 0 } } }, // TODO: this crashes the game. + // { HashStr("sprite/result/result/title_plate/plate_sita/plate_sita_plate_l"), { CSD_ALIGN_BOTTOM | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.015f, 0, -0.015f, 0 }, {}, { 0.6f, 0, 0.6f, 0, 0, 0, 0, 0 } } }, // TODO: this crashes the game. + // { HashStr("sprite/result/result/title_plate/plate_sita/plate_sita_plate_r"), { CSD_ALIGN_BOTTOM | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.015f, 0, 0.015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.6f, 0, -0.6f, 0 } } }, // TODO: this crashes the game. + // { HashStr("sprite/result/result/result/parts_anime_result/amiplate/ami_plate"), { CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_FLIP_VERTICAL | CSD_UV_MODIFIER | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.025f, 0, -0.025f, 0 }, {}, { -0.01285f, 0.002f, -0.01285f, 0.002f, -0.01285f, 0.002f, -0.01285f, 0.002f } } }, + // { HashStr("sprite/result/result/town_mission/parts_anime_result_t/amiplate_t/ami_plate_t"), { CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_FLIP_VERTICAL | CSD_UV_MODIFIER | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.025f, 0, -0.025f, 0 }, {}, { -0.01285f, 0.002f, -0.01285f, 0.002f, -0.01285f, 0.002f, -0.01285f, 0.002f } } }, + // { HashStr("sprite/result/result/bonus/parts_anime_bonus/amiplate/ami_plate"), { CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_FLIP_VERTICAL | CSD_UV_MODIFIER | CSD_REPEAT_UV_MODIFIER, { 0.025f, 0, 0.025f, 0, 0, 0, 0, 0 }, {}, { 0.027f, 0.002f, 0.027f, 0.002f, 0.027f, 0.002f, 0.027f, 0.002f } } }, + + // result amiplate fade (needs special case for 16:9 or narrower) + // { HashStr("sprite/result/result/result/parts_anime_result/amiplate/ami_plate"), { CSD_COLOUR_MODIFIER, {}, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFF00 }, } }, + // { HashStr("sprite/result/result/town_mission/parts_anime_result_t/amiplate_t/ami_plate_t"), { CSD_COLOUR_MODIFIER, {}, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFF00 } } }, + // { HashStr("sprite/result/result/bonus/parts_anime_bonus/amiplate/ami_plate"), { CSD_COLOUR_MODIFIER, {}, { 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF } } }, // sonicteam_logo { HashStr("sprite/logo/sonicteam_logo/sonicteam"), { CSD_SCALE } }, diff --git a/MarathonRecomp/patches/misc_patches.cpp b/MarathonRecomp/patches/misc_patches.cpp index 9acb86771..95b629aa5 100644 --- a/MarathonRecomp/patches/misc_patches.cpp +++ b/MarathonRecomp/patches/misc_patches.cpp @@ -11,16 +11,16 @@ void AchievementManagerUnlockMidAsmHook(PPCRegister& id) void SetLifeBarAnimation(PPCRegister& r3, PPCRegister& r4, PPCRegister& r5, PPCRegister& r31) { - static bool g_initContextualHUD{}; - static char* g_lifeBarSceneName{}; + static bool s_initContextualHUD{}; + static char* s_lifeBarSceneName{}; - if (!g_initContextualHUD) + if (!s_initContextualHUD) { constexpr const char* LIFE_BAR_ANIME = "life_bar_anime"; - g_lifeBarSceneName = (char*)g_userHeap.Alloc(strlen(LIFE_BAR_ANIME) + 1); + s_lifeBarSceneName = (char*)g_userHeap.Alloc(strlen(LIFE_BAR_ANIME) + 1); - strcpy(g_lifeBarSceneName, LIFE_BAR_ANIME); + strcpy(s_lifeBarSceneName, LIFE_BAR_ANIME); if (Config::RestoreContextualHUDColours) { @@ -32,7 +32,7 @@ void SetLifeBarAnimation(PPCRegister& r3, PPCRegister& r4, PPCRegister& r5, PPCR *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Blaze] = 2.0f; } - g_initContextualHUD = true; + s_initContextualHUD = true; } auto base = g_memory.base; @@ -45,7 +45,7 @@ void SetLifeBarAnimation(PPCRegister& r3, PPCRegister& r4, PPCRegister& r5, PPCR pCsdObject->m_pCsdResource->m_FilePath == "sprite/tagdisplay_2p") && strcmp(pSceneName, "life_ber_anime") == 0) { - r4.u32 = g_memory.MapVirtual(g_lifeBarSceneName); + r4.u32 = g_memory.MapVirtual(s_lifeBarSceneName); } if (!Config::RestoreContextualHUDColours) @@ -98,16 +98,6 @@ void PostureDisableEdgeGrabLeftover(PPCRegister& posture) *(volatile uint8_t*)(base + (posture.u32 + 0x3C0)) = 1; } -// Sonicteam::EventEntityTask::Update -PPC_FUNC_IMPL(__imp__sub_8264AC48); -PPC_FUNC(sub_8264AC48) -{ - if (Config::CutsceneAspectRatio == ECutsceneAspectRatio::Original) - BlackBar::g_isPillarbox = true; - - __imp__sub_8264AC48(ctx, base); -} - void PedestrianAnimationLOD(PPCRegister& val) { val.u32 = 0; From c7741ef6a133534bdc393a6826ae01f6727b6881 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sun, 24 Aug 2025 04:07:24 +0100 Subject: [PATCH 13/52] Check suffix against XNCP name case insensitive --- MarathonRecomp/framework.h | 20 +++++++++++++++++++ .../patches/aspect_ratio_patches.cpp | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/MarathonRecomp/framework.h b/MarathonRecomp/framework.h index cb89adb77..b6a4d2e23 100644 --- a/MarathonRecomp/framework.h +++ b/MarathonRecomp/framework.h @@ -64,6 +64,26 @@ inline std::unique_ptr ReadAllBytes(const char* filePath, size_t& fil return data; } +inline bool strcmpIgnoreCase(const char* a, const char* b) +{ + for (size_t i = 0; i < strlen(a); i++) + { + if (a[i] != '\0' && b[i] == '\0') + return false; + + if (a[i] == '\0' && b[i] != '\0') + return false; + + auto c1 = std::tolower((uint8_t)a[i]); + auto c2 = std::tolower((uint8_t)b[i]); + + if (c1 != c2) + return false; + } + + return true; +} + #ifndef __cpp_lib_atomic_ref // Polyfill for std::atomic_ref namespace std { diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 095eaf6c8..0a889500d 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -200,7 +200,7 @@ PPC_FUNC(sub_82617570) // Truncate language names to redirect CSD modifiers. if (suffixLen < nameLen) { - if (strcmp(pName + nameLen - suffixLen, pSuffix) == 0) + if (strcmpIgnoreCase(pName + nameLen - suffixLen, pSuffix)) { pName[nameLen - suffixLen] = '\0'; From 9c9d4e13092f7a0b0cdfc88db4f0773606223fd6 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sun, 24 Aug 2025 04:13:19 +0100 Subject: [PATCH 14/52] Remove battle_result language suffix --- .../patches/aspect_ratio_patches.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 0a889500d..8059456ac 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1057,15 +1057,15 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/battledisplay_2p/custom_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, // battle_result - { HashStr("sprite/battle_result/battle_result_English/congratulations"), { CSD_ALIGN_TOP } }, - { HashStr("sprite/battle_result/battle_result_English/plate/plate_ue/plate1"), { CSD_ALIGN_TOP | CSD_EXTEND_LEFT } }, - { HashStr("sprite/battle_result/battle_result_English/plate/plate_ue/plate2"), { CSD_ALIGN_TOP } }, - { HashStr("sprite/battle_result/battle_result_English/plate/plate_ue/plate3"), { CSD_ALIGN_TOP } }, - { HashStr("sprite/battle_result/battle_result_English/plate/plate_ue/plate4"), { CSD_ALIGN_TOP | CSD_EXTEND_RIGHT } }, - { HashStr("sprite/battle_result/battle_result_English/plate/plate_sita/plate5"), { CSD_ALIGN_BOTTOM | CSD_EXTEND_LEFT } }, - { HashStr("sprite/battle_result/battle_result_English/plate/plate_sita/plate6"), { CSD_ALIGN_BOTTOM } }, - { HashStr("sprite/battle_result/battle_result_English/plate/plate_sita/plate7"), { CSD_ALIGN_BOTTOM } }, - { HashStr("sprite/battle_result/battle_result_English/plate/plate_sita/plate8"), { CSD_ALIGN_BOTTOM | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/battle_result/battle_result/congratulations"), { CSD_ALIGN_TOP } }, + { HashStr("sprite/battle_result/battle_result/plate/plate_ue/plate1"), { CSD_ALIGN_TOP | CSD_EXTEND_LEFT } }, + { HashStr("sprite/battle_result/battle_result/plate/plate_ue/plate2"), { CSD_ALIGN_TOP } }, + { HashStr("sprite/battle_result/battle_result/plate/plate_ue/plate3"), { CSD_ALIGN_TOP } }, + { HashStr("sprite/battle_result/battle_result/plate/plate_ue/plate4"), { CSD_ALIGN_TOP | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/battle_result/battle_result/plate/plate_sita/plate5"), { CSD_ALIGN_BOTTOM | CSD_EXTEND_LEFT } }, + { HashStr("sprite/battle_result/battle_result/plate/plate_sita/plate6"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/battle_result/battle_result/plate/plate_sita/plate7"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/battle_result/battle_result/plate/plate_sita/plate8"), { CSD_ALIGN_BOTTOM | CSD_EXTEND_RIGHT } }, // black_out { HashStr("sprite/black_out/black_out"), { CSD_STRETCH } }, From 4092228b88c8b586d4f2b4121df63de53e37aad6 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sun, 24 Aug 2025 04:31:09 +0100 Subject: [PATCH 15/52] Fix Audio/Theatre Room "pod" box fill --- .../patches/aspect_ratio_patches.cpp | 54 ++++++++++++++++--- MarathonRecomp/patches/aspect_ratio_patches.h | 10 ++-- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 8059456ac..224d8a7fc 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -29,6 +29,8 @@ static float g_radarMapY{}; static float g_radarMapCoverWidth{}; static float g_radarMapCoverHeight{}; +static float g_podBaseRightX{}; + // Explicit translations don't get affected by gameplay UI downscaling. static float g_scenePositionX{}; static float g_scenePositionY{}; @@ -278,7 +280,7 @@ PPC_FUNC(sub_828C8F60) g_cornerExtract = false; #ifdef CORNER_DEBUG - if (g_sceneModifier->cornerMax == FLT_MAX) + if (g_sceneModifier->CornerMax == FLT_MAX) { fmt::print("Corners: "); for (auto corner : g_corners) @@ -478,7 +480,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) } #ifdef CORNER_DEBUG - if ((offsetScaleModifier.Flags & (OFFSET_SCALE_LEFT | OFFSET_SCALE_RIGHT)) != 0 && offsetScaleModifier.cornerMax == FLT_MAX) + if ((offsetScaleModifier.Flags & (CSD_OFFSET_SCALE_LEFT | CSD_OFFSET_SCALE_RIGHT)) != 0 && offsetScaleModifier.CornerMax == FLT_MAX) fmt::println("Corner: {}", corner); #endif @@ -508,6 +510,10 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) } else if ((modifier.Flags & CSD_EXTEND_RIGHT) != 0 && (i == 2 || i == 3)) { + // Preserve Audio Room "pod" base right edge. + if ((modifier.Flags & CSD_POD_BASE) != 0) + g_podBaseRightX = x; + x = std::max(x, float(Video::s_viewportWidth)); } @@ -604,9 +610,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) getVertex(3)->X = getVertex(3)->X - width; } - auto isFlipVert = (modifier.Flags & CSD_REPEAT_FLIP_VERTICAL) != 0; - - if (isFlipVert) + if ((modifier.Flags & CSD_REPEAT_FLIP_VERTICAL) != 0) { getVertex(0)->Y = getVertex(0)->Y + height; getVertex(1)->Y = getVertex(1)->Y - height; @@ -640,8 +644,44 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) } else { + auto r3 = ctx.r3; + auto r5 = ctx.r5; + + ctx.r3 = r3; ctx.r4 = ctx.r1; + ctx.r5 = r5; original(ctx, base); + + // Clone Audio Room "pod" left edge to fill + // in the rest of the box at ultrawide. + if ((modifier.Flags & CSD_POD_CLONE) != 0) + { + auto v0 = getVertex(0); + auto v1 = getVertex(1); + auto v2 = getVertex(2); + auto v3 = getVertex(3); + auto vpWidth = (float)Video::s_viewportWidth; + + // Shift cloned element to base edge. + v0->X = g_podBaseRightX; + v1->X = g_podBaseRightX; + + // Stretch cloned element to screen edge. + v2->X = vpWidth; + v3->X = vpWidth; + + // Shift UVs to remove cloned element edge line. + v0->U = v0->U + 0.008f; + v0->V = v0->V + 0.008f; + v1->U = v1->U + 0.008f; + v1->V = v1->V + 0.008f; + + ctx.r3 = r3; + ctx.r4 = ctx.r1; + ctx.r5 = r5; + original(ctx, base); + } + ctx.r1.u32 += size; } } @@ -1015,8 +1055,8 @@ PPC_FUNC(sub_8263CC40) const xxHashMap g_csdModifiers = { // audio - { HashStr("sprite/audio/audio/pod/pod"), { CSD_POD } }, - { HashStr("sprite/audio/audio/pod/pod/Cast_1084"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/audio/audio/pod/pod/Cast_1084"), { CSD_POD_BASE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/audio/audio/pod/pod/Cast_1086"), { CSD_POD_CLONE } }, { HashStr("sprite/audio/audio/pod/pod/Cast_1088"), { CSD_EXTEND_RIGHT } }, // background diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index d908b95f3..93c5c100a 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -74,10 +74,12 @@ enum CsdFlags CSD_CORNER_EXTRACT = 1 << 26, CSD_RADARMAP = 1 << 27, - CSD_POD = 1 << 28, - CSD_MULTIPLAYER = 1 << 29, - CSD_MULTIPLAYER_CENTER = 1 << 30 + CSD_POD_BASE = 1 << 28, + CSD_POD_CLONE = 1 << 29, + + CSD_MULTIPLAYER = 1 << 30, + CSD_MULTIPLAYER_CENTER = 1 << 31 }; struct CsdUVs @@ -102,7 +104,7 @@ struct CsdColours struct CsdModifier { - uint32_t Flags{}; + int64_t Flags{}; CsdUVs UVs{}; CsdColours Colours{}; CsdUVs RepeatUVs{}; From 8af2280d457c33f5a31991602d8704c49fb3a7c1 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Mon, 25 Aug 2025 20:23:19 +0100 Subject: [PATCH 16/52] Implement ultrawide main menu chevron animation (WIP) TODO: - Make the last two foreground arrows on the left slightly more opaque and fade out later. - Make the background arrows fade out first before the foreground starts fading. --- MarathonRecomp/api/CSD/Manager/csdmScene.h | 15 +- MarathonRecomp/api/Marathon.h | 1 + MarathonRecomp/api/Sonicteam/MainMenuTask.h | 34 ++ .../patches/aspect_ratio_patches.cpp | 376 +++++++++++++++--- MarathonRecomp/patches/aspect_ratio_patches.h | 84 ++-- 5 files changed, 423 insertions(+), 87 deletions(-) create mode 100644 MarathonRecomp/api/Sonicteam/MainMenuTask.h diff --git a/MarathonRecomp/api/CSD/Manager/csdmScene.h b/MarathonRecomp/api/CSD/Manager/csdmScene.h index f6148315b..3ffd570c4 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmScene.h +++ b/MarathonRecomp/api/CSD/Manager/csdmScene.h @@ -44,7 +44,9 @@ namespace Chao::CSD struct Scene { - MARATHON_INSERT_PADDING(0x30); + MARATHON_INSERT_PADDING(8); + be FPS; + MARATHON_INSERT_PADDING(0x24); be CastNodeCount; MARATHON_INSERT_PADDING(4); xpointer pCastNodes; @@ -52,6 +54,17 @@ namespace Chao::CSD be CastCount; MARATHON_INSERT_PADDING(4); xpointer pCastIndices; + MARATHON_INSERT_PADDING(4); + be AnimationCount; + MARATHON_INSERT_PADDING(4); + xpointer pAnimationKeyFrameDataList; + MARATHON_INSERT_PADDING(4); + xpointer pAnimationDictionary; + MARATHON_INSERT_PADDING(4); + be AspectRatio; + MARATHON_INSERT_PADDING(4); + xpointer pAnimationFrameDataList; + MARATHON_INSERT_PADDING(4); }; struct SceneIndex diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index d7a4693e0..04293307c 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -39,6 +39,7 @@ #include "Sonicteam/HUDRaderMap.h" #include "Sonicteam/HudTextParts.h" #include "Sonicteam/ImageFilter.h" +#include "Sonicteam/MainMenuTask.h" #include "Sonicteam/MovieObject.h" #include "Sonicteam/MovieObjectWmv.h" #include "Sonicteam/MovieTask.h" diff --git a/MarathonRecomp/api/Sonicteam/MainMenuTask.h b/MarathonRecomp/api/Sonicteam/MainMenuTask.h new file mode 100644 index 000000000..a4dfc3701 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/MainMenuTask.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class MainMenuTask : public SoX::Engine::Task + { + public: + enum MainMenuState : uint32_t + { + MainMenuState_MainMenuBack = 1, + MainMenuState_MainMenu = 2, + MainMenuState_MainMenuExitPrompt = 4, + MainMenuState_SinglePlayer = 6, + MainMenuState_EpisodeSelect = 9, + MainMenuState_TrialSelect = 0x0D, + MainMenuState_SelectCharacter = 0x0F, + MainMenuState_ActTrial = 0x11, + MainMenuState_TownTrial = 0x15, + MainMenuState_Multiplayer = 0x17, + MainMenuState_Extras = 0x1C, + MainMenuState_Tag = 0x1E, + MainMenuState_Battle = 0x22, + MainMenuState_GoldMedalResults = 0x26, + MainMenuState_AudioRoom = 0x2F, + MainMenuState_TheaterRoom = 0x31, + MainMenuState_Options = 0x33, + MainMenuState_MainMenuExit = 0x3C + }; + + be m_State; + }; +} diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 224d8a7fc..4e2fbe982 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -12,6 +12,9 @@ // #define CORNER_DEBUG +constexpr float CHEVRON_INTRO_DURATION = 0.083f; +constexpr float CHEVRON_OUTRO_DURATION = 2.01666666666667f; + static Mutex g_pathMutex; static std::map g_paths{}; @@ -35,6 +38,57 @@ static float g_podBaseRightX{}; static float g_scenePositionX{}; static float g_scenePositionY{}; +static float g_bgArrowsEnd{}; +static float g_fgArrowsEnd{}; +static float g_chevronLoopTime{}; +static float g_chevronAspectRatio{}; + +struct ChevronAnim +{ + uint8_t EndAlpha{}; + + float IntroDuration{}; + float IntroStartTime{}; + + float OutroDuration{}; + float OutroStartTime{}; + + float TimeElapsed{}; + + uint8_t CurrentAlpha{}; + float CurrentOffsetX{}; + + void Reset() + { + TimeElapsed = 0.0f; + CurrentAlpha = 0; + } + + void Update(float deltaTime) + { + TimeElapsed += deltaTime; + + if (TimeElapsed < IntroStartTime && TimeElapsed < OutroStartTime) + { + CurrentAlpha = std::lerp(0, EndAlpha, std::clamp((TimeElapsed - IntroStartTime) / IntroDuration, 0.0f, 1.0f)); + } + else + { + CurrentAlpha = std::lerp(EndAlpha, 0, std::clamp((TimeElapsed - OutroStartTime) / OutroDuration, 0.0f, 1.0f)); + } + + CurrentOffsetX = std::lerp(0.0f, 2.0f, std::clamp(TimeElapsed / g_chevronLoopTime, 0.0f, 1.0f)); + + if (TimeElapsed < g_chevronLoopTime) + return; + + Reset(); + } +}; + +static std::unordered_map g_bgArrows{}; +static std::unordered_map g_fgArrows{}; + static class LoadingPillarboxEvent : public HookEvent { public: @@ -263,37 +317,45 @@ PPC_FUNC(sub_825CB378) PPC_FUNC_IMPL(__imp__sub_828C8F60); PPC_FUNC(sub_828C8F60) { + auto pScene = (Chao::CSD::Scene*)(base + ctx.r3.u32); + g_sceneModifier = FindCsdModifier(ctx.r3.u32); if (g_sceneModifier.has_value()) { - if (g_aspectRatio > WIDE_ASPECT_RATIO && (g_sceneModifier->Flags & (CSD_OFFSET_SCALE_LEFT | CSD_OFFSET_SCALE_RIGHT | CSD_CORNER_EXTRACT)) != 0) + if (g_aspectRatio > WIDE_ASPECT_RATIO) { - auto r3 = ctx.r3; - auto r4 = ctx.r4; - auto r5 = ctx.r5; - auto r6 = ctx.r6; + if ((g_sceneModifier->Flags & CSD_SCENE_DISABLE_MOTION) != 0) + pScene->FPS = 0; + + if ((g_sceneModifier->Flags & (CSD_OFFSET_SCALE_LEFT | CSD_OFFSET_SCALE_RIGHT | CSD_CORNER_EXTRACT)) != 0) + { + auto r3 = ctx.r3; + auto r4 = ctx.r4; + auto r5 = ctx.r5; + auto r6 = ctx.r6; - // Queue draw calls, but don't actually draw anything. We just want to extract the corner. - g_cornerExtract = true; - __imp__sub_828C8F60(ctx, base); - g_cornerExtract = false; + // Queue draw calls, but don't actually draw anything. We just want to extract the corner. + g_cornerExtract = true; + __imp__sub_828C8F60(ctx, base); + g_cornerExtract = false; #ifdef CORNER_DEBUG - if (g_sceneModifier->CornerMax == FLT_MAX) - { - fmt::print("Corners: "); - for (auto corner : g_corners) - fmt::print("{} ", corner); + if (g_sceneModifier->CornerMax == FLT_MAX) + { + fmt::print("Corners: "); + for (auto corner : g_corners) + fmt::print("{} ", corner); - fmt::println(""); - } + fmt::println(""); + } #endif - ctx.r3 = r3; - ctx.r4 = r4; - ctx.r5 = r5; - ctx.r6 = r6; + ctx.r3 = r3; + ctx.r4 = r4; + ctx.r5 = r5; + ctx.r6 = r6; + } } } @@ -328,7 +390,18 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) } if ((modifier.Flags & CSD_SKIP) != 0) - return; + { + if ((modifier.Flags & CSD_CHEVRON) != 0) + { + // Don't draw non-extended arrows at ultrawide. + if (g_aspectRatio > WIDE_ASPECT_RATIO) + return; + } + else + { + return; + } + } if (g_cornerExtract) { @@ -412,6 +485,10 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) offsetX /= 1.5f; } + // Don't offset arrows at 16:9 or narrower. + if ((modifier.Flags & CSD_CHEVRON) != 0 && g_aspectRatio <= WIDE_ASPECT_RATIO) + offsetX = 0.0f; + if ((modifier.Flags & CSD_SCALE) != 0) { scaleX *= g_aspectRatioGameplayScale; @@ -570,6 +647,10 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & CSD_COLOUR_MODIFIER) != 0) applyColourModifier(modifier.Colours); + // Don't repeat arrows at 16:9 or narrower. + if ((modifier.Flags & CSD_CHEVRON) != 0 && g_aspectRatio <= WIDE_ASPECT_RATIO) + modifier.Flags &= ~(CSD_REPEAT_LEFT | CSD_REPEAT_RIGHT); + auto isRepeatLeft = (modifier.Flags & CSD_REPEAT_LEFT) != 0; auto isRepeatRight = (modifier.Flags & CSD_REPEAT_RIGHT) != 0; @@ -577,31 +658,11 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) { auto r3 = ctx.r3; auto r5 = ctx.r5; - auto vertexIndex = isRepeatLeft ? 2 : 0; - auto x = getVertex(vertexIndex)->X; - - while (isRepeatLeft ? x > 0.0f : x < float(Video::s_viewportWidth)) - { - ctx.r3 = r3; - ctx.r4 = ctx.r1; - ctx.r5 = r5; - original(ctx, base); - - if (isRepeatLeft) - { - for (size_t i = 0; i < r5.u32; i++) - getVertex(i)->X = getVertex(i)->X - width; - } - else if (isRepeatRight) - { - for (size_t i = 0; i < r5.u32; i++) - getVertex(i)->X = getVertex(i)->X + width; - } - x = getVertex(vertexIndex)->X; - - auto isFlipHorz = (modifier.Flags & CSD_REPEAT_FLIP_HORIZONTAL) != 0; + auto isFlipHorz = (modifier.Flags & CSD_REPEAT_FLIP_HORIZONTAL) != 0; + auto applyRepeatModifiers = [&]() + { if (isFlipHorz) { getVertex(0)->X = getVertex(0)->X + width; @@ -623,18 +684,184 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & CSD_REPEAT_COLOUR_MODIFIER) != 0) applyColourModifier(modifier.RepeatColours); + }; + + if (isRepeatLeft) + { + if ((modifier.Flags & CSD_CHEVRON) != 0) + { + // Shift root arrow forwards to use entirely custom arrows. + for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->X = getVertex(i)->X + width; + } + + auto x = getVertex(2)->X; + auto arrowIndex = 0; + auto arrowCount = 0; + + if ((modifier.Flags & CSD_CHEVRON) != 0) + { + // Compute number of foreground arrows. + while (x > 0.0f) + { + x = x - width; + arrowCount++; + } - if ((modifier.Flags & CSD_REPEAT_EXTEND) != 0) + g_fgArrowsEnd = (CHEVRON_INTRO_DURATION / 2.0f) * float(arrowCount + 1); + + // Reset X to first vertex. + x = getVertex(0)->X; + } + + while (x > 0.0f) { + ctx.r3 = r3; + ctx.r4 = ctx.r1; + ctx.r5 = r5; + original(ctx, base); + + if ((modifier.Flags & CSD_CHEVRON) != 0) + { + auto halfWidth = width / 2.5f; + auto endAlpha = (uint8_t)std::lerp(30, 10, x / float(Video::s_viewportWidth)); + auto introDuration = CHEVRON_INTRO_DURATION; + auto introStartTime = (introDuration / 2.0f) * float(arrowIndex + 1) + g_bgArrowsEnd; + auto outroDuration = CHEVRON_OUTRO_DURATION; + auto outroStartTime = (g_fgArrowsEnd + introStartTime) + 0.5f; + + g_chevronLoopTime = outroStartTime + outroDuration; + + if (g_fgArrows.count(arrowIndex)) + { + g_fgArrows[arrowIndex].EndAlpha = endAlpha; + g_fgArrows[arrowIndex].IntroDuration = introDuration; + g_fgArrows[arrowIndex].IntroStartTime = introStartTime; + g_fgArrows[arrowIndex].OutroDuration = outroDuration; + g_fgArrows[arrowIndex].OutroStartTime = outroStartTime; + } + else + { + g_fgArrows.emplace(arrowIndex, ChevronAnim{ endAlpha, introDuration, introStartTime, outroDuration, outroStartTime }); + } + + for (size_t i = 0; i < r5.u32; i++) + { + getVertex(i)->X = getVertex(i)->X - (width - halfWidth) - g_bgArrows[arrowIndex].CurrentOffsetX; + getVertex(i)->Colour = 0xFFFFFF00 | g_fgArrows[arrowIndex].CurrentAlpha; + } + + // TODO: The last two foreground arrows are slightly more opaque + // than the rest. They also need to fade out later too. + // if (getVertex(0)->X > 0.0f && getVertex(0)->X - halfWidth < 0.0f) + // { + // for (size_t j = 0; j < r5.u32; j++) + // getVertex(j)->Colour = 0xFFFFFF00 | (g_fgArrows[arrowIndex].CurrentAlpha + 10); + // } + + arrowIndex++; + } + else + { + for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->X = getVertex(i)->X - width; + } + + x = getVertex(2)->X; + + applyRepeatModifiers(); + + if ((modifier.Flags & CSD_REPEAT_EXTEND) != 0) + { + for (size_t i = 0; i < r5.u32; i++) + { + if (i == (isFlipHorz ? 2 : 0) || i == (isFlipHorz ? 3 : 1)) + getVertex(i)->X = std::min(getVertex(i)->X.get(), 0.0f); + } + } + } + } + + if (isRepeatRight) + { + if ((modifier.Flags & CSD_CHEVRON) != 0) + { + // Shift root arrow backwards to use entirely custom arrows. for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->X = getVertex(i)->X - width; + } + + auto x = getVertex(0)->X; + auto arrowIndex = 0; + auto arrowCount = 0; + + if ((modifier.Flags & CSD_CHEVRON) != 0) + { + // Compute number of background arrows. + while (x < float(Video::s_viewportWidth)) { - if (isRepeatLeft && (i == (isFlipHorz ? 2 : 0) || i == (isFlipHorz ? 3 : 1))) + x = x + width; + arrowCount++; + } + + g_bgArrowsEnd = (CHEVRON_INTRO_DURATION / 2.0f) * float(arrowCount + 1); + + // Reset X to first vertex. + x = getVertex(0)->X; + } + + while (x < float(Video::s_viewportWidth)) + { + ctx.r3 = r3; + ctx.r4 = ctx.r1; + ctx.r5 = r5; + original(ctx, base); + + if ((modifier.Flags & CSD_CHEVRON) != 0) + { + auto endAlpha = (uint8_t)std::lerp(5, 35, x / float(Video::s_viewportWidth)); + auto introDuration = CHEVRON_INTRO_DURATION; + auto introStartTime = (introDuration / 2.0f) * float(arrowIndex + 1); + auto outroDuration = CHEVRON_OUTRO_DURATION; + auto outroStartTime = (g_bgArrowsEnd + introStartTime) + 0.5f; + + if (g_bgArrows.count(arrowIndex)) + { + g_bgArrows[arrowIndex].EndAlpha = endAlpha; + g_bgArrows[arrowIndex].IntroDuration = introDuration; + g_bgArrows[arrowIndex].IntroStartTime = introStartTime; + g_bgArrows[arrowIndex].OutroDuration = outroDuration; + g_bgArrows[arrowIndex].OutroStartTime = outroStartTime; + } + else { - getVertex(i)->X = std::min(getVertex(i)->X.get(), 0.0f); + g_bgArrows.emplace(arrowIndex, ChevronAnim{ endAlpha, introDuration, introStartTime, outroDuration, outroStartTime }); } - else if (isRepeatRight && (i == (isFlipHorz ? 0 : 2) || i == (isFlipHorz ? 1 : 3))) + + for (size_t i = 0; i < r5.u32; i++) + { + getVertex(i)->X = (getVertex(i)->X + (width - (width / 4.0f))) + g_bgArrows[arrowIndex].CurrentOffsetX; + getVertex(i)->Colour = 0xFFFFFF00 | g_bgArrows[arrowIndex].CurrentAlpha; + } + + arrowIndex++; + } + else + { + for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->X = getVertex(i)->X + width; + } + + x = getVertex(0)->X; + + applyRepeatModifiers(); + + if ((modifier.Flags & CSD_REPEAT_EXTEND) != 0) + { + for (size_t i = 0; i < r5.u32; i++) { - getVertex(i)->X = std::max(getVertex(i)->X.get(), float(Video::s_viewportWidth)); + if (i == (isFlipHorz ? 0 : 2) || i == (isFlipHorz ? 1 : 3)) + getVertex(i)->X = std::max(getVertex(i)->X.get(), float(Video::s_viewportWidth)); } } } @@ -708,6 +935,41 @@ PPC_FUNC(sub_82631718) Draw(ctx, base, __imp__sub_82631718, 0x0C); } +// Sonicteam::MainMenuTask::Update +PPC_FUNC_IMPL(__imp__sub_824FFCF8); +PPC_FUNC(sub_824FFCF8) +{ + auto pMainMenuTask = (Sonicteam::MainMenuTask*)(base + ctx.r3.u32); + auto deltaTime = ctx.f1.f64; + + if (g_aspectRatio > WIDE_ASPECT_RATIO) + { + auto aspectRatioChanged = false; + + if (g_aspectRatio != g_chevronAspectRatio) + aspectRatioChanged = true; + + g_chevronAspectRatio = g_aspectRatio; + + if (pMainMenuTask->m_State == Sonicteam::MainMenuTask::MainMenuState_MainMenuBack || + pMainMenuTask->m_State == Sonicteam::MainMenuTask::MainMenuState_AudioRoom || + pMainMenuTask->m_State == Sonicteam::MainMenuTask::MainMenuState_TheaterRoom || + aspectRatioChanged) + { + g_bgArrows.clear(); + g_fgArrows.clear(); + } + + for (auto& arrow : g_bgArrows) + arrow.second.Update(deltaTime); + + for (auto& arrow : g_fgArrows) + arrow.second.Update(deltaTime); + } + + __imp__sub_824FFCF8(ctx, base); +} + // Sonicteam::EventEntityTask::Update PPC_FUNC_IMPL(__imp__sub_8264AC48); PPC_FUNC(sub_8264AC48) @@ -1061,6 +1323,22 @@ const xxHashMap g_csdModifiers = // background { HashStr("sprite/background/background/mainmenu_back"), { CSD_STRETCH | CSD_PROHIBIT_BLACK_BAR } }, + { HashStr("sprite/background/background/main_menu"), { CSD_SCENE_DISABLE_MOTION } }, + { HashStr("sprite/background/background/main_menu/main_menu1/yaji1"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu1/yaji2"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu1/yaji3"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu1/yaji4"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu1/yaji5"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu1/yaji6"), { CSD_CHEVRON | CSD_ALIGN_RIGHT | CSD_REPEAT_LEFT } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji7"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji8"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji9"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji10"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji11"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji12"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji13"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji14"), { CSD_CHEVRON | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji15"), { CSD_CHEVRON | CSD_ALIGN_LEFT | CSD_REPEAT_RIGHT } }, // battledisplay_1p { HashStr("sprite/battledisplay_1p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index 93c5c100a..06c88a93f 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -2,6 +2,9 @@ #include +#define MAKE_BITFLAG32(bit) 1U << bit +#define MAKE_BITFLAG64(bit) 1ULL << bit + inline constexpr float NARROW_ASPECT_RATIO = 4.0f / 3.0f; inline constexpr float WIDE_ASPECT_RATIO = 16.0f / 9.0f; inline constexpr float STEAM_DECK_ASPECT_RATIO = 16.0f / 10.0f; @@ -23,63 +26,67 @@ class AspectRatioPatches // -------------- CSD --------------- // -enum CsdFlags +enum CsdFlags : uint64_t { - CSD_ALIGN_CENTER = 0 << 0, + CSD_ALIGN_CENTER = 0, - CSD_ALIGN_TOP = 1 << 0, - CSD_ALIGN_LEFT = 1 << 1, - CSD_ALIGN_BOTTOM = 1 << 2, - CSD_ALIGN_RIGHT = 1 << 3, + CSD_ALIGN_TOP = MAKE_BITFLAG64(0), + CSD_ALIGN_LEFT = MAKE_BITFLAG64(1), + CSD_ALIGN_BOTTOM = MAKE_BITFLAG64(2), + CSD_ALIGN_RIGHT = MAKE_BITFLAG64(3), CSD_ALIGN_TOP_LEFT = CSD_ALIGN_TOP | CSD_ALIGN_LEFT, CSD_ALIGN_TOP_RIGHT = CSD_ALIGN_TOP | CSD_ALIGN_RIGHT, CSD_ALIGN_BOTTOM_LEFT = CSD_ALIGN_BOTTOM | CSD_ALIGN_LEFT, CSD_ALIGN_BOTTOM_RIGHT = CSD_ALIGN_BOTTOM | CSD_ALIGN_RIGHT, - CSD_STRETCH_HORIZONTAL = 1 << 4, - CSD_STRETCH_VERTICAL = 1 << 5, + CSD_STRETCH_HORIZONTAL = MAKE_BITFLAG64(4), + CSD_STRETCH_VERTICAL = MAKE_BITFLAG64(5), CSD_STRETCH = CSD_STRETCH_HORIZONTAL | CSD_STRETCH_VERTICAL, - CSD_SCALE = 1 << 6, + CSD_SCALE = MAKE_BITFLAG64(6), + + CSD_EXTEND_LEFT = MAKE_BITFLAG64(7), + CSD_EXTEND_RIGHT = MAKE_BITFLAG64(8), - CSD_EXTEND_LEFT = 1 << 7, - CSD_EXTEND_RIGHT = 1 << 8, + CSD_STORE_LEFT_CORNER = MAKE_BITFLAG64(9), + CSD_STORE_RIGHT_CORNER = MAKE_BITFLAG64(10), - CSD_STORE_LEFT_CORNER = 1 << 9, - CSD_STORE_RIGHT_CORNER = 1 << 10, + CSD_SKIP = MAKE_BITFLAG64(11), - CSD_SKIP = 1 << 11, + CSD_OFFSET_SCALE_LEFT = MAKE_BITFLAG64(12), + CSD_OFFSET_SCALE_RIGHT = MAKE_BITFLAG64(13), - CSD_OFFSET_SCALE_LEFT = 1 << 12, - CSD_OFFSET_SCALE_RIGHT = 1 << 13, + CSD_REPEAT_LEFT = MAKE_BITFLAG64(14), + CSD_REPEAT_RIGHT = MAKE_BITFLAG64(15), + CSD_REPEAT_FLIP_HORIZONTAL = MAKE_BITFLAG64(16), + CSD_REPEAT_FLIP_VERTICAL = MAKE_BITFLAG64(17), + CSD_REPEAT_EXTEND = MAKE_BITFLAG64(18), - CSD_REPEAT_LEFT = 1 << 14, - CSD_REPEAT_RIGHT = 1 << 15, - CSD_REPEAT_FLIP_HORIZONTAL = 1 << 16, - CSD_REPEAT_FLIP_VERTICAL = 1 << 17, - CSD_REPEAT_EXTEND = 1 << 18, + CSD_UV_MODIFIER = MAKE_BITFLAG64(19), + CSD_COLOUR_MODIFIER = MAKE_BITFLAG64(20), + CSD_REPEAT_UV_MODIFIER = MAKE_BITFLAG64(21), + CSD_REPEAT_COLOUR_MODIFIER = MAKE_BITFLAG64(22), - CSD_UV_MODIFIER = 1 << 19, - CSD_COLOUR_MODIFIER = 1 << 20, - CSD_REPEAT_UV_MODIFIER = 1 << 21, - CSD_REPEAT_COLOUR_MODIFIER = 1 << 22, + CSD_PILLARBOX = MAKE_BITFLAG64(23), + CSD_PROHIBIT_BLACK_BAR = MAKE_BITFLAG64(24), - CSD_PILLARBOX = 1 << 23, - CSD_PROHIBIT_BLACK_BAR = 1 << 24, + CSD_UNSTRETCH_HORIZONTAL = MAKE_BITFLAG64(25), - CSD_UNSTRETCH_HORIZONTAL = 1 << 25, + CSD_CORNER_EXTRACT = MAKE_BITFLAG64(26), - CSD_CORNER_EXTRACT = 1 << 26, + CSD_RADARMAP = MAKE_BITFLAG64(27), - CSD_RADARMAP = 1 << 27, + CSD_POD_BASE = MAKE_BITFLAG64(28), + CSD_POD_CLONE = MAKE_BITFLAG64(29), - CSD_POD_BASE = 1 << 28, - CSD_POD_CLONE = 1 << 29, + CSD_MULTIPLAYER = MAKE_BITFLAG64(30), + CSD_MULTIPLAYER_CENTER = MAKE_BITFLAG64(31), - CSD_MULTIPLAYER = 1 << 30, - CSD_MULTIPLAYER_CENTER = 1 << 31 + CSD_CHEVRON = MAKE_BITFLAG64(32), + + CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(33) }; struct CsdUVs @@ -134,10 +141,10 @@ TextFontPictureModifier FindFontPictureModifier(xxHashMap g_movieModifiers; MovieModifier FindMovieModifier(XXH64_hash_t nameHash); + +#undef MAKE_BITFLAG64 +#undef MAKE_BITFLAG32 From b3b6506cce4730815519531eec8539a1f872c526 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Mon, 25 Aug 2025 20:32:52 +0100 Subject: [PATCH 17/52] Added ultrawide-only CSD modifier --- MarathonRecomp/patches/aspect_ratio_patches.cpp | 14 ++++++++++---- MarathonRecomp/patches/aspect_ratio_patches.h | 4 +++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 4e2fbe982..21b73dced 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -323,11 +323,14 @@ PPC_FUNC(sub_828C8F60) if (g_sceneModifier.has_value()) { + if ((g_sceneModifier->Flags & CSD_MODIFIER_ULTRAWIDE_ONLY) != 0 && g_aspectRatio <= WIDE_ASPECT_RATIO) + g_sceneModifier->Flags &= (~g_sceneModifier->Flags) | CSD_MODIFIER_ULTRAWIDE_ONLY; + + if ((g_sceneModifier->Flags & CSD_SCENE_DISABLE_MOTION) != 0) + pScene->FPS = 0; + if (g_aspectRatio > WIDE_ASPECT_RATIO) { - if ((g_sceneModifier->Flags & CSD_SCENE_DISABLE_MOTION) != 0) - pScene->FPS = 0; - if ((g_sceneModifier->Flags & (CSD_OFFSET_SCALE_LEFT | CSD_OFFSET_SCALE_RIGHT | CSD_CORNER_EXTRACT)) != 0) { auto r3 = ctx.r3; @@ -388,6 +391,9 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) { modifier = g_sceneModifier.value(); } + + if ((modifier.Flags & CSD_MODIFIER_ULTRAWIDE_ONLY) != 0 && g_aspectRatio <= WIDE_ASPECT_RATIO) + modifier.Flags &= (~modifier.Flags) | CSD_MODIFIER_ULTRAWIDE_ONLY; if ((modifier.Flags & CSD_SKIP) != 0) { @@ -1323,7 +1329,7 @@ const xxHashMap g_csdModifiers = // background { HashStr("sprite/background/background/mainmenu_back"), { CSD_STRETCH | CSD_PROHIBIT_BLACK_BAR } }, - { HashStr("sprite/background/background/main_menu"), { CSD_SCENE_DISABLE_MOTION } }, + { HashStr("sprite/background/background/main_menu"), { CSD_SCENE_DISABLE_MOTION | CSD_MODIFIER_ULTRAWIDE_ONLY } }, { HashStr("sprite/background/background/main_menu/main_menu1/yaji1"), { CSD_CHEVRON | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu1/yaji2"), { CSD_CHEVRON | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu1/yaji3"), { CSD_CHEVRON | CSD_SKIP } }, diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index 06c88a93f..e55e8253a 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -86,7 +86,9 @@ enum CsdFlags : uint64_t CSD_CHEVRON = MAKE_BITFLAG64(32), - CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(33) + CSD_MODIFIER_ULTRAWIDE_ONLY = MAKE_BITFLAG64(33), + + CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(34) }; struct CsdUVs From 48d8b6984d5201601dd809a7cc01f47e2371816a Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 26 Aug 2025 01:56:48 +0100 Subject: [PATCH 18/52] Improve ultrawide chevron offset and alpha motion --- .../patches/aspect_ratio_patches.cpp | 43 ++++++++----------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 21b73dced..3081ab83b 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -77,7 +77,7 @@ struct ChevronAnim CurrentAlpha = std::lerp(EndAlpha, 0, std::clamp((TimeElapsed - OutroStartTime) / OutroDuration, 0.0f, 1.0f)); } - CurrentOffsetX = std::lerp(0.0f, 2.0f, std::clamp(TimeElapsed / g_chevronLoopTime, 0.0f, 1.0f)); + CurrentOffsetX = std::lerp(0.0f, 5.0f, std::clamp(TimeElapsed / g_chevronLoopTime, 0.0f, 1.0f)); if (TimeElapsed < g_chevronLoopTime) return; @@ -694,23 +694,24 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if (isRepeatLeft) { + auto x = getVertex(2)->X; + auto arrowIndex = 0; + auto arrowCount = 0; + auto arrowWidth = (width - (width / 2.5f)); + if ((modifier.Flags & CSD_CHEVRON) != 0) { // Shift root arrow forwards to use entirely custom arrows. for (size_t i = 0; i < r5.u32; i++) - getVertex(i)->X = getVertex(i)->X + width; + getVertex(i)->X = getVertex(i)->X + arrowWidth; } - auto x = getVertex(2)->X; - auto arrowIndex = 0; - auto arrowCount = 0; - if ((modifier.Flags & CSD_CHEVRON) != 0) { // Compute number of foreground arrows. while (x > 0.0f) { - x = x - width; + x = x - arrowWidth; arrowCount++; } @@ -729,7 +730,6 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & CSD_CHEVRON) != 0) { - auto halfWidth = width / 2.5f; auto endAlpha = (uint8_t)std::lerp(30, 10, x / float(Video::s_viewportWidth)); auto introDuration = CHEVRON_INTRO_DURATION; auto introStartTime = (introDuration / 2.0f) * float(arrowIndex + 1) + g_bgArrowsEnd; @@ -753,18 +753,10 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) for (size_t i = 0; i < r5.u32; i++) { - getVertex(i)->X = getVertex(i)->X - (width - halfWidth) - g_bgArrows[arrowIndex].CurrentOffsetX; + getVertex(i)->X = (getVertex(i)->X - arrowWidth) - g_bgArrows[arrowIndex].CurrentOffsetX; getVertex(i)->Colour = 0xFFFFFF00 | g_fgArrows[arrowIndex].CurrentAlpha; } - // TODO: The last two foreground arrows are slightly more opaque - // than the rest. They also need to fade out later too. - // if (getVertex(0)->X > 0.0f && getVertex(0)->X - halfWidth < 0.0f) - // { - // for (size_t j = 0; j < r5.u32; j++) - // getVertex(j)->Colour = 0xFFFFFF00 | (g_fgArrows[arrowIndex].CurrentAlpha + 10); - // } - arrowIndex++; } else @@ -790,23 +782,24 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if (isRepeatRight) { + auto x = getVertex(0)->X; + auto arrowIndex = 0; + auto arrowCount = 0; + auto arrowWidth = (width - (width / 4.0f)); + if ((modifier.Flags & CSD_CHEVRON) != 0) { // Shift root arrow backwards to use entirely custom arrows. for (size_t i = 0; i < r5.u32; i++) - getVertex(i)->X = getVertex(i)->X - width; + getVertex(i)->X = getVertex(i)->X - arrowWidth; } - auto x = getVertex(0)->X; - auto arrowIndex = 0; - auto arrowCount = 0; - if ((modifier.Flags & CSD_CHEVRON) != 0) { // Compute number of background arrows. while (x < float(Video::s_viewportWidth)) { - x = x + width; + x = x + arrowWidth; arrowCount++; } @@ -825,7 +818,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & CSD_CHEVRON) != 0) { - auto endAlpha = (uint8_t)std::lerp(5, 35, x / float(Video::s_viewportWidth)); + auto endAlpha = (uint8_t)std::lerp(10, 35, x / float(Video::s_viewportWidth)); auto introDuration = CHEVRON_INTRO_DURATION; auto introStartTime = (introDuration / 2.0f) * float(arrowIndex + 1); auto outroDuration = CHEVRON_OUTRO_DURATION; @@ -846,7 +839,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) for (size_t i = 0; i < r5.u32; i++) { - getVertex(i)->X = (getVertex(i)->X + (width - (width / 4.0f))) + g_bgArrows[arrowIndex].CurrentOffsetX; + getVertex(i)->X = (getVertex(i)->X + arrowWidth) + g_bgArrows[arrowIndex].CurrentOffsetX; getVertex(i)->Colour = 0xFFFFFF00 | g_bgArrows[arrowIndex].CurrentAlpha; } From f6e663c2b9743fca0fbe97fef93d1baaec642b6a Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Thu, 28 Aug 2025 02:47:18 +0100 Subject: [PATCH 19/52] Improve text scaling Co-Authored-By: Skyth (Asilkan) <19259897+blueskythlikesclouds@users.noreply.github.com> --- .../patches/aspect_ratio_patches.cpp | 84 ++++--------------- 1 file changed, 15 insertions(+), 69 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 3081ab83b..eca2db33e 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -584,8 +584,8 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) { auto vertex = getVertex(i); - float x = offsetX + (vertex->X - pivotX) * (scaleX * (1280.0f / Video::s_viewportWidth)); - float y = offsetY + (vertex->Y - pivotY) * (scaleY * (720.0f / Video::s_viewportHeight)); + float x = offsetX + (vertex->X - pivotX) * scaleX; + float y = offsetY + (vertex->Y - pivotY) * scaleY; if ((modifier.Flags & CSD_EXTEND_LEFT) != 0 && (i == 0 || i == 1)) { @@ -1208,41 +1208,20 @@ void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity, xxHashMapm_Y = pTextEntity->m_Y * scaleY + offsetY; - pTextEntity->m_ScaleY = scaleY * scaleFactor; - - g_textScaleY = pTextEntity->m_ScaleY; - } - - auto scaleX = 1.0f * (WIDE_ASPECT_RATIO / g_aspectRatio) * scaleY; - auto scaledWidth = baseWidth * scaleX; - auto offsetX = (baseWidth - scaledWidth) / 2.0f; - - pTextEntity->m_X = pTextEntity->m_X * scaleX + offsetX; - pTextEntity->m_ScaleX = scaleX * scaleFactor; + pTextEntity->m_X = offsetX + pTextEntity->m_X * scale; + pTextEntity->m_Y = offsetY + pTextEntity->m_Y * scale; + pTextEntity->m_ScaleX = scale; + pTextEntity->m_ScaleY = scale; __imp__sub_8262D868(ctx, base); @@ -1259,47 +1238,14 @@ PPC_FUNC(sub_8262D868) ReplaceTextVariables(pTextEntity, pftModifier); } -// Sonicteam::HUDButtonWindow::Draw -PPC_FUNC_IMPL(__imp__sub_824D4628); -PPC_FUNC(sub_824D4628) +// Set CSD scale. +PPC_FUNC_IMPL(__imp__sub_828C78E0); +PPC_FUNC(sub_828C78E0) { - auto pHUDButtonWindow = (Sonicteam::HUDButtonWindow*)g_memory.Translate(ctx.r3.u32); - - auto width = Video::s_viewportWidth; - auto height = Video::s_viewportHeight; - auto baseWidth = height * WIDE_ASPECT_RATIO; - auto baseHeight = width / WIDE_ASPECT_RATIO; - - auto horizontalBar = 0.0f; - auto verticalBar = 0.0f; - - if (width < baseWidth) - horizontalBar = (baseWidth - width) / 2.0f; - - if (height < baseHeight) - verticalBar = (baseHeight - height) / 2.0f; - - // LOGFN_UTILITY("vert: {}", verticalBar); - // LOGFN_UTILITY("horz: {}", horizontalBar); - - // pHUDButtonWindow->m_pHudTextParts->m_OffsetX = -55.0f; - - if (horizontalBar > 0) - pHUDButtonWindow->m_pHudTextParts->m_OffsetY = horizontalBar; - - __imp__sub_824D4628(ctx, base); -} - -// Sonicteam::HudTextParts::HudTextParts -PPC_FUNC_IMPL(__imp__sub_824D04C8); -PPC_FUNC(sub_824D04C8) -{ - auto pTextBookName = (const char*)g_memory.Translate(ctx.r7.u32); - auto pTextCardName = (const char*)g_memory.Translate(ctx.r8.u32); - - __imp__sub_824D04C8(ctx, base); + ctx.f1.f64 = 1280.0; + ctx.f2.f64 = 720.0; - auto pHudTextParts = (Sonicteam::HudTextParts*)g_memory.Translate(ctx.r3.u32); + __imp__sub_828C78E0(ctx, base); } // Sonicteam::TextFontPicture::LoadResource From 7a5f6e9f4a0af0d3c98c83b11964bcaf0efdbca9 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Thu, 28 Aug 2025 02:47:34 +0100 Subject: [PATCH 20/52] Improve narrow UI scaling --- .../patches/SaveDataTask_patches.cpp | 2 +- .../patches/aspect_ratio_patches.cpp | 182 +++++++++++++----- MarathonRecomp/patches/aspect_ratio_patches.h | 5 +- 3 files changed, 136 insertions(+), 53 deletions(-) diff --git a/MarathonRecomp/patches/SaveDataTask_patches.cpp b/MarathonRecomp/patches/SaveDataTask_patches.cpp index f1023ce60..4e7f7b4cd 100644 --- a/MarathonRecomp/patches/SaveDataTask_patches.cpp +++ b/MarathonRecomp/patches/SaveDataTask_patches.cpp @@ -47,7 +47,7 @@ void SaveAlertThreeOptionRemoveDeviceSelect(PPCRegister& r5) options[2] = 0; } -// Load system message. +// Load text card. PPC_FUNC_IMPL(__imp__sub_825ECB48); PPC_FUNC(sub_825ECB48) { diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index eca2db33e..e4ccf1d23 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -394,6 +394,9 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & CSD_MODIFIER_ULTRAWIDE_ONLY) != 0 && g_aspectRatio <= WIDE_ASPECT_RATIO) modifier.Flags &= (~modifier.Flags) | CSD_MODIFIER_ULTRAWIDE_ONLY; + + if ((modifier.Flags & CSD_MODIFIER_NARROW_ONLY) != 0 && g_aspectRatio >= WIDE_ASPECT_RATIO) + modifier.Flags &= (~modifier.Flags) | CSD_MODIFIER_NARROW_ONLY; if ((modifier.Flags & CSD_SKIP) != 0) { @@ -537,6 +540,21 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) } } + if ((modifier.Flags & CSD_MOVIE) != 0) + { + auto vpWidth = float(Video::s_viewportWidth); + auto vpHeight = float(Video::s_viewportHeight); + + if (g_aspectRatio > WIDE_ASPECT_RATIO) + { + offsetX -= (vpWidth - (vpHeight * WIDE_ASPECT_RATIO)) / 2.0f; + } + else + { + offsetY -= (vpHeight - (vpWidth / WIDE_ASPECT_RATIO)) / 2.0f; + } + } + if (g_aspectRatio > WIDE_ASPECT_RATIO) { CsdModifier offsetScaleModifier{}; @@ -704,10 +722,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) // Shift root arrow forwards to use entirely custom arrows. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->X = getVertex(i)->X + arrowWidth; - } - if ((modifier.Flags & CSD_CHEVRON) != 0) - { // Compute number of foreground arrows. while (x > 0.0f) { @@ -792,10 +807,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) // Shift root arrow backwards to use entirely custom arrows. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->X = getVertex(i)->X - arrowWidth; - } - if ((modifier.Flags & CSD_CHEVRON) != 0) - { // Compute number of background arrows. while (x < float(Video::s_viewportWidth)) { @@ -880,7 +892,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) // Clone Audio Room "pod" left edge to fill // in the rest of the box at ultrawide. - if ((modifier.Flags & CSD_POD_CLONE) != 0) + if ((modifier.Flags & CSD_POD_CLONE) != 0 && g_aspectRatio > WIDE_ASPECT_RATIO) { auto v0 = getVertex(0); auto v1 = getVertex(1); @@ -1214,9 +1226,9 @@ PPC_FUNC(sub_8262D868) { auto pTextEntity = (Sonicteam::TextEntity*)(base + ctx.r3.u32); - auto scale = g_aspectRatioScale * g_aspectRatioGameplayScale; auto offsetX = g_aspectRatioOffsetX + (640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale); auto offsetY = g_aspectRatioOffsetY + (360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale); + auto scale = g_aspectRatioScale * g_aspectRatioGameplayScale; pTextEntity->m_X = offsetX + pTextEntity->m_X * scale; pTextEntity->m_Y = offsetY + pTextEntity->m_Y * scale; @@ -1262,9 +1274,16 @@ PPC_FUNC(sub_8263CC40) const xxHashMap g_csdModifiers = { // audio - { HashStr("sprite/audio/audio/pod/pod/Cast_1084"), { CSD_POD_BASE | CSD_EXTEND_RIGHT } }, - { HashStr("sprite/audio/audio/pod/pod/Cast_1086"), { CSD_POD_CLONE } }, - { HashStr("sprite/audio/audio/pod/pod/Cast_1088"), { CSD_EXTEND_RIGHT } }, + { HashStr("sprite/audio/audio/pod/pod"), { CSD_SCALE } }, + { HashStr("sprite/audio/audio/pod/pod/Cast_1084"), { CSD_POD_BASE | CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/audio/audio/pod/pod/Cast_1085"), { CSD_SCALE } }, + { HashStr("sprite/audio/audio/pod/pod/Cast_1086"), { CSD_POD_CLONE | CSD_SCALE } }, + { HashStr("sprite/audio/audio/pod/pod/Cast_1087"), { CSD_SCALE } }, + { HashStr("sprite/audio/audio/pod/pod/Cast_1088"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/audio/audio/genre"), { CSD_SCALE } }, + { HashStr("sprite/audio/audio/genre_cursor"), { CSD_SCALE } }, + { HashStr("sprite/audio/audio/audio_cursor"), { CSD_SCALE } }, + { HashStr("sprite/audio/audio/audio_cursor2"), { CSD_SCALE } }, // background { HashStr("sprite/background/background/mainmenu_back"), { CSD_STRETCH | CSD_PROHIBIT_BLACK_BAR } }, @@ -1363,45 +1382,35 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0001"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0002"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_EXTEND_RIGHT } }, + // cri_logo + { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/bg"), { CSD_STRETCH } }, + { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/criware"), { CSD_SCALE } }, + // gadget_ber { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar"), { CSD_ALIGN_BOTTOM_RIGHT } }, { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar_anime"), { CSD_ALIGN_BOTTOM_RIGHT } }, { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar_ue"), { CSD_ALIGN_BOTTOM_RIGHT } }, { HashStr("sprite/gadget_ber/gadget_bar/icon_text"), { CSD_ALIGN_BOTTOM_RIGHT } }, - // cri_logo - { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/bg"), { CSD_STRETCH } }, - { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/criware"), { CSD_SCALE } }, + // goldmedal + { HashStr("sprite/goldmedal/goldmedal/ranking"), { CSD_SCALE } }, + { HashStr("sprite/goldmedal/goldmedal/goldmedal"), { CSD_SCALE } }, + { HashStr("sprite/goldmedal/goldmedal/charaselect_cursor"), { CSD_SCALE } }, + { HashStr("sprite/goldmedal/goldmedal/charaselect_cursor2"), { CSD_SCALE } }, + { HashStr("sprite/goldmedal/goldmedal/board_cursor_sita"), { CSD_SCALE } }, + { HashStr("sprite/goldmedal/goldmedal/board_cursor_ue"), { CSD_SCALE } }, + { HashStr("sprite/goldmedal/goldmedal/total_medal"), { CSD_SCALE } }, + { HashStr("sprite/goldmedal/goldmedal/sonic"), { CSD_SCALE } }, + { HashStr("sprite/goldmedal/goldmedal/shadow"), { CSD_SCALE } }, + { HashStr("sprite/goldmedal/goldmedal/silver"), { CSD_SCALE } }, + { HashStr("sprite/goldmedal/goldmedal/last"), { CSD_SCALE } }, // loading - { HashStr("sprite/loading/loading/Scene_0000/Loading"), { CSD_ALIGN_BOTTOM } }, - { HashStr("sprite/loading/loading/Scene_0000/Loading_02"), { CSD_ALIGN_BOTTOM } }, - { HashStr("sprite/loading/loading/Scene_0000/arrow_01"), { CSD_ALIGN_BOTTOM } }, - { HashStr("sprite/loading/loading/Scene_0000/arrow_02"), { CSD_ALIGN_BOTTOM } }, - { HashStr("sprite/loading/loading/Scene_0000/arrow_03"), { CSD_ALIGN_BOTTOM } }, - - // main_menu - { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita3"), { CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita5"), { CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita8"), { CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/stage_plate/stage_plate/stage_plate2"), { CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1337"), { CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1340"), { CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/mission_plate/mission_plate/mission_plate2"), { CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1332"), { CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1336"), { CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/text"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/cover_l"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.05f, 0, -0.05f, 0 } } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/cover_c"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/vocer_r"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, 0.0015f, 0, 0.0015f, 0 }, {}, { -0.05f, 0, -0.05f, 0, 0, 0, 0, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0.1f, 0, 0.1f, 0, -0.1f, 0, -0.1f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966"), { CSD_ALIGN_TOP | CSD_SCALE } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.8f, 0, -0.8f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, - { HashStr("sprite/main_menu/titlebar_effect"), { CSD_ALIGN_TOP | CSD_SCALE } }, + { HashStr("sprite/loading/loading/Scene_0000/Loading"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, + { HashStr("sprite/loading/loading/Scene_0000/Loading_02"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, + { HashStr("sprite/loading/loading/Scene_0000/arrow_01"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, + { HashStr("sprite/loading/loading/Scene_0000/arrow_02"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, + { HashStr("sprite/loading/loading/Scene_0000/arrow_03"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, // maindisplay { HashStr("sprite/maindisplay/power"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, @@ -1425,6 +1434,63 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/maindisplay/custom_gem"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/maindisplay/custom_level"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, + // main_menu + { HashStr("sprite/main_menu/main_menu_cursor"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/main_menu_cursor2"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/main_menu_cursor3"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/eposodeselect"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/episodeselect_cursor1"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/episodeselect_cursor2"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita3"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita5"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita6"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita7"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita8"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita9"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/savedate/savedata/goldmedal"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/savedate/savedata/zanki_3"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/savedate/savedata/zanki_2"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/savedate/savedata/zanki"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/savedate/savedata/ring"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/character_choice"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/character_cursor1"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/character_cursor2"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/stage_plate/stage_plate"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/stage_plate/stage_plate/stage_plate2"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1337"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1338"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1339"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1340"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/mission_plate/mission_plate"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/mission_plate/mission_plate/mission_plate2"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1332"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1334"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1335"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1336"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/stage_cursor"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/mission_cursor"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/stage_cursor2"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/text"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/cover_l"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.05f, 0, -0.05f, 0 } } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/cover_c"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/vocer_r"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, 0.0015f, 0, 0.0015f, 0 }, {}, { -0.05f, 0, -0.05f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/mission_text/rank"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/mission_text/item_icon"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/mission_text/m_ring_icon"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0.1f, 0, 0.1f, 0, -0.1f, 0, -0.1f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966"), { CSD_ALIGN_TOP | CSD_SCALE } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.8f, 0, -0.8f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/titlebar_effect"), { CSD_ALIGN_TOP | CSD_SCALE } }, + + // pausemenu + { HashStr("sprite/pausemenu/pausemenu/pause_menu"), { CSD_SCALE } }, + { HashStr("sprite/pausemenu/pausemenu/pause_menu_cursor"), { CSD_SCALE } }, + { HashStr("sprite/pausemenu/pausemenu/mission"), { CSD_SCALE } }, + // radarmap_cover { HashStr("sprite/radarmap_cover/radarmap_cover/Scene_0000"), { CSD_RADARMAP | CSD_ALIGN_TOP_RIGHT | CSD_SCALE } }, @@ -1575,8 +1641,19 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/logo/sonicteam_logo/sonicteam"), { CSD_SCALE } }, // tag_character - { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug1"), { CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER | CSD_REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, - { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug1"), { CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER | CSD_REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, + { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug1"), { CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER | CSD_REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, + { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug2"), { CSD_SCALE } }, + { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug1"), { CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER | CSD_REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, + { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug2"), { CSD_SCALE } }, + { HashStr("sprite/tag_character/tag_character/1p_cursor"), { CSD_SCALE } }, + { HashStr("sprite/tag_character/tag_character/2p_cursor"), { CSD_SCALE } }, + { HashStr("sprite/tag_character/tag_character/1p_name"), { CSD_SCALE } }, + { HashStr("sprite/tag_character/tag_character/2p_name"), { CSD_SCALE } }, + { HashStr("sprite/tag_character/tag_character/stage_window"), { CSD_SCALE } }, + { HashStr("sprite/tag_character/tag_character/stage"), { CSD_SCALE } }, + { HashStr("sprite/tag_character/tag_character/controller_1p"), { CSD_SCALE } }, + { HashStr("sprite/tag_character/tag_character/controller_2p"), { CSD_SCALE } }, + { HashStr("sprite/tag_character/tag_character/entry"), { CSD_SCALE } }, // tagdisplay_1p { HashStr("sprite/tagdisplay_1p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, @@ -1626,21 +1703,24 @@ const xxHashMap g_csdModifiers = // title { HashStr("sprite/title/title/Scene_Title/Logo_add"), { CSD_SCALE } }, { HashStr("sprite/title/title/Scene_Title/Logo"), { CSD_SCALE } }, - { HashStr("sprite/title/title/Scene_Title/copyright"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/title/title/Scene_Title/copyright"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, // titleloop_sth - // TODO: adjust this if letterboxed. - { HashStr("sprite/logo/titleloop_sth/Scene_0000"), { CSD_ALIGN_BOTTOM } }, + { HashStr("sprite/logo/titleloop_sth/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE | CSD_MOVIE | CSD_MODIFIER_NARROW_ONLY } }, // towndisplay { HashStr("sprite/towndisplay/ring"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/towndisplay/ring_anime"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, // trickpoint - // TODO: offset score text properly. - { HashStr("sprite/trickpoint/trickpoint/Scene_0000"), { CSD_ALIGN_TOP_RIGHT } }, - { HashStr("sprite/trickpoint/trickpoint/Scene_0001"), { CSD_ALIGN_TOP_RIGHT } }, - { HashStr("sprite/trickpoint/trickpoint/score"), { CSD_ALIGN_TOP_RIGHT } }, + { HashStr("sprite/trickpoint/trickpoint/Scene_0000"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/trickpoint/trickpoint/Scene_0001"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + { HashStr("sprite/trickpoint/trickpoint/score"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, + + // windowtest + { HashStr("sprite/windowtest/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/windowtest/okuribotan"), { CSD_SCALE } }, + { HashStr("sprite/windowtest/cursor"), { CSD_SCALE } }, }; std::optional FindCsdModifier(uint32_t data) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index e55e8253a..924b9e25e 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -87,8 +87,11 @@ enum CsdFlags : uint64_t CSD_CHEVRON = MAKE_BITFLAG64(32), CSD_MODIFIER_ULTRAWIDE_ONLY = MAKE_BITFLAG64(33), + CSD_MODIFIER_NARROW_ONLY = MAKE_BITFLAG64(34), - CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(34) + CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(35), + + CSD_MOVIE = MAKE_BITFLAG64(36) }; struct CsdUVs From e0477d5a611f87cd599643ca6e45d9707719cee9 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Thu, 28 Aug 2025 05:07:22 +0100 Subject: [PATCH 21/52] Fixed button prompts with short strings not changing platform --- MarathonRecomp/api/Sonicteam/TextCard.h | 2 +- MarathonRecomp/api/Sonicteam/TextEntity.h | 11 +- MarathonRecomp/api/stdx/wstring.h | 135 ++++++++++++++++++ .../patches/aspect_ratio_patches.cpp | 6 +- 4 files changed, 144 insertions(+), 10 deletions(-) create mode 100644 MarathonRecomp/api/stdx/wstring.h diff --git a/MarathonRecomp/api/Sonicteam/TextCard.h b/MarathonRecomp/api/Sonicteam/TextCard.h index 7ac63a6b4..4f788b753 100644 --- a/MarathonRecomp/api/Sonicteam/TextCard.h +++ b/MarathonRecomp/api/Sonicteam/TextCard.h @@ -8,7 +8,7 @@ namespace Sonicteam { public: boost::shared_ptr m_spResource; - xpointer m_pText; + xpointer m_pText; xpointer m_pVariables; MARATHON_INSERT_PADDING(0x30); }; diff --git a/MarathonRecomp/api/Sonicteam/TextEntity.h b/MarathonRecomp/api/Sonicteam/TextEntity.h index 422a51a2f..25c25ce71 100644 --- a/MarathonRecomp/api/Sonicteam/TextEntity.h +++ b/MarathonRecomp/api/Sonicteam/TextEntity.h @@ -2,6 +2,7 @@ #include #include +#include namespace Sonicteam { @@ -36,11 +37,9 @@ namespace Sonicteam MARATHON_INSERT_PADDING(4); be m_X; be m_Y; - MARATHON_INSERT_PADDING(0x0C); - xpointer m_pText; - MARATHON_INSERT_PADDING(0x0C); - be m_TextLength; - MARATHON_INSERT_PADDING(0x24); + MARATHON_INSERT_PADDING(0x08); + stdx::wstring m_Text; + MARATHON_INSERT_PADDING(0x20); xpointer m_pVariables; MARATHON_INSERT_PADDING(0x4C); be m_ScaleX; @@ -58,7 +57,7 @@ namespace Sonicteam float w{}; float h{}; - if (m_TextLength <= 0) + if (m_Text.size() <= 0) return { w, h }; if (m_FieldE0) diff --git a/MarathonRecomp/api/stdx/wstring.h b/MarathonRecomp/api/stdx/wstring.h new file mode 100644 index 000000000..71b683491 --- /dev/null +++ b/MarathonRecomp/api/stdx/wstring.h @@ -0,0 +1,135 @@ +#pragma once + +#include +#include + +namespace stdx +{ + class wstring + { + private: + union _Bxty + { + _Bxty() {} + + uint16_t _buffer[8]; + xpointer _str; + }; + + be _Myproxy; + _Bxty _bx; + be _Mysize; + be _Myres; + + bool is_short() const + { + return _Mysize <= 8; + } + + size_t length(const uint16_t* str) const + { + size_t result = 0; + uint16_t c = 0xFFFF; + + while (c != 0) + { + c = str[result]; + result++; + } + + return result; + } + + bool compare(const uint16_t* a, const uint16_t* b) const + { + for (size_t i = 0; i < length(a); i += 2) + { + if (a[i] != 0 && b[i] == 0) + return false; + + if (a[i] == 0 && b[i] != 0) + return false; + + if (a[i] != b[i]) + return false; + } + + return true; + } + + public: + const uint16_t* c_str() const + { + return is_short() ? (const uint16_t*)&_bx._buffer : (const uint16_t*)_bx._str.get(); + } + + size_t size() const + { + return _Mysize.get(); + } + + size_t capacity() const + { + return _Myres.get(); + } + + wstring() + { + _Myres = 0xF; + _Mysize = 0; + _bx._buffer[0] = '\0'; + _bx._buffer[1] = '\0'; + } + + wstring(xpointer str) : wstring(str.get()) {} + + wstring(const uint16_t* str) + { + _Myres = 0xF; + _Mysize = 0; + _bx._buffer[0] = '\0'; + _bx._buffer[1] = '\0'; + + auto len = length(str); + + if (len <= 0xF) + { + memcpy((void*)&_bx._buffer, str, len + 2); + _Mysize = (uint32_t)(len); + } + else + { + if (is_short() || capacity() < len + 2) + { + auto new_buf = g_userHeap.Alloc(len + 2); + memset((void*)(new_buf), 0, len + 2); + memcpy((void*)(new_buf), (const void*)(str), len + 2); + _bx._str = new_buf; + _Myres = len + 2; + } + else + { + memcpy((void*)_bx._str.get(), (void*)str, len + 2); + } + + _Mysize = len; + } + } + + ~wstring() + { + if (!is_short()) + g_userHeap.Free((void*)_bx._str.get()); + + _Myres = 0xF; + _Mysize = 0; + _bx._buffer[0] = '\0'; + _bx._buffer[1] = '\0'; + } + + bool operator==(const uint16_t* str) const + { + return compare(c_str(), str); + } + }; +}; diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index e4ccf1d23..b1d301259 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1142,9 +1142,10 @@ void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity, xxHashMapm_TextLength * 2; i++) + for (int i = 0; i < pTextEntity->m_Text.size(); i++) { - auto c = ByteSwap(pTextEntity->m_pText[i]); + auto str = pTextEntity->m_Text.c_str(); + auto c = ByteSwap(str[i]); if (c != L'$') continue; @@ -1246,7 +1247,6 @@ PPC_FUNC(sub_8262D868) ? g_pftModifierPS3 : g_pftModifierXenon; - // TODO (Hyper): why does this not apply to the B button in Audio Room? ReplaceTextVariables(pTextEntity, pftModifier); } From 418d92a2a8e4ecbb587a39d716cc022af4564123 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Thu, 28 Aug 2025 07:10:53 +0100 Subject: [PATCH 22/52] Fixed 2P UI element not extending properly at ultrawide --- MarathonRecomp/patches/aspect_ratio_patches.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index b1d301259..f445823b7 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1643,7 +1643,7 @@ const xxHashMap g_csdModifiers = // tag_character { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug1"), { CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER | CSD_REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug2"), { CSD_SCALE } }, - { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug1"), { CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER | CSD_REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, + { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug1"), { CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER | CSD_REPEAT_COLOUR_MODIFIER, {}, {}, { 0.5f, 0, 0.5f, 0, 0, 0, 0, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug2"), { CSD_SCALE } }, { HashStr("sprite/tag_character/tag_character/1p_cursor"), { CSD_SCALE } }, { HashStr("sprite/tag_character/tag_character/2p_cursor"), { CSD_SCALE } }, From 6f94fa55e01d3bdc10f852c2b6500ccbf298b928 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 30 Aug 2025 02:51:45 +0100 Subject: [PATCH 23/52] Align CRI technology logos to the bottom right corner --- .../patches/aspect_ratio_patches.cpp | 137 ++++++++++++++++-- MarathonRecomp/patches/aspect_ratio_patches.h | 5 +- 2 files changed, 126 insertions(+), 16 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index f445823b7..405f89227 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -545,13 +545,13 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) auto vpWidth = float(Video::s_viewportWidth); auto vpHeight = float(Video::s_viewportHeight); - if (g_aspectRatio > WIDE_ASPECT_RATIO) + if (g_aspectRatio > g_aspectRatioMovie) { - offsetX -= (vpWidth - (vpHeight * WIDE_ASPECT_RATIO)) / 2.0f; + offsetX -= (vpWidth - (vpHeight * g_aspectRatioMovie)) / 2.0f; } else { - offsetY -= (vpHeight - (vpWidth / WIDE_ASPECT_RATIO)) / 2.0f; + offsetY -= (vpHeight - (vpWidth / g_aspectRatioMovie)) / 2.0f; } } @@ -920,6 +920,109 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) original(ctx, base); } + // Don't adjust CRI logo at 16:9 or if the alignment mode is centre at ultrawide. + if (g_aspectRatio >= WIDE_ASPECT_RATIO && Config::UIAlignmentMode == EUIAlignmentMode::Centre) + modifier.Flags &= ~CSD_CRI_LOGO; + + // Clone CRI logo to move the technology + // logos to the true bottom right corner. + if ((modifier.Flags & CSD_CRI_LOGO) != 0) + { + auto v0 = getVertex(0); + auto v1 = getVertex(1); + auto v2 = getVertex(2); + auto v3 = getVertex(3); + + auto originalTop = v0->Y; + auto originalHeight = v1->Y - v0->Y; + + auto vpWidth = float(Video::s_viewportWidth); + auto vpHeight = float(Video::s_viewportHeight); + + auto width = 422.0f; + auto height = 132.0f; + auto widthScaled = width * g_aspectRatioScale; + auto heightScaled = height * g_aspectRatioScale; + + ///////////////////////////////////////////////////////////////////////// + // Clone and scale white square to block the original technology logos // + ///////////////////////////////////////////////////////////////////////// + + auto top = originalTop + (originalHeight - heightScaled); + auto left = g_aspectRatio >= WIDE_ASPECT_RATIO ? 0.0f : vpWidth - widthScaled; + + // Top Left + v0->X = left; + v0->Y = top; + v0->U = 0.0f; + v0->V = 0.01f; + + // Bottom Left + v1->X = left; + v1->Y = vpHeight; + v1->U = 0.0f; + v1->V = 0.01f; + + // Top Right + v2->X = vpWidth; + v2->Y = top; + v2->U = 0.0f; + v2->V = 0.01f; + + // Bottom Right + v3->X = vpWidth; + v3->Y = vpHeight; + v3->U = 0.0f; + v3->V = 0.01f; + + ctx.r3 = r3; + ctx.r4 = ctx.r1; + ctx.r5 = r5; + original(ctx, base); + + ///////////////////////////////////////////// + // Clone and scale to fit technology logos // + ///////////////////////////////////////////// + + v0 = getVertex(0); + v1 = getVertex(1); + v2 = getVertex(2); + v3 = getVertex(3); + + auto uv = PIXELS_TO_UV_COORDS(2048, 512, 858, 380, width, height); + auto& min = std::get<0>(uv); + auto& max = std::get<1>(uv); + + // Top Left + v0->X = vpWidth - widthScaled; + v0->Y = vpHeight - heightScaled; + v0->U = min.x; + v0->V = min.y; + + // Bottom Left + v1->X = vpWidth - widthScaled; + v1->Y = vpHeight; + v1->U = min.x; + v1->V = max.y; + + // Top Right + v2->X = vpWidth; + v2->Y = vpHeight - heightScaled; + v2->U = max.x; + v2->V = min.y; + + // Bottom Right + v3->X = vpWidth; + v3->Y = vpHeight; + v3->U = max.x; + v3->V = max.y; + + ctx.r3 = r3; + ctx.r4 = ctx.r1; + ctx.r5 = r5; + original(ctx, base); + } + ctx.r1.u32 += size; } } @@ -1051,7 +1154,8 @@ PPC_FUNC(sub_8264CC90) } auto movieModifier = FindMovieModifier(movieNameHash); - auto movieAspectRatio = (float)pMovieObjectWmv->m_Width / (float)pMovieObjectWmv->m_Height; + + g_aspectRatioMovie = (float)pMovieObjectWmv->m_Width / (float)pMovieObjectWmv->m_Height; float width, height, left, right, top, bottom; @@ -1062,17 +1166,17 @@ PPC_FUNC(sub_8264CC90) auto movieRectTop = s_movieTop; auto movieRectBottom = s_movieBottom; - if (g_aspectRatio > movieAspectRatio) + if (g_aspectRatio > g_aspectRatioMovie) { // Scale width at wide aspect ratios. - movieRectLeft = s_movieLeft / (g_aspectRatio / movieAspectRatio); - movieRectRight = s_movieRight / (g_aspectRatio / movieAspectRatio); + movieRectLeft = s_movieLeft / (g_aspectRatio / g_aspectRatioMovie); + movieRectRight = s_movieRight / (g_aspectRatio / g_aspectRatioMovie); } else { // Scale height at narrow aspect ratios. - movieRectTop = s_movieTop / (movieAspectRatio / g_aspectRatio); - movieRectBottom = s_movieBottom / (movieAspectRatio / g_aspectRatio); + movieRectTop = s_movieTop / (g_aspectRatioMovie / g_aspectRatio); + movieRectBottom = s_movieBottom / (g_aspectRatioMovie / g_aspectRatio); } auto movieRectCentreX = (movieRectLeft + movieRectRight) / 2.0f; @@ -1091,17 +1195,17 @@ PPC_FUNC(sub_8264CC90) width = 2.0f; height = 2.0f; - if (g_aspectRatio > movieAspectRatio) + if (g_aspectRatio > g_aspectRatioMovie) { if ((movieModifier.Flags & MOVIE_CROP_WIDE) != 0) { // Crop vertically at wide aspect ratios. - height = 2.0f * (g_aspectRatio / movieAspectRatio); + height = 2.0f * (g_aspectRatio / g_aspectRatioMovie); } else { // Pillarbox at wide aspect ratios. - width = 2.0f * (movieAspectRatio / g_aspectRatio); + width = 2.0f * (g_aspectRatioMovie / g_aspectRatio); } } else @@ -1109,12 +1213,12 @@ PPC_FUNC(sub_8264CC90) if ((movieModifier.Flags & MOVIE_CROP_NARROW) != 0) { // Crop horizontally at narrow aspect ratios. - width = 2.0f * (movieAspectRatio / g_aspectRatio); + width = 2.0f * (g_aspectRatioMovie / g_aspectRatio); } else { // Letterbox at narrow aspect ratios. - height = 2.0f * (g_aspectRatio / movieAspectRatio); + height = 2.0f * (g_aspectRatio / g_aspectRatioMovie); } } @@ -1227,6 +1331,8 @@ PPC_FUNC(sub_8262D868) { auto pTextEntity = (Sonicteam::TextEntity*)(base + ctx.r3.u32); + LOGFN_UTILITY("TextEntity: {:08X}", (uint64_t)pTextEntity); + auto offsetX = g_aspectRatioOffsetX + (640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale); auto offsetY = g_aspectRatioOffsetY + (360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale); auto scale = g_aspectRatioScale * g_aspectRatioGameplayScale; @@ -1384,7 +1490,7 @@ const xxHashMap g_csdModifiers = // cri_logo { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/bg"), { CSD_STRETCH } }, - { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/criware"), { CSD_SCALE } }, + { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/criware"), { CSD_CRI_LOGO | CSD_SCALE } }, // gadget_ber { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar"), { CSD_ALIGN_BOTTOM_RIGHT } }, @@ -1482,6 +1588,7 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966"), { CSD_ALIGN_TOP | CSD_SCALE } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966/Cast_0967"), { CSD_ALIGN_TOP | CSD_SCALE } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.8f, 0, -0.8f, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, { HashStr("sprite/main_menu/titlebar_effect"), { CSD_ALIGN_TOP | CSD_SCALE } }, diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index 924b9e25e..2515d4b5d 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -10,6 +10,7 @@ inline constexpr float WIDE_ASPECT_RATIO = 16.0f / 9.0f; inline constexpr float STEAM_DECK_ASPECT_RATIO = 16.0f / 10.0f; inline float g_aspectRatio; +inline float g_aspectRatioMovie; inline float g_aspectRatioOffsetX; inline float g_aspectRatioOffsetY; inline float g_aspectRatioScale; @@ -91,7 +92,9 @@ enum CsdFlags : uint64_t CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(35), - CSD_MOVIE = MAKE_BITFLAG64(36) + CSD_MOVIE = MAKE_BITFLAG64(36), + + CSD_CRI_LOGO = MAKE_BITFLAG64(37) }; struct CsdUVs From 9f4d3cc23c68b76cf779c130ffd2b87a3039f484 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Wed, 3 Sep 2025 13:38:04 +0100 Subject: [PATCH 24/52] Fix chevron animation offset with centred UI Alignment Mode --- MarathonRecomp/patches/aspect_ratio_patches.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index bf059bab9..13a125b0f 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -431,7 +431,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if (Config::UIAlignmentMode == EUIAlignmentMode::Centre || BlackBar::g_isPillarbox) { - if (g_aspectRatio >= WIDE_ASPECT_RATIO) + if ((modifier.Flags & CSD_CHEVRON) == 0 && g_aspectRatio >= WIDE_ASPECT_RATIO) modifier.Flags &= ~(CSD_ALIGN_LEFT | CSD_ALIGN_RIGHT); } From e5038c47bec0f55cd02209e0c2b8d7a2e1bfb4ed Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Wed, 3 Sep 2025 14:03:26 +0100 Subject: [PATCH 25/52] Remove result debug key --- MarathonRecomp/app.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/MarathonRecomp/app.cpp b/MarathonRecomp/app.cpp index 219074a0f..d0d5ceb96 100644 --- a/MarathonRecomp/app.cpp +++ b/MarathonRecomp/app.cpp @@ -95,15 +95,8 @@ PPC_FUNC(sub_825EA610) // Allow variable FPS when config is not 60 FPS. App::s_pApp->m_pDoc->m_VFrame = Config::FPS != 60; - - AudioPatches::Update(App::s_deltaTime); - // TODO: REMOVE THIS!!! - if (auto pGameMode = App::s_pApp->m_pDoc->GetDocMode()) - { - if (GetAsyncKeyState(VK_F5) & 1) - pGameMode->m_pGameImp->m_GameState = Sonicteam::GameImp::GameState_Result; - } + AudioPatches::Update(App::s_deltaTime); __imp__sub_825EA610(ctx, base); } From 0ef0108582bcbe47fbab652c6c3de611db296ba2 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Wed, 3 Sep 2025 16:25:23 +0100 Subject: [PATCH 26/52] Scale town NPC balloon icons --- MarathonRecomp/api/Marathon.h | 3 +++ .../api/Sonicteam/CObjBalloonIconDrawable.h | 27 +++++++++++++++++++ .../SoX/Scenery/CameraEventCallback.h | 12 +++++++++ .../api/Sonicteam/SoX/Scenery/Drawable.h | 14 ++++++++++ MarathonRecomp/api/Sonicteam/TextEntity.h | 12 ++------- .../patches/aspect_ratio_patches.cpp | 19 +++++++++++++ 6 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 MarathonRecomp/api/Sonicteam/CObjBalloonIconDrawable.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraEventCallback.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/Scenery/Drawable.h diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index b68b73331..a40026654 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -19,6 +19,7 @@ #include "Sonicteam/AlertWindowTask.h" #include "Sonicteam/AppMarathon.h" #include "Sonicteam/AudioEngineXenon.h" +#include "Sonicteam/CObjBalloonIconDrawable.h" #include "Sonicteam/Camera/CameraMode.h" #include "Sonicteam/Camera/Cameraman.h" #include "Sonicteam/Camera/SonicCamera.h" @@ -110,6 +111,8 @@ #include "Sonicteam/SoX/Physics/World.h" #include "Sonicteam/SoX/RefCountObject.h" #include "Sonicteam/SoX/RefSharedPointer.h" +#include "Sonicteam/SoX/Scenery/CameraEventCallback.h" +#include "Sonicteam/SoX/Scenery/Drawable.h" #include "Sonicteam/StdImageFilters/BurnoutBlurFilter.h" #include "Sonicteam/StdImageFilters/SingleTechniqueFilter.h" #include "Sonicteam/System/CreateStatic.h" diff --git a/MarathonRecomp/api/Sonicteam/CObjBalloonIconDrawable.h b/MarathonRecomp/api/Sonicteam/CObjBalloonIconDrawable.h new file mode 100644 index 000000000..8ce6a771b --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/CObjBalloonIconDrawable.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +namespace Sonicteam +{ + class CObjBalloonIconDrawable : public SoX::Scenery::Drawable + { + public: + struct Vertex + { + be X; + be Y; + MARATHON_INSERT_PADDING(0x10); + be Colour; // ARGB8888 + be U; + be V; + MARATHON_INSERT_PADDING(0x18); + }; + + xpointer m_pMyGraphicsDevice; + MARATHON_INSERT_PADDING(0x24); + Vertex m_Vertices[4]; // BL, TL, BR, TR + }; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraEventCallback.h b/MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraEventCallback.h new file mode 100644 index 000000000..c8fc05bd9 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraEventCallback.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace Sonicteam::SoX::Scenery +{ + class CameraEventCallback + { + public: + xpointer m_pVftable; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/Scenery/Drawable.h b/MarathonRecomp/api/Sonicteam/SoX/Scenery/Drawable.h new file mode 100644 index 000000000..53762cd26 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Scenery/Drawable.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +namespace Sonicteam::SoX::Scenery +{ + class Drawable : public RefCountObject, public CameraEventCallback + { + public: + MARATHON_INSERT_PADDING(0x64); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/TextEntity.h b/MarathonRecomp/api/Sonicteam/TextEntity.h index 15f19d522..910b2780a 100644 --- a/MarathonRecomp/api/Sonicteam/TextEntity.h +++ b/MarathonRecomp/api/Sonicteam/TextEntity.h @@ -16,19 +16,11 @@ namespace Sonicteam { be X; be Y; - be Field08; - be Field0C; - be Field10; - be Field14; + MARATHON_INSERT_PADDING(0x10); be Colour; // ARGB8888 be U; be V; - be Field24; - be Field28; - be Field2C; - be Field30; - be Field34; - be Field38; + MARATHON_INSERT_PADDING(0x18); }; MARATHON_INSERT_PADDING(4); diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 13a125b0f..20edd09aa 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1126,6 +1126,25 @@ PPC_FUNC(sub_824F1538) pHUDRaderMap->m_Y = g_radarMapY - g_radarMapCoverHeight / 2; } +// Sonicteam::CObjBalloonIconDrawable::Draw +PPC_FUNC_IMPL(__imp__sub_82352220); +PPC_FUNC(sub_82352220) +{ + auto pCObjBalloonIconDrawable = (Sonicteam::CObjBalloonIconDrawable*)(base + ctx.r3.u32); + auto scale = (g_aspectRatio / WIDE_ASPECT_RATIO); + + pCObjBalloonIconDrawable->m_Vertices[0].X = -1.0f / scale; + pCObjBalloonIconDrawable->m_Vertices[0].Y = 0.0f; + pCObjBalloonIconDrawable->m_Vertices[1].X = -1.0f / scale; + pCObjBalloonIconDrawable->m_Vertices[1].Y = g_aspectRatio / scale; + pCObjBalloonIconDrawable->m_Vertices[2].X = 1.0f / scale; + pCObjBalloonIconDrawable->m_Vertices[2].Y = 0.0f; + pCObjBalloonIconDrawable->m_Vertices[3].X = 1.0f / scale; + pCObjBalloonIconDrawable->m_Vertices[3].Y = g_aspectRatio / scale; + + __imp__sub_82352220(ctx, base); +} + // Sonicteam::MovieObjectWmv::Draw PPC_FUNC_IMPL(__imp__sub_8264CC90); PPC_FUNC(sub_8264CC90) From 91905f1777ae87b855c154bbaf80a4c468cb16dc Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:06:00 +0100 Subject: [PATCH 27/52] Clean-up --- MarathonRecomp/api/CSD/Manager/csdmNode.h | 12 ---- MarathonRecomp/api/CSD/Manager/csdmNode.inl | 44 ------------- .../api/CSD/Manager/csdmNodeObserver.h | 2 +- .../api/CSD/Manager/csdmObserverBase.h | 8 +-- .../api/CSD/Manager/csdmObserverBase.inl | 8 --- MarathonRecomp/api/CSD/Manager/csdmProject.h | 10 --- .../api/CSD/Manager/csdmProject.inl | 40 ------------ MarathonRecomp/api/CSD/Manager/csdmRCObject.h | 12 +--- .../api/CSD/Manager/csdmRCObject.inl | 35 ----------- .../api/CSD/Manager/csdmRCObjectImp.h | 2 +- .../api/CSD/Manager/csdmRCObjectImp.inl | 14 ----- MarathonRecomp/api/CSD/Manager/csdmRCPtr.h | 21 +------ MarathonRecomp/api/CSD/Manager/csdmRCPtr.inl | 45 -------------- MarathonRecomp/api/CSD/Manager/csdmRCPtrAbs.h | 27 +------- .../api/CSD/Manager/csdmRCPtrAbs.inl | 58 ----------------- .../api/CSD/Manager/csdmResourceBase.h | 11 +--- .../api/CSD/Manager/csdmResourceBase.inl | 15 ----- MarathonRecomp/api/CSD/Manager/csdmScene.h | 29 +++------ MarathonRecomp/api/CSD/Manager/csdmScene.inl | 54 ---------------- .../api/CSD/Manager/csdmSceneObserver.h | 2 +- .../api/CSD/Manager/csdmSubjectBase.h | 11 +--- .../api/CSD/Manager/csdmSubjectBase.inl | 14 ----- .../api/Sonicteam/SoX/Graphics/TechniqueFXL.h | 1 + MarathonRecomp/api/Sonicteam/SoX/IResource2.h | 2 +- .../api/Sonicteam/SoX/IResourceMgr.h | 9 ++- MarathonRecomp/api/stdx/wstring.h | 35 +---------- MarathonRecomp/framework.h | 44 +++++++++++++ .../patches/aspect_ratio_patches.cpp | 62 +++++-------------- MarathonRecomp/patches/aspect_ratio_patches.h | 6 +- 29 files changed, 101 insertions(+), 532 deletions(-) delete mode 100644 MarathonRecomp/api/CSD/Manager/csdmNode.inl delete mode 100644 MarathonRecomp/api/CSD/Manager/csdmObserverBase.inl delete mode 100644 MarathonRecomp/api/CSD/Manager/csdmProject.inl delete mode 100644 MarathonRecomp/api/CSD/Manager/csdmRCObject.inl delete mode 100644 MarathonRecomp/api/CSD/Manager/csdmRCObjectImp.inl delete mode 100644 MarathonRecomp/api/CSD/Manager/csdmRCPtr.inl delete mode 100644 MarathonRecomp/api/CSD/Manager/csdmRCPtrAbs.inl delete mode 100644 MarathonRecomp/api/CSD/Manager/csdmResourceBase.inl delete mode 100644 MarathonRecomp/api/CSD/Manager/csdmScene.inl delete mode 100644 MarathonRecomp/api/CSD/Manager/csdmSubjectBase.inl diff --git a/MarathonRecomp/api/CSD/Manager/csdmNode.h b/MarathonRecomp/api/CSD/Manager/csdmNode.h index 13b807a16..efedb3975 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmNode.h +++ b/MarathonRecomp/api/CSD/Manager/csdmNode.h @@ -16,17 +16,5 @@ namespace Chao::CSD MARATHON_INSERT_PADDING(0x34); xpointer m_pMotionPattern; MARATHON_INSERT_PADDING(0x18); - - ~CNode(); - - void SetText(const char* in_pText); - void SetText(const wchar_t* in_pText); - void SetPosition(float in_X, float in_Y); - void SetHideFlag(uint32_t in_HideFlag); - void SetRotation(float in_Rotation); - void SetScale(float in_X, float in_Y); - void SetPatternIndex(uint32_t in_PatternIndex); }; } - -#include diff --git a/MarathonRecomp/api/CSD/Manager/csdmNode.inl b/MarathonRecomp/api/CSD/Manager/csdmNode.inl deleted file mode 100644 index ee913007b..000000000 --- a/MarathonRecomp/api/CSD/Manager/csdmNode.inl +++ /dev/null @@ -1,44 +0,0 @@ -#include "Marathon.inl" - -namespace Chao::CSD -{ - inline CNode::~CNode() - { - MARATHON_VIRTUAL_FUNCTION(void, 0, this); - } - - inline void CNode::SetText(const char* in_pText) - { - // GuestToHostFunction(sub_830BF640, this, in_pText); - } - - inline void CNode::SetText(const wchar_t* in_pText) - { - // GuestToHostFunction(sub_830BF640, this, in_pText); - } - - inline void CNode::SetPosition(float in_X, float in_Y) - { - // GuestToHostFunction(sub_830BF078, this, in_X, in_Y); - } - - inline void CNode::SetHideFlag(uint32_t in_HideFlag) - { - // GuestToHostFunction(sub_830BF080, this, in_HideFlag); - } - - inline void CNode::SetRotation(float in_Rotation) - { - // GuestToHostFunction(sub_830BF088, this, in_Rotation); - } - - inline void CNode::SetScale(float in_X, float in_Y) - { - // GuestToHostFunction(sub_830BF090, this, in_X, in_Y); - } - - inline void CNode::SetPatternIndex(uint32_t in_PatternIndex) - { - // GuestToHostFunction(sub_830BF300, this, in_PatternIndex); - } -} diff --git a/MarathonRecomp/api/CSD/Manager/csdmNodeObserver.h b/MarathonRecomp/api/CSD/Manager/csdmNodeObserver.h index abfba9805..ff72cb3e1 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmNodeObserver.h +++ b/MarathonRecomp/api/CSD/Manager/csdmNodeObserver.h @@ -1,6 +1,6 @@ #pragma once -#include "CSD/Manager/csdmObserverBase.h" +#include namespace Chao::CSD { diff --git a/MarathonRecomp/api/CSD/Manager/csdmObserverBase.h b/MarathonRecomp/api/CSD/Manager/csdmObserverBase.h index e6089e651..af69a5a07 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmObserverBase.h +++ b/MarathonRecomp/api/CSD/Manager/csdmObserverBase.h @@ -1,6 +1,6 @@ #pragma once -#include "Marathon.inl" +#include namespace Chao::CSD { @@ -10,14 +10,10 @@ namespace Chao::CSD public: struct Vftable { - be m_fpDtor; + be fpDestroy; }; xpointer m_pVftable; MARATHON_INSERT_PADDING(0x08); - - ~CObserverBase(); }; } - -#include "CSD/Manager/csdmObserverBase.inl" diff --git a/MarathonRecomp/api/CSD/Manager/csdmObserverBase.inl b/MarathonRecomp/api/CSD/Manager/csdmObserverBase.inl deleted file mode 100644 index beeabb4fa..000000000 --- a/MarathonRecomp/api/CSD/Manager/csdmObserverBase.inl +++ /dev/null @@ -1,8 +0,0 @@ -namespace Chao::CSD -{ - template - inline CObserverBase::~CObserverBase() - { - GuestToHostFunction(m_pVftable->m_fpDtor, this); - } -} diff --git a/MarathonRecomp/api/CSD/Manager/csdmProject.h b/MarathonRecomp/api/CSD/Manager/csdmProject.h index 1788eed92..ac8b38b8c 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmProject.h +++ b/MarathonRecomp/api/CSD/Manager/csdmProject.h @@ -22,15 +22,5 @@ namespace Chao::CSD MARATHON_INSERT_PADDING(0x1C); RCPtr m_rcTexList; MARATHON_INSERT_PADDING(0x1C); - - RCPtr CreateScene(const char* in_pName) const; - RCPtr CreateScene(const char* in_pName, const char* in_pMotionName) const; - - void DestroyScene(CScene* in_pScene); - void DestroyScene(RCPtr& inout_rcScene); - - static void DestroyScene(CProject* in_pProject, RCPtr& inout_rcScene); }; } - -#include diff --git a/MarathonRecomp/api/CSD/Manager/csdmProject.inl b/MarathonRecomp/api/CSD/Manager/csdmProject.inl deleted file mode 100644 index 17a5a599b..000000000 --- a/MarathonRecomp/api/CSD/Manager/csdmProject.inl +++ /dev/null @@ -1,40 +0,0 @@ -#include "kernel/function.h" - -namespace Chao::CSD -{ - inline RCPtr CProject::CreateScene(const char* in_pName) const - { - RCPtr rcScene; - // GuestToHostFunction(sub_830BEE00, this, rcScene, in_pName, nullptr); - return rcScene; - } - - inline RCPtr CProject::CreateScene(const char* in_pName, const char* in_pMotionName) const - { - RCPtr rcScene; - // GuestToHostFunction(sub_830BECE0, this, rcScene, in_pName, in_pMotionName, nullptr); - return rcScene; - } - - inline void CProject::DestroyScene(CScene* in_pScene) - { - // GuestToHostFunction(sub_830BE298, this, in_pScene); - } - - inline void CProject::DestroyScene(RCPtr& inout_rcScene) - { - if (!inout_rcScene) - return; - - DestroyScene(inout_rcScene.Get()); - inout_rcScene = nullptr; - } - - inline void CProject::DestroyScene(CProject* in_pProject, RCPtr& inout_rcScene) - { - if (in_pProject) - in_pProject->DestroyScene(inout_rcScene); - else - inout_rcScene = nullptr; - } -} diff --git a/MarathonRecomp/api/CSD/Manager/csdmRCObject.h b/MarathonRecomp/api/CSD/Manager/csdmRCObject.h index 143dcb07a..8294228ff 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmRCObject.h +++ b/MarathonRecomp/api/CSD/Manager/csdmRCObject.h @@ -1,6 +1,6 @@ #pragma once -#include "CSD/Manager/csdmRCPtrAbs.h" +#include namespace Chao::CSD { @@ -9,8 +9,8 @@ namespace Chao::CSD public: struct Vftable { - be m_fpDtor; - be m_fpDeallocate; + be fpDestroy; + be fpDeallocate; }; xpointer m_pVftable; @@ -18,11 +18,5 @@ namespace Chao::CSD be m_RefCount; xpointer m_pDealloctor; be m_eDealloctor; - - ~RCObject(); - void Deallocate(void* in_pMemory); - void Release(); }; } - -#include "CSD/Manager/csdmRCObject.inl" diff --git a/MarathonRecomp/api/CSD/Manager/csdmRCObject.inl b/MarathonRecomp/api/CSD/Manager/csdmRCObject.inl deleted file mode 100644 index e15c82663..000000000 --- a/MarathonRecomp/api/CSD/Manager/csdmRCObject.inl +++ /dev/null @@ -1,35 +0,0 @@ -namespace Chao::CSD -{ - inline RCPtrAbs::RCObject::~RCObject() - { - GuestToHostFunction(m_pVftable->m_fpDtor, this); - } - - inline RCPtrAbs::~RCPtrAbs() - { - RCPtrAbs::RCObject* pObj = m_pObject; - - m_pObject = nullptr; - - if (pObj) - pObj->Release(); - } - - inline void* RCPtrAbs::GetAbs() const - { - if (!m_pObject) - return nullptr; - - return m_pObject->m_pMemory; - } - - inline void RCPtrAbs::RCObject::Deallocate(void* in_pMemory) - { - GuestToHostFunction(m_pVftable->m_fpDeallocate, this, in_pMemory); - } - - inline void RCPtrAbs::RCObject::Release() - { - // GuestToHostFunction(sub_830BA068, this); - } -} diff --git a/MarathonRecomp/api/CSD/Manager/csdmRCObjectImp.h b/MarathonRecomp/api/CSD/Manager/csdmRCObjectImp.h index aa814cfa7..8b672b34d 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmRCObjectImp.h +++ b/MarathonRecomp/api/CSD/Manager/csdmRCObjectImp.h @@ -1,6 +1,6 @@ #pragma once -#include "CSD/Manager/csdmRCPtr.h" +#include namespace Chao::CSD { diff --git a/MarathonRecomp/api/CSD/Manager/csdmRCObjectImp.inl b/MarathonRecomp/api/CSD/Manager/csdmRCObjectImp.inl deleted file mode 100644 index 74465e618..000000000 --- a/MarathonRecomp/api/CSD/Manager/csdmRCObjectImp.inl +++ /dev/null @@ -1,14 +0,0 @@ -namespace Chao::CSD -{ - template - void RCPtr::RCObjectImp::Deallocate(void* in_pMemory) - { - // delete static_cast(in_pMemory); - } - - template - RCPtrAbs::RCObject* RCPtr::CreateRCObject() - { - return new RCObjectImp(); - } -} diff --git a/MarathonRecomp/api/CSD/Manager/csdmRCPtr.h b/MarathonRecomp/api/CSD/Manager/csdmRCPtr.h index 10878100b..36651138c 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmRCPtr.h +++ b/MarathonRecomp/api/CSD/Manager/csdmRCPtr.h @@ -1,6 +1,6 @@ #pragma once -#include "CSD/Manager/csdmRCPtrAbs.h" +#include namespace Chao::CSD { @@ -9,24 +9,5 @@ namespace Chao::CSD { public: class RCObjectImp; - - RCPtr() : RCPtrAbs() {} - RCPtr(T* in_pMemory) : RCPtrAbs(in_pMemory) {} - RCPtr(const RCPtr& in_rOther) : RCPtrAbs(in_rOther) {} - RCPtr(RCPtr&& in_rOther) : RCPtrAbs(std::move(in_rOther)) {} - - void Attach(T* in_pObject); - - T* Get() const; - void Set(const RCPtr& in_rOther); - - T* operator*() const; - T* operator->() const; - - RCPtr& operator=(const RCPtr& in_rOther); - - operator bool() const; }; } - -#include "CSD/Manager/csdmRCPtr.inl" diff --git a/MarathonRecomp/api/CSD/Manager/csdmRCPtr.inl b/MarathonRecomp/api/CSD/Manager/csdmRCPtr.inl deleted file mode 100644 index 01bcd6e10..000000000 --- a/MarathonRecomp/api/CSD/Manager/csdmRCPtr.inl +++ /dev/null @@ -1,45 +0,0 @@ -namespace Chao::CSD -{ - template - void RCPtr::Attach(T* in_pObject) - { - RCPtrAbs::AttachAbs(in_pObject); - } - - template - T* RCPtr::Get() const - { - return static_cast(RCPtrAbs::GetAbs()); - } - - template - void RCPtr::Set(const RCPtr& in_rOther) - { - RCPtrAbs::SetAbs(in_rOther); - } - - template - T* RCPtr::operator*() const - { - return Get(); - } - - template - T* RCPtr::operator->() const - { - return Get(); - } - - template - RCPtr& RCPtr::operator=(const RCPtr& in_rOther) - { - Set(in_rOther); - return *this; - } - - template - RCPtr::operator bool() const - { - return m_pObject != nullptr; - } -} diff --git a/MarathonRecomp/api/CSD/Manager/csdmRCPtrAbs.h b/MarathonRecomp/api/CSD/Manager/csdmRCPtrAbs.h index 71f6437ed..487128d3a 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmRCPtrAbs.h +++ b/MarathonRecomp/api/CSD/Manager/csdmRCPtrAbs.h @@ -1,6 +1,6 @@ #pragma once -#include "Marathon.inl" +#include namespace Chao::CSD { @@ -11,32 +11,11 @@ namespace Chao::CSD struct Vftable { - be m_fpDtor; - be m_fpCreateRCObject; + be fpDestroy; + be fpCreateRCObject; }; xpointer m_pVftable; xpointer m_pObject; - - RCPtrAbs(); - RCPtrAbs(void* in_pMemory); - RCPtrAbs(const RCPtrAbs& in_rOther); - RCPtrAbs(RCPtrAbs&& in_rPtr); - - ~RCPtrAbs(); - RCObject* CreateRCObject(); - - void AttachAbs(void* in_pMemory); - void* GetAbs() const; - void SetAbs(const RCPtrAbs& in_rPtr); - - void* operator*() const; - void* operator->() const; - - RCPtrAbs& operator=(const RCPtrAbs& in_rPtr); - - operator bool() const; }; } - -#include "CSD/Manager/csdmRCPtrAbs.inl" diff --git a/MarathonRecomp/api/CSD/Manager/csdmRCPtrAbs.inl b/MarathonRecomp/api/CSD/Manager/csdmRCPtrAbs.inl deleted file mode 100644 index 8387e8e57..000000000 --- a/MarathonRecomp/api/CSD/Manager/csdmRCPtrAbs.inl +++ /dev/null @@ -1,58 +0,0 @@ -namespace Chao::CSD -{ - inline RCPtrAbs::RCPtrAbs() - { - AttachAbs(nullptr); - } - - inline RCPtrAbs::RCPtrAbs(void* in_pMemory) - { - AttachAbs(in_pMemory); - } - - inline RCPtrAbs::RCPtrAbs(const RCPtrAbs& in_rOther) - { - SetAbs(in_rOther); - } - - inline RCPtrAbs::RCPtrAbs(RCPtrAbs&& in_rPtr) : m_pObject(in_rPtr.m_pObject) - { - in_rPtr.m_pObject = nullptr; - } - - inline RCPtrAbs::RCObject* RCPtrAbs::CreateRCObject() - { - return GuestToHostFunction(m_pVftable->m_fpCreateRCObject, this); - } - - inline void RCPtrAbs::AttachAbs(void* in_pMemory) - { - // GuestToHostFunction(sub_830BA298, this, in_pMemory); - } - - inline void RCPtrAbs::SetAbs(const RCPtrAbs& in_rPtr) - { - // GuestToHostFunction(sub_830BA328, this, in_rPtr); - } - - inline void* RCPtrAbs::operator*() const - { - return GetAbs(); - } - - inline void* RCPtrAbs::operator->() const - { - return GetAbs(); - } - - inline RCPtrAbs& RCPtrAbs::operator=(const RCPtrAbs& in_rPtr) - { - SetAbs(in_rPtr); - return *this; - } - - inline RCPtrAbs::operator bool() const - { - return m_pObject != nullptr; - } -} diff --git a/MarathonRecomp/api/CSD/Manager/csdmResourceBase.h b/MarathonRecomp/api/CSD/Manager/csdmResourceBase.h index ae6c5740b..63afeb830 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmResourceBase.h +++ b/MarathonRecomp/api/CSD/Manager/csdmResourceBase.h @@ -1,6 +1,6 @@ #pragma once -#include "CSD/Manager/csdmRCPtr.h" +#include namespace Chao::CSD { @@ -10,17 +10,12 @@ namespace Chao::CSD public: struct Vftable { - be m_fpDtor; - be m_fpCopyResource; + be fpDestroy; + be fpCopyResource; }; xpointer m_pVftable; MARATHON_INSERT_PADDING(4); xpointer m_pResource; - - ~CResourceBase(); - void CopyResource(const CResourceBase& in_rOther); }; } - -#include "CSD/Manager/csdmResourceBase.h" diff --git a/MarathonRecomp/api/CSD/Manager/csdmResourceBase.inl b/MarathonRecomp/api/CSD/Manager/csdmResourceBase.inl deleted file mode 100644 index d459e3c99..000000000 --- a/MarathonRecomp/api/CSD/Manager/csdmResourceBase.inl +++ /dev/null @@ -1,15 +0,0 @@ -namespace Chao::CSD -{ - template - inline CResourceBase::~CResourceBase() - { - GuestToHostFunction(m_pVftable->m_fpDtor, this); - } - - template - inline void CResourceBase::CopyResource(const CResourceBase& in_rOther) - { - m_rcResourceHolder = in_rOther.m_rcResourceHolder; - m_pResource = in_rOther.m_pResource; - } -} diff --git a/MarathonRecomp/api/CSD/Manager/csdmScene.h b/MarathonRecomp/api/CSD/Manager/csdmScene.h index 3ffd570c4..ea6deab69 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmScene.h +++ b/MarathonRecomp/api/CSD/Manager/csdmScene.h @@ -13,7 +13,7 @@ namespace Chao::CSD struct Cast { - MARATHON_INSERT_PADDING(0x144); // TODO: might be 0x130? + MARATHON_INSERT_PADDING(0x144); }; struct CastLink @@ -96,12 +96,12 @@ namespace Chao::CSD xpointer pSceneNodeIndices; }; - enum EMotionRepeatType : uint32_t + enum MotionRepeatType : uint32_t { - eMotionRepeatType_PlayOnce = 0, - eMotionRepeatType_Loop = 1, - eMotionRepeatType_PingPong = 2, - eMotionRepeatType_PlayThenDestroy = 3 + MotionRepeatType_PlayOnce, + MotionRepeatType_Loop, + MotionRepeatType_PingPong, + MotionRepeatType_PlayThenDestroy }; class CScene : public CResourceBase, SubjectBase, CBase @@ -116,22 +116,7 @@ namespace Chao::CSD MARATHON_INSERT_PADDING(0x0C); be m_MotionDisableFlag; MARATHON_INSERT_PADDING(0x10); - be m_MotionRepeatType; + be m_MotionRepeatType; MARATHON_INSERT_PADDING(0x2C); - - ~CScene(); - void Update(float in_DeltaTime = 0.0f); - void Render(void* in_pUnk); - - void GetNode(RCPtr& out_rResult, const char* in_pName); - - bool SetMotion(const char* in_pName); - void SetMotionFrame(float in_MotionFrame); - void SetPosition(float in_X, float in_Y); - void SetHideFlag(uint32_t in_HideFlag); - void SetRotation(float in_Angle); - void SetScale(float in_X, float in_Y); }; } - -#include "CSD/Manager/csdmScene.inl" diff --git a/MarathonRecomp/api/CSD/Manager/csdmScene.inl b/MarathonRecomp/api/CSD/Manager/csdmScene.inl deleted file mode 100644 index 73faa5c86..000000000 --- a/MarathonRecomp/api/CSD/Manager/csdmScene.inl +++ /dev/null @@ -1,54 +0,0 @@ -namespace Chao::CSD -{ - inline CScene::~CScene() - { - MARATHON_VIRTUAL_FUNCTION(void, 0, this); - } - - inline void CScene::Update(float in_DeltaTime) - { - MARATHON_VIRTUAL_FUNCTION(void, 2, this, in_DeltaTime); - } - - inline void CScene::Render(void* in_pUnk) - { - MARATHON_VIRTUAL_FUNCTION(void, 3, this, in_pUnk); - } - - inline void CScene::GetNode(RCPtr& out_rResult, const char* in_pName) - { - // GuestToHostFunction(sub_830BCCA8, &out_rResult, this, in_pName); - } - - inline bool CScene::SetMotion(const char* in_pName) - { - // return GuestToHostFunction(sub_830BA760, this, in_pName); - return true; - } - - inline void CScene::SetMotionFrame(float in_MotionFrame) - { - m_PrevMotionFrame = in_MotionFrame; - m_MotionFrame = in_MotionFrame; - } - - inline void CScene::SetPosition(float in_X, float in_Y) - { - // GuestToHostFunction(sub_830BB550, this, in_X, in_Y); - } - - inline void CScene::SetHideFlag(uint32_t in_HideFlag) - { - // GuestToHostFunction(sub_830BB378, this, in_HideFlag); - } - - inline void CScene::SetRotation(float in_Angle) - { - // GuestToHostFunction(sub_830BB5F8, this, in_Angle); - } - - inline void CScene::SetScale(float in_X, float in_Y) - { - // GuestToHostFunction(sub_830BB650, this, in_X, in_Y); - } -} diff --git a/MarathonRecomp/api/CSD/Manager/csdmSceneObserver.h b/MarathonRecomp/api/CSD/Manager/csdmSceneObserver.h index 2e6abb2c4..524cfaab9 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmSceneObserver.h +++ b/MarathonRecomp/api/CSD/Manager/csdmSceneObserver.h @@ -1,6 +1,6 @@ #pragma once -#include "CSD/Manager/csdmObserverBase.h" +#include namespace Chao::CSD { diff --git a/MarathonRecomp/api/CSD/Manager/csdmSubjectBase.h b/MarathonRecomp/api/CSD/Manager/csdmSubjectBase.h index 00b6407e3..5c4895129 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmSubjectBase.h +++ b/MarathonRecomp/api/CSD/Manager/csdmSubjectBase.h @@ -1,6 +1,6 @@ #pragma once -#include "Marathon.inl" +#include namespace Chao::CSD { @@ -10,16 +10,11 @@ namespace Chao::CSD public: struct Vftable { - be m_fpDtor; - be m_fpGetObservee; + be fpDestroy; + be fpGetObservee; }; xpointer m_pVftable; MARATHON_INSERT_PADDING(0x0C); - - ~SubjectBase(); - TObservee* GetObservee() const; }; } - -#include "CSD/Manager/csdmSubjectBase.inl" diff --git a/MarathonRecomp/api/CSD/Manager/csdmSubjectBase.inl b/MarathonRecomp/api/CSD/Manager/csdmSubjectBase.inl deleted file mode 100644 index 9c9b7f8fe..000000000 --- a/MarathonRecomp/api/CSD/Manager/csdmSubjectBase.inl +++ /dev/null @@ -1,14 +0,0 @@ -namespace Chao::CSD -{ - template - inline SubjectBase::~SubjectBase() - { - GuestToHostFunction(m_pVftable->m_fpDtor, this); - } - - template - inline TObservee* SubjectBase::GetObservee() const - { - return nullptr; - } -} diff --git a/MarathonRecomp/api/Sonicteam/SoX/Graphics/TechniqueFXL.h b/MarathonRecomp/api/Sonicteam/SoX/Graphics/TechniqueFXL.h index a6a5309ae..8887b4b88 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Graphics/TechniqueFXL.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/TechniqueFXL.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace Sonicteam::SoX::Graphics { diff --git a/MarathonRecomp/api/Sonicteam/SoX/IResource2.h b/MarathonRecomp/api/Sonicteam/SoX/IResource2.h index 044a61501..2251aa7d0 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/IResource2.h +++ b/MarathonRecomp/api/Sonicteam/SoX/IResource2.h @@ -5,6 +5,6 @@ namespace Sonicteam::SoX { - template + template class IResource2 : public IResource {}; } diff --git a/MarathonRecomp/api/Sonicteam/SoX/IResourceMgr.h b/MarathonRecomp/api/Sonicteam/SoX/IResourceMgr.h index 63853e831..eedf59aed 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/IResourceMgr.h +++ b/MarathonRecomp/api/Sonicteam/SoX/IResourceMgr.h @@ -7,7 +7,14 @@ namespace Sonicteam::SoX class IResourceMgr { public: - xpointer m_pVftable; + struct Vftable + { + be fpDestroy; + be fpMakeResource; + MARATHON_INSERT_PADDING(4); + }; + + xpointer m_pVftable; MARATHON_INSERT_PADDING(0x0C); }; } diff --git a/MarathonRecomp/api/stdx/wstring.h b/MarathonRecomp/api/stdx/wstring.h index 71b683491..cd45fba21 100644 --- a/MarathonRecomp/api/stdx/wstring.h +++ b/MarathonRecomp/api/stdx/wstring.h @@ -26,37 +26,6 @@ namespace stdx return _Mysize <= 8; } - size_t length(const uint16_t* str) const - { - size_t result = 0; - uint16_t c = 0xFFFF; - - while (c != 0) - { - c = str[result]; - result++; - } - - return result; - } - - bool compare(const uint16_t* a, const uint16_t* b) const - { - for (size_t i = 0; i < length(a); i += 2) - { - if (a[i] != 0 && b[i] == 0) - return false; - - if (a[i] == 0 && b[i] != 0) - return false; - - if (a[i] != b[i]) - return false; - } - - return true; - } - public: const uint16_t* c_str() const { @@ -90,7 +59,7 @@ namespace stdx _bx._buffer[0] = '\0'; _bx._buffer[1] = '\0'; - auto len = length(str); + auto len = strlenU16(str); if (len <= 0xF) { @@ -129,7 +98,7 @@ namespace stdx bool operator==(const uint16_t* str) const { - return compare(c_str(), str); + return strcmpU16(c_str(), str); } }; }; diff --git a/MarathonRecomp/framework.h b/MarathonRecomp/framework.h index b6a4d2e23..143cb0afb 100644 --- a/MarathonRecomp/framework.h +++ b/MarathonRecomp/framework.h @@ -84,6 +84,50 @@ inline bool strcmpIgnoreCase(const char* a, const char* b) return true; } +inline size_t strlenU16(const uint16_t* str) +{ + size_t result = 0; + uint16_t c = 0xFFFF; + + while (c != 0) + { + c = str[result]; + result++; + } + + return result; +} + +inline bool strcmpU16(const uint16_t* a, const uint16_t* b) +{ + for (size_t i = 0; i < strlenU16(a); i += 2) + { + if (a[i] != 0 && b[i] == 0) + return false; + + if (a[i] == 0 && b[i] != 0) + return false; + + if (a[i] != b[i]) + return false; + } + + return true; +} + +inline void printU16(const uint16_t* str) +{ + for (size_t i = 0; i < strlenU16(str); i++) + { + auto c0 = str[i] >> 8; + auto c1 = str[i] & 0xFF; + + printf("%c%c", c0, c1); + } + + printf("\n"); +} + #ifndef __cpp_lib_atomic_ref // Polyfill for std::atomic_ref namespace std { diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 20edd09aa..379f20f21 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -34,10 +34,6 @@ static float g_radarMapCoverHeight{}; static float g_podBaseRightX{}; -// Explicit translations don't get affected by gameplay UI downscaling. -static float g_scenePositionX{}; -static float g_scenePositionY{}; - static float g_bgArrowsEnd{}; static float g_fgArrowsEnd{}; static float g_chevronLoopTime{}; @@ -289,30 +285,6 @@ PPC_FUNC(sub_82656650) __imp__sub_82656650(ctx, base); } -// Chao::CSD::CScene::Render -PPC_FUNC_IMPL(__imp__sub_825CB378); -PPC_FUNC(sub_825CB378) -{ - g_scenePositionX = 0.0f; - g_scenePositionY = 0.0f; - - uint32_t motionPattern = PPC_LOAD_U32(ctx.r3.u32 + 0x70); - if (motionPattern != NULL) - { - uint32_t member = PPC_LOAD_U32(motionPattern + 8); - if (member != NULL) - { - uint32_t x = PPC_LOAD_U32(member + 0x2C); - uint32_t y = PPC_LOAD_U32(member + 0x30); - - g_scenePositionX = 1280.0f * reinterpret_cast(x); - g_scenePositionY = 720.0f * reinterpret_cast(y); - } - } - - __imp__sub_825CB378(ctx, base); -} - // Chao::CSD::Scene::Render PPC_FUNC_IMPL(__imp__sub_828C8F60); PPC_FUNC(sub_828C8F60) @@ -501,7 +473,6 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & CSD_SCALE) != 0) { scaleX *= g_aspectRatioGameplayScale; - pivotX = g_scenePositionX; if ((modifier.Flags & CSD_ALIGN_RIGHT) != 0) offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; @@ -529,7 +500,6 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & CSD_SCALE) != 0) { scaleY *= g_aspectRatioGameplayScale; - pivotY = g_scenePositionY; if ((modifier.Flags & CSD_ALIGN_BOTTOM) != 0) offsetY += 720.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; @@ -1131,7 +1101,7 @@ PPC_FUNC_IMPL(__imp__sub_82352220); PPC_FUNC(sub_82352220) { auto pCObjBalloonIconDrawable = (Sonicteam::CObjBalloonIconDrawable*)(base + ctx.r3.u32); - auto scale = (g_aspectRatio / WIDE_ASPECT_RATIO); + auto scale = g_aspectRatio / WIDE_ASPECT_RATIO; pCObjBalloonIconDrawable->m_Vertices[0].X = -1.0f / scale; pCObjBalloonIconDrawable->m_Vertices[0].Y = 0.0f; @@ -1256,7 +1226,7 @@ PPC_FUNC(sub_8264CC90) __imp__sub_8264CC90(ctx, base); } -void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity, xxHashMap pftModifier) +void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity) { if (!pTextEntity || !pTextEntity->m_ImageVertexCount) return; @@ -1285,6 +1255,15 @@ void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity, xxHashMapm_TextureWidth; auto h = g_pTextFontPicture->m_TextureHeight; + auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation; + + if (Config::ControllerIcons == EControllerIcons::Auto) + isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation; + + auto& pftModifier = isPlayStation + ? g_pftModifierPS3 + : g_pftModifierXenon; + auto baseParams = FindFontPictureModifier(g_pftModifierXenon, variable.second); auto newParams = FindFontPictureModifier(pftModifier, variable.second); @@ -1350,8 +1329,6 @@ PPC_FUNC(sub_8262D868) { auto pTextEntity = (Sonicteam::TextEntity*)(base + ctx.r3.u32); - LOGFN_UTILITY("TextEntity: {:08X}", (uint64_t)pTextEntity); - auto offsetX = g_aspectRatioOffsetX + (640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale); auto offsetY = g_aspectRatioOffsetY + (360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale); auto scale = g_aspectRatioScale * g_aspectRatioGameplayScale; @@ -1363,16 +1340,7 @@ PPC_FUNC(sub_8262D868) __imp__sub_8262D868(ctx, base); - auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation; - - if (Config::ControllerIcons == EControllerIcons::Auto) - isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation; - - auto& pftModifier = isPlayStation - ? g_pftModifierPS3 - : g_pftModifierXenon; - - ReplaceTextVariables(pTextEntity, pftModifier); + ReplaceTextVariables(pTextEntity); } // Set CSD scale. @@ -1394,7 +1362,7 @@ PPC_FUNC(sub_8263CC40) __imp__sub_8263CC40(ctx, base); } -// -------------- MODIFIERS --------------- // +// -------------- CSD MODIFIERS --------------- // const xxHashMap g_csdModifiers = { @@ -1871,6 +1839,8 @@ std::optional FindCsdModifier(uint32_t data) return {}; } +// -------------- TEXT MODIFIERS -------------- // + const xxHashMap g_pftModifierXenon = { { HashStr("button_a"), { 0, 0, 28, 28 } }, @@ -1909,6 +1879,8 @@ TextFontPictureModifier FindFontPictureModifier(xxHashMap g_movieModifiers = { { HashStr("sound\\title_loop_GBn.wmv"), { MOVIE_CROP_NARROW } } diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index 2515d4b5d..119e6ff76 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -25,7 +25,7 @@ class AspectRatioPatches static void ComputeOffsets(); }; -// -------------- CSD --------------- // +// -------------- CSD MODIFIERS --------------- // enum CsdFlags : uint64_t { @@ -132,7 +132,7 @@ extern const xxHashMap g_csdModifiers; std::optional FindCsdModifier(uint32_t data); -// -------------- TEXT -------------- // +// -------------- TEXT MODIFIERS -------------- // struct TextFontPictureModifier { @@ -147,7 +147,7 @@ extern const xxHashMap g_pftModifierPS3; TextFontPictureModifier FindFontPictureModifier(xxHashMap pftModifier, std::string_view& name); -// ------------- MOVIE -------------- // +// ------------- MOVIE MODIFIERS -------------- // enum MovieFlags : uint32_t { From bd7386989582231feb4822a9e32a563f7c9ea006 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:06:32 +0100 Subject: [PATCH 28/52] API: radar research --- MarathonRecomp/api/Marathon.h | 1 + MarathonRecomp/api/Sonicteam/HUDPopupScreen.h | 17 +++++++++++++--- .../api/Sonicteam/RaderMapManager.h | 20 +++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 MarathonRecomp/api/Sonicteam/RaderMapManager.h diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index f1060f666..1724c011f 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -77,6 +77,7 @@ #include "Sonicteam/Player/State/SonicSpinDash.h" #include "Sonicteam/Player/State/TailsContext.h" #include "Sonicteam/Player/Zock.h" +#include "Sonicteam/RaderMapManager.h" #include "Sonicteam/SaveDataTask.h" #include "Sonicteam/SaveDataTaskXENON.h" #include "Sonicteam/SelectWindowTask.h" diff --git a/MarathonRecomp/api/Sonicteam/HUDPopupScreen.h b/MarathonRecomp/api/Sonicteam/HUDPopupScreen.h index c213bf8de..3eae97e65 100644 --- a/MarathonRecomp/api/Sonicteam/HUDPopupScreen.h +++ b/MarathonRecomp/api/Sonicteam/HUDPopupScreen.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -9,14 +10,24 @@ namespace Sonicteam class HUDPopupScreen : public SoX::Engine::Task { public: + enum HUDPopupScreenState : uint32_t + { + HUDPopupScreenState_Opening = 1, + HUDPopupScreenState_Idle, + HUDPopupScreenState_Closing + }; + xpointer m_pCsdObject; xpointer m_pMainTexture; xpointer m_pMaskTexture; - xpointer m_pTechnique; - MARATHON_INSERT_PADDING(8); + xpointer m_pTechnique; stdx::string m_SceneName; stdx::string m_SpriteName; - MARATHON_INSERT_PADDING(4); + be m_State; + MARATHON_INSERT_PADDING(1); + bool m_IsClosing; + MARATHON_INSERT_PADDING(2); + be m_ClosingTime; be m_X; be m_Y; MARATHON_INSERT_PADDING(8); diff --git a/MarathonRecomp/api/Sonicteam/RaderMapManager.h b/MarathonRecomp/api/Sonicteam/RaderMapManager.h new file mode 100644 index 000000000..557084827 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/RaderMapManager.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class RaderMapManager : public Actor + { + public: + xpointer m_pClump; + xpointer m_pFrameGP1; + xpointer m_pFrameGP2; + MARATHON_INSERT_PADDING(0x0C); + SoX::Math::Vector m_Transform; + be m_Zoom; + MARATHON_INSERT_PADDING(0x0C); + SoX::Math::Vector m_Field90; + MARATHON_INSERT_PADDING(0x10); + }; +} From 0e70a430b259780ff825411568c2c68ec76e560f Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:07:00 +0100 Subject: [PATCH 29/52] API: TextBook research --- MarathonRecomp/api/Marathon.h | 2 + MarathonRecomp/api/Sonicteam/TextBook.h | 50 ++++++++++++++++++++++ MarathonRecomp/api/Sonicteam/TextBookMgr.h | 8 ++++ MarathonRecomp/api/Sonicteam/TextCard.h | 1 - 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 MarathonRecomp/api/Sonicteam/TextBook.h create mode 100644 MarathonRecomp/api/Sonicteam/TextBookMgr.h diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index 1724c011f..692b23909 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -121,6 +121,8 @@ #include "Sonicteam/StdImageFilters/SingleTechniqueFilter.h" #include "Sonicteam/System/CreateStatic.h" #include "Sonicteam/System/Singleton.h" +#include "Sonicteam/TextBook.h" +#include "Sonicteam/TextBookMgr.h" #include "Sonicteam/TextCard.h" #include "Sonicteam/TextEntity.h" #include "Sonicteam/TextFontPicture.h" diff --git a/MarathonRecomp/api/Sonicteam/TextBook.h b/MarathonRecomp/api/Sonicteam/TextBook.h new file mode 100644 index 000000000..f0c9185ec --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/TextBook.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include + +namespace Sonicteam +{ + class TextBook : public SoX::IResource2 + { + public: + struct Entry + { + xpointer Field00; + xpointer Previous; + xpointer Next; + stdx::string Name; + boost::shared_ptr spTextCard; + MARATHON_INSERT_PADDING(1); + bool Field31; + MARATHON_INSERT_PADDING(2); + }; + + MARATHON_INSERT_PADDING(4); + boost::shared_ptr m_spResource; + stdx::string m_Name; + MARATHON_INSERT_PADDING(4); + xpointer m_pRootEntry; + be m_EntryCount; + + const char* FindName(const uint16_t* pText) const + { + auto pCardTable = m_spResource.get() + 0x2C; + + for (size_t i = 0; i < m_EntryCount; i++) + { + auto pCard = pCardTable + (i * 0x0C); + auto pCardName = *(xpointer*)pCard; + auto pCardText = *(xpointer*)(pCard + 4); + + if (!strcmpU16(pCardText, pText)) + continue; + + return pCardName; + } + + return nullptr; + } + }; +} diff --git a/MarathonRecomp/api/Sonicteam/TextBookMgr.h b/MarathonRecomp/api/Sonicteam/TextBookMgr.h new file mode 100644 index 000000000..3c269d094 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/TextBookMgr.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class TextBookMgr : public SoX::IResourceMgr, public System::Singleton> {}; +} diff --git a/MarathonRecomp/api/Sonicteam/TextCard.h b/MarathonRecomp/api/Sonicteam/TextCard.h index 4f788b753..fbee3f461 100644 --- a/MarathonRecomp/api/Sonicteam/TextCard.h +++ b/MarathonRecomp/api/Sonicteam/TextCard.h @@ -10,6 +10,5 @@ namespace Sonicteam boost::shared_ptr m_spResource; xpointer m_pText; xpointer m_pVariables; - MARATHON_INSERT_PADDING(0x30); }; } From ad6a9f8dd120bf50577f6ec930651a4eb4678829 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:08:37 +0100 Subject: [PATCH 30/52] Align mission timer to the top of the screen --- MarathonRecomp/api/Marathon.h | 1 + MarathonRecomp/api/Sonicteam/HUDLimitTime.h | 19 +++++++++++++++++++ .../patches/aspect_ratio_patches.cpp | 13 +++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 MarathonRecomp/api/Sonicteam/HUDLimitTime.h diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index 692b23909..e65a26c48 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -34,6 +34,7 @@ #include "Sonicteam/Globals.h" #include "Sonicteam/HUDButtonWindow.h" #include "Sonicteam/HUDCALLBACK.h" +#include "Sonicteam/HUDLimitTime.h" #include "Sonicteam/HUDLoading.h" #include "Sonicteam/HUDMainDisplay.h" #include "Sonicteam/HUDPopupScreen.h" diff --git a/MarathonRecomp/api/Sonicteam/HUDLimitTime.h b/MarathonRecomp/api/Sonicteam/HUDLimitTime.h new file mode 100644 index 000000000..ff353620a --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HUDLimitTime.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class HUDLimitTime : public SoX::RefCountObject, public SoX::Engine::Task + { + public: + static constexpr float ms_AlertThreshold = 10.0f; + + xpointer m_pCsdObject; + be m_X; + be m_Y; + MARATHON_INSERT_PADDING(0x30); + be m_Time; + bool m_IsAboveAlertThreshold; + }; +} diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 379f20f21..888c5e0b9 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1082,6 +1082,19 @@ PPC_FUNC(sub_824D32C8) __imp__sub_824D32C8(ctx, base); } +// Sonicteam::HUDLimitTime::ProcessMessage +PPC_FUNC_IMPL(__imp__sub_824D6E50); +PPC_FUNC(sub_824D6E50) +{ + auto pHUDLimitTime = (Sonicteam::HUDLimitTime*)(base + ctx.r3.u32); + + pHUDLimitTime->m_Y = 64.0f - (g_aspectRatioOffsetY / g_aspectRatioScale); + + GuestToHostFunction(sub_824D6D38, pHUDLimitTime, (double)pHUDLimitTime->m_X, (double)pHUDLimitTime->m_Y); + + __imp__sub_824D6E50(ctx, base); +} + // Sonicteam::HUDRaderMap::Update PPC_FUNC_IMPL(__imp__sub_824F1538); PPC_FUNC(sub_824F1538) From 373d009108e684821786a7090d3e50cad6a68953 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:15:14 +0100 Subject: [PATCH 31/52] Move Unicode string functions to Marathon.inl --- MarathonRecomp/api/CSD/Platform/csdTexList.h | 12 ++--- .../api/CSD/Platform/csdTexList.inl | 7 --- MarathonRecomp/api/Marathon.inl | 44 +++++++++++++++++++ MarathonRecomp/framework.h | 44 ------------------- 4 files changed, 48 insertions(+), 59 deletions(-) delete mode 100644 MarathonRecomp/api/CSD/Platform/csdTexList.inl diff --git a/MarathonRecomp/api/CSD/Platform/csdTexList.h b/MarathonRecomp/api/CSD/Platform/csdTexList.h index 88fb7c81f..c24cc7702 100644 --- a/MarathonRecomp/api/CSD/Platform/csdTexList.h +++ b/MarathonRecomp/api/CSD/Platform/csdTexList.h @@ -1,7 +1,7 @@ #pragma once -#include "CSD/Manager/csdmBase.h" -#include "CSD/Manager/csdmRCPtr.h" +#include +#include namespace Chao::CSD { @@ -10,14 +10,10 @@ namespace Chao::CSD public: struct Vftable { - be m_fpDtor; + be fpDestroy; }; xpointer m_pVftable; - RCPtr m_rcData; - - ~CTexList(); + RCPtr m_pRCData; }; } - -#include "CSD/Platform/csdTexList.inl" diff --git a/MarathonRecomp/api/CSD/Platform/csdTexList.inl b/MarathonRecomp/api/CSD/Platform/csdTexList.inl deleted file mode 100644 index 148350263..000000000 --- a/MarathonRecomp/api/CSD/Platform/csdTexList.inl +++ /dev/null @@ -1,7 +0,0 @@ -namespace Chao::CSD -{ - inline CTexList::~CTexList() - { - GuestToHostFunction(m_pVftable->m_fpDtor, this); - } -} diff --git a/MarathonRecomp/api/Marathon.inl b/MarathonRecomp/api/Marathon.inl index b56e04b03..6d65ea1a3 100644 --- a/MarathonRecomp/api/Marathon.inl +++ b/MarathonRecomp/api/Marathon.inl @@ -91,3 +91,47 @@ inline std::vector> MapTextVariabl return result; } + +inline size_t strlenU16(const uint16_t* str) +{ + size_t result = 0; + uint16_t c = 0xFFFF; + + while (c != 0) + { + c = str[result]; + result++; + } + + return result; +} + +inline bool strcmpU16(const uint16_t* a, const uint16_t* b) +{ + for (size_t i = 0; i < strlenU16(a); i += 2) + { + if (a[i] != 0 && b[i] == 0) + return false; + + if (a[i] == 0 && b[i] != 0) + return false; + + if (a[i] != b[i]) + return false; + } + + return true; +} + +inline void printU16(const uint16_t* str) +{ + for (size_t i = 0; i < strlenU16(str); i++) + { + auto c0 = str[i] >> 8; + auto c1 = str[i] & 0xFF; + + printf("%c%c", c0, c1); + } + + printf("\n"); +} diff --git a/MarathonRecomp/framework.h b/MarathonRecomp/framework.h index 143cb0afb..b6a4d2e23 100644 --- a/MarathonRecomp/framework.h +++ b/MarathonRecomp/framework.h @@ -84,50 +84,6 @@ inline bool strcmpIgnoreCase(const char* a, const char* b) return true; } -inline size_t strlenU16(const uint16_t* str) -{ - size_t result = 0; - uint16_t c = 0xFFFF; - - while (c != 0) - { - c = str[result]; - result++; - } - - return result; -} - -inline bool strcmpU16(const uint16_t* a, const uint16_t* b) -{ - for (size_t i = 0; i < strlenU16(a); i += 2) - { - if (a[i] != 0 && b[i] == 0) - return false; - - if (a[i] == 0 && b[i] != 0) - return false; - - if (a[i] != b[i]) - return false; - } - - return true; -} - -inline void printU16(const uint16_t* str) -{ - for (size_t i = 0; i < strlenU16(str); i++) - { - auto c0 = str[i] >> 8; - auto c1 = str[i] & 0xFF; - - printf("%c%c", c0, c1); - } - - printf("\n"); -} - #ifndef __cpp_lib_atomic_ref // Polyfill for std::atomic_ref namespace std { From 4607d60d4f8095a67b9b93c5188a29591c74791d Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 6 Sep 2025 23:13:18 +0100 Subject: [PATCH 32/52] Implement text entity modifiers Co-Authored-By: Skyth (Asilkan) <19259897+blueskythlikesclouds@users.noreply.github.com> --- MarathonRecomp/api/Marathon.h | 1 + MarathonRecomp/api/Sonicteam/CsdLink.h | 13 +- MarathonRecomp/api/Sonicteam/HUDMainDisplay.h | 10 ++ .../api/Sonicteam/MainDisplayTask.h | 12 ++ MarathonRecomp/api/Sonicteam/TextEntity.h | 47 ++----- .../patches/aspect_ratio_patches.cpp | 125 ++++++++++++++++-- MarathonRecompLib/config/Marathon.toml | 10 ++ 7 files changed, 167 insertions(+), 51 deletions(-) create mode 100644 MarathonRecomp/api/Sonicteam/MainDisplayTask.h diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index e65a26c48..9ddc5b1ef 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -41,6 +41,7 @@ #include "Sonicteam/HUDRaderMap.h" #include "Sonicteam/HudTextParts.h" #include "Sonicteam/ImageFilter.h" +#include "Sonicteam/MainDisplayTask.h" #include "Sonicteam/MainMenuTask.h" #include "Sonicteam/MovieObject.h" #include "Sonicteam/MovieObjectWmv.h" diff --git a/MarathonRecomp/api/Sonicteam/CsdLink.h b/MarathonRecomp/api/Sonicteam/CsdLink.h index 8bc739976..c55a759c8 100644 --- a/MarathonRecomp/api/Sonicteam/CsdLink.h +++ b/MarathonRecomp/api/Sonicteam/CsdLink.h @@ -7,10 +7,21 @@ namespace Sonicteam class CsdLink { public: - xpointer m_pVftable; + struct Vftable + { + MARATHON_INSERT_PADDING(8); + be fpUpdate; + }; + + xpointer m_pVftable; xpointer m_pNext; xpointer m_pPrevious; be m_Priority; MARATHON_INSERT_PADDING(8); + + int Update(double deltaTime = 0.0) + { + return GuestToHostFunction(m_pVftable->fpUpdate.get(), this, deltaTime); + } }; } diff --git a/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h b/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h index dab9d73d6..08d135b1e 100644 --- a/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h +++ b/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h @@ -9,5 +9,15 @@ namespace Sonicteam public: MARATHON_INSERT_PADDING(0x24); be m_Character; + MARATHON_INSERT_PADDING(0x108); + boost::shared_ptr m_Field184; + boost::shared_ptr m_TrickPointText; + boost::shared_ptr m_Field194; + MARATHON_INSERT_PADDING(0x10); + boost::shared_ptr m_SavePointTimeText; }; + + MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_Field184, 0x184); + MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_TrickPointText, 0x18C); + MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_Field194, 0x194); } diff --git a/MarathonRecomp/api/Sonicteam/MainDisplayTask.h b/MarathonRecomp/api/Sonicteam/MainDisplayTask.h new file mode 100644 index 000000000..7dfb018a9 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/MainDisplayTask.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class MainDisplayTask : public SoX::Engine::Task + { + public: + xpointer m_pHUDMainDisplay; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/TextEntity.h b/MarathonRecomp/api/Sonicteam/TextEntity.h index 910b2780a..810a204ed 100644 --- a/MarathonRecomp/api/Sonicteam/TextEntity.h +++ b/MarathonRecomp/api/Sonicteam/TextEntity.h @@ -35,13 +35,18 @@ namespace Sonicteam MARATHON_INSERT_PADDING(4); xpointer m_pVariables; stdx::wstring m_Field80; - stdx::wstring m_Field9C; + MARATHON_INSERT_PADDING(0x14); + be m_Width; + be m_FieldB4; be m_FieldB8; be m_FieldBC; MARATHON_INSERT_PADDING(0x0C); be m_ScaleX; be m_ScaleY; - MARATHON_INSERT_PADDING(0x0C); + MARATHON_INSERT_PADDING(8); + bool m_FieldDC; + bool m_FieldDD; + MARATHON_INSERT_PADDING(2); xpointer m_FieldE0; // Only present when there's character vertices. xpointer m_FieldE4; // Only present when there's image vertices. xpointer m_pCharacterVertices; // BL/TL/TR BL/TR/BR (two triangles per character) @@ -53,45 +58,9 @@ namespace Sonicteam be m_Field100; be m_Field104; MARATHON_INSERT_PADDING(8); - - std::pair Measure() const - { - float w{}; - float h{}; - - if (m_Text.size() <= 0) - return { w, h }; - - if (m_FieldE0) - { - for (int i = 0; i < m_CharacterVertexCount; i += 6) - { - auto& tl = m_pCharacterVertices[i + 0]; - auto& bl = m_pCharacterVertices[i + 1]; - auto& tr = m_pCharacterVertices[i + 2]; - - w += tr.X - tl.X; - h = std::max(h, bl.Y - tl.Y); - } - } - - if (m_FieldE4) - { - for (int i = 0; i < m_ImageVertexCount; i += 6) - { - auto& tl = m_pImageVertices[i + 0]; - auto& bl = m_pImageVertices[i + 1]; - auto& tr = m_pImageVertices[i + 2]; - - w += tr.X - tl.X; - h = std::max(h, bl.Y - tl.Y); - } - } - - return { w, h }; - } }; + MARATHON_ASSERT_OFFSETOF(TextEntity, m_Width, 0xB0); MARATHON_ASSERT_OFFSETOF(TextEntity, m_FieldE0, 0xE0); MARATHON_ASSERT_OFFSETOF(TextEntity, m_FieldE4, 0xE4); MARATHON_ASSERT_OFFSETOF(TextEntity, m_pCharacterVertices, 0xE8); diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 888c5e0b9..9ec94a7a4 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -403,8 +403,17 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if (Config::UIAlignmentMode == EUIAlignmentMode::Centre || BlackBar::g_isPillarbox) { - if ((modifier.Flags & CSD_CHEVRON) == 0 && g_aspectRatio >= WIDE_ASPECT_RATIO) - modifier.Flags &= ~(CSD_ALIGN_LEFT | CSD_ALIGN_RIGHT); + if ((modifier.Flags & CSD_CHEVRON) == 0) + { + if (g_aspectRatio > WIDE_ASPECT_RATIO) + { + modifier.Flags &= ~(CSD_ALIGN_LEFT | CSD_ALIGN_RIGHT); + } + else if (g_aspectRatio < WIDE_ASPECT_RATIO) + { + modifier.Flags &= ~(CSD_ALIGN_TOP | CSD_ALIGN_BOTTOM); + } + } } uint32_t size = ctx.r5.u32 * stride; @@ -1019,6 +1028,16 @@ PPC_FUNC(sub_82631718) Draw(ctx, base, __imp__sub_82631718, 0x0C); } +// Set CSD scale. +PPC_FUNC_IMPL(__imp__sub_828C78E0); +PPC_FUNC(sub_828C78E0) +{ + ctx.f1.f64 = 1280.0; + ctx.f2.f64 = 720.0; + + __imp__sub_828C78E0(ctx, base); +} + // Sonicteam::MainMenuTask::Update PPC_FUNC_IMPL(__imp__sub_824FFCF8); PPC_FUNC(sub_824FFCF8) @@ -1336,15 +1355,75 @@ void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity) } } +void TextEntityAlloc(PPCRegister& r3) +{ + r3.u32 += sizeof(uint64_t); +} + +// Sonicteam::TextEntity::TextEntity +PPC_FUNC_IMPL(__imp__sub_8262DC60); +PPC_FUNC(sub_8262DC60) +{ + auto pTextModifier = (uint64_t*)(base + ctx.r3.u32 + sizeof(Sonicteam::TextEntity)); + + // Default text modifier to scale. + *pTextModifier = CSD_SCALE; + + __imp__sub_8262DC60(ctx, base); +} + // Sonicteam::TextEntity::Draw PPC_FUNC_IMPL(__imp__sub_8262D868); PPC_FUNC(sub_8262D868) { auto pTextEntity = (Sonicteam::TextEntity*)(base + ctx.r3.u32); + auto textModifier = *(uint64_t*)(base + ctx.r3.u32 + sizeof(Sonicteam::TextEntity)); + + auto x = pTextEntity->m_X; + auto y = pTextEntity->m_Y; + auto scaleX = pTextEntity->m_ScaleX; + auto scaleY = pTextEntity->m_ScaleY; + + auto offsetX = 0.0f; + auto offsetY = 0.0f; + auto scale = g_aspectRatioScale; + + if (Config::UIAlignmentMode == EUIAlignmentMode::Centre || BlackBar::g_isVisible) + { + if (g_aspectRatio > WIDE_ASPECT_RATIO) + { + textModifier &= ~(CSD_ALIGN_LEFT | CSD_ALIGN_RIGHT); + } + else if (g_aspectRatio < WIDE_ASPECT_RATIO) + { + textModifier &= ~(CSD_ALIGN_TOP | CSD_ALIGN_BOTTOM); + } + } - auto offsetX = g_aspectRatioOffsetX + (640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale); - auto offsetY = g_aspectRatioOffsetY + (360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale); - auto scale = g_aspectRatioScale * g_aspectRatioGameplayScale; + if ((textModifier & CSD_ALIGN_RIGHT) != 0) + offsetX = g_aspectRatioOffsetX * 2.0f; + else if ((textModifier & CSD_ALIGN_LEFT) == 0) + offsetX = g_aspectRatioOffsetX; + + if ((textModifier & CSD_ALIGN_BOTTOM) != 0) + offsetY = g_aspectRatioOffsetY * 2.0f; + else if ((textModifier & CSD_ALIGN_TOP) == 0) + offsetY = g_aspectRatioOffsetY; + + if ((textModifier & CSD_SCALE) != 0) + { + scale *= g_aspectRatioGameplayScale; + + if ((textModifier & CSD_ALIGN_RIGHT) != 0) + offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; + else if ((textModifier & CSD_ALIGN_LEFT) == 0) + offsetX += 640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; + + if ((textModifier & CSD_ALIGN_BOTTOM) != 0) + offsetY += 720.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; + else if ((textModifier & CSD_ALIGN_TOP) == 0) + offsetY += 360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; + } pTextEntity->m_X = offsetX + pTextEntity->m_X * scale; pTextEntity->m_Y = offsetY + pTextEntity->m_Y * scale; @@ -1353,17 +1432,41 @@ PPC_FUNC(sub_8262D868) __imp__sub_8262D868(ctx, base); + pTextEntity->m_X = x; + pTextEntity->m_Y = y; + pTextEntity->m_ScaleX = scaleX; + pTextEntity->m_ScaleY = scaleY; + ReplaceTextVariables(pTextEntity); } -// Set CSD scale. -PPC_FUNC_IMPL(__imp__sub_828C78E0); -PPC_FUNC(sub_828C78E0) +void SetTextEntityModifier(Sonicteam::TextEntity* pTextEntity, uint64_t flags) { - ctx.f1.f64 = 1280.0; - ctx.f2.f64 = 720.0; + if (!pTextEntity) + return; + + auto pTextModifier = (uint64_t*)(reinterpret_cast(pTextEntity) + sizeof(Sonicteam::TextEntity)); + + *pTextModifier = flags; + + pTextEntity->m_FieldDD = true; + pTextEntity->Update(); +} + +// Sonicteam::HUDMainDisplay::Update +PPC_FUNC_IMPL(__imp__sub_824DCF40); +PPC_FUNC(sub_824DCF40) +{ + auto pHUDMainDisplay = (Sonicteam::HUDMainDisplay*)(base + ctx.r3.u32); + + SetTextEntityModifier(pHUDMainDisplay->m_Field184.get(), CSD_ALIGN_RIGHT | CSD_SCALE); + SetTextEntityModifier(pHUDMainDisplay->m_TrickPointText.get(), CSD_ALIGN_RIGHT | CSD_SCALE); + SetTextEntityModifier(pHUDMainDisplay->m_Field194.get(), CSD_ALIGN_RIGHT | CSD_SCALE); + SetTextEntityModifier(pHUDMainDisplay->m_SavePointTimeText.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); + + __imp__sub_824DCF40(ctx, base); +} - __imp__sub_828C78E0(ctx, base); } // Sonicteam::TextFontPicture::LoadResource diff --git a/MarathonRecompLib/config/Marathon.toml b/MarathonRecompLib/config/Marathon.toml index 84aa785cf..f58264bbe 100644 --- a/MarathonRecompLib/config/Marathon.toml +++ b/MarathonRecompLib/config/Marathon.toml @@ -364,3 +364,13 @@ jump_address = 0x8258E308 name = "NOP" address = 0x8258E600 jump_address = 0x8258E604 + +[[midasm_hook]] +name = "TextEntityAlloc" +address = 0x825EDB70 +registers = ["r3"] + +[[midasm_hook]] +name = "TextEntityAlloc" +address = 0x825EDCEC +registers = ["r3"] From 03b417dc00563ac73f014a2fe954500bc3fddb7d Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 6 Sep 2025 23:19:48 +0100 Subject: [PATCH 33/52] Fix message window vertical alignment --- MarathonRecomp/ui/message_window.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MarathonRecomp/ui/message_window.cpp b/MarathonRecomp/ui/message_window.cpp index 6fa133913..a4a13aa3f 100644 --- a/MarathonRecomp/ui/message_window.cpp +++ b/MarathonRecomp/ui/message_window.cpp @@ -387,7 +387,7 @@ void MessageWindow::Draw() } } - ImVec2 msgMin = { g_aspectRatioOffsetX + Scale(96), Scale(96) }; + ImVec2 msgMin = { g_aspectRatioOffsetX + Scale(96), g_aspectRatioOffsetY + Scale(96) }; ImVec2 msgMax = { msgMin.x + Scale(1088), msgMin.y + Scale(384) }; ImVec2 msgCentre = { (msgMin.x / 2) + (msgMax.x / 2), (msgMin.y / 2) + (msgMax.y / 2) }; @@ -408,7 +408,7 @@ void MessageWindow::Draw() drawList->PopClipRect(); - ImVec2 selMin = { msgMin.x, msgMax.y + (msgMin.y / 2) }; + ImVec2 selMin = { msgMin.x, msgMax.y + ((msgMin.y - g_aspectRatioOffsetY) / 2) }; ImVec2 selMax = { msgMax.x, selMin.y + Scale(128) }; DrawContainer(selMin, selMax); From c53ff648861b67ce2c5e7c5d7bde47ec01c32e79 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 6 Sep 2025 23:21:36 +0100 Subject: [PATCH 34/52] Support letterboxing in ImGui black bar --- .../patches/aspect_ratio_patches.cpp | 50 ++++++++++--------- MarathonRecomp/patches/aspect_ratio_patches.h | 46 +++++++++-------- MarathonRecomp/ui/black_bar.cpp | 33 ++++++------ MarathonRecomp/ui/black_bar.h | 2 +- 4 files changed, 70 insertions(+), 61 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 9ec94a7a4..6112f44a6 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -90,7 +90,8 @@ static class LoadingPillarboxEvent : public HookEvent public: void Update(float deltaTime) override { - BlackBar::g_isPillarbox = true; + if (g_aspectRatio > WIDE_ASPECT_RATIO) + BlackBar::g_isVisible = true; } } g_loadingPillarboxEvent{}; @@ -395,13 +396,13 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) return; } - if ((modifier.Flags & CSD_PILLARBOX) != 0) - BlackBar::g_isPillarbox = true; + if ((modifier.Flags & CSD_BLACK_BAR) != 0) + BlackBar::g_isVisible = true; if ((modifier.Flags & CSD_PROHIBIT_BLACK_BAR) != 0) - BlackBar::g_isPillarbox = false; + BlackBar::g_isVisible = false; - if (Config::UIAlignmentMode == EUIAlignmentMode::Centre || BlackBar::g_isPillarbox) + if (Config::UIAlignmentMode == EUIAlignmentMode::Centre || BlackBar::g_isVisible) { if ((modifier.Flags & CSD_CHEVRON) == 0) { @@ -436,14 +437,14 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) return reinterpret_cast(stack + (index * stride)); }; - float offsetX = 0.0f; - float offsetY = 0.0f; - float pivotX = 0.0f; - float pivotY = 0.0f; - float scaleX = 1.0f; - float scaleY = 1.0f; + auto offsetX = 0.0f; + auto offsetY = 0.0f; + auto pivotX = 0.0f; + auto pivotY = 0.0f; + auto scaleX = 1.0f; + auto scaleY = 1.0f; - bool needsStretch = g_aspectRatio >= WIDE_ASPECT_RATIO; + auto needsStretch = g_aspectRatio >= WIDE_ASPECT_RATIO; if (needsStretch && (modifier.Flags & CSD_STRETCH_HORIZONTAL) != 0) { @@ -570,12 +571,12 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) offsetX = Video::s_viewportWidth - (Video::s_viewportWidth - offsetX) * (1280.0f - corner) / (1280.0f - offsetScaleModifier.CornerMax); } - float firstX = 0.0f; - float firstY = 0.0f; - float lastX = 0.0f; - float lastY = 0.0f; - float width = 0.0f; - float height = 0.0f; + auto firstX = 0.0f; + auto firstY = 0.0f; + auto lastX = 0.0f; + auto lastY = 0.0f; + auto width = 0.0f; + auto height = 0.0f; for (size_t i = 0; i < ctx.r5.u32; i++) { @@ -656,13 +657,16 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) auto isRepeatLeft = (modifier.Flags & CSD_REPEAT_LEFT) != 0; auto isRepeatRight = (modifier.Flags & CSD_REPEAT_RIGHT) != 0; + auto isRepeatUp = (modifier.Flags & CSD_REPEAT_UP) != 0; + auto isRepeatDown = (modifier.Flags & CSD_REPEAT_DOWN) != 0; - if (isRepeatLeft || isRepeatRight) + if (isRepeatLeft || isRepeatRight || isRepeatUp || isRepeatDown) { auto r3 = ctx.r3; auto r5 = ctx.r5; auto isFlipHorz = (modifier.Flags & CSD_REPEAT_FLIP_HORIZONTAL) != 0; + auto isFlipVert = (modifier.Flags & CSD_REPEAT_FLIP_VERTICAL) != 0; auto applyRepeatModifiers = [&]() { @@ -674,7 +678,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) getVertex(3)->X = getVertex(3)->X - width; } - if ((modifier.Flags & CSD_REPEAT_FLIP_VERTICAL) != 0) + if (isFlipVert) { getVertex(0)->Y = getVertex(0)->Y + height; getVertex(1)->Y = getVertex(1)->Y - height; @@ -1078,7 +1082,7 @@ PPC_FUNC_IMPL(__imp__sub_8264AC48); PPC_FUNC(sub_8264AC48) { if (Config::CutsceneAspectRatio == ECutsceneAspectRatio::Original) - BlackBar::g_isPillarbox = true; + BlackBar::g_isVisible = true; __imp__sub_8264AC48(ctx, base); } @@ -1087,7 +1091,7 @@ PPC_FUNC(sub_8264AC48) PPC_FUNC_IMPL(__imp__sub_824F4D80); PPC_FUNC(sub_824F4D80) { - BlackBar::g_isPillarbox = true; + BlackBar::g_isVisible = true; __imp__sub_824F4D80(ctx, base); } @@ -1096,7 +1100,7 @@ PPC_FUNC(sub_824F4D80) PPC_FUNC_IMPL(__imp__sub_824D32C8); PPC_FUNC(sub_824D32C8) { - BlackBar::g_isPillarbox = true; + BlackBar::g_isVisible = true; __imp__sub_824D32C8(ctx, base); } diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index 119e6ff76..cf6e70be3 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -61,40 +61,42 @@ enum CsdFlags : uint64_t CSD_REPEAT_LEFT = MAKE_BITFLAG64(14), CSD_REPEAT_RIGHT = MAKE_BITFLAG64(15), - CSD_REPEAT_FLIP_HORIZONTAL = MAKE_BITFLAG64(16), - CSD_REPEAT_FLIP_VERTICAL = MAKE_BITFLAG64(17), - CSD_REPEAT_EXTEND = MAKE_BITFLAG64(18), + CSD_REPEAT_UP = MAKE_BITFLAG64(16), + CSD_REPEAT_DOWN = MAKE_BITFLAG64(17), + CSD_REPEAT_FLIP_HORIZONTAL = MAKE_BITFLAG64(18), + CSD_REPEAT_FLIP_VERTICAL = MAKE_BITFLAG64(19), + CSD_REPEAT_EXTEND = MAKE_BITFLAG64(20), - CSD_UV_MODIFIER = MAKE_BITFLAG64(19), - CSD_COLOUR_MODIFIER = MAKE_BITFLAG64(20), - CSD_REPEAT_UV_MODIFIER = MAKE_BITFLAG64(21), - CSD_REPEAT_COLOUR_MODIFIER = MAKE_BITFLAG64(22), + CSD_UV_MODIFIER = MAKE_BITFLAG64(21), + CSD_COLOUR_MODIFIER = MAKE_BITFLAG64(22), + CSD_REPEAT_UV_MODIFIER = MAKE_BITFLAG64(23), + CSD_REPEAT_COLOUR_MODIFIER = MAKE_BITFLAG64(24), - CSD_PILLARBOX = MAKE_BITFLAG64(23), - CSD_PROHIBIT_BLACK_BAR = MAKE_BITFLAG64(24), + CSD_BLACK_BAR = MAKE_BITFLAG64(25), + CSD_PROHIBIT_BLACK_BAR = MAKE_BITFLAG64(26), - CSD_UNSTRETCH_HORIZONTAL = MAKE_BITFLAG64(25), + CSD_UNSTRETCH_HORIZONTAL = MAKE_BITFLAG64(27), - CSD_CORNER_EXTRACT = MAKE_BITFLAG64(26), + CSD_CORNER_EXTRACT = MAKE_BITFLAG64(28), - CSD_RADARMAP = MAKE_BITFLAG64(27), + CSD_RADARMAP = MAKE_BITFLAG64(29), - CSD_POD_BASE = MAKE_BITFLAG64(28), - CSD_POD_CLONE = MAKE_BITFLAG64(29), + CSD_POD_BASE = MAKE_BITFLAG64(30), + CSD_POD_CLONE = MAKE_BITFLAG64(31), - CSD_MULTIPLAYER = MAKE_BITFLAG64(30), - CSD_MULTIPLAYER_CENTER = MAKE_BITFLAG64(31), + CSD_MULTIPLAYER = MAKE_BITFLAG64(32), + CSD_MULTIPLAYER_CENTER = MAKE_BITFLAG64(33), - CSD_CHEVRON = MAKE_BITFLAG64(32), + CSD_CHEVRON = MAKE_BITFLAG64(34), - CSD_MODIFIER_ULTRAWIDE_ONLY = MAKE_BITFLAG64(33), - CSD_MODIFIER_NARROW_ONLY = MAKE_BITFLAG64(34), + CSD_MODIFIER_ULTRAWIDE_ONLY = MAKE_BITFLAG64(35), + CSD_MODIFIER_NARROW_ONLY = MAKE_BITFLAG64(36), - CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(35), + CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(37), - CSD_MOVIE = MAKE_BITFLAG64(36), + CSD_MOVIE = MAKE_BITFLAG64(38), - CSD_CRI_LOGO = MAKE_BITFLAG64(37) + CSD_CRI_LOGO = MAKE_BITFLAG64(39) }; struct CsdUVs diff --git a/MarathonRecomp/ui/black_bar.cpp b/MarathonRecomp/ui/black_bar.cpp index a7dd3bd58..549ad6d18 100644 --- a/MarathonRecomp/ui/black_bar.cpp +++ b/MarathonRecomp/ui/black_bar.cpp @@ -4,24 +4,27 @@ void BlackBar::Draw() { - if (!g_isPillarbox) - return; + if (g_isVisible) + { + auto drawList = ImGui::GetBackgroundDrawList(); + auto& res = ImGui::GetIO().DisplaySize; - auto drawList = ImGui::GetBackgroundDrawList(); - auto& res = ImGui::GetIO().DisplaySize; + if (g_aspectRatio > WIDE_ASPECT_RATIO) + { + auto width = (res.x - (res.y * 16.0f / 9.0f)) / 2.0f; - float width = (res.x - (res.y * 16.0f / 9.0f)) / 2.0f; + drawList->AddRectFilled({ 0.0f, 0.0f }, { width, res.y }, IM_COL32_BLACK); + drawList->AddRectFilled({ res.x - width, 0.0f }, res, IM_COL32_BLACK); + } + else if (WIDE_ASPECT_RATIO > g_aspectRatio) + { + auto height = (res.y - (res.x * 9.0f / 16.0f)) / 2.0f; - drawList->AddRectFilled( - { 0.0f, 0.0f }, - { width, res.y }, - IM_COL32_BLACK); - - drawList->AddRectFilled( - { res.x - width, 0.0f }, - res, - IM_COL32_BLACK); + drawList->AddRectFilled({ 0.0f, 0.0f }, { res.x, height }, IM_COL32_BLACK); + drawList->AddRectFilled({ 0.0f, res.y - height }, res, IM_COL32_BLACK); + } + } if (!App::s_isLoading) - g_isPillarbox = false; + g_isVisible = false; } diff --git a/MarathonRecomp/ui/black_bar.h b/MarathonRecomp/ui/black_bar.h index a5a3ebe00..655424f3b 100644 --- a/MarathonRecomp/ui/black_bar.h +++ b/MarathonRecomp/ui/black_bar.h @@ -3,7 +3,7 @@ class BlackBar { public: - static inline bool g_isPillarbox; + static inline bool g_isVisible; static void Draw(); }; From 32e1f9544c9e749b0dc8e909bf8a694e945013f8 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 6 Sep 2025 23:23:20 +0100 Subject: [PATCH 35/52] Remove main menu vertical alignments This will only scale the main menu instead, allowing narrow aspect ratios to look a bit more proper. Needs work to extend the metal plates past 16:9. --- .../patches/aspect_ratio_patches.cpp | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 6112f44a6..2a07aad56 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1499,23 +1499,28 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/audio/audio/audio_cursor2"), { CSD_SCALE } }, // background - { HashStr("sprite/background/background/mainmenu_back"), { CSD_STRETCH | CSD_PROHIBIT_BLACK_BAR } }, + { HashStr("sprite/background/background/mainmenu_back"), { CSD_STRETCH_HORIZONTAL | CSD_PROHIBIT_BLACK_BAR } }, + { HashStr("sprite/background/background/tag"), { CSD_SCALE } }, + { HashStr("sprite/background/background/movie"), { CSD_SCALE } }, { HashStr("sprite/background/background/main_menu"), { CSD_SCENE_DISABLE_MOTION | CSD_MODIFIER_ULTRAWIDE_ONLY } }, - { HashStr("sprite/background/background/main_menu/main_menu1/yaji1"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu1/yaji2"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu1/yaji3"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu1/yaji4"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu1/yaji5"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu1/yaji6"), { CSD_CHEVRON | CSD_ALIGN_RIGHT | CSD_REPEAT_LEFT } }, - { HashStr("sprite/background/background/main_menu/main_menu2/yaji7"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu2/yaji8"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu2/yaji9"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu2/yaji10"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu2/yaji11"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu2/yaji12"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu2/yaji13"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu2/yaji14"), { CSD_CHEVRON | CSD_SKIP } }, - { HashStr("sprite/background/background/main_menu/main_menu2/yaji15"), { CSD_CHEVRON | CSD_ALIGN_LEFT | CSD_REPEAT_RIGHT } }, + { HashStr("sprite/background/background/main_menu/main_menu1/yaji1"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu1/yaji2"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu1/yaji3"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu1/yaji4"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu1/yaji5"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu1/yaji6"), { CSD_CHEVRON | CSD_SCALE | CSD_ALIGN_RIGHT | CSD_REPEAT_LEFT } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji7"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji8"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji9"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji10"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji11"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji12"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji13"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji14"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, + { HashStr("sprite/background/background/main_menu/main_menu2/yaji15"), { CSD_CHEVRON | CSD_SCALE | CSD_ALIGN_LEFT | CSD_REPEAT_RIGHT } }, + { HashStr("sprite/background/background/cd"), { CSD_SCALE } }, + { HashStr("sprite/background/background/battle"), { CSD_SCALE } }, + { HashStr("sprite/background/background/fileselect"), { CSD_SCALE } }, // battledisplay_1p { HashStr("sprite/battledisplay_1p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, @@ -1592,8 +1597,8 @@ const xxHashMap g_csdModifiers = { HashStr("event/e1141/sonic_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, // button_window - { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0001"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, - { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0002"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0001"), { CSD_SCALE } }, + { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0002"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, // cri_logo { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/bg"), { CSD_STRETCH } }, @@ -1683,22 +1688,22 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/main_menu/stage_cursor"), { CSD_SCALE } }, { HashStr("sprite/main_menu/mission_cursor"), { CSD_SCALE } }, { HashStr("sprite/main_menu/stage_cursor2"), { CSD_SCALE } }, - { HashStr("sprite/main_menu/text"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/cover_l"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.05f, 0, -0.05f, 0 } } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/cover_c"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, - { HashStr("sprite/main_menu/text_cover/Null_0290/vocer_r"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, 0.0015f, 0, 0.0015f, 0 }, {}, { -0.05f, 0, -0.05f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/text"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/cover_l"), { CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.05f, 0, -0.05f, 0 } } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/cover_c"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/text_cover/Null_0290/vocer_r"), { CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, 0.0015f, 0, 0.0015f, 0 }, {}, { -0.05f, 0, -0.05f, 0, 0, 0, 0, 0 } } }, { HashStr("sprite/main_menu/mission_text/rank"), { CSD_SCALE } }, { HashStr("sprite/main_menu/mission_text/item_icon"), { CSD_SCALE } }, { HashStr("sprite/main_menu/mission_text/m_ring_icon"), { CSD_SCALE } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0.1f, 0, 0.1f, 0, -0.1f, 0, -0.1f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_EXTEND_RIGHT } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966"), { CSD_ALIGN_TOP | CSD_SCALE } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966/Cast_0967"), { CSD_ALIGN_TOP | CSD_SCALE } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.8f, 0, -0.8f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { CSD_ALIGN_BOTTOM | CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, - { HashStr("sprite/main_menu/titlebar_effect"), { CSD_ALIGN_TOP | CSD_SCALE } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { CSD_SCALE | CSD_EXTEND_RIGHT | CSD_REPEAT_UP | CSD_REPEAT_FLIP_VERTICAL | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0.1f, 0, 0.1f, 0, -0.1f, 0, -0.1f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966/Cast_0967"), { CSD_SCALE } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.8f, 0, -0.8f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/titlebar_effect"), { CSD_SCALE } }, // pausemenu { HashStr("sprite/pausemenu/pausemenu/pause_menu"), { CSD_SCALE } }, From 39051157274a1a401af07c74962199734341a482 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 6 Sep 2025 23:24:01 +0100 Subject: [PATCH 36/52] Remove mission timer alignment for centred mode at narrow aspect ratios --- MarathonRecomp/patches/aspect_ratio_patches.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 2a07aad56..6265a7c70 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1109,6 +1109,12 @@ PPC_FUNC(sub_824D32C8) PPC_FUNC_IMPL(__imp__sub_824D6E50); PPC_FUNC(sub_824D6E50) { + if (g_aspectRatio < WIDE_ASPECT_RATIO && Config::UIAlignmentMode == EUIAlignmentMode::Centre) + { + __imp__sub_824D6E50(ctx, base); + return; + } + auto pHUDLimitTime = (Sonicteam::HUDLimitTime*)(base + ctx.r3.u32); pHUDLimitTime->m_Y = 64.0f - (g_aspectRatioOffsetY / g_aspectRatioScale); From dddfc60361de7282d5c1a98d3bec3e3a70c902e1 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sun, 7 Sep 2025 00:32:23 +0100 Subject: [PATCH 37/52] Align talk window to bottom and fix loading screen scaling --- MarathonRecomp/CMakeLists.txt | 1 + MarathonRecomp/api/Marathon.h | 3 + .../api/Sonicteam/HUDMessageWindow.h | 17 +++ MarathonRecomp/api/Sonicteam/HUDStageTitle.h | 13 ++ MarathonRecomp/api/Sonicteam/HintWindowTask.h | 14 ++ .../patches/MainMenuTask_patches.cpp | 13 ++ MarathonRecomp/patches/MainMenuTask_patches.h | 10 ++ .../patches/aspect_ratio_patches.cpp | 120 ++++++++++++------ MarathonRecomp/patches/hook_event.h | 19 +++ MarathonRecomp/patches/loading_patches.h | 2 + 10 files changed, 173 insertions(+), 39 deletions(-) create mode 100644 MarathonRecomp/api/Sonicteam/HUDMessageWindow.h create mode 100644 MarathonRecomp/api/Sonicteam/HUDStageTitle.h create mode 100644 MarathonRecomp/api/Sonicteam/HintWindowTask.h create mode 100644 MarathonRecomp/patches/MainMenuTask_patches.cpp create mode 100644 MarathonRecomp/patches/MainMenuTask_patches.h diff --git a/MarathonRecomp/CMakeLists.txt b/MarathonRecomp/CMakeLists.txt index fd82d9141..13405a8c9 100644 --- a/MarathonRecomp/CMakeLists.txt +++ b/MarathonRecomp/CMakeLists.txt @@ -141,6 +141,7 @@ set(MARATHON_RECOMP_PATCHES_CXX_SOURCES "patches/fps_patches.cpp" "patches/frontend_listener.cpp" "patches/loading_patches.cpp" + "patches/MainMenuTask_patches.cpp" "patches/misc_patches.cpp" "patches/player_patches.cpp" "patches/SaveDataTask_patches.cpp" diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index 9ddc5b1ef..3f5460741 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -37,8 +37,11 @@ #include "Sonicteam/HUDLimitTime.h" #include "Sonicteam/HUDLoading.h" #include "Sonicteam/HUDMainDisplay.h" +#include "Sonicteam/HUDMessageWindow.h" #include "Sonicteam/HUDPopupScreen.h" #include "Sonicteam/HUDRaderMap.h" +#include "Sonicteam/HUDStageTitle.h" +#include "Sonicteam/HintWindowTask.h" #include "Sonicteam/HudTextParts.h" #include "Sonicteam/ImageFilter.h" #include "Sonicteam/MainDisplayTask.h" diff --git a/MarathonRecomp/api/Sonicteam/HUDMessageWindow.h b/MarathonRecomp/api/Sonicteam/HUDMessageWindow.h new file mode 100644 index 000000000..dc6239e72 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HUDMessageWindow.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace Sonicteam +{ + class HUDMessageWindow : public SoX::RefCountObject, public SoX::Engine::Task + { + public: + xpointer m_pCsdObject; + MARATHON_INSERT_PADDING(0x1C); + xpointer m_pThis; + MARATHON_INSERT_PADDING(0x14); + xpointer m_pHintWindowTask; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/HUDStageTitle.h b/MarathonRecomp/api/Sonicteam/HUDStageTitle.h new file mode 100644 index 000000000..3cee36a21 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HUDStageTitle.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class HUDStageTitle : public SoX::Engine::Task + { + public: + MARATHON_INSERT_PADDING(8); + xpointer m_pHudTextParts; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/HintWindowTask.h b/MarathonRecomp/api/Sonicteam/HintWindowTask.h new file mode 100644 index 000000000..f60735597 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HintWindowTask.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class HintWindowTask : public SoX::Engine::Task + { + public: + MARATHON_INSERT_PADDING(0x40); + boost::shared_ptr m_Field7C; + boost::shared_ptr m_Field84; + }; +} diff --git a/MarathonRecomp/patches/MainMenuTask_patches.cpp b/MarathonRecomp/patches/MainMenuTask_patches.cpp new file mode 100644 index 000000000..b09fc9338 --- /dev/null +++ b/MarathonRecomp/patches/MainMenuTask_patches.cpp @@ -0,0 +1,13 @@ +#include "MainMenuTask_patches.h" + +// Sonicteam::MainMenuTask::Update +PPC_FUNC_IMPL(__imp__sub_824FFCF8); +PPC_FUNC(sub_824FFCF8) +{ + auto pMainMenuTask = (Sonicteam::MainMenuTask*)(base + ctx.r3.u32); + + for (auto& event : MainMenuTaskPatches::Events) + event->Update(pMainMenuTask, ctx.f1.f64); + + __imp__sub_824FFCF8(ctx, base); +} diff --git a/MarathonRecomp/patches/MainMenuTask_patches.h b/MarathonRecomp/patches/MainMenuTask_patches.h new file mode 100644 index 000000000..20cbc6389 --- /dev/null +++ b/MarathonRecomp/patches/MainMenuTask_patches.h @@ -0,0 +1,10 @@ +#pragma once + +#include "hook_event.h" +#include + +class MainMenuTaskPatches +{ +public: + static inline std::vector*> Events{}; +}; diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 6265a7c70..e3838da3c 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,6 @@ static float g_podBaseRightX{}; static float g_bgArrowsEnd{}; static float g_fgArrowsEnd{}; static float g_chevronLoopTime{}; -static float g_chevronAspectRatio{}; struct ChevronAnim { @@ -96,6 +96,41 @@ static class LoadingPillarboxEvent : public HookEvent } g_loadingPillarboxEvent{}; +static class ChevronAnimResetEvent : public ContextHookEvent +{ + float m_chevronAspectRatio{}; + +public: + void Update(Sonicteam::MainMenuTask* pThis, float deltaTime) override + { + if (g_aspectRatio <= WIDE_ASPECT_RATIO) + return; + + auto aspectRatioChanged = false; + + if (g_aspectRatio != m_chevronAspectRatio) + aspectRatioChanged = true; + + m_chevronAspectRatio = g_aspectRatio; + + if (pThis->m_State == Sonicteam::MainMenuTask::MainMenuState_MainMenuBack || + pThis->m_State == Sonicteam::MainMenuTask::MainMenuState_AudioRoom || + pThis->m_State == Sonicteam::MainMenuTask::MainMenuState_TheaterRoom || + aspectRatioChanged) + { + g_bgArrows.clear(); + g_fgArrows.clear(); + } + + for (auto& arrow : g_bgArrows) + arrow.second.Update(deltaTime); + + for (auto& arrow : g_fgArrows) + arrow.second.Update(deltaTime); + } +} +g_chevronAnimResetEvent{}; + float ComputeScale(float aspectRatio) { return ((aspectRatio * 720.0f) / 1280.0f) / sqrt((aspectRatio * 720.0f) / 1280.0f); @@ -104,6 +139,7 @@ float ComputeScale(float aspectRatio) void AspectRatioPatches::Init() { LoadingPatches::Events.push_back(&g_loadingPillarboxEvent); + MainMenuTaskPatches::Events.push_back(&g_chevronAnimResetEvent); } void AspectRatioPatches::ComputeOffsets() @@ -1042,41 +1078,6 @@ PPC_FUNC(sub_828C78E0) __imp__sub_828C78E0(ctx, base); } -// Sonicteam::MainMenuTask::Update -PPC_FUNC_IMPL(__imp__sub_824FFCF8); -PPC_FUNC(sub_824FFCF8) -{ - auto pMainMenuTask = (Sonicteam::MainMenuTask*)(base + ctx.r3.u32); - auto deltaTime = ctx.f1.f64; - - if (g_aspectRatio > WIDE_ASPECT_RATIO) - { - auto aspectRatioChanged = false; - - if (g_aspectRatio != g_chevronAspectRatio) - aspectRatioChanged = true; - - g_chevronAspectRatio = g_aspectRatio; - - if (pMainMenuTask->m_State == Sonicteam::MainMenuTask::MainMenuState_MainMenuBack || - pMainMenuTask->m_State == Sonicteam::MainMenuTask::MainMenuState_AudioRoom || - pMainMenuTask->m_State == Sonicteam::MainMenuTask::MainMenuState_TheaterRoom || - aspectRatioChanged) - { - g_bgArrows.clear(); - g_fgArrows.clear(); - } - - for (auto& arrow : g_bgArrows) - arrow.second.Update(deltaTime); - - for (auto& arrow : g_fgArrows) - arrow.second.Update(deltaTime); - } - - __imp__sub_824FFCF8(ctx, base); -} - // Sonicteam::EventEntityTask::Update PPC_FUNC_IMPL(__imp__sub_8264AC48); PPC_FUNC(sub_8264AC48) @@ -1389,6 +1390,9 @@ PPC_FUNC(sub_8262D868) auto pTextEntity = (Sonicteam::TextEntity*)(base + ctx.r3.u32); auto textModifier = *(uint64_t*)(base + ctx.r3.u32 + sizeof(Sonicteam::TextEntity)); + if ((textModifier & CSD_SKIP) != 0) + return; + auto x = pTextEntity->m_X; auto y = pTextEntity->m_Y; auto scaleX = pTextEntity->m_ScaleX; @@ -1477,6 +1481,16 @@ PPC_FUNC(sub_824DCF40) __imp__sub_824DCF40(ctx, base); } +// Sonicteam::HintWindowTask::Update +PPC_FUNC_IMPL(__imp__sub_824D12F0); +PPC_FUNC(sub_824D12F0) +{ + auto pHintWindowTask = (Sonicteam::HintWindowTask*)(base + ctx.r3.u32); + + SetTextEntityModifier(pHintWindowTask->m_Field7C.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); + SetTextEntityModifier(pHintWindowTask->m_Field84.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); + + __imp__sub_824D12F0(ctx, base); } // Sonicteam::TextFontPicture::LoadResource @@ -1629,6 +1643,15 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/goldmedal/goldmedal/silver"), { CSD_SCALE } }, { HashStr("sprite/goldmedal/goldmedal/last"), { CSD_SCALE } }, + // tips_sh_x + { HashStr("sprite/interlude/tips_x/tips_sh_x/Scene_0000"), { CSD_SCALE } }, + + // tips_si_x + { HashStr("sprite/interlude/tips_x/tips_si_x/Scene_0000"), { CSD_SCALE } }, + + // tips_so_x + { HashStr("sprite/interlude/tips_x/tips_so_x/Scene_0000"), { CSD_SCALE } }, + // loading { HashStr("sprite/loading/loading/Scene_0000/Loading"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, { HashStr("sprite/loading/loading/Scene_0000/Loading_02"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, @@ -1711,6 +1734,9 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, { HashStr("sprite/main_menu/titlebar_effect"), { CSD_SCALE } }, + // map_twn + { HashStr("sprite/interlude/map_twn/map_twn/Scene_0000"), { CSD_SCALE } }, + // pausemenu { HashStr("sprite/pausemenu/pausemenu/pause_menu"), { CSD_SCALE } }, { HashStr("sprite/pausemenu/pausemenu/pause_menu_cursor"), { CSD_SCALE } }, @@ -1865,6 +1891,22 @@ const xxHashMap g_csdModifiers = // sonicteam_logo { HashStr("sprite/logo/sonicteam_logo/sonicteam"), { CSD_SCALE } }, + // stagetitle + { HashStr("sprite/interlude/stagetitle/stage_title_aqa/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_cmn/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_csc/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_dtd/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_end/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_flc/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_kdv/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_rct/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_tpj/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_twn_a/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_twn_b/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_twn_c/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_wap/Scene_0000"), { CSD_SCALE } }, + { HashStr("sprite/interlude/stagetitle/stage_title_wvo/Scene_0000"), { CSD_SCALE } }, + // tag_character { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug1"), { CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER | CSD_REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug2"), { CSD_SCALE } }, @@ -1921,9 +1963,9 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/tagdisplay_2p/custom_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, // talkwindow - { HashStr("sprite/talkwindow/talkwindow/window"), { CSD_SCALE } }, - { HashStr("sprite/talkwindow/talkwindow/nafuda"), { CSD_SCALE } }, - { HashStr("sprite/talkwindow/talkwindow/Scene_0021"), { CSD_SCALE } }, + { HashStr("sprite/talkwindow/talkwindow/window"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/talkwindow/talkwindow/nafuda"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/talkwindow/talkwindow/Scene_0021"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, // title { HashStr("sprite/title/title/Scene_Title/Logo_add"), { CSD_SCALE } }, diff --git a/MarathonRecomp/patches/hook_event.h b/MarathonRecomp/patches/hook_event.h index b230e4a4b..6e700b05b 100644 --- a/MarathonRecomp/patches/hook_event.h +++ b/MarathonRecomp/patches/hook_event.h @@ -16,3 +16,22 @@ class HookEvent : public IHookEvent void Update(float deltaTime) override {} void Postfix() override {} }; + +template +class IContextHookEvent +{ +public: + virtual ~IContextHookEvent() = default; + virtual void Prefix(T* pThis) = 0; + virtual void Update(T* pThis, float deltaTime) = 0; + virtual void Postfix(T* pThis) = 0; +}; + +template +class ContextHookEvent : public IContextHookEvent +{ +public: + void Prefix(T* pThis) override {} + void Update(T* pThis, float deltaTime) override {} + void Postfix(T* pThis) override {} +}; diff --git a/MarathonRecomp/patches/loading_patches.h b/MarathonRecomp/patches/loading_patches.h index aba89e152..6b6354ee8 100644 --- a/MarathonRecomp/patches/loading_patches.h +++ b/MarathonRecomp/patches/loading_patches.h @@ -1,3 +1,5 @@ +#pragma once + #include "hook_event.h" class LoadingPatches From 56a766414d7ffe1348688461567d41c4890b5805 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sun, 7 Sep 2025 18:31:43 +0100 Subject: [PATCH 38/52] Implement VERT+ --- MarathonRecomp/api/Marathon.h | 7 +++ MarathonRecomp/api/Sonicteam/MainMenuTask.h | 5 ++ MarathonRecomp/api/Sonicteam/MainMode.h | 16 +++++ MarathonRecomp/api/Sonicteam/MyTransforms.h | 13 ++++ MarathonRecomp/api/Sonicteam/Player/Object.h | 15 ++++- .../api/Sonicteam/Player/RootFrame.h | 11 ++-- .../api/Sonicteam/Player/State/IContext.h | 4 ++ .../api/Sonicteam/RaderMapManager.h | 4 +- MarathonRecomp/api/Sonicteam/SoX/Component.h | 14 ++++- MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.h | 9 +-- .../api/Sonicteam/SoX/Engine/Doc.inl | 8 --- .../api/Sonicteam/SoX/Engine/DocMode.h | 1 + .../api/Sonicteam/SoX/Graphics/Frame.h | 3 +- .../api/Sonicteam/SoX/Graphics/FrameGP.h | 13 ++++ .../Sonicteam/SoX/Graphics/FrameObserver.h | 15 +++++ .../api/Sonicteam/SoX/Graphics/Transforms.h | 19 ++++++ .../api/Sonicteam/SoX/Math/Matrix.h | 51 +++++++++++----- .../api/Sonicteam/SoX/Scenery/Camera.h | 59 +++++++++++++++++++ .../api/Sonicteam/SoX/Scenery/CameraImp.h | 40 +++++++++++++ MarathonRecomp/app.cpp | 4 +- MarathonRecomp/patches/camera_patches.cpp | 31 ++++++++++ MarathonRecomp/patches/camera_patches.h | 7 +++ MarathonRecomp/patches/patches.h | 8 +++ MarathonRecompLib/config/Marathon.toml | 10 ++++ 24 files changed, 324 insertions(+), 43 deletions(-) create mode 100644 MarathonRecomp/api/Sonicteam/MainMode.h create mode 100644 MarathonRecomp/api/Sonicteam/MyTransforms.h delete mode 100644 MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.inl create mode 100644 MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameGP.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/Graphics/Transforms.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/Scenery/Camera.h create mode 100644 MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraImp.h create mode 100644 MarathonRecomp/patches/camera_patches.h create mode 100644 MarathonRecomp/patches/patches.h diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index 3f5460741..0c2085f4b 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -46,12 +46,14 @@ #include "Sonicteam/ImageFilter.h" #include "Sonicteam/MainDisplayTask.h" #include "Sonicteam/MainMenuTask.h" +#include "Sonicteam/MainMode.h" #include "Sonicteam/MovieObject.h" #include "Sonicteam/MovieObjectWmv.h" #include "Sonicteam/MovieTask.h" #include "Sonicteam/MyGraphicsDevice.h" #include "Sonicteam/MyPhantom.h" #include "Sonicteam/MyTexture.h" +#include "Sonicteam/MyTransforms.h" #include "Sonicteam/Player/IDynamicLink.h" #include "Sonicteam/Player/IExportExternalFlag.h" #include "Sonicteam/Player/IExportPostureRequestFlag.h" @@ -96,8 +98,11 @@ #include "Sonicteam/SoX/Engine/Task.h" #include "Sonicteam/SoX/Graphics/Device.h" #include "Sonicteam/SoX/Graphics/Frame.h" +#include "Sonicteam/SoX/Graphics/FrameGP.h" +#include "Sonicteam/SoX/Graphics/FrameObserver.h" #include "Sonicteam/SoX/Graphics/Technique.h" #include "Sonicteam/SoX/Graphics/TechniqueFXL.h" +#include "Sonicteam/SoX/Graphics/Transforms.h" #include "Sonicteam/SoX/Graphics/Xenon/DeviceXenon.h" #include "Sonicteam/SoX/Graphics/Xenon/TextureXenon.h" #include "Sonicteam/SoX/IResource.h" @@ -120,7 +125,9 @@ #include "Sonicteam/SoX/Physics/World.h" #include "Sonicteam/SoX/RefCountObject.h" #include "Sonicteam/SoX/RefSharedPointer.h" +#include "Sonicteam/SoX/Scenery/Camera.h" #include "Sonicteam/SoX/Scenery/CameraEventCallback.h" +#include "Sonicteam/SoX/Scenery/CameraImp.h" #include "Sonicteam/SoX/Scenery/Drawable.h" #include "Sonicteam/StdImageFilters/BurnoutBlurFilter.h" #include "Sonicteam/StdImageFilters/SingleTechniqueFilter.h" diff --git a/MarathonRecomp/api/Sonicteam/MainMenuTask.h b/MarathonRecomp/api/Sonicteam/MainMenuTask.h index a4dfc3701..8af83e1ff 100644 --- a/MarathonRecomp/api/Sonicteam/MainMenuTask.h +++ b/MarathonRecomp/api/Sonicteam/MainMenuTask.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include namespace Sonicteam { @@ -30,5 +32,8 @@ namespace Sonicteam }; be m_State; + MARATHON_INSERT_PADDING(0x248); + xpointer m_Field298; + xpointer m_SelectCharacters[9]; }; } diff --git a/MarathonRecomp/api/Sonicteam/MainMode.h b/MarathonRecomp/api/Sonicteam/MainMode.h new file mode 100644 index 000000000..a64aa7301 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/MainMode.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +namespace Sonicteam +{ + class MainMode : public SoX::Engine::DocMode + { + public: + MARATHON_INSERT_PADDING(0x24); + boost::shared_ptr m_spSelectCamera; + xpointer m_pFrameGP; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/MyTransforms.h b/MarathonRecomp/api/Sonicteam/MyTransforms.h new file mode 100644 index 000000000..11f3f1b9e --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/MyTransforms.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace Sonicteam +{ + class MyTransforms : public SoX::Graphics::Transforms + { + public: + MARATHON_INSERT_PADDING(8); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/Player/Object.h b/MarathonRecomp/api/Sonicteam/Player/Object.h index 8ea599e9e..186fde0fd 100644 --- a/MarathonRecomp/api/Sonicteam/Player/Object.h +++ b/MarathonRecomp/api/Sonicteam/Player/Object.h @@ -1,19 +1,28 @@ #pragma once #include -#include #include +#include +#include #include -#include +#include #include #include -#include +#include namespace Sonicteam::Player { class Object : public Actor { public: + class CreationParams + { + public: + xpointer m_pPlayerLua; + MARATHON_INSERT_PADDING(0x2C); + SoX::Math::Vector m_Position; + }; + stdx::string m_PlayerLua; stdx::string m_PlayerPackage; MARATHON_INSERT_PADDING(8); diff --git a/MarathonRecomp/api/Sonicteam/Player/RootFrame.h b/MarathonRecomp/api/Sonicteam/Player/RootFrame.h index b47b070da..943b95abf 100644 --- a/MarathonRecomp/api/Sonicteam/Player/RootFrame.h +++ b/MarathonRecomp/api/Sonicteam/Player/RootFrame.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -8,13 +9,13 @@ namespace Sonicteam::Player { - class RootFrame : public SoX::Graphics::Frame, public IPlugIn + class RootFrame : public SoX::Graphics::Frame, public IPlugIn, public IExportExternalFlag { public: - SoX::Math::Matrix4x4 m_Transform70; - SoX::Math::Matrix4x4 m_TransformB0; - SoX::Math::Vector m_PositionF0; - SoX::Math::Vector m_Position100; + SoX::Math::Matrix4x4 m_Field70; + SoX::Math::Matrix4x4 m_FieldB0; + SoX::Math::Vector m_FieldF0; + SoX::Math::Vector m_Field100; SoX::Math::Vector m_Impulse; MARATHON_INSERT_PADDING(0x30); be m_ExternalFlag; diff --git a/MarathonRecomp/api/Sonicteam/Player/State/IContext.h b/MarathonRecomp/api/Sonicteam/Player/State/IContext.h index dcfb1ef8b..614543da8 100644 --- a/MarathonRecomp/api/Sonicteam/Player/State/IContext.h +++ b/MarathonRecomp/api/Sonicteam/Player/State/IContext.h @@ -1,6 +1,10 @@ #pragma once #include +#include +#include +#include +#include namespace Sonicteam::Player::State { diff --git a/MarathonRecomp/api/Sonicteam/RaderMapManager.h b/MarathonRecomp/api/Sonicteam/RaderMapManager.h index 557084827..c7677b98b 100644 --- a/MarathonRecomp/api/Sonicteam/RaderMapManager.h +++ b/MarathonRecomp/api/Sonicteam/RaderMapManager.h @@ -8,8 +8,8 @@ namespace Sonicteam { public: xpointer m_pClump; - xpointer m_pFrameGP1; - xpointer m_pFrameGP2; + xpointer m_pFrameGP1; + xpointer m_pFrameGP2; MARATHON_INSERT_PADDING(0x0C); SoX::Math::Vector m_Transform; be m_Zoom; diff --git a/MarathonRecomp/api/Sonicteam/SoX/Component.h b/MarathonRecomp/api/Sonicteam/SoX/Component.h index 5730e5a11..2579a9f5c 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Component.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Component.h @@ -5,9 +5,21 @@ namespace Sonicteam::SoX { + namespace Engine + { + class DocMode; + } + class Component : public Object { public: - MARATHON_INSERT_PADDING(0x1C); + xpointer m_pDocMode; + MARATHON_INSERT_PADDING(0x18); + + template + T* GetDocMode() + { + return (T*)m_pDocMode.get(); + } }; } diff --git a/MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.h b/MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.h index 1e7e0903e..d26ac95ac 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.h @@ -17,9 +17,10 @@ namespace Sonicteam::SoX::Engine xpointer m_pDocModeExecutor; MARATHON_INSERT_PADDING(0x3C); - template - inline T* GetDocMode(); + template + inline T* GetDocMode() + { + return (T*)m_pDocMode.get(); + } }; } - -#include diff --git a/MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.inl b/MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.inl deleted file mode 100644 index 20ed38122..000000000 --- a/MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.inl +++ /dev/null @@ -1,8 +0,0 @@ -namespace Sonicteam::SoX::Engine -{ - template - inline T* Doc::GetDocMode() - { - return (T*)m_pDocMode.get(); - } -} diff --git a/MarathonRecomp/api/Sonicteam/SoX/Engine/DocMode.h b/MarathonRecomp/api/Sonicteam/SoX/Engine/DocMode.h index 51f1142df..c445e83f4 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Engine/DocMode.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Engine/DocMode.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace Sonicteam::SoX::Engine { diff --git a/MarathonRecomp/api/Sonicteam/SoX/Graphics/Frame.h b/MarathonRecomp/api/Sonicteam/SoX/Graphics/Frame.h index 2e7ef91ba..907ace5c0 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Graphics/Frame.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/Frame.h @@ -1,12 +1,11 @@ #pragma once #include - #include namespace Sonicteam::SoX::Graphics { - class Frame : public SoX::RefCountObject + class Frame : public RefCountObject { public: MARATHON_INSERT_PADDING(0x18); // SimpleNode diff --git a/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameGP.h b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameGP.h new file mode 100644 index 000000000..b114be731 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameGP.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace Sonicteam::SoX::Graphics +{ + class FrameGP : public Frame + { + public: + MARATHON_INSERT_PADDING(0x84); + stdx::string m_Name; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h new file mode 100644 index 000000000..3e06f0317 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace Sonicteam::SoX::Graphics +{ + class FrameObserver + { + public: + xpointer m_pVftable; + MARATHON_INSERT_PADDING(8); + xpointer m_pThis; + xpointer m_pFrameGP; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/Graphics/Transforms.h b/MarathonRecomp/api/Sonicteam/SoX/Graphics/Transforms.h new file mode 100644 index 000000000..21141cff0 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/Transforms.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace Sonicteam::SoX::Graphics +{ + class Transforms + { + public: + struct Vftable + { + MARATHON_INSERT_PADDING(4); + be fpComputeProjectionMatrix; + }; + + xpointer m_pVftable; + MARATHON_INSERT_PADDING(0xEC); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/Math/Matrix.h b/MarathonRecomp/api/Sonicteam/SoX/Math/Matrix.h index 3af351866..a3a79a360 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Math/Matrix.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Math/Matrix.h @@ -7,21 +7,40 @@ namespace Sonicteam::SoX::Math class Matrix4x4 { public: - be M00; - be M01; - be M02; - be M03; - be M10; - be M11; - be M12; - be M13; - be M20; - be M21; - be M22; - be M23; - be M30; - be M31; - be M32; - be M33; + be M[4][4]; + + Matrix4x4() {} + + Matrix4x4(Matrix4x4& other) + { + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + M[i][j] = other.M[i][j]; + } + } + + Matrix4x4 Multiply(const Matrix4x4& other) const + { + Matrix4x4 result{}; + + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + result.M[i][j] = 0.0f; + + for (int k = 0; k < 4; k++) + result.M[i][j] = result.M[i][j] + (M[i][k] * other.M[k][j]); + } + } + + return result; + } + + Matrix4x4 operator*(Matrix4x4 other) + { + return Multiply(other); + } }; } diff --git a/MarathonRecomp/api/Sonicteam/SoX/Scenery/Camera.h b/MarathonRecomp/api/Sonicteam/SoX/Scenery/Camera.h new file mode 100644 index 000000000..56c76dce6 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Scenery/Camera.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +namespace Sonicteam::SoX::Scenery +{ + class Camera + { + public: + struct Vftable + { + MARATHON_INSERT_PADDING(0x3C); + be fpSetFar; + be fpSetFOV; + be fpSetViewMatrix; + MARATHON_INSERT_PADDING(4); + be fpSetAspectRatio; + MARATHON_INSERT_PADDING(0x3C); + be fpUpdate; + }; + + xpointer m_pVftable; + + int SetFar(double zFar) + { + return GuestToHostFunction(m_pVftable->fpSetFar.get(), this, zFar); + } + + int SetFOV(double fov) + { + return GuestToHostFunction(m_pVftable->fpSetFOV.get(), this, fov); + } + + int SetAspectRatio(float width, float height) + { + struct AspectRatio + { + be Width; + be Height; + }; + + guest_stack_var aspectRatio(width, height); + + return GuestToHostFunction(m_pVftable->fpSetAspectRatio.get(), this, aspectRatio.get()); + } + + int Update() + { + struct Unknown + { + MARATHON_INSERT_PADDING(0x40); + }; + + guest_stack_var unknown; + + return GuestToHostFunction(m_pVftable->fpUpdate.get(), unknown.get(), this); + } + }; +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraImp.h b/MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraImp.h new file mode 100644 index 000000000..b74ac0b4e --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraImp.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace Sonicteam::SoX::Scenery +{ + class CameraImp : public Camera + { + public: + class frameObserver : public Graphics::FrameObserver + { + public: + xpointer m_pCameraImp; + be m_Field18; + MARATHON_INSERT_PADDING(0x14); + SoX::Math::Matrix4x4 m_Field30; + SoX::Math::Matrix4x4 m_Field70; + SoX::Math::Quaternion m_FieldB0; + SoX::Math::Vector m_FieldC0; + MARATHON_INSERT_PADDING(0x20); + xpointer m_pWorldIntersectionStandard; + MARATHON_INSERT_PADDING(0x10); + xpointer m_pMyTransforms; + MARATHON_INSERT_PADDING(8); + }; + + stdx::string m_Name; + be m_FOV; + be m_AspectRatioWidth; + be m_AspectRatioHeight; + be m_Near; + be m_Far; + xpointer m_pWorldImp; + MARATHON_INSERT_PADDING(0x18); + SoX::Math::Matrix4x4 m_ViewMatrix; + SoX::Math::Matrix4x4 m_Field90; + SoX::Math::Matrix4x4 m_FieldD0; + frameObserver m_FrameObserver; + }; +} diff --git a/MarathonRecomp/app.cpp b/MarathonRecomp/app.cpp index d0d5ceb96..4c08a7efa 100644 --- a/MarathonRecomp/app.cpp +++ b/MarathonRecomp/app.cpp @@ -4,8 +4,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -61,7 +61,7 @@ PPC_FUNC(sub_8262A568) App::s_pApp = (Sonicteam::AppMarathon*)g_memory.Translate(ctx.r3.u32); - AspectRatioPatches::Init(); + InitPatches(); } // Sonicteam::DocMarathonState::Update diff --git a/MarathonRecomp/patches/camera_patches.cpp b/MarathonRecomp/patches/camera_patches.cpp index 23520fd9e..53a975ac2 100644 --- a/MarathonRecomp/patches/camera_patches.cpp +++ b/MarathonRecomp/patches/camera_patches.cpp @@ -1,6 +1,37 @@ +#include "camera_patches.h" #include +#include +#include #include +static class MainMenuCameraUpdateEvent : public ContextHookEvent +{ +public: + void Update(Sonicteam::MainMenuTask* pThis, float deltaTime) override + { + auto pMainMode = pThis->GetDocMode(); + + if (!pMainMode) + return; + + pMainMode->m_spSelectCamera->Update(); + } +} +g_mainMenuCameraUpdateEvent{}; + +void CameraPatches::Init() +{ + MainMenuTaskPatches::Events.push_back(&g_mainMenuCameraUpdateEvent); +} + +void CameraImp_SetFOV(PPCRegister& f1) +{ + if (g_aspectRatio >= WIDE_ASPECT_RATIO) + return; + + f1.f64 = 2.0 * atan(tan(0.5 * f1.f64) / g_aspectRatio * WIDE_ASPECT_RATIO); +} + // Load Sonicteam::Camera::SonicCamera parameters PPC_FUNC_IMPL(__imp__sub_82192218); PPC_FUNC(sub_82192218) diff --git a/MarathonRecomp/patches/camera_patches.h b/MarathonRecomp/patches/camera_patches.h new file mode 100644 index 000000000..d43caeeb6 --- /dev/null +++ b/MarathonRecomp/patches/camera_patches.h @@ -0,0 +1,7 @@ +#pragma once + +class CameraPatches +{ +public: + static void Init(); +}; diff --git a/MarathonRecomp/patches/patches.h b/MarathonRecomp/patches/patches.h new file mode 100644 index 000000000..2b287d990 --- /dev/null +++ b/MarathonRecomp/patches/patches.h @@ -0,0 +1,8 @@ +#include "aspect_ratio_patches.h" +#include "camera_patches.h" + +inline void InitPatches() +{ + AspectRatioPatches::Init(); + CameraPatches::Init(); +} diff --git a/MarathonRecompLib/config/Marathon.toml b/MarathonRecompLib/config/Marathon.toml index f58264bbe..603302d6a 100644 --- a/MarathonRecompLib/config/Marathon.toml +++ b/MarathonRecompLib/config/Marathon.toml @@ -374,3 +374,13 @@ registers = ["r3"] name = "TextEntityAlloc" address = 0x825EDCEC registers = ["r3"] + +[[midasm_hook]] +name = "CameraImp_SetFOV" +address = 0x82590980 +registers = ["f1"] + +[[midasm_hook]] +name = "CameraImp_SetFOV" +address = 0x82590AB4 +registers = ["f1"] From 30aa6821ce940963da0945afcbb3f4b1e47df7c3 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sun, 7 Sep 2025 18:51:08 +0100 Subject: [PATCH 39/52] Fix NPC balloon icon scale at narrow aspect ratios --- MarathonRecomp/patches/aspect_ratio_patches.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index e3838da3c..2a48a1f88 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1144,7 +1144,10 @@ PPC_FUNC_IMPL(__imp__sub_82352220); PPC_FUNC(sub_82352220) { auto pCObjBalloonIconDrawable = (Sonicteam::CObjBalloonIconDrawable*)(base + ctx.r3.u32); - auto scale = g_aspectRatio / WIDE_ASPECT_RATIO; + auto scale = g_aspectRatioScale; + + if (g_aspectRatio > WIDE_ASPECT_RATIO) + scale = g_aspectRatio / WIDE_ASPECT_RATIO; pCObjBalloonIconDrawable->m_Vertices[0].X = -1.0f / scale; pCObjBalloonIconDrawable->m_Vertices[0].Y = 0.0f; From 0785e45b6710905a0aa5a71bb27505a0a06ccc8c Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Mon, 8 Sep 2025 00:22:08 +0100 Subject: [PATCH 40/52] Align NPC dialogue window text to bottom --- MarathonRecomp/api/Marathon.h | 1 + MarathonRecomp/api/Sonicteam/HintWindowTask.h | 4 ++-- MarathonRecomp/api/Sonicteam/MessageWindowTask.h | 14 ++++++++++++++ MarathonRecomp/patches/aspect_ratio_patches.cpp | 16 ++++++++++++++-- 4 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 MarathonRecomp/api/Sonicteam/MessageWindowTask.h diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index 773f50315..568fa5b65 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -47,6 +47,7 @@ #include "Sonicteam/MainDisplayTask.h" #include "Sonicteam/MainMenuTask.h" #include "Sonicteam/MainMode.h" +#include "Sonicteam/MessageWindowTask.h" #include "Sonicteam/MovieObject.h" #include "Sonicteam/MovieObjectWmv.h" #include "Sonicteam/MovieTask.h" diff --git a/MarathonRecomp/api/Sonicteam/HintWindowTask.h b/MarathonRecomp/api/Sonicteam/HintWindowTask.h index f60735597..ab3c938a8 100644 --- a/MarathonRecomp/api/Sonicteam/HintWindowTask.h +++ b/MarathonRecomp/api/Sonicteam/HintWindowTask.h @@ -8,7 +8,7 @@ namespace Sonicteam { public: MARATHON_INSERT_PADDING(0x40); - boost::shared_ptr m_Field7C; - boost::shared_ptr m_Field84; + boost::shared_ptr m_Field8C; + boost::shared_ptr m_Field94; }; } diff --git a/MarathonRecomp/api/Sonicteam/MessageWindowTask.h b/MarathonRecomp/api/Sonicteam/MessageWindowTask.h new file mode 100644 index 000000000..de5ef0a17 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/MessageWindowTask.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class MessageWindowTask : public SoX::Engine::Task + { + public: + MARATHON_INSERT_PADDING(0x44); + boost::shared_ptr m_Field90; + boost::shared_ptr m_Field98; + }; +} diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 2a48a1f88..18cc5ea8b 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1490,12 +1490,24 @@ PPC_FUNC(sub_824D12F0) { auto pHintWindowTask = (Sonicteam::HintWindowTask*)(base + ctx.r3.u32); - SetTextEntityModifier(pHintWindowTask->m_Field7C.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); - SetTextEntityModifier(pHintWindowTask->m_Field84.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); + SetTextEntityModifier(pHintWindowTask->m_Field8C.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); + SetTextEntityModifier(pHintWindowTask->m_Field94.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); __imp__sub_824D12F0(ctx, base); } +// Sonicteam::MessageWindowTask::Update +PPC_FUNC_IMPL(__imp__sub_82507E68); +PPC_FUNC(sub_82507E68) +{ + auto pMessageWindowTask = (Sonicteam::MessageWindowTask*)(base + ctx.r3.u32); + + SetTextEntityModifier(pMessageWindowTask->m_Field90.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); + SetTextEntityModifier(pMessageWindowTask->m_Field98.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); + + __imp__sub_82507E68(ctx, base); +} + // Sonicteam::TextFontPicture::LoadResource PPC_FUNC_IMPL(__imp__sub_8263CC40); PPC_FUNC(sub_8263CC40) From e7b56470174640750aa77eb894b8d1ea99d86657 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 9 Sep 2025 01:14:49 +0100 Subject: [PATCH 41/52] API: mapped cameras in GameImp --- MarathonRecomp/api/Sonicteam/GameImp.h | 37 +++++++++++++++---- MarathonRecomp/api/Sonicteam/GameImp.inl | 8 ---- .../api/Sonicteam/SoX/Graphics/FrameGP.h | 2 + .../Sonicteam/SoX/Graphics/FrameObserver.h | 2 + .../api/Sonicteam/SoX/Scenery/CameraImp.h | 6 +++ 5 files changed, 40 insertions(+), 15 deletions(-) delete mode 100644 MarathonRecomp/api/Sonicteam/GameImp.inl diff --git a/MarathonRecomp/api/Sonicteam/GameImp.h b/MarathonRecomp/api/Sonicteam/GameImp.h index 9b1459c01..83c0015cb 100644 --- a/MarathonRecomp/api/Sonicteam/GameImp.h +++ b/MarathonRecomp/api/Sonicteam/GameImp.h @@ -1,9 +1,13 @@ #pragma once #include +#include #include +#include +#include #include #include +#include namespace Sonicteam { @@ -53,14 +57,13 @@ namespace Sonicteam bool m_IsStage; MARATHON_INSERT_PADDING(0x0C); be m_Field1180; - MARATHON_INSERT_PADDING(0x838); + MARATHON_INSERT_PADDING(0x58); + xpointer>> m_pvspCameras; + MARATHON_INSERT_PADDING(0x7DC); SoX::RefSharedPointer m_spPhysicsWorld; xpointer m_pMyCollisionFilter; - template - inline T* GetPhysicsWorld(); - - int32_t PlayerActorIDToIndex(int32_t actorId) const + int PlayerActorIDToIndex(int32_t actorId) const { for (int i = 0; i < 4; i++) { @@ -70,7 +73,27 @@ namespace Sonicteam return -1; } + + SoX::Scenery::CameraImp* GetCameraImp(const char* pName) const + { + if (!m_pvspCameras) + return nullptr; + + for (auto& spCamera : *m_pvspCameras) + { + auto pCameraImp = (SoX::Scenery::CameraImp*)spCamera.get(); + + if (pCameraImp->m_Name == pName) + return pCameraImp; + } + + return nullptr; + } + + template + T* GetPhysicsWorld() const + { + return (T*)m_spPhysicsWorld.get(); + } }; } - -#include diff --git a/MarathonRecomp/api/Sonicteam/GameImp.inl b/MarathonRecomp/api/Sonicteam/GameImp.inl deleted file mode 100644 index 40ad4b37d..000000000 --- a/MarathonRecomp/api/Sonicteam/GameImp.inl +++ /dev/null @@ -1,8 +0,0 @@ -namespace Sonicteam -{ - template - inline T* GameImp::GetPhysicsWorld() - { - return (T*)m_spPhysicsWorld.get(); - } -} diff --git a/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameGP.h b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameGP.h index b114be731..9ef00f56a 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameGP.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameGP.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include namespace Sonicteam::SoX::Graphics { diff --git a/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h index 3e06f0317..c080aff9a 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include namespace Sonicteam::SoX::Graphics { diff --git a/MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraImp.h b/MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraImp.h index b74ac0b4e..5d2b42f14 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraImp.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraImp.h @@ -1,6 +1,12 @@ #pragma once #include +#include +#include +#include +#include +#include +#include namespace Sonicteam::SoX::Scenery { From ceb6f0b28afae5309da2f74e9da9a071b6fa238b Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 13 Sep 2025 15:26:19 +0100 Subject: [PATCH 42/52] Scale main menu borders for narrow aspect ratios --- .../patches/aspect_ratio_patches.cpp | 313 +++++++++++++----- MarathonRecomp/patches/aspect_ratio_patches.h | 51 +-- 2 files changed, 259 insertions(+), 105 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 18cc5ea8b..6a39906ba 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -388,6 +388,9 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) { CsdModifier modifier{}; + auto vpWidth = float(Video::s_viewportWidth); + auto vpHeight = float(Video::s_viewportHeight); + if (g_castModifier.has_value()) { modifier = g_castModifier.value(); @@ -401,9 +404,11 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) modifier = g_sceneModifier.value(); } + // Remove all flags if the aspect ratio is above 16:9. if ((modifier.Flags & CSD_MODIFIER_ULTRAWIDE_ONLY) != 0 && g_aspectRatio <= WIDE_ASPECT_RATIO) modifier.Flags &= (~modifier.Flags) | CSD_MODIFIER_ULTRAWIDE_ONLY; + // Remove all flags if the aspect ratio is below 16:9. if ((modifier.Flags & CSD_MODIFIER_NARROW_ONLY) != 0 && g_aspectRatio >= WIDE_ASPECT_RATIO) modifier.Flags &= (~modifier.Flags) | CSD_MODIFIER_NARROW_ONLY; @@ -417,6 +422,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) } else { + // Skip drawing this cast. return; } } @@ -432,31 +438,38 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) return; } + // Draw black bars if this cast is drawn. if ((modifier.Flags & CSD_BLACK_BAR) != 0) BlackBar::g_isVisible = true; + // Prohibit black bars from being drawn if this cast is drawn. if ((modifier.Flags & CSD_PROHIBIT_BLACK_BAR) != 0) BlackBar::g_isVisible = false; if (Config::UIAlignmentMode == EUIAlignmentMode::Centre || BlackBar::g_isVisible) { + // Prevent chevron arrows from being unaligned by centred aspect ratio. if ((modifier.Flags & CSD_CHEVRON) == 0) { if (g_aspectRatio > WIDE_ASPECT_RATIO) { + // Remove horizontal alignments at wide aspect ratios. modifier.Flags &= ~(CSD_ALIGN_LEFT | CSD_ALIGN_RIGHT); } else if (g_aspectRatio < WIDE_ASPECT_RATIO) { + // Remove vertical alignments at narrow aspect ratios. modifier.Flags &= ~(CSD_ALIGN_TOP | CSD_ALIGN_BOTTOM); } } } - uint32_t size = ctx.r5.u32 * stride; + // Reserve stack space for vertices. + auto size = ctx.r5.u32 * stride; ctx.r1.u32 -= size; - uint8_t* stack = base + ctx.r1.u32; + // Copy vertices to stack. + auto stack = base + ctx.r1.u32; memcpy(stack, base + ctx.r4.u32, size); struct CSDVertex @@ -484,7 +497,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if (needsStretch && (modifier.Flags & CSD_STRETCH_HORIZONTAL) != 0) { - scaleX = Video::s_viewportWidth / 1280.0f; + scaleX = vpWidth / 1280.0f; } else { @@ -493,7 +506,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if (needsStretch && (modifier.Flags & CSD_UNSTRETCH_HORIZONTAL) != 0) { pivotX = getVertex(0)->X; - offsetX = pivotX * Video::s_viewportWidth / 1280.0f; + offsetX = pivotX * vpWidth / 1280.0f; } else { @@ -532,7 +545,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & CSD_STRETCH_VERTICAL) != 0) { - scaleY = Video::s_viewportHeight / 720.0f; + scaleY = vpHeight / 720.0f; } else { @@ -556,11 +569,9 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) } } + // Offset cast to movie aspect ratio boundaries. if ((modifier.Flags & CSD_MOVIE) != 0) { - auto vpWidth = float(Video::s_viewportWidth); - auto vpHeight = float(Video::s_viewportHeight); - if (g_aspectRatio > g_aspectRatioMovie) { offsetX -= (vpWidth - (vpHeight * g_aspectRatioMovie)) / 2.0f; @@ -574,14 +585,13 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if (g_aspectRatio > WIDE_ASPECT_RATIO) { CsdModifier offsetScaleModifier{}; - float corner = 0.0f; + + auto corner = 0.0f; if (g_castModifier.has_value()) { offsetScaleModifier = g_castModifier.value(); - - uint32_t vertexIndex = ((offsetScaleModifier.Flags & CSD_STORE_LEFT_CORNER) != 0) ? 0 : 3; - corner = getVertex(vertexIndex)->X; + corner = getVertex(((offsetScaleModifier.Flags & CSD_STORE_LEFT_CORNER) != 0) ? 0 : 3)->X; } if (offsetScaleModifier.CornerMax == 0.0f && g_castNodeModifier.has_value()) @@ -604,7 +614,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((offsetScaleModifier.Flags & CSD_OFFSET_SCALE_LEFT) != 0) offsetX *= corner / offsetScaleModifier.CornerMax; else if ((offsetScaleModifier.Flags & CSD_OFFSET_SCALE_RIGHT) != 0) - offsetX = Video::s_viewportWidth - (Video::s_viewportWidth - offsetX) * (1280.0f - corner) / (1280.0f - offsetScaleModifier.CornerMax); + offsetX = vpWidth - (vpWidth - offsetX) * (1280.0f - corner) / (1280.0f - offsetScaleModifier.CornerMax); } auto firstX = 0.0f; @@ -618,8 +628,8 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) { auto vertex = getVertex(i); - float x = offsetX + (vertex->X - pivotX) * scaleX; - float y = offsetY + (vertex->Y - pivotY) * scaleY; + auto x = offsetX + (vertex->X - pivotX) * scaleX; + auto y = offsetY + (vertex->Y - pivotY) * scaleY; if ((modifier.Flags & CSD_EXTEND_LEFT) != 0 && (i == 0 || i == 1)) { @@ -631,7 +641,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & CSD_POD_BASE) != 0) g_podBaseRightX = x; - x = std::max(x, float(Video::s_viewportWidth)); + x = std::max(x, vpWidth); } switch (i) @@ -661,6 +671,22 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) g_radarMapCoverHeight = height; } + auto flipHorizontally = [&]() + { + getVertex(0)->X = getVertex(0)->X + width; + getVertex(1)->X = getVertex(1)->X + width; + getVertex(2)->X = getVertex(2)->X - width; + getVertex(3)->X = getVertex(3)->X - width; + }; + + auto flipVertically = [&]() + { + getVertex(0)->Y = getVertex(0)->Y + height; + getVertex(1)->Y = getVertex(1)->Y - height; + getVertex(2)->Y = getVertex(2)->Y + height; + getVertex(3)->Y = getVertex(3)->Y - height; + }; + auto applyUVModifier = [&](CsdUVs uvModifier) { getVertex(0)->U = getVertex(0)->U + uvModifier.U0; @@ -693,34 +719,22 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) auto isRepeatLeft = (modifier.Flags & CSD_REPEAT_LEFT) != 0; auto isRepeatRight = (modifier.Flags & CSD_REPEAT_RIGHT) != 0; - auto isRepeatUp = (modifier.Flags & CSD_REPEAT_UP) != 0; - auto isRepeatDown = (modifier.Flags & CSD_REPEAT_DOWN) != 0; - if (isRepeatLeft || isRepeatRight || isRepeatUp || isRepeatDown) - { - auto r3 = ctx.r3; - auto r5 = ctx.r5; + auto r3 = ctx.r3; + auto r5 = ctx.r5; + if (isRepeatLeft || isRepeatRight) + { auto isFlipHorz = (modifier.Flags & CSD_REPEAT_FLIP_HORIZONTAL) != 0; auto isFlipVert = (modifier.Flags & CSD_REPEAT_FLIP_VERTICAL) != 0; auto applyRepeatModifiers = [&]() { if (isFlipHorz) - { - getVertex(0)->X = getVertex(0)->X + width; - getVertex(1)->X = getVertex(1)->X + width; - getVertex(2)->X = getVertex(2)->X - width; - getVertex(3)->X = getVertex(3)->X - width; - } + flipHorizontally(); if (isFlipVert) - { - getVertex(0)->Y = getVertex(0)->Y + height; - getVertex(1)->Y = getVertex(1)->Y - height; - getVertex(2)->Y = getVertex(2)->Y + height; - getVertex(3)->Y = getVertex(3)->Y - height; - } + flipVertically(); if ((modifier.Flags & CSD_REPEAT_UV_MODIFIER) != 0) applyUVModifier(modifier.RepeatUVs); @@ -764,27 +778,30 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & CSD_CHEVRON) != 0) { - auto endAlpha = (uint8_t)std::lerp(30, 10, x / float(Video::s_viewportWidth)); + /////////////////////////////////////////////////////////// + // Clone foreground arrows to recreate chevron animation // + /////////////////////////////////////////////////////////// + + // These values are based off the animation in background.xncp. + auto endAlpha = (uint8_t)std::lerp(30, 10, x / vpWidth); auto introDuration = CHEVRON_INTRO_DURATION; auto introStartTime = (introDuration / 2.0f) * float(arrowIndex + 1) + g_bgArrowsEnd; auto outroDuration = CHEVRON_OUTRO_DURATION; auto outroStartTime = (g_fgArrowsEnd + introStartTime) + 0.5f; + // This will be set by the last foreground arrow in the + // sequence, therefore making it the loop end point. g_chevronLoopTime = outroStartTime + outroDuration; - if (g_fgArrows.count(arrowIndex)) - { - g_fgArrows[arrowIndex].EndAlpha = endAlpha; - g_fgArrows[arrowIndex].IntroDuration = introDuration; - g_fgArrows[arrowIndex].IntroStartTime = introStartTime; - g_fgArrows[arrowIndex].OutroDuration = outroDuration; - g_fgArrows[arrowIndex].OutroStartTime = outroStartTime; - } - else - { - g_fgArrows.emplace(arrowIndex, ChevronAnim{ endAlpha, introDuration, introStartTime, outroDuration, outroStartTime }); - } + // Store current arrow animation progress. + auto& arrow = g_fgArrows[arrowIndex]; + arrow.EndAlpha = endAlpha; + arrow.IntroDuration = introDuration; + arrow.IntroStartTime = introStartTime; + arrow.OutroDuration = outroDuration; + arrow.OutroStartTime = outroStartTime; + // Animate arrow position and alpha. for (size_t i = 0; i < r5.u32; i++) { getVertex(i)->X = (getVertex(i)->X - arrowWidth) - g_bgArrows[arrowIndex].CurrentOffsetX; @@ -795,10 +812,12 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) } else { + // Move cloned cast to left edge of existing cast. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->X = getVertex(i)->X - width; } + // Update loop condition. x = getVertex(2)->X; applyRepeatModifiers(); @@ -828,7 +847,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) getVertex(i)->X = getVertex(i)->X - arrowWidth; // Compute number of background arrows. - while (x < float(Video::s_viewportWidth)) + while (x < vpWidth) { x = x + arrowWidth; arrowCount++; @@ -840,7 +859,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) x = getVertex(0)->X; } - while (x < float(Video::s_viewportWidth)) + while (x < vpWidth) { ctx.r3 = r3; ctx.r4 = ctx.r1; @@ -849,25 +868,26 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & CSD_CHEVRON) != 0) { - auto endAlpha = (uint8_t)std::lerp(10, 35, x / float(Video::s_viewportWidth)); + /////////////////////////////////////////////////////////// + // Clone background arrows to recreate chevron animation // + /////////////////////////////////////////////////////////// + + // These values are based off the animation in background.xncp. + auto endAlpha = (uint8_t)std::lerp(10, 35, x / vpWidth); auto introDuration = CHEVRON_INTRO_DURATION; auto introStartTime = (introDuration / 2.0f) * float(arrowIndex + 1); auto outroDuration = CHEVRON_OUTRO_DURATION; auto outroStartTime = (g_bgArrowsEnd + introStartTime) + 0.5f; - if (g_bgArrows.count(arrowIndex)) - { - g_bgArrows[arrowIndex].EndAlpha = endAlpha; - g_bgArrows[arrowIndex].IntroDuration = introDuration; - g_bgArrows[arrowIndex].IntroStartTime = introStartTime; - g_bgArrows[arrowIndex].OutroDuration = outroDuration; - g_bgArrows[arrowIndex].OutroStartTime = outroStartTime; - } - else - { - g_bgArrows.emplace(arrowIndex, ChevronAnim{ endAlpha, introDuration, introStartTime, outroDuration, outroStartTime }); - } + // Store current arrow animation progress. + auto& arrow = g_bgArrows[arrowIndex]; + arrow.EndAlpha = endAlpha; + arrow.IntroDuration = introDuration; + arrow.IntroStartTime = introStartTime; + arrow.OutroDuration = outroDuration; + arrow.OutroStartTime = outroStartTime; + // Animate arrow position and alpha. for (size_t i = 0; i < r5.u32; i++) { getVertex(i)->X = (getVertex(i)->X + arrowWidth) + g_bgArrows[arrowIndex].CurrentOffsetX; @@ -878,32 +898,30 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) } else { + // Move cloned cast to right edge of existing cast. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->X = getVertex(i)->X + width; } + // Update loop condition. x = getVertex(0)->X; applyRepeatModifiers(); + // Extend to the right of the screen. if ((modifier.Flags & CSD_REPEAT_EXTEND) != 0) { for (size_t i = 0; i < r5.u32; i++) { if (i == (isFlipHorz ? 0 : 2) || i == (isFlipHorz ? 1 : 3)) - getVertex(i)->X = std::max(getVertex(i)->X.get(), float(Video::s_viewportWidth)); + getVertex(i)->X = std::max(getVertex(i)->X.get(), vpWidth); } } } } - - ctx.r1.u32 += size; } else { - auto r3 = ctx.r3; - auto r5 = ctx.r5; - ctx.r3 = r3; ctx.r4 = ctx.r1; ctx.r5 = r5; @@ -917,7 +935,6 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) auto v1 = getVertex(1); auto v2 = getVertex(2); auto v3 = getVertex(3); - auto vpWidth = (float)Video::s_viewportWidth; // Shift cloned element to base edge. v0->X = g_podBaseRightX; @@ -955,9 +972,6 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) auto originalTop = v0->Y; auto originalHeight = v1->Y - v0->Y; - auto vpWidth = float(Video::s_viewportWidth); - auto vpHeight = float(Video::s_viewportHeight); - auto width = 422.0f; auto height = 132.0f; auto widthScaled = width * g_aspectRatioScale; @@ -1041,9 +1055,146 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) ctx.r5 = r5; original(ctx, base); } + } + + if (g_aspectRatio > WIDE_ASPECT_RATIO) + { + ///////////////////////////////////////////////// + // Scale metal borders to wide aspect ratios // + ///////////////////////////////////////////////// + + if ((modifier.Flags & CSD_MAIN_MENU_PARTS_CAST_0222) != 0) + { + // Move cloned cast to left edge of existing cast. + for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->X = getVertex(i)->X - width; + + flipHorizontally(); + + // Extend to the left of the screen. + getVertex(2)->X = 0.0f; + getVertex(3)->X = 0.0f; + + // Crop UVs to remove metal notch. + getVertex(0)->U = getVertex(0)->U + 0.0015f; + getVertex(1)->U = getVertex(1)->U + 0.0015f; + getVertex(2)->U = getVertex(2)->U + -0.1f; + getVertex(3)->U = getVertex(3)->U + -0.1f; + + ctx.r3 = r3; + ctx.r4 = ctx.r1; + ctx.r5 = r5; + original(ctx, base); + } + + if ((modifier.Flags & CSD_MAIN_MENU_PARTS_CAST_0226) != 0) + { + // Move cloned cast to left edge of existing cast. + for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->X = getVertex(i)->X - width; + + flipHorizontally(); + + // Extend to the left of the screen. + getVertex(2)->X = 0.0f; + getVertex(3)->X = 0.0f; - ctx.r1.u32 += size; + // Crop UVs to remove metal dip. + getVertex(2)->U = getVertex(2)->U + -0.8f; + getVertex(3)->U = getVertex(3)->U + -0.8f; + + ctx.r3 = r3; + ctx.r4 = ctx.r1; + ctx.r5 = r5; + original(ctx, base); + } + + if ((modifier.Flags & CSD_MAIN_MENU_PARTS_CAST_0227) != 0) + { + // Move cloned cast to right edge of existing cast. + for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->X = getVertex(i)->X + width; + + flipHorizontally(); + + // Extend to the right of the screen. + getVertex(0)->X = vpWidth; + getVertex(1)->X = vpWidth; + + // Crop UVs to remove metal dip. + getVertex(0)->U = getVertex(0)->U + -0.8f; + getVertex(1)->U = getVertex(1)->U + -0.8f; + + ctx.r3 = r3; + ctx.r4 = ctx.r1; + ctx.r5 = r5; + original(ctx, base); + } } + else + { + ///////////////////////////////////////////////// + // Scale metal borders to narrow aspect ratios // + ///////////////////////////////////////////////// + + if ((modifier.Flags & (CSD_MAIN_MENU_PARTS_CAST_0221 | CSD_MAIN_MENU_PARTS_CAST_0222)) != 0) + { + // Move cloned cast above existing cast. + for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->Y = getVertex(i)->Y - height + 6.0f; + + flipVertically(); + + // Extend to the top of the screen + // if the first clone no longer fits. + if (getVertex(1)->Y - height > 0.0f) + { + getVertex(1)->Y = 0.0f; + getVertex(3)->Y = 0.0f; + } + + // Crop UVs to remove metal notch. + getVertex(0)->V = getVertex(0)->V + 0.005f; + getVertex(1)->V = getVertex(1)->V + -0.075f; + getVertex(2)->V = getVertex(2)->V + 0.005f; + getVertex(3)->V = getVertex(3)->V + -0.075f; + + ctx.r3 = r3; + ctx.r4 = ctx.r1; + ctx.r5 = r5; + original(ctx, base); + } + + if ((modifier.Flags & (CSD_MAIN_MENU_PARTS_CAST_0226 | CSD_MAIN_MENU_PARTS_CAST_0227)) != 0) + { + // Move cloned cast below existing cast. + for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->Y = getVertex(i)->Y + height - 18.0f; + + flipVertically(); + + // Extend to the bottom of the screen + // if the first clone no longer fits. + if (getVertex(0)->Y - height < vpHeight) + { + getVertex(0)->Y = vpHeight; + getVertex(2)->Y = vpHeight; + } + + // Crop UVs to remove metal dip. + getVertex(0)->V = getVertex(0)->V + 0.065f; + getVertex(1)->V = getVertex(1)->V + -0.0175f; + getVertex(2)->V = getVertex(2)->V + 0.065f; + getVertex(3)->V = getVertex(3)->V + -0.0175f; + + ctx.r3 = r3; + ctx.r4 = ctx.r1; + ctx.r5 = r5; + original(ctx, base); + } + } + + ctx.r1.u32 += size; } // Sonicteam::CPlatformMarathon::Draw @@ -1051,8 +1202,8 @@ PPC_FUNC_IMPL(__imp__sub_826315C8); PPC_FUNC(sub_826315C8) { // r3 = Sonicteam::CPlatformMarathon* - // r4 = pointer to vertices - // r5 = vertex count + // r4 = Vertex[r5] + // r5 = Vertex Count Draw(ctx, base, __imp__sub_826315C8, 0x14); } @@ -1062,8 +1213,8 @@ PPC_FUNC_IMPL(__imp__sub_82631718); PPC_FUNC(sub_82631718) { // r3 = Sonicteam::CPlatformMarathon* - // r4 = pointer to vertices - // r5 = vertex count + // r4 = Vertex[r5] + // r5 = Vertex Count Draw(ctx, base, __imp__sub_82631718, 0x0C); } @@ -1739,14 +1890,14 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/main_menu/mission_text/rank"), { CSD_SCALE } }, { HashStr("sprite/main_menu/mission_text/item_icon"), { CSD_SCALE } }, { HashStr("sprite/main_menu/mission_text/m_ring_icon"), { CSD_SCALE } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { CSD_SCALE | CSD_EXTEND_RIGHT | CSD_REPEAT_UP | CSD_REPEAT_FLIP_VERTICAL | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, 0, 0, 0, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0.1f, 0, 0.1f, 0, -0.1f, 0, -0.1f, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { CSD_MAIN_MENU_PARTS_CAST_0221 | CSD_SCALE | CSD_EXTEND_RIGHT, { 0, 0.005f, 0, 0, 0, 0.005f, 0, 0 }, {}, { 0, 0, 0, -0.0175f, 0, 0, 0, -0.0175f } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { CSD_MAIN_MENU_PARTS_CAST_0222 | CSD_SCALE | CSD_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966"), { CSD_SCALE } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966/Cast_0967"), { CSD_SCALE } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.8f, 0, -0.8f, 0 } } }, - { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { -0.8f, 0, -0.8f, 0, 0, 0, 0, 0 } } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { CSD_MAIN_MENU_PARTS_CAST_0226 | CSD_SCALE } }, + { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { CSD_MAIN_MENU_PARTS_CAST_0227 | CSD_SCALE } }, { HashStr("sprite/main_menu/titlebar_effect"), { CSD_SCALE } }, // map_twn diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index cf6e70be3..b01d18b1b 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -61,42 +61,45 @@ enum CsdFlags : uint64_t CSD_REPEAT_LEFT = MAKE_BITFLAG64(14), CSD_REPEAT_RIGHT = MAKE_BITFLAG64(15), - CSD_REPEAT_UP = MAKE_BITFLAG64(16), - CSD_REPEAT_DOWN = MAKE_BITFLAG64(17), - CSD_REPEAT_FLIP_HORIZONTAL = MAKE_BITFLAG64(18), - CSD_REPEAT_FLIP_VERTICAL = MAKE_BITFLAG64(19), - CSD_REPEAT_EXTEND = MAKE_BITFLAG64(20), + CSD_REPEAT_FLIP_HORIZONTAL = MAKE_BITFLAG64(16), + CSD_REPEAT_FLIP_VERTICAL = MAKE_BITFLAG64(17), + CSD_REPEAT_EXTEND = MAKE_BITFLAG64(18), - CSD_UV_MODIFIER = MAKE_BITFLAG64(21), - CSD_COLOUR_MODIFIER = MAKE_BITFLAG64(22), - CSD_REPEAT_UV_MODIFIER = MAKE_BITFLAG64(23), - CSD_REPEAT_COLOUR_MODIFIER = MAKE_BITFLAG64(24), + CSD_UV_MODIFIER = MAKE_BITFLAG64(19), + CSD_COLOUR_MODIFIER = MAKE_BITFLAG64(20), + CSD_REPEAT_UV_MODIFIER = MAKE_BITFLAG64(21), + CSD_REPEAT_COLOUR_MODIFIER = MAKE_BITFLAG64(22), - CSD_BLACK_BAR = MAKE_BITFLAG64(25), - CSD_PROHIBIT_BLACK_BAR = MAKE_BITFLAG64(26), + CSD_BLACK_BAR = MAKE_BITFLAG64(23), + CSD_PROHIBIT_BLACK_BAR = MAKE_BITFLAG64(24), - CSD_UNSTRETCH_HORIZONTAL = MAKE_BITFLAG64(27), + CSD_UNSTRETCH_HORIZONTAL = MAKE_BITFLAG64(25), - CSD_CORNER_EXTRACT = MAKE_BITFLAG64(28), + CSD_CORNER_EXTRACT = MAKE_BITFLAG64(26), - CSD_RADARMAP = MAKE_BITFLAG64(29), + CSD_RADARMAP = MAKE_BITFLAG64(27), - CSD_POD_BASE = MAKE_BITFLAG64(30), - CSD_POD_CLONE = MAKE_BITFLAG64(31), + CSD_POD_BASE = MAKE_BITFLAG64(28), + CSD_POD_CLONE = MAKE_BITFLAG64(29), - CSD_MULTIPLAYER = MAKE_BITFLAG64(32), - CSD_MULTIPLAYER_CENTER = MAKE_BITFLAG64(33), + CSD_MULTIPLAYER = MAKE_BITFLAG64(30), + CSD_MULTIPLAYER_CENTER = MAKE_BITFLAG64(31), - CSD_CHEVRON = MAKE_BITFLAG64(34), + CSD_CHEVRON = MAKE_BITFLAG64(32), - CSD_MODIFIER_ULTRAWIDE_ONLY = MAKE_BITFLAG64(35), - CSD_MODIFIER_NARROW_ONLY = MAKE_BITFLAG64(36), + CSD_MODIFIER_ULTRAWIDE_ONLY = MAKE_BITFLAG64(33), + CSD_MODIFIER_NARROW_ONLY = MAKE_BITFLAG64(34), - CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(37), + CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(35), - CSD_MOVIE = MAKE_BITFLAG64(38), + CSD_MOVIE = MAKE_BITFLAG64(36), - CSD_CRI_LOGO = MAKE_BITFLAG64(39) + CSD_CRI_LOGO = MAKE_BITFLAG64(37), + + CSD_MAIN_MENU_PARTS_CAST_0221 = MAKE_BITFLAG64(38), + CSD_MAIN_MENU_PARTS_CAST_0222 = MAKE_BITFLAG64(39), + CSD_MAIN_MENU_PARTS_CAST_0226 = MAKE_BITFLAG64(40), + CSD_MAIN_MENU_PARTS_CAST_0227 = MAKE_BITFLAG64(41) }; struct CsdUVs From 4a5d017f1edccd0cc18867faa5a1b405a415cb76 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 13 Sep 2025 17:31:56 +0100 Subject: [PATCH 43/52] Fix multiplayer main menu text lingering at ultrawide --- MarathonRecomp/api/Marathon.h | 1 + MarathonRecomp/api/Marathon.inl | 11 ++-- MarathonRecomp/api/Sonicteam/HUDMainDisplay.h | 6 +-- MarathonRecomp/api/Sonicteam/HUDMainMenu.h | 13 +++++ MarathonRecomp/api/Sonicteam/HudTextParts.h | 12 ++--- .../patches/MainMenuTask_patches.cpp | 2 + MarathonRecomp/patches/MainMenuTask_patches.h | 1 + .../patches/aspect_ratio_patches.cpp | 54 ++++++++++++++++++- 8 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 MarathonRecomp/api/Sonicteam/HUDMainMenu.h diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index 568fa5b65..19b6fb9fe 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -37,6 +37,7 @@ #include "Sonicteam/HUDLimitTime.h" #include "Sonicteam/HUDLoading.h" #include "Sonicteam/HUDMainDisplay.h" +#include "Sonicteam/HUDMainMenu.h" #include "Sonicteam/HUDMessageWindow.h" #include "Sonicteam/HUDPopupScreen.h" #include "Sonicteam/HUDRaderMap.h" diff --git a/MarathonRecomp/api/Marathon.inl b/MarathonRecomp/api/Marathon.inl index 6d65ea1a3..433ce6a3d 100644 --- a/MarathonRecomp/api/Marathon.inl +++ b/MarathonRecomp/api/Marathon.inl @@ -106,17 +106,20 @@ inline size_t strlenU16(const uint16_t* str) return result; } -inline bool strcmpU16(const uint16_t* a, const uint16_t* b) +inline bool strcmpU16(const uint16_t* a, const uint16_t* b, bool endianSwapA = false, bool endianSwapB = false) { for (size_t i = 0; i < strlenU16(a); i += 2) { - if (a[i] != 0 && b[i] == 0) + auto c1 = endianSwapA ? ByteSwap(a[i]) : a[i]; + auto c2 = endianSwapB ? ByteSwap(b[i]) : b[i]; + + if (c1 != 0 && c2 == 0) return false; - if (a[i] == 0 && b[i] != 0) + if (c1 == 0 && c2 != 0) return false; - if (a[i] != b[i]) + if (c1 != c2) return false; } diff --git a/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h b/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h index 08d135b1e..3b4120129 100644 --- a/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h +++ b/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h @@ -11,13 +11,13 @@ namespace Sonicteam be m_Character; MARATHON_INSERT_PADDING(0x108); boost::shared_ptr m_Field184; - boost::shared_ptr m_TrickPointText; + boost::shared_ptr m_spTrickPointText; boost::shared_ptr m_Field194; MARATHON_INSERT_PADDING(0x10); - boost::shared_ptr m_SavePointTimeText; + boost::shared_ptr m_spSavePointTimeText; }; MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_Field184, 0x184); - MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_TrickPointText, 0x18C); + MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_spTrickPointText, 0x18C); MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_Field194, 0x194); } diff --git a/MarathonRecomp/api/Sonicteam/HUDMainMenu.h b/MarathonRecomp/api/Sonicteam/HUDMainMenu.h new file mode 100644 index 000000000..ecc5fe1d3 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HUDMainMenu.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class HUDMainMenu : public SoX::RefCountObject, public SoX::Engine::Task + { + public: + MARATHON_INSERT_PADDING(0x4FC); + xpointer m_pHudTextRoot; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/HudTextParts.h b/MarathonRecomp/api/Sonicteam/HudTextParts.h index bbb7a4a8a..c118c800b 100644 --- a/MarathonRecomp/api/Sonicteam/HudTextParts.h +++ b/MarathonRecomp/api/Sonicteam/HudTextParts.h @@ -4,26 +4,24 @@ #include #include #include +#include namespace Sonicteam { class HudTextParts : public SoX::RefCountObject { public: - MARATHON_INSERT_PADDING(8); + xpointer m_pNext; + xpointer m_pDocMarathonState; boost::shared_ptr m_spTextCard; boost::shared_ptr m_spTextEntity; xpointer m_pCsdObject; - MARATHON_INSERT_PADDING(0x38); + stdx::string m_SceneName; + stdx::string m_CastName; be m_Priority; be m_OffsetX; be m_OffsetY; MARATHON_INSERT_PADDING(4); be m_AlignmentFlags; - MARATHON_INSERT_PADDING(0x0C); - be m_Field7C; - be m_Field80; }; - - MARATHON_ASSERT_OFFSETOF(HudTextParts, m_Field80, 0x80); } diff --git a/MarathonRecomp/patches/MainMenuTask_patches.cpp b/MarathonRecomp/patches/MainMenuTask_patches.cpp index b09fc9338..3c83651d9 100644 --- a/MarathonRecomp/patches/MainMenuTask_patches.cpp +++ b/MarathonRecomp/patches/MainMenuTask_patches.cpp @@ -6,6 +6,8 @@ PPC_FUNC(sub_824FFCF8) { auto pMainMenuTask = (Sonicteam::MainMenuTask*)(base + ctx.r3.u32); + MainMenuTaskPatches::State = (Sonicteam::MainMenuTask::MainMenuState)pMainMenuTask->m_State.get(); + for (auto& event : MainMenuTaskPatches::Events) event->Update(pMainMenuTask, ctx.f1.f64); diff --git a/MarathonRecomp/patches/MainMenuTask_patches.h b/MarathonRecomp/patches/MainMenuTask_patches.h index 20cbc6389..6fd677510 100644 --- a/MarathonRecomp/patches/MainMenuTask_patches.h +++ b/MarathonRecomp/patches/MainMenuTask_patches.h @@ -6,5 +6,6 @@ class MainMenuTaskPatches { public: + static inline Sonicteam::MainMenuTask::MainMenuState State{}; static inline std::vector*> Events{}; }; diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 6a39906ba..998ff5db3 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1621,6 +1621,56 @@ void SetTextEntityModifier(Sonicteam::TextEntity* pTextEntity, uint64_t flags) pTextEntity->Update(); } +// Sonicteam::HUDMainMenu::Update +PPC_FUNC_IMPL(__imp__sub_824E11D0); +PPC_FUNC(sub_824E11D0) +{ + auto pHUDMainMenu = (Sonicteam::HUDMainMenu*)(base + ctx.r3.u32); + auto pHudTextRoot = pHUDMainMenu->m_pHudTextRoot->m_pNext; + + static bool s_preservedTextPositions{}; + static float s_multiplayerTextOffsetX{}; + static float s_tagTextOffsetX{}; + static float s_battleTextOffsetX{}; + + auto isTrialSelect = MainMenuTaskPatches::State >= 12 && MainMenuTaskPatches::State <= 15; + auto isTag = MainMenuTaskPatches::State == Sonicteam::MainMenuTask::MainMenuState_Tag; + + while (pHudTextRoot) + { + if (pHudTextRoot->m_SceneName == "main_menu") + { + if (pHudTextRoot->m_CastName == "multi_player") + { + if (!s_preservedTextPositions) + s_multiplayerTextOffsetX = pHudTextRoot->m_OffsetX; + + pHudTextRoot->m_OffsetX = isTrialSelect ? -10000.0f : s_multiplayerTextOffsetX; + } + else if (pHudTextRoot->m_CastName == "tag") + { + if (!s_preservedTextPositions) + s_tagTextOffsetX = pHudTextRoot->m_OffsetX; + + pHudTextRoot->m_OffsetX = (isTrialSelect || isTag) ? -10000.0f : s_tagTextOffsetX; + } + else if (pHudTextRoot->m_CastName == "battle") + { + if (!s_preservedTextPositions) + s_battleTextOffsetX = pHudTextRoot->m_OffsetX; + + pHudTextRoot->m_OffsetX = isTrialSelect ? -10000.0f : s_battleTextOffsetX; + } + } + + pHudTextRoot = pHudTextRoot->m_pNext; + } + + s_preservedTextPositions = true; + + __imp__sub_824E11D0(ctx, base); +} + // Sonicteam::HUDMainDisplay::Update PPC_FUNC_IMPL(__imp__sub_824DCF40); PPC_FUNC(sub_824DCF40) @@ -1628,9 +1678,9 @@ PPC_FUNC(sub_824DCF40) auto pHUDMainDisplay = (Sonicteam::HUDMainDisplay*)(base + ctx.r3.u32); SetTextEntityModifier(pHUDMainDisplay->m_Field184.get(), CSD_ALIGN_RIGHT | CSD_SCALE); - SetTextEntityModifier(pHUDMainDisplay->m_TrickPointText.get(), CSD_ALIGN_RIGHT | CSD_SCALE); + SetTextEntityModifier(pHUDMainDisplay->m_spTrickPointText.get(), CSD_ALIGN_RIGHT | CSD_SCALE); SetTextEntityModifier(pHUDMainDisplay->m_Field194.get(), CSD_ALIGN_RIGHT | CSD_SCALE); - SetTextEntityModifier(pHUDMainDisplay->m_SavePointTimeText.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); + SetTextEntityModifier(pHUDMainDisplay->m_spSavePointTimeText.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); __imp__sub_824DCF40(ctx, base); } From 758bbf1b3fd0c11e762ca84708f7f1c4927da895 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 13 Sep 2025 18:42:37 +0100 Subject: [PATCH 44/52] Fix multiplayer gauge and itembox alignment --- .../patches/aspect_ratio_patches.cpp | 19 +++++++--------- MarathonRecomp/patches/aspect_ratio_patches.h | 22 +++++++++---------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 998ff5db3..c349a465b 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -176,6 +176,8 @@ void AspectRatioPatches::ComputeOffsets() g_aspectRatioGameplayScale = ComputeScale(NARROW_ASPECT_RATIO); } + g_aspectRatioMultiplayerOffsetX = g_aspectRatioOffsetX / 2.0f; + g_aspectRatioNarrowScale = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (WIDE_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f); g_radarMapScale = 256 * g_aspectRatioScale * g_aspectRatioGameplayScale; } @@ -514,17 +516,12 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) { offsetX = g_aspectRatioOffsetX * 2.0f; - if ((modifier.Flags & CSD_MULTIPLAYER) != 0) - offsetX /= 2.0f; + if ((modifier.Flags & CSD_MULTIPLAYER) != 0 && g_aspectRatio > WIDE_ASPECT_RATIO) + offsetX = g_aspectRatioMultiplayerOffsetX * 2.0f; } else if ((modifier.Flags & CSD_ALIGN_LEFT) == 0) - { offsetX = g_aspectRatioOffsetX; - if ((modifier.Flags & CSD_MULTIPLAYER_CENTER) != 0) - offsetX /= 1.5f; - } - // Don't offset arrows at 16:9 or narrower. if ((modifier.Flags & CSD_CHEVRON) != 0 && g_aspectRatio <= WIDE_ASPECT_RATIO) offsetX = 0.0f; @@ -1762,7 +1759,7 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/battledisplay_1p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/power_a"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/power_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, - { HashStr("sprite/battledisplay_1p/itembox_01"), { CSD_MULTIPLAYER | CSD_MULTIPLAYER_CENTER | CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/battledisplay_1p/itembox_01"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/ring"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/ring_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/enemy"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, @@ -1779,7 +1776,7 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/battledisplay_2p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/power_a"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/power_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, - { HashStr("sprite/battledisplay_2p/itembox_01"), { CSD_MULTIPLAYER | CSD_MULTIPLAYER_CENTER | CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/battledisplay_2p/itembox_01"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/ring"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/ring_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/enemy"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, @@ -2142,7 +2139,7 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/tagdisplay_1p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/power_a"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/power_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, - { HashStr("sprite/tagdisplay_1p/itembox_01"), { CSD_MULTIPLAYER | CSD_MULTIPLAYER_CENTER | CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_1p/itembox_01"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/score"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/time"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/ring"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, @@ -2162,7 +2159,7 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/tagdisplay_2p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/power_a"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/power_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, - { HashStr("sprite/tagdisplay_2p/itembox_01"), { CSD_MULTIPLAYER | CSD_MULTIPLAYER_CENTER | CSD_ALIGN_BOTTOM | CSD_SCALE } }, + { HashStr("sprite/tagdisplay_2p/itembox_01"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/score"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/time"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/ring"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index b01d18b1b..5dd140369 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -13,6 +13,7 @@ inline float g_aspectRatio; inline float g_aspectRatioMovie; inline float g_aspectRatioOffsetX; inline float g_aspectRatioOffsetY; +inline float g_aspectRatioMultiplayerOffsetX; inline float g_aspectRatioScale; inline float g_aspectRatioGameplayScale; inline float g_aspectRatioNarrowScale; @@ -83,23 +84,22 @@ enum CsdFlags : uint64_t CSD_POD_CLONE = MAKE_BITFLAG64(29), CSD_MULTIPLAYER = MAKE_BITFLAG64(30), - CSD_MULTIPLAYER_CENTER = MAKE_BITFLAG64(31), - CSD_CHEVRON = MAKE_BITFLAG64(32), + CSD_CHEVRON = MAKE_BITFLAG64(31), - CSD_MODIFIER_ULTRAWIDE_ONLY = MAKE_BITFLAG64(33), - CSD_MODIFIER_NARROW_ONLY = MAKE_BITFLAG64(34), + CSD_MODIFIER_ULTRAWIDE_ONLY = MAKE_BITFLAG64(32), + CSD_MODIFIER_NARROW_ONLY = MAKE_BITFLAG64(33), - CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(35), + CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(34), - CSD_MOVIE = MAKE_BITFLAG64(36), + CSD_MOVIE = MAKE_BITFLAG64(35), - CSD_CRI_LOGO = MAKE_BITFLAG64(37), + CSD_CRI_LOGO = MAKE_BITFLAG64(36), - CSD_MAIN_MENU_PARTS_CAST_0221 = MAKE_BITFLAG64(38), - CSD_MAIN_MENU_PARTS_CAST_0222 = MAKE_BITFLAG64(39), - CSD_MAIN_MENU_PARTS_CAST_0226 = MAKE_BITFLAG64(40), - CSD_MAIN_MENU_PARTS_CAST_0227 = MAKE_BITFLAG64(41) + CSD_MAIN_MENU_PARTS_CAST_0221 = MAKE_BITFLAG64(37), + CSD_MAIN_MENU_PARTS_CAST_0222 = MAKE_BITFLAG64(38), + CSD_MAIN_MENU_PARTS_CAST_0226 = MAKE_BITFLAG64(39), + CSD_MAIN_MENU_PARTS_CAST_0227 = MAKE_BITFLAG64(40) }; struct CsdUVs From ab63805d3247b4a9ea872ebd985c0770d8b8117b Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 13 Sep 2025 18:58:42 +0100 Subject: [PATCH 45/52] Draw letter/pillarbox for credits --- MarathonRecomp/patches/aspect_ratio_patches.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index c349a465b..a13ec399d 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1254,6 +1254,15 @@ PPC_FUNC(sub_824D32C8) __imp__sub_824D32C8(ctx, base); } +// Sonicteam::EndingMode::Update +PPC_FUNC_IMPL(__imp__sub_824A4A40); +PPC_FUNC(sub_824A4A40) +{ + BlackBar::g_isVisible = true; + + __imp__sub_824A4A40(ctx, base); +} + // Sonicteam::HUDLimitTime::ProcessMessage PPC_FUNC_IMPL(__imp__sub_824D6E50); PPC_FUNC(sub_824D6E50) From 618c3ac3973ae7aa30c36a80008ae77fe0110f89 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 13 Sep 2025 19:02:05 +0100 Subject: [PATCH 46/52] API: remove self include from FrameObserver --- MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h | 1 - 1 file changed, 1 deletion(-) diff --git a/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h index c080aff9a..b736c7de5 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h @@ -2,7 +2,6 @@ #include #include -#include namespace Sonicteam::SoX::Graphics { From f3b2a194874fd9684de1e1f9089b2bc57cea07b9 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 13 Sep 2025 19:16:44 +0100 Subject: [PATCH 47/52] API: fix camera mappings in GameImp --- MarathonRecomp/api/Sonicteam/GameImp.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/MarathonRecomp/api/Sonicteam/GameImp.h b/MarathonRecomp/api/Sonicteam/GameImp.h index 83c0015cb..e59d7b9b0 100644 --- a/MarathonRecomp/api/Sonicteam/GameImp.h +++ b/MarathonRecomp/api/Sonicteam/GameImp.h @@ -57,9 +57,9 @@ namespace Sonicteam bool m_IsStage; MARATHON_INSERT_PADDING(0x0C); be m_Field1180; - MARATHON_INSERT_PADDING(0x58); - xpointer>> m_pvspCameras; - MARATHON_INSERT_PADDING(0x7DC); + MARATHON_INSERT_PADDING(0x54); + stdx::vector>> m_vvspCameras; + MARATHON_INSERT_PADDING(0x7D4); SoX::RefSharedPointer m_spPhysicsWorld; xpointer m_pMyCollisionFilter; @@ -74,12 +74,12 @@ namespace Sonicteam return -1; } - SoX::Scenery::CameraImp* GetCameraImp(const char* pName) const + SoX::Scenery::CameraImp* GetCameraImp(const char* pName, int which = 0) { - if (!m_pvspCameras) + if (m_vvspCameras.empty()) return nullptr; - for (auto& spCamera : *m_pvspCameras) + for (auto& spCamera : m_vvspCameras[which]) { auto pCameraImp = (SoX::Scenery::CameraImp*)spCamera.get(); From dc0ed5f9deae8eab9462521134b090cd9583af1e Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 13 Sep 2025 19:21:10 +0100 Subject: [PATCH 48/52] API: rename field in HudTextParts --- MarathonRecomp/api/Sonicteam/HudTextParts.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MarathonRecomp/api/Sonicteam/HudTextParts.h b/MarathonRecomp/api/Sonicteam/HudTextParts.h index c118c800b..8fe8f5172 100644 --- a/MarathonRecomp/api/Sonicteam/HudTextParts.h +++ b/MarathonRecomp/api/Sonicteam/HudTextParts.h @@ -12,7 +12,7 @@ namespace Sonicteam { public: xpointer m_pNext; - xpointer m_pDocMarathonState; + xpointer m_pDoc; boost::shared_ptr m_spTextCard; boost::shared_ptr m_spTextEntity; xpointer m_pCsdObject; From f64b4ab417dda5b0a42cee8015b5b2e4a961b8a7 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 13 Sep 2025 19:29:53 +0100 Subject: [PATCH 49/52] Copy font picture on resource load --- MarathonRecomp/patches/aspect_ratio_patches.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index a13ec399d..b9bd8ee58 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -23,7 +23,7 @@ static std::optional g_sceneModifier{}; static std::optional g_castNodeModifier{}; static std::optional g_castModifier{}; -static Sonicteam::TextFontPicture* g_pTextFontPicture{}; +static Sonicteam::TextFontPicture g_textFontPicture{}; static float g_corners[8]{}; static bool g_cornerExtract{}; @@ -1455,8 +1455,8 @@ void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity) if (variable.first == "picture") { - auto w = g_pTextFontPicture->m_TextureWidth; - auto h = g_pTextFontPicture->m_TextureHeight; + auto w = g_textFontPicture.m_TextureWidth; + auto h = g_textFontPicture.m_TextureHeight; auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation; @@ -1719,9 +1719,11 @@ PPC_FUNC(sub_82507E68) PPC_FUNC_IMPL(__imp__sub_8263CC40); PPC_FUNC(sub_8263CC40) { - g_pTextFontPicture = (Sonicteam::TextFontPicture*)(base + ctx.r3.u32); + auto pTextFontPicture = (Sonicteam::TextFontPicture*)(base + ctx.r3.u32); __imp__sub_8263CC40(ctx, base); + + g_textFontPicture = *pTextFontPicture; } // -------------- CSD MODIFIERS --------------- // From 2a65b50306d5191590ed44391b3868979f9e72a3 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 13 Sep 2025 21:07:55 +0100 Subject: [PATCH 50/52] Only store font picture texture dimensions --- MarathonRecomp/patches/aspect_ratio_patches.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index b9bd8ee58..68c22153b 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -23,7 +23,8 @@ static std::optional g_sceneModifier{}; static std::optional g_castNodeModifier{}; static std::optional g_castModifier{}; -static Sonicteam::TextFontPicture g_textFontPicture{}; +static float g_fontPictureWidth{}; +static float g_fontPictureHeight{}; static float g_corners[8]{}; static bool g_cornerExtract{}; @@ -1455,9 +1456,6 @@ void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity) if (variable.first == "picture") { - auto w = g_textFontPicture.m_TextureWidth; - auto h = g_textFontPicture.m_TextureHeight; - auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation; if (Config::ControllerIcons == EControllerIcons::Auto) @@ -1470,7 +1468,7 @@ void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity) auto baseParams = FindFontPictureModifier(g_pftModifierXenon, variable.second); auto newParams = FindFontPictureModifier(pftModifier, variable.second); - auto uv = PIXELS_TO_UV_COORDS(w, h, newParams.X, newParams.Y, newParams.Width, newParams.Height); + auto uv = PIXELS_TO_UV_COORDS(g_fontPictureWidth, g_fontPictureHeight, newParams.X, newParams.Y, newParams.Width, newParams.Height); auto& min = std::get<0>(uv); auto& max = std::get<1>(uv); @@ -1723,7 +1721,8 @@ PPC_FUNC(sub_8263CC40) __imp__sub_8263CC40(ctx, base); - g_textFontPicture = *pTextFontPicture; + g_fontPictureWidth = pTextFontPicture->m_TextureWidth; + g_fontPictureHeight = pTextFontPicture->m_TextureHeight; } // -------------- CSD MODIFIERS --------------- // From 1fb9c68e50e3aecb3537d93443210233c1020826 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 13 Sep 2025 22:08:08 +0100 Subject: [PATCH 51/52] Fix overlapping vertices --- .../patches/aspect_ratio_patches.cpp | 107 +++++++++--------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 68c22153b..4f870f9d0 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -718,9 +718,23 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) auto isRepeatLeft = (modifier.Flags & CSD_REPEAT_LEFT) != 0; auto isRepeatRight = (modifier.Flags & CSD_REPEAT_RIGHT) != 0; + auto isMainMenuPanels = (modifier.Flags & + (CSD_MAIN_MENU_PARTS_CAST_0221 | + CSD_MAIN_MENU_PARTS_CAST_0222 | + CSD_MAIN_MENU_PARTS_CAST_0226 | + CSD_MAIN_MENU_PARTS_CAST_0227)) != 0; + auto r3 = ctx.r3; auto r5 = ctx.r5; + auto drawOriginal = [&]() + { + ctx.r3 = r3; + ctx.r4 = ctx.r1; + ctx.r5 = r5; + original(ctx, base); + }; + if (isRepeatLeft || isRepeatRight) { auto isFlipHorz = (modifier.Flags & CSD_REPEAT_FLIP_HORIZONTAL) != 0; @@ -769,10 +783,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) while (x > 0.0f) { - ctx.r3 = r3; - ctx.r4 = ctx.r1; - ctx.r5 = r5; - original(ctx, base); + drawOriginal(); if ((modifier.Flags & CSD_CHEVRON) != 0) { @@ -859,10 +870,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) while (x < vpWidth) { - ctx.r3 = r3; - ctx.r4 = ctx.r1; - ctx.r5 = r5; - original(ctx, base); + drawOriginal(); if ((modifier.Flags & CSD_CHEVRON) != 0) { @@ -920,10 +928,8 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) } else { - ctx.r3 = r3; - ctx.r4 = ctx.r1; - ctx.r5 = r5; - original(ctx, base); + if (!isMainMenuPanels) + drawOriginal(); // Clone Audio Room "pod" left edge to fill // in the rest of the box at ultrawide. @@ -948,10 +954,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) v1->U = v1->U + 0.008f; v1->V = v1->V + 0.008f; - ctx.r3 = r3; - ctx.r4 = ctx.r1; - ctx.r5 = r5; - original(ctx, base); + drawOriginal(); } // Don't adjust CRI logo at 16:9 or if the alignment mode is centre at ultrawide. @@ -1006,10 +1009,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) v3->U = 0.0f; v3->V = 0.01f; - ctx.r3 = r3; - ctx.r4 = ctx.r1; - ctx.r5 = r5; - original(ctx, base); + drawOriginal(); ///////////////////////////////////////////// // Clone and scale to fit technology logos // @@ -1048,15 +1048,15 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) v3->U = max.x; v3->V = max.y; - ctx.r3 = r3; - ctx.r4 = ctx.r1; - ctx.r5 = r5; - original(ctx, base); + drawOriginal(); } } if (g_aspectRatio > WIDE_ASPECT_RATIO) { + if (isMainMenuPanels) + drawOriginal(); + ///////////////////////////////////////////////// // Scale metal borders to wide aspect ratios // ///////////////////////////////////////////////// @@ -1079,10 +1079,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) getVertex(2)->U = getVertex(2)->U + -0.1f; getVertex(3)->U = getVertex(3)->U + -0.1f; - ctx.r3 = r3; - ctx.r4 = ctx.r1; - ctx.r5 = r5; - original(ctx, base); + drawOriginal(); } if ((modifier.Flags & CSD_MAIN_MENU_PARTS_CAST_0226) != 0) @@ -1101,10 +1098,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) getVertex(2)->U = getVertex(2)->U + -0.8f; getVertex(3)->U = getVertex(3)->U + -0.8f; - ctx.r3 = r3; - ctx.r4 = ctx.r1; - ctx.r5 = r5; - original(ctx, base); + drawOriginal(); } if ((modifier.Flags & CSD_MAIN_MENU_PARTS_CAST_0227) != 0) @@ -1123,10 +1117,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) getVertex(0)->U = getVertex(0)->U + -0.8f; getVertex(1)->U = getVertex(1)->U + -0.8f; - ctx.r3 = r3; - ctx.r4 = ctx.r1; - ctx.r5 = r5; - original(ctx, base); + drawOriginal(); } } else @@ -1137,9 +1128,19 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & (CSD_MAIN_MENU_PARTS_CAST_0221 | CSD_MAIN_MENU_PARTS_CAST_0222)) != 0) { + // Move vertices for UV adjustment. + getVertex(0)->Y = getVertex(0)->Y + 7.0f; + getVertex(2)->Y = getVertex(2)->Y + 7.0f; + + // Crop UVs to move panel top to vertex edge. + getVertex(0)->V = getVertex(0)->V + 0.00675f; + getVertex(2)->V = getVertex(2)->V + 0.00675f; + + drawOriginal(); + // Move cloned cast above existing cast. for (size_t i = 0; i < r5.u32; i++) - getVertex(i)->Y = getVertex(i)->Y - height + 6.0f; + getVertex(i)->Y = getVertex(i)->Y - height; flipVertically(); @@ -1152,22 +1153,27 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) } // Crop UVs to remove metal notch. - getVertex(0)->V = getVertex(0)->V + 0.005f; - getVertex(1)->V = getVertex(1)->V + -0.075f; - getVertex(2)->V = getVertex(2)->V + 0.005f; - getVertex(3)->V = getVertex(3)->V + -0.075f; - - ctx.r3 = r3; - ctx.r4 = ctx.r1; - ctx.r5 = r5; - original(ctx, base); + getVertex(1)->V = getVertex(1)->V + -0.067f; + getVertex(3)->V = getVertex(3)->V + -0.067f; + + drawOriginal(); } if ((modifier.Flags & (CSD_MAIN_MENU_PARTS_CAST_0226 | CSD_MAIN_MENU_PARTS_CAST_0227)) != 0) { + // Move vertices for UV adjustment. + getVertex(1)->Y = getVertex(1)->Y - 20.0f; + getVertex(3)->Y = getVertex(3)->Y - 20.0f; + + // Crop UVs to move panel bottom to vertex edge. + getVertex(1)->V = getVertex(1)->V + -0.0168; + getVertex(3)->V = getVertex(3)->V + -0.0168; + + drawOriginal(); + // Move cloned cast below existing cast. for (size_t i = 0; i < r5.u32; i++) - getVertex(i)->Y = getVertex(i)->Y + height - 18.0f; + getVertex(i)->Y = getVertex(i)->Y + height; flipVertically(); @@ -1181,14 +1187,9 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) // Crop UVs to remove metal dip. getVertex(0)->V = getVertex(0)->V + 0.065f; - getVertex(1)->V = getVertex(1)->V + -0.0175f; getVertex(2)->V = getVertex(2)->V + 0.065f; - getVertex(3)->V = getVertex(3)->V + -0.0175f; - ctx.r3 = r3; - ctx.r4 = ctx.r1; - ctx.r5 = r5; - original(ctx, base); + drawOriginal(); } } From 1a4793f7005507677b241ae58bbb52809f419102 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 13 Sep 2025 23:01:54 +0100 Subject: [PATCH 52/52] Only extend metal borders at aspect ratios narrower than 4:3 --- .../patches/aspect_ratio_patches.cpp | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 4f870f9d0..4dfd9e8bc 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1132,7 +1132,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) getVertex(0)->Y = getVertex(0)->Y + 7.0f; getVertex(2)->Y = getVertex(2)->Y + 7.0f; - // Crop UVs to move panel top to vertex edge. + // Crop top UVs to move panel top to vertex edge. getVertex(0)->V = getVertex(0)->V + 0.00675f; getVertex(2)->V = getVertex(2)->V + 0.00675f; @@ -1144,17 +1144,21 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) flipVertically(); - // Extend to the top of the screen - // if the first clone no longer fits. - if (getVertex(1)->Y - height > 0.0f) + // Crop top UVs to reduce pattern repetition. + getVertex(0)->V = getVertex(0)->V + 0.005f; + getVertex(2)->V = getVertex(2)->V + 0.005f; + + if (g_aspectRatio < NARROW_ASPECT_RATIO) { + // Extend to the top of the screen + // if the first clone no longer fits. getVertex(1)->Y = 0.0f; getVertex(3)->Y = 0.0f; - } - // Crop UVs to remove metal notch. - getVertex(1)->V = getVertex(1)->V + -0.067f; - getVertex(3)->V = getVertex(3)->V + -0.067f; + // Crop bottom UVs to remove metal notch. + getVertex(1)->V = getVertex(1)->V + -0.067f; + getVertex(3)->V = getVertex(3)->V + -0.067f; + } drawOriginal(); } @@ -1165,7 +1169,7 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) getVertex(1)->Y = getVertex(1)->Y - 20.0f; getVertex(3)->Y = getVertex(3)->Y - 20.0f; - // Crop UVs to move panel bottom to vertex edge. + // Crop bottom UVs to move panel bottom to vertex edge. getVertex(1)->V = getVertex(1)->V + -0.0168; getVertex(3)->V = getVertex(3)->V + -0.0168; @@ -1177,17 +1181,21 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) flipVertically(); - // Extend to the bottom of the screen - // if the first clone no longer fits. - if (getVertex(0)->Y - height < vpHeight) + // Crop bottom UVs to reduce pattern repetition. + getVertex(1)->V = getVertex(1)->V + -0.005f; + getVertex(3)->V = getVertex(3)->V + -0.005f; + + if (g_aspectRatio < NARROW_ASPECT_RATIO) { + // Extend to the bottom of the screen + // if the first clone no longer fits. getVertex(0)->Y = vpHeight; getVertex(2)->Y = vpHeight; - } - // Crop UVs to remove metal dip. - getVertex(0)->V = getVertex(0)->V + 0.065f; - getVertex(2)->V = getVertex(2)->V + 0.065f; + // Crop top UVs to remove metal dip. + getVertex(0)->V = getVertex(0)->V + 0.065f; + getVertex(2)->V = getVertex(2)->V + 0.065f; + } drawOriginal(); }