diff --git a/MarathonRecomp/CMakeLists.txt b/MarathonRecomp/CMakeLists.txt index 500513415..13405a8c9 100644 --- a/MarathonRecomp/CMakeLists.txt +++ b/MarathonRecomp/CMakeLists.txt @@ -140,6 +140,8 @@ set(MARATHON_RECOMP_PATCHES_CXX_SOURCES "patches/camera_patches.cpp" "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/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 d1825f35d..ac8b38b8c 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmProject.h +++ b/MarathonRecomp/api/CSD/Manager/csdmProject.h @@ -1,31 +1,26 @@ #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: 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 "CSD/Manager/csdmProject.inl" 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 c3904cf96..ea6deab69 100644 --- a/MarathonRecomp/api/CSD/Manager/csdmScene.h +++ b/MarathonRecomp/api/CSD/Manager/csdmScene.h @@ -1,23 +1,107 @@ #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; - enum EMotionRepeatType : uint32_t + struct Cast { - eMotionRepeatType_PlayOnce = 0, - eMotionRepeatType_Loop = 1, - eMotionRepeatType_PingPong = 2, - eMotionRepeatType_PlayThenDestroy = 3 + MARATHON_INSERT_PADDING(0x144); + }; + + 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(8); + be FPS; + MARATHON_INSERT_PADDING(0x24); + be CastNodeCount; + MARATHON_INSERT_PADDING(4); + xpointer pCastNodes; + MARATHON_INSERT_PADDING(4); + 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 + { + 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 MotionRepeatType : uint32_t + { + MotionRepeatType_PlayOnce, + MotionRepeatType_Loop, + MotionRepeatType_PingPong, + MotionRepeatType_PlayThenDestroy }; class CScene : public CResourceBase, SubjectBase, CBase @@ -32,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/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.h b/MarathonRecomp/api/Marathon.h index d8f42868a..19b6fb9fe 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -19,23 +19,43 @@ #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" #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/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" +#include "Sonicteam/HUDStageTitle.h" +#include "Sonicteam/HintWindowTask.h" +#include "Sonicteam/HudTextParts.h" #include "Sonicteam/ImageFilter.h" +#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" #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" @@ -66,22 +86,30 @@ #include "Sonicteam/Player/State/TailsContext.h" #include "Sonicteam/Player/State/TailsFlight.h" #include "Sonicteam/Player/Zock.h" +#include "Sonicteam/RaderMapManager.h" #include "Sonicteam/SaveDataTask.h" #include "Sonicteam/SaveDataTaskXENON.h" #include "Sonicteam/SelectWindowTask.h" #include "Sonicteam/SoX/AI/StateMachine.h" +#include "Sonicteam/SoX/ApplicationXenon.h" #include "Sonicteam/SoX/Audio/IAudioEngine.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/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" +#include "Sonicteam/SoX/IResource2.h" +#include "Sonicteam/SoX/IResourceMgr.h" #include "Sonicteam/SoX/Input/Manager.h" #include "Sonicteam/SoX/Math/Matrix.h" #include "Sonicteam/SoX/Math/Quaternion.h" @@ -99,10 +127,20 @@ #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" #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" +#include "Sonicteam/TextFontPictureMgr.h" #include "Sonicteam/TitleTask.h" #include "boost/smart_ptr/make_shared_object.h" #include "boost/smart_ptr/shared_ptr.h" @@ -121,3 +159,4 @@ #include "hk330/hkpWorldObject.h" #include "stdx/string.h" #include "stdx/vector.h" +#include "stdx/wstring.h" diff --git a/MarathonRecomp/api/Marathon.inl b/MarathonRecomp/api/Marathon.inl index 134f99646..433ce6a3d 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,116 @@ 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; +} + +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, bool endianSwapA = false, bool endianSwapB = false) +{ + for (size_t i = 0; i < strlenU16(a); i += 2) + { + auto c1 = endianSwapA ? ByteSwap(a[i]) : a[i]; + auto c2 = endianSwapB ? ByteSwap(b[i]) : b[i]; + + if (c1 != 0 && c2 == 0) + return false; + + if (c1 == 0 && c2 != 0) + return false; + + if (c1 != c2) + 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/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/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/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 525b2ffa6..b2c38ba91 100644 --- a/MarathonRecomp/api/Sonicteam/Camera/SonicCamera.h +++ b/MarathonRecomp/api/Sonicteam/Camera/SonicCamera.h @@ -5,7 +5,9 @@ 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; be m_AzDamping; @@ -16,5 +18,8 @@ namespace Sonicteam::Camera 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 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/CsdManager.h b/MarathonRecomp/api/Sonicteam/CsdManager.h new file mode 100644 index 000000000..88f0c1d17 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/CsdManager.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include +#include + +namespace Sonicteam +{ + class CsdManager : public SoX::IResourceMgr, public System::Singleton> + { + public: + MARATHON_INSERT_PADDING(0x1C); + }; +} diff --git a/MarathonRecomp/api/Sonicteam/CsdObject.h b/MarathonRecomp/api/Sonicteam/CsdObject.h new file mode 100644 index 000000000..34423349e --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/CsdObject.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +namespace Sonicteam +{ + class CsdObject : public SoX::RefCountObject, public CsdLink + { + public: + 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/GameImp.h b/MarathonRecomp/api/Sonicteam/GameImp.h index 9b1459c01..e59d7b9b0 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(0x54); + stdx::vector>> m_vvspCameras; + MARATHON_INSERT_PADDING(0x7D4); 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, int which = 0) + { + if (m_vvspCameras.empty()) + return nullptr; + + for (auto& spCamera : m_vvspCameras[which]) + { + 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/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/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/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/api/Sonicteam/HUDLoading.h b/MarathonRecomp/api/Sonicteam/HUDLoading.h index 35ab2f5fe..ba43decc6 100644 --- a/MarathonRecomp/api/Sonicteam/HUDLoading.h +++ b/MarathonRecomp/api/Sonicteam/HUDLoading.h @@ -7,6 +7,11 @@ namespace Sonicteam class HUDLoading : public SoX::RefCountObject, public SoX::Engine::Task { public: + enum HUDLoadingFlags + { + HUDLoadingFlags_Finished = 6 + }; + MARATHON_INSERT_PADDING(0x5C); be m_Flags; }; diff --git a/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h b/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h new file mode 100644 index 000000000..3b4120129 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HUDMainDisplay.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class HUDMainDisplay : public SoX::RefCountObject, public SoX::Engine::Task + { + public: + MARATHON_INSERT_PADDING(0x24); + be m_Character; + MARATHON_INSERT_PADDING(0x108); + boost::shared_ptr m_Field184; + boost::shared_ptr m_spTrickPointText; + boost::shared_ptr m_Field194; + MARATHON_INSERT_PADDING(0x10); + boost::shared_ptr m_spSavePointTimeText; + }; + + MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_Field184, 0x184); + 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/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/HUDPopupScreen.h b/MarathonRecomp/api/Sonicteam/HUDPopupScreen.h new file mode 100644 index 000000000..3eae97e65 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HUDPopupScreen.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include + +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; + stdx::string m_SceneName; + stdx::string m_SpriteName; + 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/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/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..ab3c938a8 --- /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_Field8C; + boost::shared_ptr m_Field94; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/HudTextParts.h b/MarathonRecomp/api/Sonicteam/HudTextParts.h new file mode 100644 index 000000000..8fe8f5172 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/HudTextParts.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Sonicteam +{ + class HudTextParts : public SoX::RefCountObject + { + public: + xpointer m_pNext; + xpointer m_pDoc; + boost::shared_ptr m_spTextCard; + boost::shared_ptr m_spTextEntity; + xpointer m_pCsdObject; + stdx::string m_SceneName; + stdx::string m_CastName; + be m_Priority; + be m_OffsetX; + be m_OffsetY; + MARATHON_INSERT_PADDING(4); + be m_AlignmentFlags; + }; +} 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/MainMenuTask.h b/MarathonRecomp/api/Sonicteam/MainMenuTask.h new file mode 100644 index 000000000..8af83e1ff --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/MainMenuTask.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#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; + 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/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/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 new file mode 100644 index 000000000..c7677b98b --- /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); + }; +} 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/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/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/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..9ef00f56a --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameGP.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#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..b736c7de5 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#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/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/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/IResource.h b/MarathonRecomp/api/Sonicteam/SoX/IResource.h index 259a33f0b..52cfe31ff 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(0x3C); }; } diff --git a/MarathonRecomp/api/Sonicteam/SoX/IResource2.h b/MarathonRecomp/api/Sonicteam/SoX/IResource2.h new file mode 100644 index 000000000..2251aa7d0 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/IResource2.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#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..eedf59aed --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/IResourceMgr.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace Sonicteam::SoX +{ + class IResourceMgr + { + public: + struct Vftable + { + be fpDestroy; + be fpMakeResource; + MARATHON_INSERT_PADDING(4); + }; + + xpointer m_pVftable; + MARATHON_INSERT_PADDING(0x0C); + }; +} 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/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/CameraImp.h b/MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraImp.h new file mode 100644 index 000000000..5d2b42f14 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraImp.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#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/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/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 new file mode 100644 index 000000000..fbee3f461 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/TextCard.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class TextCard + { + public: + boost::shared_ptr m_spResource; + xpointer m_pText; + xpointer m_pVariables; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/TextEntity.h b/MarathonRecomp/api/Sonicteam/TextEntity.h new file mode 100644 index 000000000..810a204ed --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/TextEntity.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +namespace Sonicteam +{ + class TextFont; + class TextBoard; + + class TextEntity : public CsdLink + { + public: + struct Vertex + { + be X; + be Y; + MARATHON_INSERT_PADDING(0x10); + be Colour; // ARGB8888 + be U; + be V; + MARATHON_INSERT_PADDING(0x18); + }; + + 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(8); + stdx::wstring m_Text; + stdx::wstring m_Field5C; + MARATHON_INSERT_PADDING(4); + xpointer m_pVariables; + stdx::wstring m_Field80; + 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(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) + xpointer m_pImageVertices; // BL/TL/TR BL/TR/BR (two triangles per image) + be m_CharacterVertexCount; + be m_ImageVertexCount; + be m_TextLength; + MARATHON_INSERT_PADDING(4); + be m_Field100; + be m_Field104; + MARATHON_INSERT_PADDING(8); + }; + + 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); + 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 9efb429f1..1f029ce55 100644 --- a/MarathonRecomp/api/stdx/string.h +++ b/MarathonRecomp/api/stdx/string.h @@ -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/api/stdx/wstring.h b/MarathonRecomp/api/stdx/wstring.h new file mode 100644 index 000000000..cd45fba21 --- /dev/null +++ b/MarathonRecomp/api/stdx/wstring.h @@ -0,0 +1,104 @@ +#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; + } + + 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 = strlenU16(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 strcmpU16(c_str(), str); + } + }; +}; diff --git a/MarathonRecomp/app.cpp b/MarathonRecomp/app.cpp index eb1990630..4c08a7efa 100644 --- a/MarathonRecomp/app.cpp +++ b/MarathonRecomp/app.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ PPC_FUNC(sub_8262A568) App::s_isMissingDLC = true; App::s_language = Config::Language; + Sonicteam::Globals::Init(); Registry::Save(); struct RenderConfig @@ -58,6 +60,8 @@ PPC_FUNC(sub_8262A568) __imp__sub_8262A568(ctx, base); App::s_pApp = (Sonicteam::AppMarathon*)g_memory.Translate(ctx.r3.u32); + + InitPatches(); } // Sonicteam::DocMarathonState::Update 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/MainMenuTask_patches.cpp b/MarathonRecomp/patches/MainMenuTask_patches.cpp new file mode 100644 index 000000000..3c83651d9 --- /dev/null +++ b/MarathonRecomp/patches/MainMenuTask_patches.cpp @@ -0,0 +1,15 @@ +#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); + + MainMenuTaskPatches::State = (Sonicteam::MainMenuTask::MainMenuState)pMainMenuTask->m_State.get(); + + 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..6fd677510 --- /dev/null +++ b/MarathonRecomp/patches/MainMenuTask_patches.h @@ -0,0 +1,11 @@ +#pragma once + +#include "hook_event.h" +#include + +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 832f96438..4dfd9e8bc 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -1,42 +1,1331 @@ #include "aspect_ratio_patches.h" #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #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{}; + +static std::optional g_sceneModifier{}; +static std::optional g_castNodeModifier{}; +static std::optional g_castModifier{}; + +static float g_fontPictureWidth{}; +static float g_fontPictureHeight{}; + +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_podBaseRightX{}; + +static float g_bgArrowsEnd{}; +static float g_fgArrowsEnd{}; +static float g_chevronLoopTime{}; + +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, 5.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: + void Update(float deltaTime) override + { + if (g_aspectRatio > WIDE_ASPECT_RATIO) + BlackBar::g_isVisible = true; + } +} +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); +} + +void AspectRatioPatches::Init() +{ + LoadingPatches::Events.push_back(&g_loadingPillarboxEvent); + MainMenuTaskPatches::Events.push_back(&g_chevronAnimResetEvent); +} void AspectRatioPatches::ComputeOffsets() { float width = Video::s_viewportWidth; float height = Video::s_viewportHeight; - g_aspectRatio = width / height; - g_aspectRatioGameplayScale = 1.0f; + g_aspectRatio = width / height; + g_aspectRatioGameplayScale = 1.0f; + + if (g_aspectRatio >= NARROW_ASPECT_RATIO) + { + g_aspectRatioOffsetX = (width - height * WIDE_ASPECT_RATIO) / 2.0f; + g_aspectRatioOffsetY = 0.0f; + g_aspectRatioScale = height / 720.0f; + + // 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); + 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_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; +} + +void EmplacePath(const void* key, const std::string_view& value) +{ + std::lock_guard lock(g_pathMutex); + g_paths.emplace(key, HashStr(value)); +} + +void TraverseCast(Chao::CSD::Scene* scene, uint32_t castNodeIndex, Chao::CSD::CastNode* castNode, uint32_t castIndex, const std::string& parentPath) +{ + if (castIndex == ~0) + return; + + TraverseCast(scene, castNodeIndex, castNode, castNode->pCastLinks[castIndex].SiblingCastIndex, parentPath); + + std::string path = parentPath; + + for (size_t i = 0; i < scene->CastCount; i++) + { + auto& index = scene->pCastIndices[i]; + if (index.CastNodeIndex == castNodeIndex && index.CastIndex == castIndex) + { + path += index.pCastName; + break; + } + } + + EmplacePath(castNode->pCasts[castIndex].get(), path); + + if (castNode->RootCastIndex == castIndex) + EmplacePath(castNode, path); + + path += "/"; + + TraverseCast(scene, castNodeIndex, castNode, castNode->pCastLinks[castIndex].ChildCastIndex, path); + + // LOGFN_UTILITY("CSD hierarchy: {}", path); +} + +void TraverseScene(Chao::CSD::Scene* scene, std::string path) +{ + EmplacePath(scene, path); + path += "/"; + + for (size_t i = 0; i < scene->CastNodeCount; i++) + { + auto& castNode = scene->pCastNodes[i]; + TraverseCast(scene, i, &castNode, castNode.RootCastIndex, path); + } +} + +void TraverseSceneNode(Chao::CSD::SceneNode* sceneNode, std::string path) +{ + EmplacePath(sceneNode, path); + path += "/"; + + for (size_t i = 0; i < sceneNode->SceneCount; i++) + { + auto& sceneIndex = sceneNode->pSceneIndices[i]; + TraverseScene(sceneNode->pScenes[sceneIndex.SceneIndex], path + sceneIndex.pSceneName.get()); + } + + for (size_t i = 0; i < sceneNode->SceneNodeCount; i++) + { + auto& sceneNodeIndex = sceneNode->pSceneNodeIndices[i]; + TraverseSceneNode(&sceneNode->pSceneNodes[sceneNodeIndex.SceneNodeIndex], path + sceneNodeIndex.pSceneNodeName.get()); + } +} + +// Sonicteam::CsdResource::MakeResource +PPC_FUNC_IMPL(__imp__sub_82617570); +PPC_FUNC(sub_82617570) +{ + auto pName = reinterpret_cast(base + PPC_LOAD_U32(ctx.r4.u32 + 4)); + + __imp__sub_82617570(ctx, base); + + if (!ctx.r3.u32) + return; + + auto ppCsdObject = PPC_LOAD_U32(ctx.r3.u32); + + if (!ppCsdObject) + return; + + auto pCsdObject = reinterpret_cast(base + ppCsdObject); + + if (!pCsdObject || !pCsdObject->m_pCsdProject) + return; + + 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 (strcmpIgnoreCase(pName + nameLen - suffixLen, pSuffix)) + { + pName[nameLen - suffixLen] = '\0'; + + LOGFN_UTILITY("CSD modifier(s) redirected: {}", pName); + } + } + + TraverseSceneNode(pCsdObject->m_pCsdProject->m_pResource->pRootNode, pName); +} + +// 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); + } + + __imp__sub_82656650(ctx, base); +} + +// Chao::CSD::Scene::Render +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_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_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; + +#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_828C8F60(ctx, base); +} + +void RenderCsdCastNodeMidAsmHook(PPCRegister& r10, PPCRegister& r27) +{ + g_castNodeModifier = FindCsdModifier(r10.u32 + r27.u32); +} + +void RenderCsdCastMidAsmHook(PPCRegister& r4) +{ + g_castModifier = FindCsdModifier(r4.u32); +} + +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(); + } + else if (g_castNodeModifier.has_value()) + { + modifier = g_castNodeModifier.value(); + } + else if (g_sceneModifier.has_value()) + { + 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; + + if ((modifier.Flags & CSD_SKIP) != 0) + { + if ((modifier.Flags & CSD_CHEVRON) != 0) + { + // Don't draw non-extended arrows at ultrawide. + if (g_aspectRatio > WIDE_ASPECT_RATIO) + return; + } + else + { + // Skip drawing this cast. + return; + } + } + + if (g_cornerExtract) + { + if ((modifier.Flags & (CSD_STORE_LEFT_CORNER | CSD_STORE_RIGHT_CORNER)) != 0) + { + 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; + } + + // 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); + } + } + } + + // Reserve stack space for vertices. + auto size = ctx.r5.u32 * stride; + ctx.r1.u32 -= size; + + // Copy vertices to stack. + auto stack = base + ctx.r1.u32; + memcpy(stack, base + ctx.r4.u32, size); + + struct CSDVertex + { + be X; + be Y; + be Colour; + be U; + be V; + }; + + auto getVertex = [&](size_t index) + { + return reinterpret_cast(stack + (index * stride)); + }; + + auto offsetX = 0.0f; + auto offsetY = 0.0f; + auto pivotX = 0.0f; + auto pivotY = 0.0f; + auto scaleX = 1.0f; + auto scaleY = 1.0f; + + auto needsStretch = g_aspectRatio >= WIDE_ASPECT_RATIO; + + if (needsStretch && (modifier.Flags & CSD_STRETCH_HORIZONTAL) != 0) + { + scaleX = vpWidth / 1280.0f; + } + else + { + scaleX = g_aspectRatioScale; + + if (needsStretch && (modifier.Flags & CSD_UNSTRETCH_HORIZONTAL) != 0) + { + pivotX = getVertex(0)->X; + offsetX = pivotX * vpWidth / 1280.0f; + } + else + { + if ((modifier.Flags & CSD_ALIGN_RIGHT) != 0) + { + offsetX = g_aspectRatioOffsetX * 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; + + // 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; + + if ((modifier.Flags & CSD_ALIGN_RIGHT) != 0) + offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; + else if ((modifier.Flags & CSD_ALIGN_LEFT) == 0) + offsetX += 640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; + + offsetX += pivotX * g_aspectRatioScale; + } + } + } + + if ((modifier.Flags & CSD_STRETCH_VERTICAL) != 0) + { + scaleY = vpHeight / 720.0f; + } + else + { + scaleY = g_aspectRatioScale; + + if ((modifier.Flags & CSD_ALIGN_BOTTOM) != 0) + offsetY = g_aspectRatioOffsetY * 2.0f; + else if ((modifier.Flags & CSD_ALIGN_TOP) == 0) + offsetY = g_aspectRatioOffsetY; + + if ((modifier.Flags & CSD_SCALE) != 0) + { + scaleY *= g_aspectRatioGameplayScale; + + if ((modifier.Flags & CSD_ALIGN_BOTTOM) != 0) + offsetY += 720.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; + else if ((modifier.Flags & CSD_ALIGN_TOP) == 0) + offsetY += 360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; + + offsetY += pivotY * g_aspectRatioScale; + } + } + + // Offset cast to movie aspect ratio boundaries. + if ((modifier.Flags & CSD_MOVIE) != 0) + { + if (g_aspectRatio > g_aspectRatioMovie) + { + offsetX -= (vpWidth - (vpHeight * g_aspectRatioMovie)) / 2.0f; + } + else + { + offsetY -= (vpHeight - (vpWidth / g_aspectRatioMovie)) / 2.0f; + } + } + + if (g_aspectRatio > WIDE_ASPECT_RATIO) + { + CsdModifier offsetScaleModifier{}; + + auto corner = 0.0f; + + if (g_castModifier.has_value()) + { + offsetScaleModifier = g_castModifier.value(); + corner = getVertex(((offsetScaleModifier.Flags & CSD_STORE_LEFT_CORNER) != 0) ? 0 : 3)->X; + } + + if (offsetScaleModifier.CornerMax == 0.0f && g_castNodeModifier.has_value()) + { + offsetScaleModifier = g_castNodeModifier.value(); + corner = g_corners[offsetScaleModifier.CornerIndex]; + } + + if (offsetScaleModifier.CornerMax == 0.0f && g_sceneModifier.has_value()) + { + offsetScaleModifier = g_sceneModifier.value(); + corner = g_corners[offsetScaleModifier.CornerIndex]; + } + +#ifdef CORNER_DEBUG + if ((offsetScaleModifier.Flags & (CSD_OFFSET_SCALE_LEFT | CSD_OFFSET_SCALE_RIGHT)) != 0 && offsetScaleModifier.CornerMax == FLT_MAX) + fmt::println("Corner: {}", corner); +#endif + + if ((offsetScaleModifier.Flags & CSD_OFFSET_SCALE_LEFT) != 0) + offsetX *= corner / offsetScaleModifier.CornerMax; + else if ((offsetScaleModifier.Flags & CSD_OFFSET_SCALE_RIGHT) != 0) + offsetX = vpWidth - (vpWidth - offsetX) * (1280.0f - corner) / (1280.0f - offsetScaleModifier.CornerMax); + } + + 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++) + { + auto vertex = getVertex(i); + + 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)) + { + x = std::min(x, 0.0f); + } + 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, vpWidth); + } + + 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 & CSD_RADARMAP) != 0) + { + g_radarMapX = x; + g_radarMapY = y; + } + + vertex->X = round(x); + vertex->Y = round(y); + } + + width = lastX - firstX; + height = lastY - firstY; + + if ((modifier.Flags & CSD_RADARMAP) != 0) + { + g_radarMapCoverWidth = width; + 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; + 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; + }; + + 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 & CSD_UV_MODIFIER) != 0) + applyUVModifier(modifier.UVs); + + 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; + + 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; + auto isFlipVert = (modifier.Flags & CSD_REPEAT_FLIP_VERTICAL) != 0; + + auto applyRepeatModifiers = [&]() + { + if (isFlipHorz) + flipHorizontally(); + + if (isFlipVert) + flipVertically(); + + if ((modifier.Flags & CSD_REPEAT_UV_MODIFIER) != 0) + applyUVModifier(modifier.RepeatUVs); + + if ((modifier.Flags & CSD_REPEAT_COLOUR_MODIFIER) != 0) + applyColourModifier(modifier.RepeatColours); + }; + + 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 + arrowWidth; + + // Compute number of foreground arrows. + while (x > 0.0f) + { + x = x - arrowWidth; + arrowCount++; + } + + g_fgArrowsEnd = (CHEVRON_INTRO_DURATION / 2.0f) * float(arrowCount + 1); + + // Reset X to first vertex. + x = getVertex(0)->X; + } + + while (x > 0.0f) + { + drawOriginal(); + + if ((modifier.Flags & CSD_CHEVRON) != 0) + { + /////////////////////////////////////////////////////////// + // 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; + + // 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; + getVertex(i)->Colour = 0xFFFFFF00 | g_fgArrows[arrowIndex].CurrentAlpha; + } + + arrowIndex++; + } + 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(); + + 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) + { + 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 - arrowWidth; + + // Compute number of background arrows. + while (x < vpWidth) + { + x = x + arrowWidth; + arrowCount++; + } + + g_bgArrowsEnd = (CHEVRON_INTRO_DURATION / 2.0f) * float(arrowCount + 1); + + // Reset X to first vertex. + x = getVertex(0)->X; + } + + while (x < vpWidth) + { + drawOriginal(); + + if ((modifier.Flags & CSD_CHEVRON) != 0) + { + /////////////////////////////////////////////////////////// + // 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; + + // 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; + getVertex(i)->Colour = 0xFFFFFF00 | g_bgArrows[arrowIndex].CurrentAlpha; + } + + arrowIndex++; + } + 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(), vpWidth); + } + } + } + } + } + else + { + if (!isMainMenuPanels) + drawOriginal(); + + // Clone Audio Room "pod" left edge to fill + // in the rest of the box at ultrawide. + if ((modifier.Flags & CSD_POD_CLONE) != 0 && g_aspectRatio > WIDE_ASPECT_RATIO) + { + auto v0 = getVertex(0); + auto v1 = getVertex(1); + auto v2 = getVertex(2); + auto v3 = getVertex(3); + + // 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; + + drawOriginal(); + } + + // 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 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; + + drawOriginal(); + + ///////////////////////////////////////////// + // 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; + + drawOriginal(); + } + } + + if (g_aspectRatio > WIDE_ASPECT_RATIO) + { + if (isMainMenuPanels) + drawOriginal(); + + ///////////////////////////////////////////////// + // 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; + + drawOriginal(); + } + + 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; + + // Crop UVs to remove metal dip. + getVertex(2)->U = getVertex(2)->U + -0.8f; + getVertex(3)->U = getVertex(3)->U + -0.8f; + + drawOriginal(); + } + + 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; + + drawOriginal(); + } + } + 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 vertices for UV adjustment. + getVertex(0)->Y = getVertex(0)->Y + 7.0f; + getVertex(2)->Y = getVertex(2)->Y + 7.0f; + + // 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; + + drawOriginal(); + + // Move cloned cast above existing cast. + for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->Y = getVertex(i)->Y - height; + + flipVertically(); + + // 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 bottom UVs to remove metal notch. + getVertex(1)->V = getVertex(1)->V + -0.067f; + getVertex(3)->V = getVertex(3)->V + -0.067f; + } - if (g_aspectRatio >= NARROW_ASPECT_RATIO) - { - g_aspectRatioOffsetX = (width - height * WIDE_ASPECT_RATIO) / 2.0f; - g_aspectRatioOffsetY = 0.0f; - g_aspectRatioScale = height / 720.0f; + drawOriginal(); + } - // keep same scale above Steam Deck aspect ratio - if (g_aspectRatio < WIDE_ASPECT_RATIO) + if ((modifier.Flags & (CSD_MAIN_MENU_PARTS_CAST_0226 | CSD_MAIN_MENU_PARTS_CAST_0227)) != 0) { - // interpolate to original 4:3 scale - float steamDeckScale = g_aspectRatio / WIDE_ASPECT_RATIO; - float narrowScale = ComputeScale(NARROW_ASPECT_RATIO); - float lerpFactor = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (STEAM_DECK_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f); + // Move vertices for UV adjustment. + getVertex(1)->Y = getVertex(1)->Y - 20.0f; + getVertex(3)->Y = getVertex(3)->Y - 20.0f; - g_aspectRatioGameplayScale = narrowScale + (steamDeckScale - narrowScale) * lerpFactor; + // 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; + + drawOriginal(); + + // Move cloned cast below existing cast. + for (size_t i = 0; i < r5.u32; i++) + getVertex(i)->Y = getVertex(i)->Y + height; + + flipVertically(); + + // 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 top UVs to remove metal dip. + getVertex(0)->V = getVertex(0)->V + 0.065f; + getVertex(2)->V = getVertex(2)->V + 0.065f; + } + + drawOriginal(); } } - else + + ctx.r1.u32 += size; +} + +// Sonicteam::CPlatformMarathon::Draw +PPC_FUNC_IMPL(__imp__sub_826315C8); +PPC_FUNC(sub_826315C8) +{ + // r3 = Sonicteam::CPlatformMarathon* + // r4 = Vertex[r5] + // r5 = Vertex Count + + Draw(ctx, base, __imp__sub_826315C8, 0x14); +} + +// Sonicteam::CPlatformMarathon::DrawNoTex +PPC_FUNC_IMPL(__imp__sub_82631718); +PPC_FUNC(sub_82631718) +{ + // r3 = Sonicteam::CPlatformMarathon* + // r4 = Vertex[r5] + // r5 = Vertex Count + + 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::EventEntityTask::Update +PPC_FUNC_IMPL(__imp__sub_8264AC48); +PPC_FUNC(sub_8264AC48) +{ + if (Config::CutsceneAspectRatio == ECutsceneAspectRatio::Original) + BlackBar::g_isVisible = true; + + __imp__sub_8264AC48(ctx, base); +} + +// Sonicteam::HUDResult::Update +PPC_FUNC_IMPL(__imp__sub_824F4D80); +PPC_FUNC(sub_824F4D80) +{ + BlackBar::g_isVisible = true; + + __imp__sub_824F4D80(ctx, base); +} + +// Sonicteam::HUDBattleResult::Update +PPC_FUNC_IMPL(__imp__sub_824D32C8); +PPC_FUNC(sub_824D32C8) +{ + BlackBar::g_isVisible = true; + + __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) +{ + if (g_aspectRatio < WIDE_ASPECT_RATIO && Config::UIAlignmentMode == EUIAlignmentMode::Centre) { - // 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); - } + __imp__sub_824D6E50(ctx, base); + return; + } - g_aspectRatioNarrowScale = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (WIDE_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f); + 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) +{ + auto pHUDRaderMap = (Sonicteam::HUDRaderMap*)(base + ctx.r3.u32); + + __imp__sub_824F1538(ctx, base); + + 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; +} + +// Sonicteam::CObjBalloonIconDrawable::Draw +PPC_FUNC_IMPL(__imp__sub_82352220); +PPC_FUNC(sub_82352220) +{ + auto pCObjBalloonIconDrawable = (Sonicteam::CObjBalloonIconDrawable*)(base + ctx.r3.u32); + 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; + 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 @@ -67,7 +1356,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; @@ -78,17 +1368,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; @@ -96,7 +1386,7 @@ PPC_FUNC(sub_8264CC90) width = movieRectRight - movieRectLeft; height = movieRectTop - movieRectBottom; - + left = movieRectCentreX - (width / 2.0f); right = movieRectCentreX + (width / 2.0f); top = movieRectCentreY + (height / 2.0f); @@ -107,30 +1397,30 @@ PPC_FUNC(sub_8264CC90) width = 2.0f; height = 2.0f; - if (g_aspectRatio > movieAspectRatio) + if (g_aspectRatio > g_aspectRatioMovie) { - if ((movieModifier.Flags & MovieFlags::CROP_WIDE) != 0) + 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 { - if ((movieModifier.Flags & MovieFlags::CROP_NARROW) != 0) + 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); } } @@ -149,9 +1439,864 @@ PPC_FUNC(sub_8264CC90) __imp__sub_8264CC90(ctx, base); } -// -------------- MODIFIERS --------------- // +void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity) +{ + 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_Text.size(); i++) + { + auto str = pTextEntity->m_Text.c_str(); + auto c = ByteSwap(str[i]); + + if (c != L'$') + continue; + + // Some strings may have more placeholder + // characters than variables in the map. + if (variablesIndex >= variables.size()) + break; + + auto& variable = variables[variablesIndex]; + + if (variable.first == "picture") + { + 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); + + 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); + + 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++; + } + + variablesIndex++; + } +} + +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)); + + if ((textModifier & CSD_SKIP) != 0) + return; + + 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); + } + } + + 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; + pTextEntity->m_ScaleX = scale; + pTextEntity->m_ScaleY = scale; + + __imp__sub_8262D868(ctx, base); + + pTextEntity->m_X = x; + pTextEntity->m_Y = y; + pTextEntity->m_ScaleX = scaleX; + pTextEntity->m_ScaleY = scaleY; + + ReplaceTextVariables(pTextEntity); +} + +void SetTextEntityModifier(Sonicteam::TextEntity* pTextEntity, uint64_t flags) +{ + if (!pTextEntity) + return; + + auto pTextModifier = (uint64_t*)(reinterpret_cast(pTextEntity) + sizeof(Sonicteam::TextEntity)); + + *pTextModifier = flags; + + pTextEntity->m_FieldDD = true; + 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) +{ + auto pHUDMainDisplay = (Sonicteam::HUDMainDisplay*)(base + ctx.r3.u32); + + SetTextEntityModifier(pHUDMainDisplay->m_Field184.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_spSavePointTimeText.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); + + __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_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) +{ + auto pTextFontPicture = (Sonicteam::TextFontPicture*)(base + ctx.r3.u32); + + __imp__sub_8263CC40(ctx, base); + + g_fontPictureWidth = pTextFontPicture->m_TextureWidth; + g_fontPictureHeight = pTextFontPicture->m_TextureHeight; +} + +// -------------- CSD MODIFIERS --------------- // + +const xxHashMap g_csdModifiers = +{ + // audio + { 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_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_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 } }, + { 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_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 } }, + { 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_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 } }, + { 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 } }, + + // battle_result + { 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 } }, + + // 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/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 } }, + { 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 } }, + { 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 } }, + + // 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 } }, + + // 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 } }, + { 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 } }, + { 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 } }, + + // 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_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_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_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 + { 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 } }, + { 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 } }, + + // 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_SCALE | 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_ALIGN_TOP | CSD_SCALE | 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_ue/plate_ue_plate_l"), { CSD_ALIGN_TOP | CSD_SCALE | 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_SCALE | 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 } }, + + // 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 } }, + { 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 } }, + { 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 } }, + { 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_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 } }, + { 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_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 } }, + { 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_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 } }, + { HashStr("sprite/title/title/Scene_Title/Logo"), { CSD_SCALE } }, + { HashStr("sprite/title/title/Scene_Title/copyright"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, + + // titleloop_sth + { 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 + { 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) +{ + 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 {}; +} + +// -------------- TEXT MODIFIERS -------------- // + +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 {}; +} + +// ------------- MOVIE MODIFIERS -------------- // 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 83d2e80fc..5dd140369 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -2,47 +2,173 @@ #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; 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; +inline float g_radarMapScale; -struct AspectRatioPatches +class AspectRatioPatches { +public: + static void Init(); static void ComputeOffsets(); }; -inline float ComputeScale(float aspectRatio) +// -------------- CSD MODIFIERS --------------- // + +enum CsdFlags : uint64_t +{ + CSD_ALIGN_CENTER = 0, + + 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 = MAKE_BITFLAG64(4), + CSD_STRETCH_VERTICAL = MAKE_BITFLAG64(5), + + CSD_STRETCH = CSD_STRETCH_HORIZONTAL | CSD_STRETCH_VERTICAL, + + CSD_SCALE = MAKE_BITFLAG64(6), + + CSD_EXTEND_LEFT = MAKE_BITFLAG64(7), + CSD_EXTEND_RIGHT = MAKE_BITFLAG64(8), + + CSD_STORE_LEFT_CORNER = MAKE_BITFLAG64(9), + CSD_STORE_RIGHT_CORNER = MAKE_BITFLAG64(10), + + CSD_SKIP = MAKE_BITFLAG64(11), + + CSD_OFFSET_SCALE_LEFT = MAKE_BITFLAG64(12), + CSD_OFFSET_SCALE_RIGHT = MAKE_BITFLAG64(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_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(23), + CSD_PROHIBIT_BLACK_BAR = MAKE_BITFLAG64(24), + + CSD_UNSTRETCH_HORIZONTAL = MAKE_BITFLAG64(25), + + CSD_CORNER_EXTRACT = MAKE_BITFLAG64(26), + + CSD_RADARMAP = MAKE_BITFLAG64(27), + + CSD_POD_BASE = MAKE_BITFLAG64(28), + CSD_POD_CLONE = MAKE_BITFLAG64(29), + + CSD_MULTIPLAYER = MAKE_BITFLAG64(30), + + CSD_CHEVRON = MAKE_BITFLAG64(31), + + CSD_MODIFIER_ULTRAWIDE_ONLY = MAKE_BITFLAG64(32), + CSD_MODIFIER_NARROW_ONLY = MAKE_BITFLAG64(33), + + CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(34), + + CSD_MOVIE = MAKE_BITFLAG64(35), + + CSD_CRI_LOGO = MAKE_BITFLAG64(36), + + 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 +{ + float U0{}; + float V0{}; + float U1{}; + float V1{}; + float U2{}; + float V2{}; + float U3{}; + float V3{}; +}; + +struct CsdColours +{ + uint32_t C0{}; + uint32_t C1{}; + uint32_t C2{}; + uint32_t C3{}; +}; + +struct CsdModifier { - return ((aspectRatio * 720.0f) / 1280.0f) / sqrt((aspectRatio * 720.0f) / 1280.0f); -} + int64_t Flags{}; + CsdUVs UVs{}; + CsdColours Colours{}; + CsdUVs RepeatUVs{}; + CsdColours RepeatColours{}; + float CornerMax{}; + uint32_t CornerIndex{}; +}; + +extern const xxHashMap g_csdModifiers; + +std::optional FindCsdModifier(uint32_t data); + +// -------------- TEXT MODIFIERS -------------- // + +struct TextFontPictureModifier +{ + uint16_t X{}; + uint16_t Y{}; + uint16_t Width{}; + uint16_t Height{}; +}; + +extern const xxHashMap g_pftModifierXenon; +extern const xxHashMap g_pftModifierPS3; + +TextFontPictureModifier FindFontPictureModifier(xxHashMap pftModifier, std::string_view& name); + +// ------------- MOVIE MODIFIERS -------------- // enum MovieFlags : uint32_t { - CROP_NARROW = 1 << 0, - CROP_WIDE = 1 << 1, - CROP = CROP_NARROW | CROP_WIDE + MOVIE_CROP_NARROW = MAKE_BITFLAG32(0), + MOVIE_CROP_WIDE = MAKE_BITFLAG32(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; +MovieModifier FindMovieModifier(XXH64_hash_t nameHash); - return {}; -} +#undef MAKE_BITFLAG64 +#undef MAKE_BITFLAG32 diff --git a/MarathonRecomp/patches/camera_patches.cpp b/MarathonRecomp/patches/camera_patches.cpp index d1606ad34..653fa721f 100644 --- a/MarathonRecomp/patches/camera_patches.cpp +++ b/MarathonRecomp/patches/camera_patches.cpp @@ -1,5 +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); +} + void SonicCamera_InvertAzDriveK(PPCRegister& az_driveK) { // X axis is inverted by default. 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/hook_event.h b/MarathonRecomp/patches/hook_event.h new file mode 100644 index 000000000..6e700b05b --- /dev/null +++ b/MarathonRecomp/patches/hook_event.h @@ -0,0 +1,37 @@ +#pragma once + +class IHookEvent +{ +public: + virtual ~IHookEvent() = default; + virtual void Prefix() = 0; + virtual void Update(float deltaTime) = 0; + virtual void Postfix() = 0; +}; + +class HookEvent : public IHookEvent +{ +public: + void Prefix() override {} + 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.cpp b/MarathonRecomp/patches/loading_patches.cpp new file mode 100644 index 000000000..d4873967d --- /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::HUDLoadingFlags_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..6b6354ee8 --- /dev/null +++ b/MarathonRecomp/patches/loading_patches.h @@ -0,0 +1,9 @@ +#pragma once + +#include "hook_event.h" + +class LoadingPatches +{ +public: + static inline std::vector Events{}; +}; diff --git a/MarathonRecomp/patches/misc_patches.cpp b/MarathonRecomp/patches/misc_patches.cpp index cda64792b..0620ba93b 100644 --- a/MarathonRecomp/patches/misc_patches.cpp +++ b/MarathonRecomp/patches/misc_patches.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -8,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 s_initContextualHUD{}; + static char* s_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 (!s_initContextualHUD) + { + constexpr const char* LIFE_BAR_ANIME = "life_bar_anime"; - Contextual_init = true; -} + s_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(s_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 - }; + s_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(s_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/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/MarathonRecomp/patches/video_patches.cpp b/MarathonRecomp/patches/video_patches.cpp index 9c491cb76..cff29903c 100644 --- a/MarathonRecomp/patches/video_patches.cpp +++ b/MarathonRecomp/patches/video_patches.cpp @@ -1,11 +1,40 @@ #include +#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); +} + float ReflectionScaleFactor(EReflectionResolution ref) { switch (ref) { case EReflectionResolution::Eighth: @@ -27,6 +56,12 @@ PPC_FUNC(sub_82619D00) { auto pName = (stdx::string*)g_memory.Translate(ctx.r4.u32); + if (*pName == "radermap") + { + ctx.r5.u32 = g_radarMapScale; + ctx.r6.u32 = g_radarMapScale; + } + if (*pName == "reflection0") { ctx.r5.u32 = static_cast(static_cast(ctx.r5.u32) * @@ -35,15 +70,33 @@ PPC_FUNC(sub_82619D00) ReflectionScaleFactor(Config::ReflectionResolution)); } +#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 } -// CreateDepthStencilTexture +// 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) + { + ctx.r5.u32 = g_radarMapScale; + ctx.r6.u32 = g_radarMapScale; + } + } + if (*pName == "depthstencil_1_4") { ctx.r5.u32 = static_cast(static_cast(ctx.r5.u32) * @@ -55,8 +108,24 @@ PPC_FUNC(sub_82619B88) if (Config::ReflectionResolution == EReflectionResolution::Full) ctx.r5.u32++; } + +#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 } float ShadowScaleFactor(EShadowResolution ref) { diff --git a/MarathonRecomp/ui/black_bar.cpp b/MarathonRecomp/ui/black_bar.cpp index ebe029493..549ad6d18 100644 --- a/MarathonRecomp/ui/black_bar.cpp +++ b/MarathonRecomp/ui/black_bar.cpp @@ -1,58 +1,30 @@ #include "black_bar.h" #include +#include void BlackBar::Draw() { - if (g_inspirePillarbox) + if (g_isVisible) { auto drawList = ImGui::GetBackgroundDrawList(); auto& res = ImGui::GetIO().DisplaySize; - 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); - - g_inspirePillarbox = false; - } - - 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)); + auto width = (res.x - (res.y * 16.0f / 9.0f)) / 2.0f; - drawList->AddRectFilled( - { g_loadingBlackBarMax.x, g_loadingBlackBarMin.y }, - res, - IM_COL32(0, 0, 0, g_loadingBlackBarAlpha)); + 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 (g_aspectRatio < NARROW_ASPECT_RATIO) + else if (WIDE_ASPECT_RATIO > g_aspectRatio) { - drawList->AddRectFilled( - { 0.0f, 0.0f }, - { g_loadingBlackBarMax.x, g_loadingBlackBarMin.y }, - IM_COL32(0, 0, 0, g_loadingBlackBarAlpha)); + auto height = (res.y - (res.x * 9.0f / 16.0f)) / 2.0f; - drawList->AddRectFilled( - { g_loadingBlackBarMin.x, g_loadingBlackBarMax.y }, - res, - IM_COL32(0, 0, 0, g_loadingBlackBarAlpha)); + drawList->AddRectFilled({ 0.0f, 0.0f }, { res.x, height }, IM_COL32_BLACK); + drawList->AddRectFilled({ 0.0f, res.y - height }, res, IM_COL32_BLACK); } - - g_loadingBlackBarAlpha = 0; } + + if (!App::s_isLoading) + g_isVisible = false; } diff --git a/MarathonRecomp/ui/black_bar.h b/MarathonRecomp/ui/black_bar.h index 7cb076802..655424f3b 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_isVisible; static void Draw(); }; 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); diff --git a/MarathonRecompLib/config/Marathon.toml b/MarathonRecompLib/config/Marathon.toml index 0a8f22458..d23c14c98 100644 --- a/MarathonRecompLib/config/Marathon.toml +++ b/MarathonRecompLib/config/Marathon.toml @@ -103,23 +103,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]] @@ -127,6 +127,11 @@ name = "PostureDisableEdgeGrabLeftover" address = 0x82200568 registers = ["r31"] +[[midasm_hook]] +name = "BeginBlockGetName" +address = 0x82607850 +registers = ["r3"] + [[midasm_hook]] name = "PedestrianAnimationLOD" address = 0x824C2808 @@ -252,6 +257,28 @@ 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 + [[midasm_hook]] name = "TitleTask_RedirectStateTransitionToOutroAnim" address = 0x82512C90 @@ -338,6 +365,26 @@ name = "NOP" address = 0x8258E600 jump_address = 0x8258E604 +[[midasm_hook]] +name = "TextEntityAlloc" +address = 0x825EDB70 +registers = ["r3"] + +[[midasm_hook]] +name = "TextEntityAlloc" +address = 0x825EDCEC +registers = ["r3"] + +[[midasm_hook]] +name = "CameraImp_SetFOV" +address = 0x82590980 +registers = ["f1"] + +[[midasm_hook]] +name = "CameraImp_SetFOV" +address = 0x82590AB4 +registers = ["f1"] + [[midasm_hook]] name = "SonicCamera_InvertAzDriveK" address = 0x82191D40