diff --git a/MarathonRecomp/CMakeLists.txt b/MarathonRecomp/CMakeLists.txt index bbcc3dbd1..18714a304 100644 --- a/MarathonRecomp/CMakeLists.txt +++ b/MarathonRecomp/CMakeLists.txt @@ -88,6 +88,7 @@ set(MARATHON_RECOMP_LOCALE_CXX_SOURCES if (WIN32) set(MARATHON_RECOMP_OS_CXX_SOURCES + "os/win32/locale_win32.cpp" "os/win32/logger_win32.cpp" "os/win32/media_win32.cpp" "os/win32/process_win32.cpp" @@ -97,6 +98,7 @@ if (WIN32) ) elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") set(MARATHON_RECOMP_OS_CXX_SOURCES + "os/linux/locale_linux.cpp" "os/linux/logger_linux.cpp" "os/linux/media_linux.cpp" "os/linux/process_linux.cpp" @@ -106,6 +108,7 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") ) elseif (APPLE) set(MARATHON_RECOMP_OS_CXX_SOURCES + "os/macos/locale_macos.cpp" "os/macos/logger_macos.cpp" "os/macos/media_macos.cpp" "os/macos/process_macos.cpp" @@ -145,6 +148,7 @@ set(MARATHON_RECOMP_PATCHES_CXX_SOURCES "patches/aspect_ratio_patches.cpp" "patches/audio_patches.cpp" "patches/camera_patches.cpp" + "patches/debug_patches.cpp" "patches/fps_patches.cpp" "patches/frontend_listener.cpp" "patches/input_patches.cpp" @@ -380,6 +384,8 @@ if (WIN32) winmm windowsapp ) +elseif(NOT APPLE) + target_link_libraries(MarathonRecomp PRIVATE iconv) endif() target_link_libraries(MarathonRecomp PRIVATE diff --git a/MarathonRecomp/api/Chao/CSD/Manager/csdmScene.h b/MarathonRecomp/api/Chao/CSD/Manager/csdmScene.h index 8f3ba42b2..d0dd95ea5 100644 --- a/MarathonRecomp/api/Chao/CSD/Manager/csdmScene.h +++ b/MarathonRecomp/api/Chao/CSD/Manager/csdmScene.h @@ -127,7 +127,8 @@ namespace Chao::CSD be m_MotionSpeed; be m_MotionStartFrame; be m_MotionEndFrame; - MARATHON_INSERT_PADDING(0x0C); + MARATHON_INSERT_PADDING(0x08); + be m_Field90; CSceneMotionRepeat m_MotionRepeat; be m_MotionIndex; be m_SortOrder; @@ -184,6 +185,7 @@ namespace Chao::CSD MARATHON_ASSERT_OFFSETOF(CScene, m_MotionSpeed, 0x7C); MARATHON_ASSERT_OFFSETOF(CScene, m_MotionStartFrame, 0x80); MARATHON_ASSERT_OFFSETOF(CScene, m_MotionEndFrame, 0x84); + MARATHON_ASSERT_OFFSETOF(CScene, m_Field90, 0x90); MARATHON_ASSERT_OFFSETOF(CScene, m_MotionRepeat, 0x94); MARATHON_ASSERT_OFFSETOF(CScene, m_MotionIndex, 0xB0); MARATHON_ASSERT_OFFSETOF(CScene, m_SortOrder, 0xB4); diff --git a/MarathonRecomp/api/Marathon.h b/MarathonRecomp/api/Marathon.h index 5f7a50205..fb3cb028a 100644 --- a/MarathonRecomp/api/Marathon.h +++ b/MarathonRecomp/api/Marathon.h @@ -33,9 +33,11 @@ #include "Sonicteam/CsdManager.h" #include "Sonicteam/CsdObject.h" #include "Sonicteam/CsdResource.h" +#include "Sonicteam/DevTitleMode.h" #include "Sonicteam/DocMarathon.h" #include "Sonicteam/DocMarathonImp.h" #include "Sonicteam/DocMarathonState.h" +#include "Sonicteam/DocState.h" #include "Sonicteam/Enemy/EnemyShot.h" #include "Sonicteam/Enemy/EnemyShotNormal.h" #include "Sonicteam/Enemy/EnemyShotPoint.h" @@ -64,11 +66,19 @@ #include "Sonicteam/HudTextParts.h" #include "Sonicteam/ImageFilter.h" #include "Sonicteam/LoadingTask.h" +#include "Sonicteam/LuaNode.h" +#include "Sonicteam/LuaNodeImp.h" +#include "Sonicteam/LuaSystem.h" +#include "Sonicteam/LuaSystemManager.h" +#include "Sonicteam/LuaTableAccess.h" +#include "Sonicteam/LuaTableAccessImp.h" #include "Sonicteam/MainDisplayTask.h" #include "Sonicteam/MainMenuExpositionTask.h" #include "Sonicteam/MainMenuTask.h" #include "Sonicteam/MainMode.h" #include "Sonicteam/Message/Camera/Cameraman/MsgChangeMode.h" +#include "Sonicteam/Message/DevTitleMode/MsgMoveCursor.h" +#include "Sonicteam/Message/DevTitleMode/MsgSelectCursor.h" #include "Sonicteam/Message/Game/MsgRegisterPlayer.h" #include "Sonicteam/Message/Game/MsgTextCardAction.h" #include "Sonicteam/Message/HUDButtonWindow/MsgChangeButtons.h" @@ -104,6 +114,8 @@ #include "Sonicteam/ObjectVehicleBike.h" #include "Sonicteam/PauseAdapter.h" #include "Sonicteam/PauseTask.h" +#include "Sonicteam/PicBase.h" +#include "Sonicteam/PictureTitle.h" #include "Sonicteam/Player/CommonContextIFFlags.h" #include "Sonicteam/Player/GroundRayListener.h" #include "Sonicteam/Player/ICollisionListener.h" @@ -248,6 +260,7 @@ #include "Sonicteam/SoX/IResource.h" #include "Sonicteam/SoX/IResource2.h" #include "Sonicteam/SoX/IResourceMgr.h" +#include "Sonicteam/SoX/Input/Listener.h" #include "Sonicteam/SoX/Input/Manager.h" #include "Sonicteam/SoX/LinkNode.h" #include "Sonicteam/SoX/LinkedList.h" @@ -285,7 +298,11 @@ #include "Sonicteam/SoX/Scenery/CameraImp.h" #include "Sonicteam/SoX/Scenery/Drawable.h" #include "Sonicteam/SoX/Thread.h" +#include "Sonicteam/StageMap.h" +#include "Sonicteam/StageSelectMode.h" #include "Sonicteam/StageTitleTask.h" +#include "Sonicteam/StateDevTitle.h" +#include "Sonicteam/StateMainMode.h" #include "Sonicteam/StdImageFilters/BurnoutBlurFilter.h" #include "Sonicteam/StdImageFilters/SingleTechniqueFilter.h" #include "Sonicteam/System/CreateStatic.h" diff --git a/MarathonRecomp/api/Sonicteam/CsdLink.h b/MarathonRecomp/api/Sonicteam/CsdLink.h index d4195e8a5..bcfa03bd7 100644 --- a/MarathonRecomp/api/Sonicteam/CsdLink.h +++ b/MarathonRecomp/api/Sonicteam/CsdLink.h @@ -10,15 +10,15 @@ namespace Sonicteam struct Vftable { be fpDestroy; - MARATHON_INSERT_PADDING(4); + MARATHON_INSERT_PADDING(0x04); be fpUpdate; }; xpointer m_pVftable; xpointer m_pNext; - xpointer m_pPrevious; + xpointer m_pPrev; be m_Priority; - MARATHON_INSERT_PADDING(8); + MARATHON_INSERT_PADDING(0x08); void* Destroy(uint32_t flags = 1) { @@ -33,7 +33,7 @@ namespace Sonicteam MARATHON_ASSERT_OFFSETOF(CsdLink, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(CsdLink, m_pNext, 0x04); - MARATHON_ASSERT_OFFSETOF(CsdLink, m_pPrevious, 0x08); + MARATHON_ASSERT_OFFSETOF(CsdLink, m_pPrev, 0x08); MARATHON_ASSERT_OFFSETOF(CsdLink, m_Priority, 0x0C); MARATHON_ASSERT_SIZEOF(CsdLink, 0x18); } diff --git a/MarathonRecomp/api/Sonicteam/DevTitleMode.h b/MarathonRecomp/api/Sonicteam/DevTitleMode.h new file mode 100644 index 000000000..16a45936f --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/DevTitleMode.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +namespace Sonicteam +{ + class DevTitleMode : public SoX::Engine::DocMode + { + public: + struct Mode + { + xpointer pName; + xpointer pState; + }; + + be m_SelectedIndex; + MARATHON_INSERT_PADDING(0x04); + stdx::vector m_Modes; + MARATHON_INSERT_PADDING(0x2C); + }; + + MARATHON_ASSERT_OFFSETOF(DevTitleMode::Mode, pName, 0x00); + MARATHON_ASSERT_OFFSETOF(DevTitleMode::Mode, pState, 0x04); + MARATHON_ASSERT_SIZEOF(DevTitleMode::Mode, 0x08); + + MARATHON_ASSERT_OFFSETOF(DevTitleMode, m_SelectedIndex, 0x50); + MARATHON_ASSERT_OFFSETOF(DevTitleMode, m_Modes, 0x58); + MARATHON_ASSERT_SIZEOF(DevTitleMode, 0x94); +} diff --git a/MarathonRecomp/api/Sonicteam/DocMarathon.h b/MarathonRecomp/api/Sonicteam/DocMarathon.h index e62ea31fb..a60e5aa6b 100644 --- a/MarathonRecomp/api/Sonicteam/DocMarathon.h +++ b/MarathonRecomp/api/Sonicteam/DocMarathon.h @@ -2,6 +2,9 @@ #include #include +#include +#include +#include #include namespace Sonicteam @@ -11,14 +14,23 @@ namespace Sonicteam public: struct Vftable : public Doc::Vftable { - MARATHON_INSERT_PADDING(0x58); + MARATHON_INSERT_PADDING(0x48); + be fpSetDocState; + MARATHON_INSERT_PADDING(0x0C); be fpGetTextBookContext; MARATHON_INSERT_PADDING(0x60); be fpSetLoading; MARATHON_INSERT_PADDING(0x44); be fpGetLanguage; + MARATHON_INSERT_PADDING(0x4C); + be fpStartMainMode; }; + DocMarathon* SetDocState(DocState* in_pDocState) + { + return GuestToHostFunction(static_cast(m_pVftable.get())->fpSetDocState, this, in_pDocState); + } + TextBook::TextBookContext* GetTextBookContext() { return GuestToHostFunction(static_cast(m_pVftable.get())->fpGetTextBookContext, this); @@ -33,11 +45,18 @@ namespace Sonicteam { return GuestToHostFunction(static_cast(m_pVftable.get())->fpGetLanguage, this); } + + DocMarathon* StartMainMode() + { + return GuestToHostFunction(static_cast(m_pVftable.get())->fpStartMainMode, this); + } }; + MARATHON_ASSERT_OFFSETOF(DocMarathon::Vftable, fpSetDocState, 0x54); MARATHON_ASSERT_OFFSETOF(DocMarathon::Vftable, fpGetTextBookContext, 0x64); MARATHON_ASSERT_OFFSETOF(DocMarathon::Vftable, fpSetLoading, 0xC8); MARATHON_ASSERT_OFFSETOF(DocMarathon::Vftable, fpGetLanguage, 0x110); + MARATHON_ASSERT_OFFSETOF(DocMarathon::Vftable, fpStartMainMode, 0x160); MARATHON_ASSERT_SIZEOF(DocMarathon, 0x5C); } diff --git a/MarathonRecomp/api/Sonicteam/DocMarathonImp.h b/MarathonRecomp/api/Sonicteam/DocMarathonImp.h index 8c44ff72b..4b477a33a 100644 --- a/MarathonRecomp/api/Sonicteam/DocMarathonImp.h +++ b/MarathonRecomp/api/Sonicteam/DocMarathonImp.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,7 @@ namespace Sonicteam class DocMarathonImp : public DocMarathon { public: - MARATHON_INSERT_PADDING(0x04); + xpointer m_pDocState; xpointer m_pMyGraphicsDevice; xpointer m_pSceneParamManager; MARATHON_INSERT_PADDING(0x34); @@ -48,6 +49,7 @@ namespace Sonicteam MARATHON_INSERT_PADDING(0x2C); }; + MARATHON_ASSERT_OFFSETOF(DocMarathonImp, m_pDocState, 0x5C); MARATHON_ASSERT_OFFSETOF(DocMarathonImp, m_pMyGraphicsDevice, 0x60); MARATHON_ASSERT_OFFSETOF(DocMarathonImp, m_pSceneParamManager, 0x64); MARATHON_ASSERT_OFFSETOF(DocMarathonImp, m_InputManagers, 0x9C); diff --git a/MarathonRecomp/api/Sonicteam/DocState.h b/MarathonRecomp/api/Sonicteam/DocState.h new file mode 100644 index 000000000..ce286cf0a --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/DocState.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class DocState + { + public: + xpointer m_pVftable; + }; +} diff --git a/MarathonRecomp/api/Sonicteam/Globals.h b/MarathonRecomp/api/Sonicteam/Globals.h index aec3f3407..0879b1dbb 100644 --- a/MarathonRecomp/api/Sonicteam/Globals.h +++ b/MarathonRecomp/api/Sonicteam/Globals.h @@ -25,14 +25,14 @@ namespace Sonicteam { static inline stdx::string* ms_pCurrentRenderScript{}; - static inline be* ms_apMainDisplayColours[9]{}; + static inline be* ms_MainDisplayColours[9]{}; static void Init() { ms_pCurrentRenderScript = reinterpret_cast(MmGetHostAddress(0x82B814F8)); for (int i = 0; i < 9; i++) - ms_apMainDisplayColours[i] = reinterpret_cast*>(MmGetHostAddress(0x82036BE4 + (i * 4))); + ms_MainDisplayColours[i] = reinterpret_cast*>(MmGetHostAddress(0x82036BE4 + (i * 4))); } }; } diff --git a/MarathonRecomp/api/Sonicteam/LuaNode.h b/MarathonRecomp/api/Sonicteam/LuaNode.h new file mode 100644 index 000000000..0268a5f88 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/LuaNode.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include + +namespace Sonicteam +{ + class LuaNode + { + public: + struct Vftable + { + MARATHON_INSERT_PADDING(0x60); + be fpGetTable; + }; + + xpointer m_pVftable; + xpointer m_pLuaState; + + boost::shared_ptr GetTable(const char* in_pName) + { + const auto result = guest_stack_var>(); + const auto name = guest_stack_var(in_pName); + + GuestToHostFunction(m_pVftable->fpGetTable.get(), result.get(), this, name->c_str()); + + return *result; + } + }; + + MARATHON_ASSERT_OFFSETOF(LuaNode::Vftable, fpGetTable, 0x60); + MARATHON_ASSERT_SIZEOF(LuaNode::Vftable, 0x64); + + MARATHON_ASSERT_OFFSETOF(LuaNode, m_pVftable, 0x00); + MARATHON_ASSERT_OFFSETOF(LuaNode, m_pLuaState, 0x04); + MARATHON_ASSERT_SIZEOF(LuaNode, 0x08); +} diff --git a/MarathonRecomp/api/Sonicteam/LuaNodeImp.h b/MarathonRecomp/api/Sonicteam/LuaNodeImp.h new file mode 100644 index 000000000..588ae7356 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/LuaNodeImp.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +namespace Sonicteam +{ + class LuaNodeImp : public LuaNode + { + public: + stdx::string m_Name; + }; + + MARATHON_ASSERT_OFFSETOF(LuaNodeImp, m_Name, 0x08); + MARATHON_ASSERT_SIZEOF(LuaNodeImp, 0x24); +} diff --git a/MarathonRecomp/api/Sonicteam/LuaSystem.h b/MarathonRecomp/api/Sonicteam/LuaSystem.h new file mode 100644 index 000000000..594c9f6cb --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/LuaSystem.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include +#include + +namespace Sonicteam +{ + class LuaSystem : public LuaNodeImp, public SoX::IResource2 {}; + + MARATHON_ASSERT_SIZEOF(LuaSystem, 0x88); +} diff --git a/MarathonRecomp/api/Sonicteam/LuaSystemManager.h b/MarathonRecomp/api/Sonicteam/LuaSystemManager.h new file mode 100644 index 000000000..41d2bb384 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/LuaSystemManager.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include +#include + +namespace Sonicteam +{ + class LuaSystemManager : public SoX::IResourceMgr, public System::Singleton> {}; + + MARATHON_ASSERT_SIZEOF(LuaSystemManager, 0x10); +} diff --git a/MarathonRecomp/api/Sonicteam/LuaTableAccess.h b/MarathonRecomp/api/Sonicteam/LuaTableAccess.h new file mode 100644 index 000000000..45ccdb6fe --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/LuaTableAccess.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +namespace Sonicteam +{ + class LuaTableAccess + { + public: + struct Vftable + { + be fpDtor; + be fpIterate; + be fpGetElement; + MARATHON_INSERT_PADDING(0x1C); + be fpIsTable; + }; + + xpointer m_pVftable; + + bool Iterate() + { + return GuestToHostFunction(m_pVftable->fpIterate, this); + } + + const char* GetElement(const char* in_pName) + { + const auto name = guest_stack_var(in_pName); + + return GuestToHostFunction(m_pVftable->fpGetElement, this, name->c_str()); + } + + bool IsTable() + { + return GuestToHostFunction(m_pVftable->fpIsTable, this); + } + }; + + MARATHON_ASSERT_OFFSETOF(LuaTableAccess::Vftable, fpDtor, 0x00); + MARATHON_ASSERT_OFFSETOF(LuaTableAccess::Vftable, fpIterate, 0x04); + MARATHON_ASSERT_OFFSETOF(LuaTableAccess::Vftable, fpIsTable, 0x28); + MARATHON_ASSERT_SIZEOF(LuaTableAccess::Vftable, 0x2C); + + MARATHON_ASSERT_OFFSETOF(LuaTableAccess, m_pVftable, 0x00); + MARATHON_ASSERT_SIZEOF(LuaTableAccess, 0x04); +} diff --git a/MarathonRecomp/api/Sonicteam/LuaTableAccessImp.h b/MarathonRecomp/api/Sonicteam/LuaTableAccessImp.h new file mode 100644 index 000000000..f66b78fb9 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/LuaTableAccessImp.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +namespace Sonicteam +{ + class LuaTableAccessImp : public LuaTableAccess + { + public: + xpointer m_pLuaState; + bool m_IsRoot; + MARATHON_INSERT_PADDING(0x04); + }; + + MARATHON_ASSERT_OFFSETOF(LuaTableAccessImp, m_pLuaState, 0x04); + MARATHON_ASSERT_OFFSETOF(LuaTableAccessImp, m_IsRoot, 0x08); + MARATHON_ASSERT_SIZEOF(LuaTableAccessImp, 0x10); +} diff --git a/MarathonRecomp/api/Sonicteam/MainMode.h b/MarathonRecomp/api/Sonicteam/MainMode.h index 45e8e41ff..587a54eac 100644 --- a/MarathonRecomp/api/Sonicteam/MainMode.h +++ b/MarathonRecomp/api/Sonicteam/MainMode.h @@ -26,7 +26,8 @@ namespace Sonicteam }; be m_State; - MARATHON_INSERT_PADDING(0x20); + be m_NextState; + MARATHON_INSERT_PADDING(0x1C); boost::shared_ptr m_spSelectCamera; xpointer m_pFrameGP; MARATHON_INSERT_PADDING(4); @@ -35,6 +36,7 @@ namespace Sonicteam }; MARATHON_ASSERT_OFFSETOF(MainMode, m_State, 0x50); + MARATHON_ASSERT_OFFSETOF(MainMode, m_NextState, 0x54); MARATHON_ASSERT_OFFSETOF(MainMode, m_spSelectCamera, 0x74); MARATHON_ASSERT_OFFSETOF(MainMode, m_pFrameGP, 0x7C); MARATHON_ASSERT_OFFSETOF(MainMode, m_pMainTask, 0x84); diff --git a/MarathonRecomp/api/Sonicteam/Message/DevTitleMode/MsgMoveCursor.h b/MarathonRecomp/api/Sonicteam/Message/DevTitleMode/MsgMoveCursor.h new file mode 100644 index 000000000..33e3a810e --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/Message/DevTitleMode/MsgMoveCursor.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace Sonicteam::Message::DevTitleMode +{ + struct MsgMoveCursor : SoX::Message<0x10001> + { + enum CursorDirection + { + CursorDirection_Up, + CursorDirection_Down = 180 + }; + + be Direction{}; + + MsgMoveCursor(CursorDirection in_direction = CursorDirection_Down) + : Direction(in_direction) {} + }; + + MARATHON_ASSERT_OFFSETOF(MsgMoveCursor, Direction, 0x04); +} diff --git a/MarathonRecomp/api/Sonicteam/Message/DevTitleMode/MsgSelectCursor.h b/MarathonRecomp/api/Sonicteam/Message/DevTitleMode/MsgSelectCursor.h new file mode 100644 index 000000000..a2a7528d2 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/Message/DevTitleMode/MsgSelectCursor.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace Sonicteam::Message::DevTitleMode +{ + struct MsgSelectCursor : SoX::Message<0x10002> + { + MARATHON_INSERT_PADDING(0x04); + bool IsSelected{}; + + MsgSelectCursor(bool in_isSelected = true) : IsSelected(in_isSelected) {} + }; + + MARATHON_ASSERT_OFFSETOF(MsgSelectCursor, IsSelected, 0x08); +} diff --git a/MarathonRecomp/api/Sonicteam/PicBase.h b/MarathonRecomp/api/Sonicteam/PicBase.h new file mode 100644 index 000000000..33cca993c --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/PicBase.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +namespace Sonicteam +{ + class PicBase + { + public: + enum PicState : uint32_t + { + PicState_Load, + PicState_Init, + PicState_Start, + PicState_TitleWait, + PicState_TitleOutro, + PicState_TitleProceed, + PicState_Next + }; + + xpointer m_pVftable; + be m_PrevState; + be m_NextState; + be m_State; + xpointer m_pCsdObject; + }; + + MARATHON_ASSERT_OFFSETOF(PicBase, m_pVftable, 0x00); + MARATHON_ASSERT_OFFSETOF(PicBase, m_PrevState, 0x04); + MARATHON_ASSERT_OFFSETOF(PicBase, m_NextState, 0x08); + MARATHON_ASSERT_OFFSETOF(PicBase, m_State, 0x0C); + MARATHON_ASSERT_OFFSETOF(PicBase, m_pCsdObject, 0x10); + MARATHON_ASSERT_SIZEOF(PicBase, 0x14); +} diff --git a/MarathonRecomp/api/Sonicteam/PictureTitle.h b/MarathonRecomp/api/Sonicteam/PictureTitle.h new file mode 100644 index 000000000..89a7b9006 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/PictureTitle.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Sonicteam +{ + class PictureTitle : public PicBase, public SoX::Input::Listener + { + public: + enum PictureTitleFlags + { + PictureTitleFlags_Next = (1 << 0) + }; + + xpointer m_pDoc; + bool m_IsOutroFinished; + be m_Flags; + xpointer> m_pBackAoitaScene; + be m_BackAoitaOutroSpeed; + be m_BackAoitaOutroEndFrame; + xpointer> m_pTitleMenuScene; + be m_TitleMenuOutroSpeed; + be m_TitleMenuOutroEndFrame; + }; + + MARATHON_ASSERT_OFFSETOF(PictureTitle, m_pDoc, 0x2C); + MARATHON_ASSERT_OFFSETOF(PictureTitle, m_IsOutroFinished, 0x30); + MARATHON_ASSERT_OFFSETOF(PictureTitle, m_Flags, 0x34); + MARATHON_ASSERT_OFFSETOF(PictureTitle, m_pBackAoitaScene, 0x38); + MARATHON_ASSERT_OFFSETOF(PictureTitle, m_BackAoitaOutroSpeed, 0x3C); + MARATHON_ASSERT_OFFSETOF(PictureTitle, m_BackAoitaOutroEndFrame, 0x40); + MARATHON_ASSERT_OFFSETOF(PictureTitle, m_pTitleMenuScene, 0x44); + MARATHON_ASSERT_OFFSETOF(PictureTitle, m_TitleMenuOutroSpeed, 0x48); + MARATHON_ASSERT_OFFSETOF(PictureTitle, m_TitleMenuOutroEndFrame, 0x4C); + MARATHON_ASSERT_SIZEOF(PictureTitle, 0x50); +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.h b/MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.h index 823d9205d..1985465bf 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.h @@ -19,11 +19,11 @@ namespace Sonicteam::SoX::Engine }; xpointer m_pVftable; - MARATHON_INSERT_PADDING(4); + MARATHON_INSERT_PADDING(0x04); xpointer m_pDocMode; xpointer m_pRootTask; xpointer m_pRootGTask; - MARATHON_INSERT_PADDING(8); + MARATHON_INSERT_PADDING(0x08); xpointer m_pDocModeExecutor; xpointer m_pRenderScheduler; XRTL_CRITICAL_SECTION m_CriticalSection1; diff --git a/MarathonRecomp/api/Sonicteam/SoX/Engine/DocMode.h b/MarathonRecomp/api/Sonicteam/SoX/Engine/DocMode.h index 1c2284d03..cbe31d56e 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/Engine/DocMode.h +++ b/MarathonRecomp/api/Sonicteam/SoX/Engine/DocMode.h @@ -8,8 +8,9 @@ namespace Sonicteam::SoX::Engine class DocMode : public Task { public: - MARATHON_INSERT_PADDING(4); + be m_Type; }; + MARATHON_ASSERT_OFFSETOF(DocMode, m_Type, 0x4C); MARATHON_ASSERT_SIZEOF(DocMode, 0x50); } diff --git a/MarathonRecomp/api/Sonicteam/SoX/Input/Listener.h b/MarathonRecomp/api/Sonicteam/SoX/Input/Listener.h new file mode 100644 index 000000000..72cc021a1 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/SoX/Input/Listener.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +namespace Sonicteam::SoX::Input +{ + class Listener : public Object + { + public: + LinkNode m_Sibling; + uint8_t m_Field10; + uint8_t m_Field11; + MARATHON_INSERT_PADDING(0x06); + }; + + MARATHON_ASSERT_OFFSETOF(Listener, m_Sibling, 0x04); + MARATHON_ASSERT_OFFSETOF(Listener, m_Field10, 0x10); + MARATHON_ASSERT_OFFSETOF(Listener, m_Field11, 0x11); + MARATHON_ASSERT_SIZEOF(Listener, 0x18); +} diff --git a/MarathonRecomp/api/Sonicteam/SoX/MessageReceiver.h b/MarathonRecomp/api/Sonicteam/SoX/MessageReceiver.h index 09c45232b..c3cd33811 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/MessageReceiver.h +++ b/MarathonRecomp/api/Sonicteam/SoX/MessageReceiver.h @@ -25,6 +25,13 @@ namespace Sonicteam::SoX { return GuestToHostFunction(m_pVftable->fpProcessMessage, this, pMessage); } + + template + bool ProcessMessage(TArgs&&... in_args) + { + guest_stack_var msg(std::forward(in_args)...); + return ProcessMessage(msg); + } }; MARATHON_ASSERT_OFFSETOF(MessageReceiver, m_pVftable, 0x00); diff --git a/MarathonRecomp/api/Sonicteam/SoX/ResourceManager.h b/MarathonRecomp/api/Sonicteam/SoX/ResourceManager.h index c5c9b2b43..087dec8e9 100644 --- a/MarathonRecomp/api/Sonicteam/SoX/ResourceManager.h +++ b/MarathonRecomp/api/Sonicteam/SoX/ResourceManager.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include #include @@ -9,6 +11,7 @@ #include #include #include +#include namespace Sonicteam::SoX { @@ -39,6 +42,17 @@ namespace Sonicteam::SoX stdx::map, ResourceMgrInfo> m_Managers; MARATHON_INSERT_PADDING(0x04); stdx::map, be, stdx::string_compare> m_ManagerIDs; + + template + TRes* LoadResource(const char* in_pFilePath) + { + const auto result = boost::make_unique>(); + const auto scriptName = boost::make_unique(in_pFilePath); + + GuestToHostFunction(sub_82582648, result.get(), TMgr::GetInstance(), scriptName.get(), scriptName.get(), 0, 1); + + return static_cast(result->get()); + } }; MARATHON_ASSERT_OFFSETOF(ResourceManager::ResourceMgrInfo, fpFunc00, 0x00); diff --git a/MarathonRecomp/api/Sonicteam/StageMap.h b/MarathonRecomp/api/Sonicteam/StageMap.h new file mode 100644 index 000000000..7e2fab832 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/StageMap.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Sonicteam +{ + class StageMap : public SoX::Component + { + public: + stdx::string m_Name; + stdx::string m_Comment; + stdx::vector> m_Children; + + std::vector GetHierarchy() + { + std::vector result{}; + + auto pStageMap = this; + + while (pStageMap) + { + if (pStageMap->m_Name.empty()) + break; + + result.push_back(pStageMap->m_Name.c_str()); + + pStageMap = pStageMap->GetParent(); + } + + std::reverse(result.begin(), result.end()); + + return result; + } + + std::string GetPath() + { + std::stringstream result{}; + + const auto hierarchy = GetHierarchy(); + + for (const auto& rName : hierarchy) + result << "/" << rName; + + return result.str(); + } + + int GetTotalChildCount() + { + int result = m_Children.size(); + + for (const auto& rChild : m_Children) + result += rChild->m_Children.size(); + + return result; + } + }; + + MARATHON_ASSERT_OFFSETOF(StageMap, m_Name, 0x20); + MARATHON_ASSERT_OFFSETOF(StageMap, m_Comment, 0x3C); + MARATHON_ASSERT_OFFSETOF(StageMap, m_Children, 0x58); + MARATHON_ASSERT_SIZEOF(StageMap, 0x68); +} diff --git a/MarathonRecomp/api/Sonicteam/StageSelectMode.h b/MarathonRecomp/api/Sonicteam/StageSelectMode.h new file mode 100644 index 000000000..556fba992 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/StageSelectMode.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Sonicteam +{ + class StageSelectMode : public SoX::Engine::DocMode + { + public: + xpointer m_pRootStageMap; + xpointer m_pCurrentStageMap; + MARATHON_INSERT_PADDING(0x04); + be m_StageMapIndex; + be m_StageMapDepth; + MARATHON_INSERT_PADDING(0x18); + xpointer m_pMenuListener; + xpointer> m_pStageMapFilter; + xpointer m_pStageMapType; + xpointer m_pStageMapName; + MARATHON_INSERT_PADDING(0x04); + + private: + bool Walk(StageMap* in_pStageMap, const std::function& in_rVisitor) + { + if (!in_pStageMap) + return true; + + if (!in_rVisitor(in_pStageMap)) + return false; + + for (const auto& rChild : in_pStageMap->m_Children) + { + if (!Walk(rChild, in_rVisitor)) + return false; + } + + return true; + } + + public: + void Walk(const std::function& in_rVisitor) + { + Walk(m_pRootStageMap.get(), in_rVisitor); + } + }; + + MARATHON_ASSERT_OFFSETOF(StageSelectMode, m_pRootStageMap, 0x50); + MARATHON_ASSERT_OFFSETOF(StageSelectMode, m_pCurrentStageMap, 0x54); + MARATHON_ASSERT_OFFSETOF(StageSelectMode, m_StageMapIndex, 0x5C); + MARATHON_ASSERT_OFFSETOF(StageSelectMode, m_StageMapDepth, 0x60); + MARATHON_ASSERT_OFFSETOF(StageSelectMode, m_pMenuListener, 0x7C); + MARATHON_ASSERT_OFFSETOF(StageSelectMode, m_pStageMapFilter, 0x80); + MARATHON_ASSERT_OFFSETOF(StageSelectMode, m_pStageMapType, 0x84); + MARATHON_ASSERT_OFFSETOF(StageSelectMode, m_pStageMapName, 0x88); + MARATHON_ASSERT_SIZEOF(StageSelectMode, 0x90); +} diff --git a/MarathonRecomp/api/Sonicteam/StateDevTitle.h b/MarathonRecomp/api/Sonicteam/StateDevTitle.h new file mode 100644 index 000000000..a60470282 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/StateDevTitle.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include +#include + +class StateDevTitle : public Sonicteam::DocState, public Sonicteam::System::Singleton> +{ +public: + xpointer m_pPictureTitle; +}; + +MARATHON_ASSERT_OFFSETOF(StateDevTitle, m_pPictureTitle, 0x04); +MARATHON_ASSERT_SIZEOF(StateDevTitle, 0x08); diff --git a/MarathonRecomp/api/Sonicteam/StateMainMode.h b/MarathonRecomp/api/Sonicteam/StateMainMode.h new file mode 100644 index 000000000..a45e159d4 --- /dev/null +++ b/MarathonRecomp/api/Sonicteam/StateMainMode.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include +#include + +class StateMainMode : public Sonicteam::DocState, public Sonicteam::System::Singleton> {}; + +MARATHON_ASSERT_SIZEOF(StateMainMode, 0x04); diff --git a/MarathonRecomp/api/Sonicteam/System/Singleton.h b/MarathonRecomp/api/Sonicteam/System/Singleton.h index 11ac491f4..e56406cd5 100644 --- a/MarathonRecomp/api/Sonicteam/System/Singleton.h +++ b/MarathonRecomp/api/Sonicteam/System/Singleton.h @@ -1,16 +1,21 @@ #pragma once +#include + namespace Sonicteam::System { + class ISingleton {}; + template - class Singleton + class Singleton : public ISingleton { + private: inline static TCreator ms_Creator{}; public: static T* GetInstance() { - auto pInstance = (xpointer*)g_memory.Translate(pSingleton); + const auto pInstance = reinterpret_cast*>(g_memory.Translate(pSingleton)); if (!pInstance->ptr.get()) *pInstance = ms_Creator.Create(); @@ -18,4 +23,10 @@ namespace Sonicteam::System return *pInstance; } }; + + template + concept DerivedSingleton = std::derived_from && requires + { + { T::GetInstance() } -> std::same_as; + }; } diff --git a/MarathonRecomp/api/Sonicteam/TextBookMgr.h b/MarathonRecomp/api/Sonicteam/TextBookMgr.h index 93c5e8afc..6809ac695 100644 --- a/MarathonRecomp/api/Sonicteam/TextBookMgr.h +++ b/MarathonRecomp/api/Sonicteam/TextBookMgr.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include diff --git a/MarathonRecomp/api/lua50/lua.h b/MarathonRecomp/api/lua50/lua.h index c14e7b3d1..f8b2f4bc3 100644 --- a/MarathonRecomp/api/lua50/lua.h +++ b/MarathonRecomp/api/lua50/lua.h @@ -4,6 +4,18 @@ namespace lua50 { + static inline constexpr int LUA_TNONE = -1; + static inline constexpr int LUA_TNIL = 0; + static inline constexpr int LUA_TBOOLEAN = 1; + static inline constexpr int LUA_TLIGHTUSERDATA = 2; + static inline constexpr int LUA_TNUMBER = 3; + static inline constexpr int LUA_TSTRING = 4; + static inline constexpr int LUA_TTABLE = 5; + static inline constexpr int LUA_TFUNCTION = 6; + static inline constexpr int LUA_TUSERDATA = 7; + static inline constexpr int LUA_TTHREAD = 8; + static inline constexpr int LUA_NUMTYPES = 9; + using lua_State = xpointer; using lua_CFunction = be; diff --git a/MarathonRecomp/app.cpp b/MarathonRecomp/app.cpp index 0c937bc95..d557cf5d0 100644 --- a/MarathonRecomp/app.cpp +++ b/MarathonRecomp/app.cpp @@ -1,4 +1,5 @@ #include "app.h" +#include #include #include #include @@ -51,7 +52,7 @@ PPC_FUNC(sub_8262A568) be Height; }; - auto pRenderConfig = reinterpret_cast(g_memory.Translate(ctx.r4.u32)); + auto pRenderConfig = reinterpret_cast(base + ctx.r4.u32); pRenderConfig->Width = Video::s_viewportWidth; pRenderConfig->Height = Video::s_viewportHeight; @@ -63,8 +64,9 @@ PPC_FUNC(sub_8262A568) __imp__sub_8262A568(ctx, base); - App::s_pApp = (Sonicteam::AppMarathon*)g_memory.Translate(ctx.r3.u32); + App::s_pApp = reinterpret_cast(base + ctx.r3.u32); + ImFontAtlasSnapshot::Init(); InitPatches(); } @@ -132,20 +134,10 @@ PPC_FUNC(sub_825EA610) PPC_FUNC_IMPL(__imp__sub_82582648); PPC_FUNC(sub_82582648) { - struct File - { - public: - MARATHON_INSERT_PADDING(4); - xpointer pFilePath; - MARATHON_INSERT_PADDING(0x0C); - be Length; - be Capacity; - }; - - auto pFile = reinterpret_cast(base + ctx.r5.u32); + const auto pPath = reinterpret_cast(base + ctx.r5.u32); - if (pFile->pFilePath && pFile->Length > 0) - LOGFN_UTILITY("Loading file: {}", pFile->pFilePath.get()); + if (pPath && !pPath->empty()) + LOGFN_UTILITY("Loading file: {}", pPath->c_str()); __imp__sub_82582648(ctx, base); } diff --git a/MarathonRecomp/gpu/imgui/imgui_snapshot.cpp b/MarathonRecomp/gpu/imgui/imgui_snapshot.cpp index b6454cd04..db968bdcd 100644 --- a/MarathonRecomp/gpu/imgui/imgui_snapshot.cpp +++ b/MarathonRecomp/gpu/imgui/imgui_snapshot.cpp @@ -1,11 +1,28 @@ #include "imgui_snapshot.h" - +#include #include +#include #include +#include #include -#include -#include #include +#include + +extern ImFontBuilderIO g_fontBuilderIO; + + +struct FontInfo +{ + const char* pFileName{}; + std::vector GlyphRanges{}; +}; + +static std::map g_fonts = +{ + { ImFontType::Rodin, { "FOT-RodinPro-DB.otf" } }, + { ImFontType::NewRodin, { "FOT-NewRodinPro-UB.otf" } }, + { ImFontType::Arial, { "ArialUni.ttf" } } +}; template void ImFontAtlasSnapshot::SnapPointer(size_t offset, const T1& value, const T2& ptr, size_t count) @@ -125,7 +142,6 @@ ImFontAtlas* ImFontAtlasSnapshot::Load() return reinterpret_cast(g_imFontAtlas.get() + header->dataOffset); } - static void GetGlyphs(std::set& glyphs, const std::string_view& value) { const char* cur = value.data(); @@ -137,11 +153,9 @@ static void GetGlyphs(std::set& glyphs, const std::string_view& value) } } -static std::vector g_glyphRanges; - -void ImFontAtlasSnapshot::GenerateGlyphRanges() +static void GenerateGlyphRanges(ImFontType in_fontType, bool in_isInstaller = false) { - std::vector localeStrings; + std::vector localeStrings{}; for (auto& config : g_configDefinitions) config->GetLocaleStrings(localeStrings); @@ -160,48 +174,221 @@ void ImFontAtlasSnapshot::GenerateGlyphRanges() GetGlyphs(glyphs, value); } - for (size_t i = XDBF_LANGUAGE_ENGLISH; i <= XDBF_LANGUAGE_ITALIAN; i++) + auto& rFontInfo = g_fonts[in_fontType]; + + if (in_isInstaller) { - auto achievements = g_xdbfWrapper.GetAchievements(static_cast(i)); - for (auto& achievement : achievements) + LOGFN_WARNING("Generating font atlas for \"{}\" in the installer wizard. Glyphs may be missing!", rFontInfo.pFileName); + } + else + { + if (in_fontType == ImFontType::Rodin) + { + for (size_t i = XDBF_LANGUAGE_ENGLISH; i <= XDBF_LANGUAGE_ITALIAN; i++) + { + auto achievements = g_xdbfWrapper.GetAchievements(static_cast(i)); + + for (auto& achievement : achievements) + { + GetGlyphs(glyphs, achievement.Name); + GetGlyphs(glyphs, achievement.UnlockedDesc); + GetGlyphs(glyphs, achievement.LockedDesc); + } + } + } + else if (in_fontType == ImFontType::Arial) { - GetGlyphs(glyphs, achievement.Name); - GetGlyphs(glyphs, achievement.UnlockedDesc); - GetGlyphs(glyphs, achievement.LockedDesc); + const std::array stageSelectStrings = + { + g_memory.Translate(0x820336C4), + g_memory.Translate(0x820336C8), + g_memory.Translate(0x820336D0) + }; + + for (const auto& rStr : stageSelectStrings) + GetGlyphs(glyphs, os::locale::ConvertToUTF8(os::locale::Encoding::ShiftJIS, reinterpret_cast(rStr))); + + if (const auto pResourceManager = Sonicteam::SoX::ResourceManager::GetInstance()) + { + if (const auto pStageSelectLua = pResourceManager->LoadResource("scripts/stage/stageselect.lua")) + { + if (const auto pStageListTable = pStageSelectLua->GetTable("StageList")) + { + if (pStageListTable->Iterate() && pStageListTable->IsTable()) + { + do + { + GetGlyphs(glyphs, os::locale::ConvertToUTF8(os::locale::Encoding::ShiftJIS, pStageListTable->GetElement("Comment"))); + } + while (pStageListTable->Iterate()); + } + } + } + } } } + auto& rGlyphRanges = rFontInfo.GlyphRanges; + for (auto glyph : glyphs) { - if (g_glyphRanges.empty() || (g_glyphRanges.back() + 1) != glyph) + if (rGlyphRanges.empty() || (rGlyphRanges.back() + 1) != glyph) { - g_glyphRanges.push_back(glyph); - g_glyphRanges.push_back(glyph); + rGlyphRanges.push_back(glyph); + rGlyphRanges.push_back(glyph); } else { - g_glyphRanges.back() = glyph; + rGlyphRanges.back() = glyph; } } - g_glyphRanges.push_back(0); + rGlyphRanges.push_back(0); + + ImGui::GetIO().Fonts->AddFontFromFileTTF(rFontInfo.pFileName, 24.0f, nullptr, rGlyphRanges.data()); } -ImFont* ImFontAtlasSnapshot::GetFont(const char* name) +void ImFontAtlasSnapshot::Init(bool in_isInstaller) { - auto fontAtlas = ImGui::GetIO().Fonts; - for (auto& configData : fontAtlas->ConfigData) + auto& io = ImGui::GetIO(); + +#ifdef ENABLE_IM_FONT_ATLAS_SNAPSHOT + IM_DELETE(io.Fonts); + io.Fonts = ImFontAtlasSnapshot::Load(); + + FontTexture = LoadTexture + ( + decompressZstd + ( + g_im_font_atlas_texture, + g_im_font_atlas_texture_uncompressed_size + ) + .get(), + + g_im_font_atlas_texture_uncompressed_size + ); +#else + io.Fonts->AddFontDefault(); + + GenerateGlyphRanges(ImFontType::Rodin, in_isInstaller); + GenerateGlyphRanges(ImFontType::NewRodin, in_isInstaller); + GenerateGlyphRanges(ImFontType::Arial, in_isInstaller); + + io.Fonts->FontBuilderIO = &g_fontBuilderIO; + io.Fonts->Build(); + + FontTexture = std::make_unique(ResourceType::Texture); + + uint8_t* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + + RenderTextureDesc textureDesc; + textureDesc.dimension = RenderTextureDimension::TEXTURE_2D; + textureDesc.width = width; + textureDesc.height = height; + textureDesc.depth = 1; + textureDesc.mipLevels = 1; + textureDesc.arraySize = 1; + textureDesc.format = RenderFormat::R8G8B8A8_UNORM; + + const auto pRenderDevice = Video::GetRenderDevice(); + + FontTexture->textureHolder = pRenderDevice->createTexture(textureDesc); + FontTexture->texture = FontTexture->textureHolder.get(); + + uint32_t rowPitch = (width * 4 + PITCH_ALIGNMENT - 1) & ~(PITCH_ALIGNMENT - 1); + uint32_t slicePitch = (rowPitch * height + PLACEMENT_ALIGNMENT - 1) & ~(PLACEMENT_ALIGNMENT - 1); + auto uploadBuffer = pRenderDevice->createBuffer(RenderBufferDesc::UploadBuffer(slicePitch)); + uint8_t* mappedMemory = reinterpret_cast(uploadBuffer->map()); + + if (rowPitch == (width * 4)) { - if (strstr(configData.Name, name) != nullptr) + memcpy(mappedMemory, pixels, slicePitch); + } + else + { + for (size_t i = 0; i < height; i++) { - assert(configData.DstFont != nullptr); - return configData.DstFont; + memcpy(mappedMemory, pixels, width * 4); + pixels += width * 4; + mappedMemory += rowPitch; } } + uploadBuffer->unmap(); + + Video::ExecuteCopyCommandList([&](RenderCommandList* in_pCommandList) + { + in_pCommandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(FontTexture->texture, RenderTextureLayout::COPY_DEST)); + + in_pCommandList->copyTextureRegion( + RenderTextureCopyLocation::Subresource(FontTexture->texture, 0), + RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), RenderFormat::R8G8B8A8_UNORM, width, height, 1, rowPitch / 4, 0)); + }); + + FontTexture->layout = RenderTextureLayout::COPY_DEST; + + RenderTextureViewDesc textureViewDesc; + textureViewDesc.format = textureDesc.format; + textureViewDesc.dimension = RenderTextureViewDimension::TEXTURE_2D; + textureViewDesc.mipLevels = 1; + FontTexture->textureView = FontTexture->texture->createTextureView(textureViewDesc); + + FontTexture->descriptorIndex = g_textureDescriptorAllocator.allocate(); + g_textureDescriptorSet->setTexture(FontTexture->descriptorIndex, FontTexture->texture, RenderTextureLayout::SHADER_READ, FontTexture->textureView.get()); +#endif + + io.Fonts->SetTexID(FontTexture.get()); + +#ifndef ENABLE_IM_FONT_ATLAS_SNAPSHOT + ImFontAtlasSnapshot snapshot{}; + snapshot.Snap(); + + if (FILE* file = fopen("im_font_atlas.bin", "wb")) + { + fwrite(snapshot.data.data(), 1, snapshot.data.size(), file); + fclose(file); + } + + ddspp::Header header; + ddspp::HeaderDXT10 headerDX10; + ddspp::encode_header(ddspp::R8G8B8A8_UNORM, width, height, 1, ddspp::Texture2D, 1, 1, header, headerDX10); + + if (FILE* file = fopen("im_font_atlas.dds", "wb")) + { + fwrite(&ddspp::DDS_MAGIC, 4, 1, file); + fwrite(&header, sizeof(header), 1, file); + fwrite(&headerDX10, sizeof(headerDX10), 1, file); + fwrite(pixels, 4, width * height, file); + fclose(file); + } +#endif + + IsInit = true; +} + +ImFont* ImFontAtlasSnapshot::GetFont(ImFontType in_fontType) +{ + const auto& rFileName = g_fonts[in_fontType].pFileName; + + const auto pFontAtlas = ImGui::GetIO().Fonts; + + for (const auto& rConfigData : pFontAtlas->ConfigData) + { + if (strstr(rConfigData.Name, rFileName) != nullptr) + { + assert(rConfigData.DstFont != nullptr); + + return rConfigData.DstFont; + } + } + + LOGFN_ERROR("Failed to locate font in the font atlas: {}", rFileName); + #ifdef ENABLE_IM_FONT_ATLAS_SNAPSHOT - assert(false && "Unable to locate equivalent font in the atlas file."); + assert(false && "Failed to locate font in the font atlas."); #endif - return fontAtlas->AddFontFromFileTTF(name, 24.0f, nullptr, g_glyphRanges.data()); + return nullptr; } diff --git a/MarathonRecomp/gpu/imgui/imgui_snapshot.h b/MarathonRecomp/gpu/imgui/imgui_snapshot.h index a77745165..b5bcf3853 100644 --- a/MarathonRecomp/gpu/imgui/imgui_snapshot.h +++ b/MarathonRecomp/gpu/imgui/imgui_snapshot.h @@ -5,12 +5,24 @@ // characters in the locale get added to the atlas. #define ENABLE_IM_FONT_ATLAS_SNAPSHOT +struct GuestTexture; + +enum class ImFontType +{ + Rodin, + NewRodin, + Arial +}; + struct ImFontAtlasSnapshot { std::vector data; ankerl::unordered_dense::map objects; std::vector offsets; + static inline bool IsInit{}; + static inline std::unique_ptr FontTexture; + template void SnapPointer(size_t offset, const T1& value, const T2& ptr, size_t count); @@ -24,8 +36,8 @@ struct ImFontAtlasSnapshot static ImFontAtlas* Load(); - static void GenerateGlyphRanges(); + static void Init(bool in_isInstaller = false); // When ENABLE_IM_FONT_ATLAS_SNAPSHOT is undefined, this creates the font runtime instead. - static ImFont* GetFont(const char* name); + static ImFont* GetFont(ImFontType in_fontType); }; diff --git a/MarathonRecomp/gpu/video.cpp b/MarathonRecomp/gpu/video.cpp index 59bf26380..31b388a8d 100644 --- a/MarathonRecomp/gpu/video.cpp +++ b/MarathonRecomp/gpu/video.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -368,47 +367,6 @@ static constexpr uint32_t CONDITIONAL_SURVEY_MAX = 64; static std::unique_ptr g_conditionalSurveyBuffer; static std::unique_ptr g_conditionalSurveyDescriptorSet; -enum -{ - TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D, - TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D_ARRAY, - TEXTURE_DESCRIPTOR_NULL_TEXTURE_CUBE, - TEXTURE_DESCRIPTOR_NULL_COUNT -}; - -struct TextureDescriptorAllocator -{ - Mutex mutex; - uint32_t capacity = TEXTURE_DESCRIPTOR_NULL_COUNT; - std::vector freed; - - uint32_t allocate() - { - std::lock_guard lock(mutex); - - uint32_t value; - if (!freed.empty()) - { - value = freed.back(); - freed.pop_back(); - } - else - { - value = capacity; - ++capacity; - } - - return value; - } - - void free(uint32_t value) - { - assert(value != NULL); - std::lock_guard lock(mutex); - freed.push_back(value); - } -}; - static std::unique_ptr g_blankTextures[TEXTURE_DESCRIPTOR_NULL_COUNT]; static std::unique_ptr g_blankTextureViews[TEXTURE_DESCRIPTOR_NULL_COUNT]; @@ -1604,26 +1562,21 @@ static bool DetectWine() static constexpr size_t TEXTURE_DESCRIPTOR_SIZE = 32768; static constexpr size_t SAMPLER_DESCRIPTOR_SIZE = 1024; -static std::unique_ptr g_imFontTexture; static std::unique_ptr g_imPipelineLayout; static std::unique_ptr g_imPipeline; static std::unique_ptr g_imAdditivePipeline; -template -static void ExecuteCopyCommandList(const T& function) +void Video::ExecuteCopyCommandList(const std::function& function) { std::lock_guard lock(g_copyMutex); g_copyCommandList->begin(); - function(); + function(g_copyCommandList.get()); g_copyCommandList->end(); g_copyQueue->executeCommandLists(g_copyCommandList.get(), g_copyCommandFence.get()); g_copyQueue->waitForCommandFence(g_copyCommandFence.get()); } -static constexpr uint32_t PITCH_ALIGNMENT = 0x100; -static constexpr uint32_t PLACEMENT_ALIGNMENT = 0x200; - struct ImGuiPushConstants { ImVec2 boundsMin{}; @@ -1642,8 +1595,6 @@ struct ImGuiPushConstants float outline{}; }; -extern ImFontBuilderIO g_fontBuilderIO; - static void CreateImGuiBackend() { ImGuiIO& io = ImGui::GetIO(); @@ -1651,89 +1602,12 @@ static void CreateImGuiBackend() io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; -#ifdef ENABLE_IM_FONT_ATLAS_SNAPSHOT - IM_DELETE(io.Fonts); - io.Fonts = ImFontAtlasSnapshot::Load(); -#else - io.Fonts->AddFontDefault(); - ImFontAtlasSnapshot::GenerateGlyphRanges(); -#endif - InitImGuiUtils(); OptionsMenu::Init(); InstallerWizard::Init(); ImGui_ImplSDL3_InitForOther(GameWindow::s_pWindow); -#ifdef ENABLE_IM_FONT_ATLAS_SNAPSHOT - g_imFontTexture = LoadTexture( - decompressZstd(g_im_font_atlas_texture, g_im_font_atlas_texture_uncompressed_size).get(), g_im_font_atlas_texture_uncompressed_size); -#else - io.Fonts->FontBuilderIO = &g_fontBuilderIO; - io.Fonts->Build(); - - g_imFontTexture = std::make_unique(ResourceType::Texture); - - uint8_t* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - - RenderTextureDesc textureDesc; - textureDesc.dimension = RenderTextureDimension::TEXTURE_2D; - textureDesc.width = width; - textureDesc.height = height; - textureDesc.depth = 1; - textureDesc.mipLevels = 1; - textureDesc.arraySize = 1; - textureDesc.format = RenderFormat::R8G8B8A8_UNORM; - - g_imFontTexture->textureHolder = g_device->createTexture(textureDesc); - g_imFontTexture->texture = g_imFontTexture->textureHolder.get(); - - uint32_t rowPitch = (width * 4 + PITCH_ALIGNMENT - 1) & ~(PITCH_ALIGNMENT - 1); - uint32_t slicePitch = (rowPitch * height + PLACEMENT_ALIGNMENT - 1) & ~(PLACEMENT_ALIGNMENT - 1); - auto uploadBuffer = g_device->createBuffer(RenderBufferDesc::UploadBuffer(slicePitch)); - uint8_t* mappedMemory = reinterpret_cast(uploadBuffer->map()); - - if (rowPitch == (width * 4)) - { - memcpy(mappedMemory, pixels, slicePitch); - } - else - { - for (size_t i = 0; i < height; i++) - { - memcpy(mappedMemory, pixels, width * 4); - pixels += width * 4; - mappedMemory += rowPitch; - } - } - - uploadBuffer->unmap(); - - ExecuteCopyCommandList([&] - { - g_copyCommandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(g_imFontTexture->texture, RenderTextureLayout::COPY_DEST)); - - g_copyCommandList->copyTextureRegion( - RenderTextureCopyLocation::Subresource(g_imFontTexture->texture, 0), - RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), RenderFormat::R8G8B8A8_UNORM, width, height, 1, rowPitch / 4, 0)); - }); - - g_imFontTexture->layout = RenderTextureLayout::COPY_DEST; - - RenderTextureViewDesc textureViewDesc; - textureViewDesc.format = textureDesc.format; - textureViewDesc.dimension = RenderTextureViewDimension::TEXTURE_2D; - textureViewDesc.mipLevels = 1; - g_imFontTexture->textureView = g_imFontTexture->texture->createTextureView(textureViewDesc); - - g_imFontTexture->descriptorIndex = g_textureDescriptorAllocator.allocate(); - g_textureDescriptorSet->setTexture(g_imFontTexture->descriptorIndex, g_imFontTexture->texture, RenderTextureLayout::SHADER_READ, g_imFontTexture->textureView.get()); -#endif - - io.Fonts->SetTexID(g_imFontTexture.get()); - RenderPipelineLayoutBuilder pipelineLayoutBuilder; pipelineLayoutBuilder.begin(false, true); @@ -1778,32 +1652,6 @@ static void CreateImGuiBackend() pipelineDesc.renderTargetBlend[0].dstBlend = RenderBlend::ONE; g_imAdditivePipeline = g_device->createGraphicsPipeline(pipelineDesc); - -#ifndef ENABLE_IM_FONT_ATLAS_SNAPSHOT - ImFontAtlasSnapshot snapshot; - snapshot.Snap(); - - FILE* file = fopen("im_font_atlas.bin", "wb"); - if (file) - { - fwrite(snapshot.data.data(), 1, snapshot.data.size(), file); - fclose(file); - } - - ddspp::Header header; - ddspp::HeaderDXT10 headerDX10; - ddspp::encode_header(ddspp::R8G8B8A8_UNORM, width, height, 1, ddspp::Texture2D, 1, 1, header, headerDX10); - - file = fopen("im_font_atlas.dds", "wb"); - if (file) - { - fwrite(&ddspp::DDS_MAGIC, 4, 1, file); - fwrite(&header, sizeof(header), 1, file); - fwrite(&headerDX10, sizeof(headerDX10), 1, file); - fwrite(pixels, 4, width * height, file); - fclose(file); - } -#endif } static void CheckSwapChain() @@ -2037,6 +1885,11 @@ static bool TryCreateHostDevice(std::vector& interface return true; } +RenderDevice* Video::GetRenderDevice() +{ + return g_device.get(); +} + bool Video::CreateHostDevice(const char* sdlVideoDriver, bool graphicsApiRetry) { for (uint32_t i = 0; i < 16; i++) @@ -2580,10 +2433,10 @@ static void UnlockBuffer(GuestBuffer* buffer, bool useCopyQueue) if (useCopyQueue) { - ExecuteCopyCommandList([&] - { - g_copyCommandList->copyBufferRegion(buffer->buffer->at(0), uploadBuffer->at(0), buffer->dataSize); - }); + Video::ExecuteCopyCommandList([&](RenderCommandList* in_pCommandList) + { + in_pCommandList->copyBufferRegion(buffer->buffer->at(0), uploadBuffer->at(0), buffer->dataSize); + }); } else { @@ -2715,10 +2568,10 @@ static void DrawProfiler() if (!g_profilerVisible) return; - ImFont* font = ImFontAtlasSnapshot::GetFont("FOT-RodinPro-DB.otf"); - const auto defaultFontScale = font->Scale; - font->Scale = ImGui::GetDefaultFont()->FontSize / font->FontSize; - ImGui::PushFont(font); + const auto pFont = g_pFntRodin; + const auto defaultFontScale = pFont->Scale; + pFont->Scale = ImGui::GetDefaultFont()->FontSize / pFont->FontSize; + ImGui::PushFont(pFont); #define IMGUI_GENERIC_ROW(name, value, ...) \ do \ @@ -2955,7 +2808,7 @@ static void DrawProfiler() ImGui::End(); ImGui::PopFont(); - font->Scale = defaultFontScale; + pFont->Scale = defaultFontScale; } static void DrawFPS() @@ -2983,20 +2836,29 @@ static void DrawFPS() auto drawList = ImGui::GetBackgroundDrawList(); auto fmt = fmt::format("FPS: {:.2f}", fps); - auto font = ImFontAtlasSnapshot::GetFont("FOT-RodinPro-DB.otf"); auto fontSize = Scale(10); - auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, fmt.c_str()); + auto textSize = g_pFntRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, fmt.c_str()); ImVec2 min = { Scale(40), Scale(30) }; ImVec2 max = { min.x + std::max(Scale(75), textSize.x + Scale(10)), min.y + Scale(15) }; ImVec2 textPos = { min.x + Scale(2), CENTRE_TEXT_VERT(min, max, textSize) + Scale(0.2f) }; drawList->AddRectFilled(min, max, IM_COL32(0, 0, 0, 200)); - drawList->AddText(font, fontSize, textPos, IM_COL32_WHITE, fmt.c_str()); + drawList->AddText(g_pFntRodin, fontSize, textPos, IM_COL32_WHITE, fmt.c_str()); +} + +static std::vector> g_imguiDrawQueue{}; + +void Video::EnqueueImGuiDraw(const std::function& in_rEvent) +{ + g_imguiDrawQueue.push_back(in_rEvent); } static void DrawImGui() { + if (!ImFontAtlasSnapshot::IsInit) + return; + ImGui_ImplSDL3_NewFrame(); auto& io = ImGui::GetIO(); @@ -3044,6 +2906,11 @@ static void DrawImGui() Fader::Draw(); BlackBar::Draw(); + for (const auto& rEvent : g_imguiDrawQueue) + rEvent(); + + g_imguiDrawQueue.clear(); + assert(ImGui::GetBackgroundDrawList()->_ClipRectStack.Size == 1 && "Some clip rects were not removed from the stack!"); DrawFPS(); @@ -3181,7 +3048,7 @@ static void ProcDrawImGui(const RenderCommand& cmd) descriptorIndex = texture->descriptorIndex; - if (texture == g_imFontTexture.get()) + if (texture == ImFontAtlasSnapshot::FontTexture.get()) descriptorIndex |= 0x80000000; setPushConstants(&pushConstants.texture2DDescriptorIndex, &descriptorIndex, sizeof(descriptorIndex)); @@ -6866,19 +6733,19 @@ static bool LoadTexture(GuestTexture& texture, const uint8_t* data, size_t dataS uploadBuffer->unmap(); - ExecuteCopyCommandList([&] - { - g_copyCommandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(texture.texture, RenderTextureLayout::COPY_DEST)); + Video::ExecuteCopyCommandList([&](RenderCommandList* in_pCommandList) + { + in_pCommandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(texture.texture, RenderTextureLayout::COPY_DEST)); - for (size_t i = 0; i < slices.size(); i++) - { - auto& slice = slices[i]; + for (size_t i = 0; i < slices.size(); i++) + { + auto& slice = slices[i]; - g_copyCommandList->copyTextureRegion( - RenderTextureCopyLocation::Subresource(texture.texture, i % desc.mipLevels, i / desc.mipLevels), - RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), desc.format, slice.width, slice.height, slice.depth, (slice.dstRowPitch * 8) / ddsDesc.bitsPerPixelOrBlock * ddsDesc.blockWidth, slice.dstOffset)); - } - }); + in_pCommandList->copyTextureRegion( + RenderTextureCopyLocation::Subresource(texture.texture, i % desc.mipLevels, i / desc.mipLevels), + RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), desc.format, slice.width, slice.height, slice.depth, (slice.dstRowPitch * 8) / ddsDesc.bitsPerPixelOrBlock * ddsDesc.blockWidth, slice.dstOffset)); + } + }); return true; } @@ -6923,14 +6790,14 @@ static bool LoadTexture(GuestTexture& texture, const uint8_t* data, size_t dataS stbi_image_free(stbImage); - ExecuteCopyCommandList([&] - { - g_copyCommandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(texture.texture, RenderTextureLayout::COPY_DEST)); + Video::ExecuteCopyCommandList([&](RenderCommandList* in_pCommandList) + { + in_pCommandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(texture.texture, RenderTextureLayout::COPY_DEST)); - g_copyCommandList->copyTextureRegion( - RenderTextureCopyLocation::Subresource(texture.texture, 0), - RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), RenderFormat::R8G8B8A8_UNORM, width, height, 1, rowPitch / 4, 0)); - }); + in_pCommandList->copyTextureRegion( + RenderTextureCopyLocation::Subresource(texture.texture, 0), + RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), RenderFormat::R8G8B8A8_UNORM, width, height, 1, rowPitch / 4, 0)); + }); return true; } diff --git a/MarathonRecomp/gpu/video.h b/MarathonRecomp/gpu/video.h index 08cff1cd2..cec7149d6 100644 --- a/MarathonRecomp/gpu/video.h +++ b/MarathonRecomp/gpu/video.h @@ -21,6 +21,9 @@ #define LOAD_ZSTD_TEXTURE(name) LoadTexture(decompressZstd(name, name##_uncompressed_size).get(), name##_uncompressed_size) +static inline constexpr uint32_t PITCH_ALIGNMENT = 0x100; +static inline constexpr uint32_t PLACEMENT_ALIGNMENT = 0x200; + using namespace plume; struct Video @@ -30,6 +33,11 @@ struct Video static inline bool s_needsResize; + // TODO: unspaghettify. + static RenderDevice* GetRenderDevice(); + static void ExecuteCopyCommandList(const std::function& function); + + static void EnqueueImGuiDraw(const std::function& in_rEvent); static bool CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry); static void WaitOnSwapChain(); static void Present(); @@ -46,6 +54,50 @@ enum class Backend METAL }; +enum +{ + TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D, + TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D_ARRAY, + TEXTURE_DESCRIPTOR_NULL_TEXTURE_CUBE, + TEXTURE_DESCRIPTOR_NULL_COUNT +}; + +struct TextureDescriptorAllocator +{ + Mutex mutex; + uint32_t capacity = TEXTURE_DESCRIPTOR_NULL_COUNT; + std::vector freed; + + uint32_t allocate() + { + std::lock_guard lock(mutex); + + uint32_t value; + if (!freed.empty()) + { + value = freed.back(); + freed.pop_back(); + } + else + { + value = capacity; + ++capacity; + } + + return value; + } + + void free(uint32_t value) + { + assert(value != NULL); + std::lock_guard lock(mutex); + freed.push_back(value); + } +}; + +extern TextureDescriptorAllocator g_textureDescriptorAllocator; +extern std::unique_ptr g_textureDescriptorSet; + struct GuestSamplerState { be data[6]; diff --git a/MarathonRecomp/main.cpp b/MarathonRecomp/main.cpp index 951a7397b..b7855cb77 100644 --- a/MarathonRecomp/main.cpp +++ b/MarathonRecomp/main.cpp @@ -4,6 +4,7 @@ #include #endif #include +#include #include #include #include @@ -317,6 +318,8 @@ int main(int argc, char *argv[]) std::_Exit(1); } + ImFontAtlasSnapshot::Init(true); + if (!InstallerWizard::Run(GetGamePath(), isGameInstalled && forceDLCInstaller)) std::_Exit(0); } diff --git a/MarathonRecomp/os/linux/locale_linux.cpp b/MarathonRecomp/os/linux/locale_linux.cpp new file mode 100644 index 000000000..b6a5b5e28 --- /dev/null +++ b/MarathonRecomp/os/linux/locale_linux.cpp @@ -0,0 +1,65 @@ +#include +#include + +#define ICONV_INVALID (reinterpret_cast(-1)) + +class IconvContext +{ +public: + IconvContext(const char* to, const char* from) + { + cd = iconv_open(to, from); + assert(cd != ICONV_INVALID && "iconv_open failed"); + } + + ~IconvContext() + { + if (cd != ICONV_INVALID) + iconv_close(cd); + } + + std::string Convert(const std::string& str) + { + std::string result{}; + if (cd == ICONV_INVALID) + return result; + + auto* in = const_cast(str.data()); + size_t in_remain = str.size(); + + char buf[128]; + char* out = buf; + size_t out_remain = sizeof(buf); + + while (in_remain > 0) + { + if (iconv(cd, &in, &in_remain, &out, &out_remain) == -1) + { + break; + } + result.append(buf, out); + + out = buf; + out_remain = sizeof(buf); + } + + return result; + } + +private: + iconv_t cd = ICONV_INVALID; +}; + +std::string os::locale::ConvertToUTF8(Encoding in_encoding, const std::string& in_rStr) +{ + switch (in_encoding) + { + case Encoding::ShiftJIS: + thread_local IconvContext context("UTF-8", "CP932"); + return context.Convert(in_rStr); + } + + assert(false && "Unimplemented."); + + return ""; +} diff --git a/MarathonRecomp/os/locale.h b/MarathonRecomp/os/locale.h new file mode 100644 index 000000000..bafcc6782 --- /dev/null +++ b/MarathonRecomp/os/locale.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace os::locale +{ + enum class Encoding + { + ShiftJIS + }; + + std::string ConvertToUTF8(Encoding in_encoding, const std::string& in_rStr); +} diff --git a/MarathonRecomp/os/macos/locale_macos.cpp b/MarathonRecomp/os/macos/locale_macos.cpp new file mode 100644 index 000000000..7836e0680 --- /dev/null +++ b/MarathonRecomp/os/macos/locale_macos.cpp @@ -0,0 +1,35 @@ +#include +#include + +std::string os::locale::ConvertToUTF8(Encoding in_encoding, const std::string& in_rStr) +{ + CFStringEncoding cfEncoding; + + switch (in_encoding) + { + case Encoding::ShiftJIS: + cfEncoding = kCFStringEncodingDOSJapanese; + break; + + default: + assert(false && "Unimplemented."); + return ""; + } + + CFDataRef cfData = CFDataCreateWithBytesNoCopy(nullptr, reinterpret_cast(in_rStr.data()), + static_cast(in_rStr.size()), kCFAllocatorNull); + assert(cfData && "CFDataCreateWithBytesNoCopy failed"); + + CFStringRef cfStr = CFStringCreateFromExternalRepresentation(nullptr, cfData, cfEncoding); + CFRelease(cfData); + assert(cfStr && "CFStringCreateFromExternalRepresentation failed"); + + CFDataRef cfConvertedData = CFStringCreateExternalRepresentation(nullptr, cfStr, kCFStringEncodingUTF8, 0); + CFRelease(cfStr); + assert(cfConvertedData && "CFStringCreateExternalRepresentation failed"); + + std::string result(reinterpret_cast(CFDataGetBytePtr(cfConvertedData)), CFDataGetLength(cfConvertedData)); + CFRelease(cfConvertedData); + + return result; +} diff --git a/MarathonRecomp/os/win32/locale_win32.cpp b/MarathonRecomp/os/win32/locale_win32.cpp new file mode 100644 index 000000000..7a44bcea6 --- /dev/null +++ b/MarathonRecomp/os/win32/locale_win32.cpp @@ -0,0 +1,32 @@ +#include + +std::string os::locale::ConvertToUTF8(Encoding in_encoding, const std::string& in_rStr) +{ + switch (in_encoding) + { + case Encoding::ShiftJIS: + { + const auto lengthU16 = MultiByteToWideChar(932, 0, in_rStr.c_str(), in_rStr.size(), NULL, 0); + + if (lengthU16 <= 0) + break; + + auto wide = std::wstring(lengthU16, 0); + MultiByteToWideChar(932, 0, in_rStr.c_str(), in_rStr.size(), &wide[0], lengthU16); + + const auto lengthU8 = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), lengthU16, NULL, 0, NULL, NULL); + + if (lengthU8 <= 0) + break; + + std::string result = std::string(lengthU8, 0); + WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), lengthU16, &result[0], lengthU8, NULL, NULL); + + return result; + } + } + + assert(false && "Unimplemented."); + + return ""; +} diff --git a/MarathonRecomp/patches/TitleTask_patches.cpp b/MarathonRecomp/patches/TitleTask_patches.cpp index e60c87837..2fa576ef3 100644 --- a/MarathonRecomp/patches/TitleTask_patches.cpp +++ b/MarathonRecomp/patches/TitleTask_patches.cpp @@ -19,7 +19,14 @@ static double g_titleExitOutroTime{}; static bool g_quitMessageOpen{}; static bool g_saveDataExists{}; -static bool g_isSecretDone{}; +enum class DevTitleState +{ + Idle, + Open, + Start +}; + +static DevTitleState g_devTitleState{}; static uint32_t g_secretFlags{}; enum @@ -91,25 +98,21 @@ bool TitleTask_RedirectStateTransitionToOutroAnim(PPCRegister& r31) PPC_FUNC_IMPL(__imp__sub_825126A0); PPC_FUNC(sub_825126A0) { - auto pTitleTask = (Sonicteam::TitleTask*)(base + ctx.r3.u32); + auto pTitleTask = reinterpret_cast(base + ctx.r3.u32); auto deltaTime = ctx.f1.f64; switch (pTitleTask->m_State) { case Sonicteam::TitleTask::TitleState_Open: { - if (!Config::DisableTitleInputDelay) - break; - // Skip open animation. - GuestToHostFunction(sub_82511CA0, pTitleTask, (int)Sonicteam::TitleTask::TitleState_Wait); - - break; + if (Config::DisableTitleInputDelay) + GuestToHostFunction(sub_82511CA0, pTitleTask, int(Sonicteam::TitleTask::TitleState_Wait)); } case Sonicteam::TitleTask::TitleState_Wait: { - if (g_isSecretDone) + if (OptionsMenu::s_isDebugUnlocked) break; if (auto& spInputManager = App::s_pApp->m_pDoc->m_InputManagers[0]) @@ -131,8 +134,9 @@ PPC_FUNC(sub_825126A0) if (g_secretFlags == SECRET_ALL) { + LOGN_WARNING("Secret unlocked!"); Game_PlaySound("totalring_count"); - g_isSecretDone = true; + OptionsMenu::s_isDebugUnlocked = true; } break; @@ -148,7 +152,7 @@ PPC_FUNC(sub_825126A0) rPadState.IsDown(Sonicteam::SoX::Input::PadButton_South) && rPadState.IsDown(Sonicteam::SoX::Input::PadButton_Start)) { - OptionsMenu::s_isDebugUnlocked = true; + g_devTitleState = DevTitleState::Open; } } @@ -165,13 +169,22 @@ PPC_FUNC(sub_825126A0) AchievementManager::Reset(); } - if (!Config::DisableTitleInputDelay) - { - g_titleProceedOutroTime += deltaTime; + g_titleProceedOutroTime += deltaTime; + + if (Config::DisableTitleInputDelay) + g_titleProceedOutroTime = TITLE_OPTION_OUTRO_TOTAL_FRAMES + deltaTime; - // Wait for outro animation to complete before entering menu. - if (g_titleProceedOutroTime > TITLE_OPTION_OUTRO_TOTAL_FRAMES) + // Wait for outro animation to complete before entering menu. + if (g_titleProceedOutroTime > TITLE_OPTION_OUTRO_TOTAL_FRAMES) + { + if (g_devTitleState == DevTitleState::Open) + { + g_devTitleState = DevTitleState::Start; + } + else + { GuestToHostFunction(sub_82511CA0, pTitleTask, 9); + } } break; @@ -246,3 +259,21 @@ PPC_FUNC(sub_82511540) __imp__sub_82511540(ctx, base); } + +// Sonicteam::MainMode::Update +PPC_FUNC_IMPL(__imp__sub_82161A48); +PPC_FUNC(sub_82161A48) +{ + const auto pDoc = reinterpret_cast(base + ctx.r4.u32); + + if (g_devTitleState == DevTitleState::Start) + { + g_devTitleState = DevTitleState::Idle; + + ctx.r3.u32 = g_memory.MapVirtual(pDoc->SetDocState(StateDevTitle::GetInstance())); + + return; + } + + __imp__sub_82161A48(ctx, base); +} diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index 8cdb242ed..5d26c9d48 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -174,6 +174,22 @@ void AspectRatioPatches::ComputeOffsets() g_radarMapScale = 256 * g_aspectRatioScale * g_aspectRatioGameplayScale; } +float AspectRatioPatches::AlignX(float in_offset) +{ + if (Config::UIAlignmentMode == EUIAlignmentMode::Centre && g_aspectRatioOffsetX > 0) + return g_aspectRatioOffsetX + in_offset; + + return in_offset; +} + +float AspectRatioPatches::AlignY(float in_offset) +{ + if (Config::UIAlignmentMode == EUIAlignmentMode::Centre && g_aspectRatioOffsetY > 0) + return g_aspectRatioOffsetY + in_offset; + + return in_offset; +} + void EmplacePath(const void* key, const std::string_view& value) { std::lock_guard lock(g_pathMutex); @@ -521,7 +537,33 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) if ((modifier.Flags & CSD_CHEVRON) != 0 && g_aspectRatio <= WIDE_ASPECT_RATIO) offsetX = 0.0f; - if ((modifier.Flags & CSD_SCALE) != 0) + if ((modifier.Flags & CSD_CROP) != 0) + { + scaleX = std::max(vpWidth / 1280.0f, vpHeight / 720.0f); + + if ((modifier.Flags & CSD_ALIGN_RIGHT) != 0) + offsetX = vpWidth - (1280.0f * scaleX); + else if ((modifier.Flags & CSD_ALIGN_LEFT) != 0) + offsetX = 0.0f; + else + offsetX = (vpWidth - (1280.0f * scaleX)) * 0.5f; + + offsetX += pivotX * scaleX; + } + else if ((modifier.Flags & CSD_FIT) != 0) + { + const auto fitScale = std::min((vpWidth / 1280.0f) / g_aspectRatioScale, 1.0f); + + scaleX *= fitScale; + + if ((modifier.Flags & CSD_ALIGN_RIGHT) != 0) + offsetX += 1280.0f * (1.0f - fitScale) * g_aspectRatioScale; + else if ((modifier.Flags & CSD_ALIGN_LEFT) == 0) + offsetX += g_aspectRatioOffsetX + 640.0f * (1.0f - fitScale) * g_aspectRatioScale - g_aspectRatioOffsetX; + + offsetX += pivotX * g_aspectRatioScale; + } + else if ((modifier.Flags & CSD_SCALE) != 0) { scaleX *= g_aspectRatioGameplayScale; @@ -548,7 +590,33 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) else if ((modifier.Flags & CSD_ALIGN_TOP) == 0) offsetY = g_aspectRatioOffsetY; - if ((modifier.Flags & CSD_SCALE) != 0) + if ((modifier.Flags & CSD_CROP) != 0) + { + scaleY = std::max(vpWidth / 1280.0f, vpHeight / 720.0f); + + if ((modifier.Flags & CSD_ALIGN_BOTTOM) != 0) + offsetY = vpHeight - (720.0f * scaleY); + else if ((modifier.Flags & CSD_ALIGN_TOP) != 0) + offsetY = 0.0f; + else + offsetY = (vpHeight - (720.0f * scaleY)) * 0.5f; + + offsetY += pivotY * scaleY; + } + else if ((modifier.Flags & CSD_FIT) != 0) + { + const auto fitScale = std::min((vpWidth / 1280.0f) / g_aspectRatioScale, 1.0f); + + scaleY *= fitScale; + + if ((modifier.Flags & CSD_ALIGN_BOTTOM) != 0) + offsetY += 720.0f * (1.0f - fitScale) * g_aspectRatioScale; + else if ((modifier.Flags & CSD_ALIGN_TOP) == 0) + offsetY += g_aspectRatioOffsetY + 360.0f * (1.0f - fitScale) * g_aspectRatioScale - g_aspectRatioOffsetY; + + offsetY += pivotY * g_aspectRatioScale; + } + else if ((modifier.Flags & CSD_SCALE) != 0) { scaleY *= g_aspectRatioGameplayScale; @@ -2265,6 +2333,13 @@ const xxHashMap g_csdModifiers = { HashStr("sprite/title/title/Scene_Title/Logo"), { CSD_SCALE } }, { HashStr("sprite/title/title/Scene_Title/copyright"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, + // title (2005) + { HashStr("/back_aoita/Null_0064"), { CSD_CROP | CSD_ALIGN_LEFT } }, + { HashStr("/title_menu/Null_0030kage"), { CSD_FIT } }, + { HashStr("/title_menu/Cast_0157"), { CSD_SCALE | CSD_ALIGN_BOTTOM_RIGHT } }, + { HashStr("/title_menu/Null_0030"), { CSD_FIT } }, + { HashStr("/title_menu/Null_0030boke"), { CSD_FIT } }, + // titleloop_sth { HashStr("sprite/logo/titleloop_sth/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE | CSD_MOVIE | CSD_MODIFIER_NARROW_ONLY } }, diff --git a/MarathonRecomp/patches/aspect_ratio_patches.h b/MarathonRecomp/patches/aspect_ratio_patches.h index 15303cd09..7766ee508 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.h +++ b/MarathonRecomp/patches/aspect_ratio_patches.h @@ -27,7 +27,11 @@ class AspectRatioPatches { public: static void Init(); + static void ComputeOffsets(); + + static float AlignX(float in_offset = 0.0f); + static float AlignY(float in_offset = 0.0f); }; // -------------- CSD MODIFIERS --------------- // @@ -105,7 +109,10 @@ enum CsdFlags : uint64_t CSD_MAIN_MENU_PARTS_CAST_0226 = MAKE_BITFLAG64(39), CSD_MAIN_MENU_PARTS_CAST_0227 = MAKE_BITFLAG64(40), - CSD_BUTTON_WINDOW = MAKE_BITFLAG64(41) + CSD_BUTTON_WINDOW = MAKE_BITFLAG64(41), + + CSD_CROP = MAKE_BITFLAG64(42), + CSD_FIT = MAKE_BITFLAG64(43) }; struct CsdUVs diff --git a/MarathonRecomp/patches/debug_patches.cpp b/MarathonRecomp/patches/debug_patches.cpp new file mode 100644 index 000000000..3949ca235 --- /dev/null +++ b/MarathonRecomp/patches/debug_patches.cpp @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include +#include +#include + +// Sonicteam::SoX::Graphics::Xenon::DeviceXenon::DrawText +PPC_FUNC(sub_8289CB70) +{ + const auto pText = reinterpret_cast(base + ctx.r4.u32); + + auto pTextSeek = pText; + + while (*pTextSeek++); + + uint32_t textLengthInChars = pTextSeek - pText; + uint32_t textLengthInCharsNoTerminator = textLengthInChars - 1; + + if (textLengthInChars > 0x7FFFFFFF || !textLengthInCharsNoTerminator) + return; + + const auto text = os::locale::ConvertToUTF8(os::locale::Encoding::ShiftJIS, std::string(pText, textLengthInCharsNoTerminator)); + const auto x = float(ctx.r5.u32); + const auto y = float(ctx.r6.u32); + const auto colour = reinterpret_cast(base + ctx.r7.u32); + + Video::EnqueueImGuiDraw([=]() + { + ImGui::GetBackgroundDrawList()->AddText + ( + g_pFntArialUni, + Scale(12), + { AspectRatioPatches::AlignX(Scale(x)), Scale(y) }, + IM_COL32(colour->R(), colour->G(), colour->B(), colour->A()), + text.c_str() + ); + }); +} + +// Sonicteam::DocMarathonState::StartMainMode +PPC_FUNC_IMPL(__imp__sub_82161AB8); +PPC_FUNC(sub_82161AB8) +{ + if (!Config::BootDirectlyToDevTitle) + { + __imp__sub_82161AB8(ctx, base); + return; + } + + const auto pDoc = reinterpret_cast(base + ctx.r3.u32); + + GuestToHostFunction(sub_825E9E28, ctx.r3.u32, ctx.r4.u32); + + ctx.r3.u32 = g_memory.MapVirtual(pDoc->SetDocState(StateDevTitle::GetInstance())); +} + +// Sonicteam::Player::Object::Update +PPC_FUNC_IMPL(__imp__sub_82195500); +PPC_FUNC(sub_82195500) +{ + auto pPlayer = (Sonicteam::Player::Object*)(base + ctx.r3.u32); + auto pInputManager = pPlayer->GetInputManager(); + + if (pPlayer->m_IsPlayer && pInputManager) + { + if (Config::EnableDebugMode) + { + switch (pPlayer->m_SetupModuleIndexPostfix) + { + case 1: + { + // Toggle debug posture on Select press. + if (pPlayer->m_SetupModuleIndexPrefix == 1 && pInputManager->PadState.IsPressed(Sonicteam::SoX::Input::PadButton_Back)) + { + pPlayer->m_SetupModuleIndexPostfix = 2; + + LOGN("Debug Mode: Enabled"); + } + + break; + } + + case 2: + { + // Toggle camera volume collision on B press. + if (pInputManager->PadState.IsPressed(Sonicteam::SoX::Input::PadButton_East)) + { + auto pGame = App::s_pApp->GetGame(); + auto pZock = pPlayer->GetPlugIn("zock"); + auto collisionFilterInfo = pZock->m_spPhantomA->m_pRigidBody->m_collidable.m_broadPhaseHandle.m_collisionFilterInfo == 6 ? 0x383 : 6; + + LOGFN("Camera Volumes: {}", collisionFilterInfo != 6 ? "Enabled" : "Disabled"); + + pZock->m_spPhantomA->m_pRigidBody->m_collidable.m_broadPhaseHandle.m_collisionFilterInfo = collisionFilterInfo; + pGame->GetPhysicsWorld()->m_pWorld->updateCollisionFilterOnWorld(1, 1); + } + + break; + } + } + } + + // Toggle demo camera on right stick press. + if (Config::RestoreDemoCameraMode && pInputManager->PadState.IsPressed(Sonicteam::SoX::Input::PadButton_RightStick)) + { + auto pCameraman = static_cast(pPlayer->m_pCameraman.get()); + + if (auto pCameraMode = pCameraman->m_spCameraModeManager->m_spCameraMode.get()) + { + guest_stack_var msgChangeMode; + msgChangeMode->PadID = pInputManager->ID; + msgChangeMode->TargetActorID = pPlayer->m_ActorID; + msgChangeMode->IsDemoCamera = pCameraMode->m_pVftable.ptr != 0x82002004; + + LOGFN("Demo Camera: {}", msgChangeMode->IsDemoCamera ? "Enabled" : "Disabled"); + + pCameraman->ProcessMessage(msgChangeMode.get()); + } + } + } + + __imp__sub_82195500(ctx, base); +} + +// Add missing SetupModuleDebug to table. +void PlayerDebugMode_RegisterLuaSetup(PPCRegister& str, PPCRegister& index) +{ + if (!Config::EnableDebugMode) + return; + + auto pString = (stdx::string*)g_memory.Translate(str.u32); + + switch (index.u32) + { + case 0: + *pString = "SetupModuleDebug"; + break; + + case 1: + *pString = "SetupModule"; + break; + + case 2: + *pString = "SetupModuleDebug"; + break; + } +} + +bool PlayerDebugMode_RemapDebugExitButton(PPCRegister& r30) +{ + auto pPadState = (Sonicteam::SoX::Input::PadState*)g_memory.Translate(r30.u32); + + if (pPadState->IsPressed(Sonicteam::SoX::Input::PadButton_Back)) + { + LOGN("Debug Mode: Disabled"); + return true; + } + + return false; +} diff --git a/MarathonRecomp/patches/misc_patches.cpp b/MarathonRecomp/patches/misc_patches.cpp index 985beb477..0d59f85e1 100644 --- a/MarathonRecomp/patches/misc_patches.cpp +++ b/MarathonRecomp/patches/misc_patches.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -25,12 +24,12 @@ void SetLifeBarAnimation(PPCRegister& r3, PPCRegister& r4, PPCRegister& r5, PPCR if (Config::RestoreContextualHUDColours) { - *Sonicteam::Globals::ms_apMainDisplayColours[Sonicteam::Character_Shadow] = 1.0f; - *Sonicteam::Globals::ms_apMainDisplayColours[Sonicteam::Character_Omega] = 1.0f; - *Sonicteam::Globals::ms_apMainDisplayColours[Sonicteam::Character_Rouge] = 1.0f; - *Sonicteam::Globals::ms_apMainDisplayColours[Sonicteam::Character_Silver] = 2.0f; - *Sonicteam::Globals::ms_apMainDisplayColours[Sonicteam::Character_Amy] = 2.0f; - *Sonicteam::Globals::ms_apMainDisplayColours[Sonicteam::Character_Blaze] = 2.0f; + *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; } s_initContextualHUD = true; @@ -211,7 +210,7 @@ bool Super3_DisableChangeRequestHint() PPC_FUNC_IMPL(__imp__sub_824A6EA8); PPC_FUNC(sub_824A6EA8) { - if ((App::s_isSkipLogos || Config::SkipIntroLogos) && ctx.r4.u32 == 1) + if ((App::s_isSkipLogos || Config::BootDirectlyToTitle) && ctx.r4.u32 == 1) ctx.r4.u32 = 4; __imp__sub_824A6EA8(ctx, base); diff --git a/MarathonRecomp/patches/player_patches.cpp b/MarathonRecomp/patches/player_patches.cpp index 96526ce19..7dab0751c 100644 --- a/MarathonRecomp/patches/player_patches.cpp +++ b/MarathonRecomp/patches/player_patches.cpp @@ -14,74 +14,6 @@ void OnRegisterPlayerActor(PPCRegister& r25) pUser->BroadcastMessage(pObject->m_Name.c_str()); } -// Sonicteam::Player::Object::Update -PPC_FUNC_IMPL(__imp__sub_82195500); -PPC_FUNC(sub_82195500) -{ - auto pPlayer = (Sonicteam::Player::Object*)(base + ctx.r3.u32); - auto pInputManager = pPlayer->GetInputManager(); - - if (pPlayer->m_IsPlayer && pInputManager) - { - if (Config::EnableDebugMode) - { - switch (pPlayer->m_SetupModuleIndexPostfix) - { - case 1: - { - // Toggle debug posture on Select press. - if (pPlayer->m_SetupModuleIndexPrefix == 1 && pInputManager->PadState.IsPressed(Sonicteam::SoX::Input::PadButton_Back)) - { - pPlayer->m_SetupModuleIndexPostfix = 2; - - LOGN("Debug Mode: Enabled"); - } - - break; - } - - case 2: - { - // Toggle camera volume collision on B press. - if (pInputManager->PadState.IsPressed(Sonicteam::SoX::Input::PadButton_East)) - { - auto pGame = App::s_pApp->GetGame(); - auto pZock = pPlayer->GetPlugIn("zock"); - auto collisionFilterInfo = pZock->m_spPhantomA->m_pRigidBody->m_collidable.m_broadPhaseHandle.m_collisionFilterInfo == 6 ? 0x383 : 6; - - LOGFN("Camera Volumes: {}", collisionFilterInfo != 6 ? "Enabled" : "Disabled"); - - pZock->m_spPhantomA->m_pRigidBody->m_collidable.m_broadPhaseHandle.m_collisionFilterInfo = collisionFilterInfo; - pGame->GetPhysicsWorld()->m_pWorld->updateCollisionFilterOnWorld(1, 1); - } - - break; - } - } - } - - // Toggle demo camera on right stick press. - if (Config::RestoreDemoCameraMode && pInputManager->PadState.IsPressed(Sonicteam::SoX::Input::PadButton_RightStick)) - { - auto pCameraman = static_cast(pPlayer->m_pCameraman.get()); - - if (auto pCameraMode = pCameraman->m_spCameraModeManager->m_spCameraMode.get()) - { - guest_stack_var msgChangeMode; - msgChangeMode->PadID = pInputManager->ID; - msgChangeMode->TargetActorID = pPlayer->m_ActorID; - msgChangeMode->IsDemoCamera = pCameraMode->m_pVftable.ptr != 0x82002004; - - LOGFN("Demo Camera: {}", msgChangeMode->IsDemoCamera ? "Enabled" : "Disabled"); - - pCameraman->ProcessMessage(msgChangeMode.get()); - } - } - } - - __imp__sub_82195500(ctx, base); -} - // Sonicteam::Player::State::TailsContext::Update PPC_FUNC_IMPL(__imp__sub_8221A7D8); PPC_FUNC(sub_8221A7D8) @@ -149,43 +81,6 @@ PPC_FUNC(sub_821E8C48) __imp__sub_821E8C48(ctx, base); } -// Add missing SetupModuleDebug to table. -void PlayerDebugMode_RegisterLuaSetup(PPCRegister& str, PPCRegister& index) -{ - if (!Config::EnableDebugMode) - return; - - auto pString = (stdx::string*)g_memory.Translate(str.u32); - - switch (index.u32) - { - case 0: - *pString = "SetupModuleDebug"; - break; - - case 1: - *pString = "SetupModule"; - break; - - case 2: - *pString = "SetupModuleDebug"; - break; - } -} - -bool PlayerDebugMode_RemapDebugExitButton(PPCRegister& r30) -{ - auto pPadState = (Sonicteam::SoX::Input::PadState*)g_memory.Translate(r30.u32); - - if (pPadState->IsPressed(Sonicteam::SoX::Input::PadButton_Back)) - { - LOGN("Debug Mode: Disabled"); - return true; - } - - return false; -} - // Sonicteam::Player::Object::ProcMsgAmigoChange PPC_FUNC_IMPL(__imp__sub_8219A5B0); PPC_FUNC(sub_8219A5B0) diff --git a/MarathonRecomp/ui/imgui_utils.cpp b/MarathonRecomp/ui/imgui_utils.cpp index 5cbded176..7862980e5 100644 --- a/MarathonRecomp/ui/imgui_utils.cpp +++ b/MarathonRecomp/ui/imgui_utils.cpp @@ -19,8 +19,10 @@ #include #include +static bool g_initFonts{}; ImFont* g_pFntRodin; ImFont* g_pFntNewRodin; +ImFont* g_pFntArialUni; float g_fntRodinSize{}; @@ -36,9 +38,6 @@ std::unique_ptr g_upTexArrow; void InitImGuiUtils() { - g_pFntRodin = ImFontAtlasSnapshot::GetFont("FOT-RodinPro-DB.otf"); - g_pFntNewRodin = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-UB.otf"); - g_upTexButtonWindow = LOAD_ZSTD_TEXTURE(g_button_window); g_upTexController = LOAD_ZSTD_TEXTURE(g_controller); g_upTexKbm = LOAD_ZSTD_TEXTURE(g_kbm); @@ -52,6 +51,14 @@ void InitImGuiUtils() void UpdateImGuiUtils() { + if (!g_initFonts) + { + g_pFntRodin = ImFontAtlasSnapshot::GetFont(ImFontType::Rodin); + g_pFntNewRodin = ImFontAtlasSnapshot::GetFont(ImFontType::NewRodin); + g_pFntArialUni = ImFontAtlasSnapshot::GetFont(ImFontType::Arial); + g_initFonts = true; + } + g_fntRodinSize = Scale(Config::Language == ELanguage::Japanese ? 28 : 27, true); } diff --git a/MarathonRecomp/ui/imgui_utils.h b/MarathonRecomp/ui/imgui_utils.h index 952044e34..262728551 100644 --- a/MarathonRecomp/ui/imgui_utils.h +++ b/MarathonRecomp/ui/imgui_utils.h @@ -20,6 +20,7 @@ extern ImFont* g_pFntRodin; extern ImFont* g_pFntNewRodin; +extern ImFont* g_pFntArialUni; extern float g_fntRodinSize; diff --git a/MarathonRecomp/user/config_def.h b/MarathonRecomp/user/config_def.h index a83185975..882b481f2 100644 --- a/MarathonRecomp/user/config_def.h +++ b/MarathonRecomp/user/config_def.h @@ -105,6 +105,8 @@ CONFIG_DEFINE_ENUM_LOCALISED("Video", ECutsceneAspectRatio, CutsceneAspectRatio, CONFIG_DEFINE_ENUM_LOCALISED("Video", EUIAlignmentMode, UIAlignmentMode, EUIAlignmentMode::Edge, false); CONFIG_DEFINE_HIDDEN("Codes", bool, AntigravityRetainsMomentum, false, false); +CONFIG_DEFINE_HIDDEN("Codes", bool, BootDirectlyToTitle, false, false); +CONFIG_DEFINE_HIDDEN("Codes", bool, BootDirectlyToDevTitle, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, ControllableBoundAttack, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, ControllableSpinkick, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, ControllableTeleportDash, false, false); @@ -129,7 +131,6 @@ CONFIG_DEFINE_HIDDEN("Codes", bool, RestoreDemoCameraMode, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, RestoreLoadingTransition, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, RestorePauseMissionText, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, RestoreSonicActionGauge, false, false); -CONFIG_DEFINE_HIDDEN("Codes", bool, SkipIntroLogos, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, TailsGauge, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, UnlimitedAntigravity, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, UseOfficialAchievementText, false, false); diff --git a/MarathonRecompResources b/MarathonRecompResources index 3d6c2ffb1..59ba8f86f 160000 --- a/MarathonRecompResources +++ b/MarathonRecompResources @@ -1 +1 @@ -Subproject commit 3d6c2ffb109441e42d08e7440dd1b5df2561141c +Subproject commit 59ba8f86fa42b1f2c1ce845378d97670fa25fe6e