diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index a6969c222..b25d8d34d 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -57,6 +57,7 @@ #include "Sonicteam/MyPhantom.h" #include "Sonicteam/MyTexture.h" #include "Sonicteam/MyTransforms.h" +#include "Sonicteam/NoSyncThread.h" #include "Sonicteam/Player/IDynamicLink.h" #include "Sonicteam/Player/IExportExternalFlag.h" #include "Sonicteam/Player/IExportPostureRequestFlag.h" @@ -133,6 +134,7 @@ #include "Sonicteam/SoX/Scenery/CameraEventCallback.h" #include "Sonicteam/SoX/Scenery/CameraImp.h" #include "Sonicteam/SoX/Scenery/Drawable.h" +#include "Sonicteam/SoX/Thread.h" #include "Sonicteam/StdImageFilters/BurnoutBlurFilter.h" #include "Sonicteam/StdImageFilters/SingleTechniqueFilter.h" #include "Sonicteam/System/CreateStatic.h" diff --git a/MarathonRecomp/api/Sonicteam/HUDLoading.h b/MarathonRecomp/api/Sonicteam/HUDLoading.h index ba43decc6..f3e0c353e 100644 --- a/MarathonRecomp/api/Sonicteam/HUDLoading.h +++ b/MarathonRecomp/api/Sonicteam/HUDLoading.h @@ -9,10 +9,13 @@ namespace Sonicteam public: enum HUDLoadingFlags { - HUDLoadingFlags_Finished = 6 + HUDLoadingFlags_Finished = 6, + HUDLoadingFlags_Open = 0x200, + HUDLoadingFlags_End = 0x400 }; MARATHON_INSERT_PADDING(0x5C); be m_Flags; + MARATHON_INSERT_PADDING(8); }; } diff --git a/MarathonRecomp/api/Sonicteam/HUDMessageWindow.h b/MarathonRecomp/api/Sonicteam/HUDMessageWindow.h index dc6239e72..f7232a899 100644 --- a/MarathonRecomp/api/Sonicteam/HUDMessageWindow.h +++ b/MarathonRecomp/api/Sonicteam/HUDMessageWindow.h @@ -10,7 +10,7 @@ namespace Sonicteam public: xpointer m_pCsdObject; MARATHON_INSERT_PADDING(0x1C); - xpointer m_pThis; + xpointer m_pParent; MARATHON_INSERT_PADDING(0x14); xpointer m_pHintWindowTask; }; diff --git a/MarathonRecomp/api/Sonicteam/NoSyncThread.h b/MarathonRecomp/api/Sonicteam/NoSyncThread.h new file mode 100644 index 000000000..424a5db25 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/NoSyncThread.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +namespace Sonicteam +{ + class NoSyncThread : SoX::Thread {}; +} diff --git a/MarathonRecomp/api/Sonicteam/SelectWindowTask.h b/MarathonRecomp/api/Sonicteam/SelectWindowTask.h index b59f2a2ec..4a5e36cc8 100644 --- a/MarathonRecomp/api/Sonicteam/SelectWindowTask.h +++ b/MarathonRecomp/api/Sonicteam/SelectWindowTask.h @@ -12,5 +12,7 @@ namespace Sonicteam be m_ChosenIndex; MARATHON_INSERT_PADDING(0x28); be m_SelectedIndex; + MARATHON_INSERT_PADDING(0x14); + be m_BreatheFrame; }; } diff --git a/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h index b736c7de5..0bb699f0a 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h @@ -10,7 +10,7 @@ namespace Sonicteam::SoX::Graphics public: xpointer m_pVftable; MARATHON_INSERT_PADDING(8); - xpointer m_pThis; + xpointer m_pParent; xpointer m_pFrameGP; }; } diff --git a/MarathonRecomp/api/Sonicteam/SoX/Graphics/TechniqueFXL.h b/MarathonRecomp/api/Sonicteam/SoX/Graphics/TechniqueFXL.h index 8887b4b88..d7be8eee0 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Graphics/TechniqueFXL.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/TechniqueFXL.h @@ -10,7 +10,7 @@ namespace Sonicteam::SoX::Graphics public: xpointer m_pShader; MARATHON_INSERT_PADDING(0x10); - xpointer m_pThis; + xpointer m_pParent; MARATHON_INSERT_PADDING(4); stdx::string m_TechniqueName; }; diff --git a/MarathonRecomp/api/Sonicteam/SoX/Physics/Shape.h b/MarathonRecomp/api/Sonicteam/SoX/Physics/Shape.h index 0dc068a4c..43c657a3d 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Physics/Shape.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Physics/Shape.h @@ -10,8 +10,8 @@ namespace Sonicteam::SoX::Physics public: struct Vftable : public RefCountObject::Vftable { - be VFunction04; // (__out VECTOR* u1, __out VECTOR* u2) - be InitializeVolume; + be fpVFunction04; // (__out VECTOR* u1, __out VECTOR* u2) + be fpInitializeVolume; }; be m_ShapeType; diff --git a/MarathonRecomp/api/Sonicteam/SoX/RefCountObject.h b/MarathonRecomp/api/Sonicteam/SoX/RefCountObject.h index a1486e02c..3b1c4101a 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/RefCountObject.h +++ b/MarathonRecomp/api/Sonicteam/SoX/RefCountObject.h @@ -9,7 +9,7 @@ namespace Sonicteam::SoX public: struct Vftable { - be Destroy; + be fpDestroy; }; xpointer m_pVftable; @@ -30,7 +30,7 @@ namespace Sonicteam::SoX void* Destroy(uint32_t flag) { - return GuestToHostFunction(m_pVftable->Destroy, this, flag); + return GuestToHostFunction(m_pVftable->fpDestroy, this, flag); } }; } diff --git a/MarathonRecomp/api/Sonicteam/SoX/Thread.h b/MarathonRecomp/api/Sonicteam/SoX/Thread.h new file mode 100644 index 000000000..e053fde5b --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Thread.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace Sonicteam::SoX +{ + class Thread + { + public: + xpointer m_pVftable; + MARATHON_INSERT_PADDING(8); + xpointer m_pParent; + be m_EventHandleA; + be m_EventHandleB; + MARATHON_INSERT_PADDING(4); + be m_ThreadHandle; + bool m_Field20; + MARATHON_INSERT_PADDING(3); + be m_DeltaTime; + MARATHON_INSERT_PADDING(8); + xpointer m_pName; + MARATHON_INSERT_PADDING(4); + bool m_Field38; + bool m_Field39; + MARATHON_INSERT_PADDING(6); + xpointer m_pContext; + MARATHON_INSERT_PADDING(8); + + template + T* GetContext() + { + return (T*)m_pContext.get(); + } + }; +} diff --git a/MarathonRecomp/api/stdx/wstring.h b/MarathonRecomp/api/stdx/wstring.h index cd45fba21..3d13b0ab5 100644 --- a/MarathonRecomp/api/stdx/wstring.h +++ b/MarathonRecomp/api/stdx/wstring.h @@ -97,6 +97,11 @@ namespace stdx } bool operator==(const uint16_t* str) const + { + return strcmpU16(c_str(), str, false, true); + } + + bool operator==(xpointer str) const { return strcmpU16(c_str(), str); } diff --git a/MarathonRecomp/patches/fps_patches.cpp b/MarathonRecomp/patches/fps_patches.cpp index bab1067bd..85a6ce82a 100644 --- a/MarathonRecomp/patches/fps_patches.cpp +++ b/MarathonRecomp/patches/fps_patches.cpp @@ -1,6 +1,32 @@ #include #include +constexpr double REFERENCE_DELTA_TIME = 1.0 / 60.0; + +// Only use this in threaded context with fixed delta time! +double MakeDeltaTime(std::chrono::steady_clock::time_point& prev) +{ + auto now = std::chrono::steady_clock::now(); + auto deltaTime = std::min(std::chrono::duration(now - prev).count(), 1.0 / 15.0); + + prev = now; + now = std::chrono::steady_clock::now(); + + return deltaTime; +} + +bool HasFrameElapsed(double reference, double& timeElapsed, double deltaTime) +{ + timeElapsed += deltaTime; + + if (timeElapsed < reference) + return false; + + timeElapsed = 0.0f; + + return true; +} + // Sonicteam::SoX::Physics::Havok::WorldHavok::Update PPC_FUNC_IMPL(__imp__sub_82587AA8); PPC_FUNC(sub_82587AA8) @@ -121,3 +147,71 @@ void PlayerObject_ProcessMsgSuckPlayer_FixDeltaTime(PPCRegister& message, PPCReg deltaTime.f64 = pMessage->DeltaTime; } + +// Allocate more space to store the previous loading +// time for this instance of Sonicteam::HUDLoading. +void HUDLoadingAlloc(PPCRegister& r3) +{ + r3.u32 += sizeof(std::chrono::steady_clock::time_point); +} + +// Sonicteam::HUDLoading::HUDLoading +PPC_FUNC_IMPL(__imp__sub_824D7BC8); +PPC_FUNC(sub_824D7BC8) +{ + auto pPrevLoadingTime = (std::chrono::steady_clock::time_point*)(base + ctx.r3.u32 + sizeof(Sonicteam::HUDLoading)); + + *pPrevLoadingTime = {}; + + __imp__sub_824D7BC8(ctx, base); +} + +// Accumulate own delta time and provide it to the CSD update function. +void HUDLoading_DeltaTimeFix(PPCRegister& pThis, PPCRegister& deltaTime) +{ + auto pPrevLoadingTime = (std::chrono::steady_clock::time_point*)g_memory.Translate(pThis.u32 + sizeof(Sonicteam::HUDLoading)); + + deltaTime.f64 = MakeDeltaTime(*pPrevLoadingTime); +} + +// This function is an override of Sonicteam::SoX::Engine::Task::Update, +// it does not pass delta time to HUDCALLBACK::Update, so we must preserve +// the register here in order to do it ourselves. +void HUDWindow_PreserveDeltaTime(PPCRegister& f31, PPCRegister& f1) +{ + f31.f64 = f1.f64; +} + +// HUDCALLBACK::Update doesn't have a delta time argument, +// so we pass one in here to do some delta time accumulation +// safely in the hooks below. +void HUDWindow_Callback(PPCRegister& f1, PPCRegister& f31) +{ + f1.f64 = f31.f64; +} + +// Sonicteam::PriceListWindowTask::HUDCALLBACK::Update +PPC_FUNC_IMPL(__imp__sub_8250AAB0); +PPC_FUNC(sub_8250AAB0) +{ + static auto s_time = 0.0; + + // Fix for white ⇄ red text breathing animation. + if (!HasFrameElapsed(REFERENCE_DELTA_TIME, s_time, ctx.f1.f64)) + return; + + __imp__sub_8250AAB0(ctx, base); +} + +// Sonicteam::SelectWindowTask::HUDCALLBACK::Update +PPC_FUNC_IMPL(__imp__sub_8250D698); +PPC_FUNC(sub_8250D698) +{ + static auto s_time = 0.0; + + // Fix for white ⇄ red text breathing animation. + if (!HasFrameElapsed(REFERENCE_DELTA_TIME, s_time, ctx.f1.f64)) + return; + + __imp__sub_8250D698(ctx, base); +} diff --git a/MarathonRecompLib/config/Marathon.toml b/MarathonRecompLib/config/Marathon.toml index 6bb891b7d..beb75ac4b 100644 --- a/MarathonRecompLib/config/Marathon.toml +++ b/MarathonRecompLib/config/Marathon.toml @@ -508,3 +508,33 @@ registers = ["r28", "f1"] name = "PlayerObject_ProcessMsgSuckPlayer_FixDeltaTime" address = 0x8219B660 registers = ["r28", "f31"] + +[[midasm_hook]] +name = "HUDLoadingAlloc" +address = 0x824FAE14 +registers = ["r3"] + +[[midasm_hook]] +name = "HUDLoadingAlloc" +address = 0x824FAF60 +registers = ["r3"] + +[[midasm_hook]] +name = "HUDLoadingAlloc" +address = 0x824FB008 +registers = ["r3"] + +[[midasm_hook]] +name = "HUDLoading_DeltaTimeFix" +address = 0x824D7510 +registers = ["r31", "f1"] + +[[midasm_hook]] +name = "HUDWindow_PreserveDeltaTime" +address = 0x824FA03C +registers = ["f31", "f1"] + +[[midasm_hook]] +name = "HUDWindow_Callback" +address = 0x824FA088 +registers = ["f1", "f31"]