From dcb0f4465b5e55ce108899025d1aa45e93dd7b65 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Wed, 16 Oct 2024 23:02:14 -0600 Subject: [PATCH] Start CWorld --- config/GM8E01_00/symbols.txt | 2 +- config/GM8E01_01/symbols.txt | 2 +- include/Kyoto/Audio/CAudioSys.hpp | 10 +- include/Kyoto/Audio/CMidiManager.hpp | 2 + include/Kyoto/Audio/CStreamAudioManager.hpp | 1 + include/Kyoto/CDvdRequest.hpp | 2 +- include/MetroidPrime/CGameArea.hpp | 142 ++++++- include/MetroidPrime/CInGameTweakManager.hpp | 2 + include/MetroidPrime/CMapWorld.hpp | 171 ++++++++ include/MetroidPrime/CWorld.hpp | 108 ++++- include/dolphin/types.h | 6 +- include/static_assert.hpp | 1 + src/MetroidPrime/CStateManager.cpp | 2 +- src/MetroidPrime/CWorld.cpp | 408 +++++++++++++++++++ src/MetroidPrime/Player/CPlayer.cpp | 2 +- tools/metaforce_renames.py | 5 + 16 files changed, 827 insertions(+), 39 deletions(-) create mode 100644 include/MetroidPrime/CMapWorld.hpp create mode 100644 src/MetroidPrime/CWorld.cpp diff --git a/config/GM8E01_00/symbols.txt b/config/GM8E01_00/symbols.txt index 830acf84..4d4120de 100644 --- a/config/GM8E01_00/symbols.txt +++ b/config/GM8E01_00/symbols.txt @@ -1356,7 +1356,7 @@ LoadRelays__FR12CInputStreamRQ24rstl44vector x4_dockReferences; + rstl::reserved_vector< CVector3f, 4 > x14_planeVertices; + bool x48_isReferenced; + + public: + const rstl::reserved_vector< CVector3f, 4 >& GetPlaneVertices() const { + return x14_planeVertices; + } + int GetReferenceCount() const { return x0_referenceCount; } + const rstl::vector< SDockReference >& GetDockRefs() const { return x4_dockReferences; } + Dock(CInputStream& in, const CTransform4f& xf); + TAreaId GetConnectedAreaId(int other) const; + s16 GetOtherDockNumber(int other) const; + bool GetShouldLoadOther(int other) const; + void SetShouldLoadOther(int other, bool should); + bool ShouldLoadOtherArea(int other) const; + CVector3f GetPoint(int idx) const; + bool IsReferenced() const { return x48_isReferenced; } + void SetReferenceCount(int v) { + x0_referenceCount = v; + x48_isReferenced = true; + } + }; + virtual ~IGameArea(); virtual const CTransform4f& IGetTM() const = 0; virtual CAssetId IGetStringTableAssetId() const = 0; @@ -33,21 +79,36 @@ public: virtual rstl::pair< rstl::auto_ptr< uchar >, int > IGetScriptingMemoryAlways() const = 0; }; -enum EChain { - kC_Invalid = -1, - kC_ToDeallocate, - kC_Deallocated, - kC_Loading, - kC_Alive, - kC_AliveJudgement, -}; +struct CAreaRenderOctTree { + struct Node { + ushort x0_bitmapIdx; + ushort x2_flags; + ushort x4_children[1]; -class Dock; -class CToken; -class CDvdRequest; -class CScriptAreaAttributes; -class CWorldLight; -class CPVSAreaSet; + uint GetChildCount() const; + CAABox GetNodeBounds(const CAABox& curAABB, int idx) const; + + void RecursiveBuildOverlaps(u32* out, const CAreaRenderOctTree& parent, const CAABox& curAABB, + const CAABox& testAABB) const; + }; + + const u8* x0_buf; + int x4_; // TODO + uint x8_bitmapCount; + uint xc_meshCount; + uint x10_nodeCount; + uint x14_bitmapWordCount; + CAABox x18_aabb; + const u32* x30_bitmaps; + const u32* x34_indirectionTable; + const u8* x38_entries; + + explicit CAreaRenderOctTree(const u8* buf); + + void FindOverlappingModels(rstl::vector< u32 >& out, const CAABox& testAABB) const; + void FindOverlappingModels(u32* out, const CAABox& testAABB) const; +}; +CHECK_SIZEOF(CAreaRenderOctTree, 0x3c); class CGameArea : public IGameArea { public: @@ -96,7 +157,15 @@ public: enum EOcclusionState { kOS_Occluded, kOS_Visible }; struct CPostConstructed { - uchar x0_pad[0xa0]; + rstl::optional_object< CAreaOctTree* > x0_collision; + int x8_; // TODO + rstl::optional_object< CAreaRenderOctTree > xc_octTree; + rstl::vector< CMetroidModelInstance > x4c_insts; + rstl::single_ptr< int > x5c_; // TODO + rstl::vector< CWorldLight > x60_lightsA; + rstl::vector< CLight > x70_gfxLightsA; + rstl::vector< CWorldLight > x80_lightsB; + rstl::vector< CLight > x90_gfxLightsB; CPVSAreaSet* xa0_pvs; uchar xa4_pad[0x1020]; rstl::single_ptr< CAreaFog > x10c4_areaFog; @@ -136,8 +205,16 @@ public: bool TryTakingOutOfARAM(); bool StartStreamingMainArea(); + bool Invalidate(CStateManager* mgr); + void Validate(CStateManager& mgr); + void SetOcclusionState(EOcclusionState state); + void RemoveStaticGeometry(); + void StartStreamIn(CStateManager& mgr); + void SetChain(CGameArea* next, int chain); CAssetId GetAreaAssetId() const { return x84_mrea; } + const Dock& GetDock(int idx) const { return xcc_docks[idx]; } + int GetDockCount() const { return xcc_docks.size(); } const CAreaFog* GetAreaFog() const { return x12c_postConstructed->x10c4_areaFog.get(); } CAreaFog* AreaFog() { return x12c_postConstructed->x10c4_areaFog.get(); } EOcclusionState GetOcclusionState() const { return x12c_postConstructed->x10dc_occlusionState; } @@ -147,6 +224,9 @@ public: bool IsPostConstructed() const { return xf0_24_postConstructed; } // name? CPostConstructed* GetPostConstructed() { return x12c_postConstructed.get(); } // name? const CPostConstructed* GetPostConstructed() const { return x12c_postConstructed.get(); } // name? + CGameArea* GetNext() { return x130_next; } // name? + CGameArea* GetPrev() { return x134_prev; } // name? + int GetCurChain() const { return x138_curChain; } // name? private: void AllocNewAreaData(int, int); @@ -187,10 +267,36 @@ private: int x124_secCount; int x128_mreaDataOffset; rstl::single_ptr< CPostConstructed > x12c_postConstructed; + CGameArea* x130_next; + CGameArea* x134_prev; + int x138_curChain; }; - NESTED_CHECK_SIZEOF(CGameArea, CPostConstructed, 0x1140) +CHECK_SIZEOF(CGameArea, 0x13c) -// CHECK_SIZEOF(CGamearea::CAreaFog, 0x38) +class CDummyGameArea final : public IGameArea { + friend class CDummyWorld; + +public: + CDummyGameArea(CInputStream& in, int idx, int mlvlVersion); + rstl::pair< rstl::auto_ptr< uchar >, int > IGetScriptingMemoryAlways() const override; + int IGetAreaSaveId() const override; + CAssetId IGetAreaAssetId() const override; + bool IIsActive() const override; + TAreaId IGetAttachedAreaId(int) const override; + uint IGetNumAttachedAreas() const override; + CAssetId IGetStringTableAssetId() const override; + const CTransform4f& IGetTM() const override; + +private: + int x4_mlvlVersion; + CAssetId x8_nameSTRG; + CAssetId xc_mrea; + int x10_areaId; + CTransform4f x14_transform; + rstl::vector< u16 > x44_attachedAreaIndices; + rstl::vector< Dock > x54_docks; +}; +CHECK_SIZEOF(CDummyGameArea, 0x64) #endif // _CGAMEAREA diff --git a/include/MetroidPrime/CInGameTweakManager.hpp b/include/MetroidPrime/CInGameTweakManager.hpp index 7f8b7b73..fceaabdf 100644 --- a/include/MetroidPrime/CInGameTweakManager.hpp +++ b/include/MetroidPrime/CInGameTweakManager.hpp @@ -1,6 +1,7 @@ #ifndef _CINGAMETWEAKMANAGER #define _CINGAMETWEAKMANAGER +#include "Kyoto/SObjectTag.hpp" #include "types.h" #include "rstl/string.hpp" @@ -61,6 +62,7 @@ public: bool HasTweakValue(const rstl::string& name) const; const CTweakValue* GetTweakValue(const rstl::string& name) const; bool ReadFromMemoryCard(const rstl::string&); + rstl::vector< CAssetId > GetSongAssetsInWorld(CAssetId world) const; static rstl::string sub_8021cb38(CAssetId, const rstl::string&); diff --git a/include/MetroidPrime/CMapWorld.hpp b/include/MetroidPrime/CMapWorld.hpp new file mode 100644 index 00000000..4ed898de --- /dev/null +++ b/include/MetroidPrime/CMapWorld.hpp @@ -0,0 +1,171 @@ +#ifndef _CMAPWORLD +#define _CMAPWORLD + +#include "Kyoto/Graphics/CColor.hpp" +#include "MetroidPrime/CMapArea.hpp" + +class CStateManager; + +class CMapWorld { +public: + /* skDrawProfileItemNames; */ + enum EMapAreaList { kMAL_Loaded, kMAL_Loading, kMAL_Unloaded }; + + class CMapAreaBFSInfo { + int x0_areaIdx; + int x4_depth; + float x8_surfDrawDepth; + float xc_outlineDrawDepth; + + public: + CMapAreaBFSInfo(int areaIdx, int depth, float a, float b) + : x0_areaIdx(areaIdx), x4_depth(depth), x8_surfDrawDepth(a), xc_outlineDrawDepth(b) {} + int GetAreaIndex() const { return x0_areaIdx; } + int GetDepth() const { return x4_depth; } + float GetOutlineDrawDepth() const { return x8_surfDrawDepth; } + float GetSurfaceDrawDepth() const { return xc_outlineDrawDepth; } + }; + + class CMapObjectSortInfo { + float x0_zDist; + int x4_areaIdx; + int x8_typeAndIdx; + CColor xc_surfColor; + CColor x10_outlineColor; + + public: + enum EObjectCode { + kOC_Invalid = -1, + kOC_Object = 1 << 16, + kOC_DoorSurface = 2 << 16, + kOC_Door = 3 << 16, + kOC_Surface = 4 << 16 + }; + + CMapObjectSortInfo(float zDist, int areaIdx, EObjectCode type, int idx, const CColor& surfColor, + const CColor& outlineColor) + : x0_zDist(zDist) + , x4_areaIdx(areaIdx) + , x8_typeAndIdx(int(type) | idx) + , xc_surfColor(surfColor) + , x10_outlineColor(outlineColor) {} + const CColor& GetOutlineColor() const { return x10_outlineColor; } + const CColor& GetSurfaceColor() const { return xc_surfColor; } + uint GetLocalObjectIndex() const { return x8_typeAndIdx & 0xffff; } + EObjectCode GetObjectCode() const { return EObjectCode(x8_typeAndIdx & 0xffff0000); } + uint GetAreaIndex() const { return x4_areaIdx; } + float GetZDistance() const { return x0_zDist; } + }; + + class CMapAreaData { + int x0_areaIdx; + TCachedToken< CMapArea > x4_area; + EMapAreaList x10_list; + CMapAreaData* x14_next; + + public: + CMapAreaData(CAssetId areaRes, EMapAreaList list, CMapAreaData* next); + void Lock() { x4_area.Lock(); } + void Unlock() { x4_area.Unlock(); } + bool IsLoaded() const { return x4_area.IsLoaded(); } + CMapArea* MapArea() { return x4_area.GetT(); } + const CMapArea* GetMapArea() const { return x4_area.GetObject(); } + CMapAreaData* GetNextMapAreaData() { return x14_next; } + const CMapAreaData* GetNextMapAreaData() const { return x14_next; } + EMapAreaList GetContainingList() const { return x10_list; } + void SetContainingList(EMapAreaList list) { x10_list = list; } + void SetNextMapArea(CMapAreaData* next) { x14_next = next; } + }; + + class CMapWorldDrawParms { + float x0_alphaSurfVisited; + float x4_alphaOlVisited; + float x8_alphaSurfUnvisited; + float xc_alphaOlUnvisited; + float x10_alpha; + float x14_outlineWidthScale; + const CStateManager& x18_mgr; + const CTransform4f& x1c_modelXf; + const CTransform4f& x20_viewXf; + const IWorld& x24_wld; + const CMapWorldInfo& x28_mwInfo; + float x2c_playerFlashIntensity; + float x30_hintFlashIntensity; + float x34_objectScale; + bool x38_sortDoorSurfs; + + public: + CMapWorldDrawParms(float alphaSurfVisited, float alphaOlVisited, float alphaSurfUnvisited, + float alphaOlUnvisited, float alpha, float outlineWidthScale, + const CStateManager& mgr, const CTransform4f& modelXf, + const CTransform4f& viewXf, const IWorld& wld, const CMapWorldInfo& mwInfo, + float playerFlash, float hintFlash, float objectScale, bool sortDoorSurfs) + : x0_alphaSurfVisited(alphaSurfVisited) + , x4_alphaOlVisited(alphaOlVisited) + , x8_alphaSurfUnvisited(alphaSurfUnvisited) + , xc_alphaOlUnvisited(alphaOlUnvisited) + , x10_alpha(alpha) + , x14_outlineWidthScale(outlineWidthScale) + , x18_mgr(mgr) + , x1c_modelXf(modelXf) + , x20_viewXf(viewXf) + , x24_wld(wld) + , x28_mwInfo(mwInfo) + , x2c_playerFlashIntensity(playerFlash) + , x30_hintFlashIntensity(hintFlash) + , x34_objectScale(objectScale) + , x38_sortDoorSurfs(sortDoorSurfs) {} + const IWorld& GetWorld() const { return x24_wld; } + float GetOutlineWidthScale() const { return x14_outlineWidthScale; } + const CTransform4f& GetPlaneProjectionTransform() const { return x1c_modelXf; } + float GetHintAreaFlashIntensity() const { return x30_hintFlashIntensity; } + float GetPlayerAreaFlashIntensity() const { return x2c_playerFlashIntensity; } + const CTransform4f& GetCameraTransform() const { return x20_viewXf; } + float GetAlphaOutlineUnvisited() const { return xc_alphaOlUnvisited; } + float GetAlphaSurfaceUnvisited() const { return x8_alphaSurfUnvisited; } + float GetAlphaOutlineVisited() const { return x4_alphaOlVisited; } + float GetAlphaSurfaceVisited() const { return x0_alphaSurfVisited; } + float GetAlpha() const { return x10_alpha; } + const CMapWorldInfo& GetMapWorldInfo() const { return x28_mwInfo; } + const CStateManager& GetStateManager() const { return x18_mgr; } + bool GetIsSortDoorSurfaces() const { return x38_sortDoorSurfs; } + float GetObjectScale() const { return x34_objectScale; } + }; + +private: + rstl::vector< CMapAreaData > x0_areas; + rstl::reserved_vector< CMapAreaData*, 3 > x10_listHeads; + rstl::vector< bool > x20_traversed; + CVector3f x30_worldSpherePoint; + float x3c_worldSphereRadius; + float x40_worldSphereHalfDepth; + +public: + explicit CMapWorld(CInputStream& in); + uint GetNumAreas() const { return x0_areas.size(); } + CMapArea* GetMapArea(int aid) { return x0_areas[aid].MapArea(); } + const CMapArea* GetMapArea(int aid) const { return x0_areas[aid].GetMapArea(); } + bool IsMapAreaInBFSInfoVector(const CMapAreaData* area, + const rstl::vector< CMapAreaBFSInfo >& vec) const; + void SetWhichMapAreasLoaded(const IWorld& wld, int start, int count); + bool IsMapAreasStreaming(); + void MoveMapAreaToList(CMapAreaData* data, EMapAreaList list); + int GetCurrentMapAreaDepth(const IWorld& wld, TAreaId aid); + rstl::vector< int > GetVisibleAreas(const IWorld& wld, const CMapWorldInfo& mwInfo) const; + void Draw(const CMapWorldDrawParms& parms, int curArea, int otherArea, float depth1, float depth2, + bool inMapScreen); + void DoBFS(const IWorld& wld, int startArea, int areaCount, float surfDepth, float outlineDepth, + bool checkLoad, rstl::vector< CMapAreaBFSInfo >& bfsInfos); + bool IsMapAreaValid(const IWorld& wld, int areaIdx, bool checkLoad) const; + void DrawAreas(const CMapWorldDrawParms& parms, int selArea, + const rstl::vector< CMapAreaBFSInfo >& bfsInfos, bool inMapScreen); + void RecalculateWorldSphere(const CMapWorldInfo& mwInfo, const IWorld& wld); + CVector3f ConstrainToWorldVolume(const CVector3f& point, const CVector3f& lookVec) const; + void ClearTraversedFlags(); + void SetWhichMapAreasLoaded(const IWorld& wld, int start, int count, bool load); + bool IsMapAreasStreaming() const; +}; +NESTED_CHECK_SIZEOF(CMapWorld, CMapAreaData, 0x18) +CHECK_SIZEOF(CMapWorld, 0x44) + +#endif // _CMAPWORLD diff --git a/include/MetroidPrime/CWorld.hpp b/include/MetroidPrime/CWorld.hpp index e56e3ad4..08f2f2ce 100644 --- a/include/MetroidPrime/CWorld.hpp +++ b/include/MetroidPrime/CWorld.hpp @@ -1,6 +1,7 @@ #ifndef _CWORLD #define _CWORLD +#include "Kyoto/SObjectTag.hpp" #include "types.h" #include "MetroidPrime/CGameArea.hpp" @@ -10,16 +11,23 @@ #include "Kyoto/IObjectStore.hpp" #include "Kyoto/TToken.hpp" -#include "rstl/string.hpp" #include "rstl/reserved_vector.hpp" +#include "rstl/string.hpp" +#include "rstl/vector.hpp" +class CAudioGroupSet; +class CDvdRequest; +class CGameArea; class CMapWorld; class CModel; +class CRelay; +class CStateManager; class IGameArea; +class CResFactory; class IWorld { public: - virtual ~IWorld(); + virtual ~IWorld() {} virtual CAssetId IGetWorldAssetId() const = 0; virtual CAssetId IGetStringTableAssetId() const = 0; virtual CAssetId IGetSaveWorldAssetId() const = 0; @@ -33,19 +41,16 @@ public: virtual int IGetAreaCount() const = 0; }; -class CGameArea; -class CRelay; -class CSoundGroupData; -class CDvdRequest; -class IFactory; - -class CRelay { +class CRelay /* name? */ { public: + explicit CRelay(CInputStream& in); const TEditorId& GetRelayId() const { return x0_relay; } const TEditorId& GetTargetId() const { return x4_target; } ushort GetMessage() const { return x8_msg; } bool GetActive() const { return xa_active; } + static rstl::vector< CRelay > ReadMemoryRelays(CInputStream& in); // name? + private: TEditorId x0_relay; TEditorId x4_target; @@ -61,8 +66,35 @@ enum EEnvFxType { kEFX_UnderwaterFlake, }; -class CWorld : public IWorld { +class CWorld final : public IWorld { public: + struct CSoundGroupData { + int x0_groupId; + CAssetId x4_agscId; + bool x8_24_loadedIntoAram : 1; + bool x8_25_loaded : 1; + rstl::string xc_name; + TCachedToken< CAudioGroupSet > x1c_groupData; + + public: + CSoundGroupData(int grpId, CAssetId agsc); + }; + + enum EAreaTravelType { + kATT_Zero, + kATT_One, + }; + + enum EChain { + kC_Invalid = -1, + kC_ToDeallocate, + kC_Deallocated, + kC_Loading, + kC_Alive, + kC_AliveJudgement, + }; + + CWorld(IObjectStore& objStore, CResFactory& resFactory, CAssetId mlvlId); ~CWorld(); CAssetId IGetWorldAssetId() const override; CAssetId IGetStringTableAssetId() const override; @@ -75,9 +107,18 @@ public: bool ICheckWorldComplete() override; rstl::string IGetDefaultAudioTrack() const override; int IGetAreaCount() const override; + bool CheckWorldComplete(CStateManager* mgr, TAreaId aid, CAssetId mreaId); void SetLoadPauseState(bool); void TouchSky() const; + void StopSounds(); + void UnloadSoundGroups(); + bool ScheduleAreaToLoad(CGameArea* area, CStateManager& mgr); + void MoveToChain(CGameArea* area, EChain chain); + void TravelToArea(TAreaId aid, CStateManager& mgr, EAreaTravelType type); + CMapWorld* GetMapWorld() const; + void LoadSoundGroups(); + void LoadSoundGroup(uchar groupId, CAssetId agscId, CSoundGroupData& data); const CGameArea& GetAreaAlways(TAreaId id) const { return *x18_areas[id.Value()]; } CGameArea* Area(TAreaId id) { return x18_areas[id.Value()].get(); } @@ -114,10 +155,10 @@ private: CAssetId x10_savwId; rstl::vector< rstl::auto_ptr< CGameArea > > x18_areas; CAssetId x24_mapwId; - CMapWorld* x28_mapWorld; + rstl::single_ptr< TCachedToken< CMapWorld > > x28_mapWorld; rstl::vector< CRelay > x2c_relays; - rstl::rc_ptr< CDvdRequest > x3c_loadToken; - rstl::single_ptr< uchar > x40_loadBuf; + rstl::single_ptr< CDvdRequest > x3c_loadToken; + rstl::single_ptr< char > x40_loadBuf; uint x44_bufSz; uint x48_chainCount; CGameArea* x4c_chainHeads[5]; @@ -139,4 +180,45 @@ private: }; CHECK_SIZEOF(CWorld, 0xf4) +struct SWorldLayers /* name? */ { + static SWorldLayers ReadWorldLayers(CInputStream& in, int version, CAssetId mlvlId); +}; + +class CDummyWorld : public IWorld { + enum Phase { + kP_Loading, + kP_LoadingMap, + kP_LoadingMapAreas, + kP_Done, + }; + + bool x4_loadMap; + Phase x8_phase; + CAssetId xc_mlvlId; + CAssetId x10_strgId; + CAssetId x14_savwId; + rstl::vector< rstl::auto_ptr< CDummyGameArea > > x18_areas; + CAssetId x28_mapWorldId; + rstl::single_ptr< TCachedToken< CMapWorld > > x2c_mapWorld; + rstl::single_ptr< CDvdRequest > x30_loadToken; + rstl::single_ptr< char > x34_loadBuf; + uint x38_bufSz; + TAreaId x3c_curAreaId; + +public: + CDummyWorld(CAssetId mlvlId, bool loadMap); + ~CDummyWorld() override; + CAssetId IGetWorldAssetId() const override; + CAssetId IGetStringTableAssetId() const override; + CAssetId IGetSaveWorldAssetId() const override; + const CMapWorld* IGetMapWorld() const override; + CMapWorld* IMapWorld() override; + const IGameArea* IGetAreaAlways(TAreaId id) const override; + TAreaId IGetCurrentAreaId() const override; + TAreaId IGetAreaId(CAssetId id) const override; + bool ICheckWorldComplete() override; + rstl::string IGetDefaultAudioTrack() const override; + int IGetAreaCount() const override; +}; + #endif // _CWORLD diff --git a/include/dolphin/types.h b/include/dolphin/types.h index 7ddd6a60..84efb061 100644 --- a/include/dolphin/types.h +++ b/include/dolphin/types.h @@ -75,13 +75,17 @@ typedef int BOOL; #if defined(__cplusplus) && __cplusplus < 201103L #if defined(__clang__) -// Allow override in < C++11 mode with clangd +// Allow override/final in < C++11 mode with clangd #pragma clang diagnostic ignored "-Wc++11-extensions" #else // Define override as nothing #ifndef override #define override #endif +// Define final as nothing +#ifndef final +#define final +#endif #endif // defined(__clang__) #endif // defined(__cplusplus) && __cplusplus < 201103L diff --git a/include/static_assert.hpp b/include/static_assert.hpp index 404fe22e..a087130b 100644 --- a/include/static_assert.hpp +++ b/include/static_assert.hpp @@ -27,6 +27,7 @@ typedef unsigned long size_t; #define CHECK_OFFSETOF(cls, member, offset) \ extern int cls##_check_offset##[_n_is_equal< offsetof(cls, member), offset >::value]; #elif defined(__clang__) && defined(__powerpc__) // Enable for clangd +#pragma clang diagnostic ignored "-Wc11-extensions" // Allow _Static_assert #pragma clang diagnostic ignored "-Wc++17-extensions" // Allow _Static_assert without message #define CHECK_SIZEOF(cls, size) _Static_assert(sizeof(cls) == size); #define NESTED_CHECK_SIZEOF(parent, cls, size) _Static_assert(sizeof(parent::cls) == size); diff --git a/src/MetroidPrime/CStateManager.cpp b/src/MetroidPrime/CStateManager.cpp index f7d6fcdf..a444f44a 100644 --- a/src/MetroidPrime/CStateManager.cpp +++ b/src/MetroidPrime/CStateManager.cpp @@ -404,7 +404,7 @@ void CStateManager::DrawWorld() const { int areaCount = 0; const CGameArea* areaArr[10]; - CGameArea::CConstChainIterator it = x850_world->GetChainHead(kC_Alive); + CGameArea::CConstChainIterator it = x850_world->GetChainHead(CWorld::kC_Alive); CGameArea::CConstChainIterator end = x850_world->GetAliveAreasEnd(); for (; it != end; ++it) { if (areaCount == 10) { diff --git a/src/MetroidPrime/CWorld.cpp b/src/MetroidPrime/CWorld.cpp new file mode 100644 index 00000000..dc8769d9 --- /dev/null +++ b/src/MetroidPrime/CWorld.cpp @@ -0,0 +1,408 @@ +#include "MetroidPrime/CWorld.hpp" +#include "Kyoto/Alloc/IAllocator.hpp" +#include "Kyoto/Audio/CAudioSys.hpp" +#include "Kyoto/Audio/CMidiManager.hpp" +#include "Kyoto/Audio/CStreamAudioManager.hpp" +#include "Kyoto/CDvdRequest.hpp" // IWYU pragma: keep +#include "Kyoto/CResFactory.hpp" +#include "Kyoto/CSimplePool.hpp" +#include "Kyoto/SObjectTag.hpp" +#include "Kyoto/Streams/CMemoryInStream.hpp" +#include "MetroidPrime/CGameArea.hpp" +#include "MetroidPrime/CInGameTweakManager.hpp" +#include "MetroidPrime/CMain.hpp" +#include "MetroidPrime/CMapWorld.hpp" +#include "MetroidPrime/Player/CGameState.hpp" +#include "MetroidPrime/Player/CWorldTransManager.hpp" +#include "MetroidPrime/ScriptObjects/CScriptRoomAcoustics.hpp" +#include "MetroidPrime/TCastTo.hpp" +#include "MetroidPrime/TGameTypes.hpp" +#include "rstl/vector.hpp" + +CWorld::CWorld(IObjectStore& objStore, CResFactory& resFactory, CAssetId mlvlId) +: x4_phase(kP_Loading) +, x8_mlvlId(mlvlId) +, xc_strgId(kInvalidAssetId) +, x10_savwId(kInvalidAssetId) +, x18_areas() +, x24_mapwId(kInvalidAssetId) +, x28_mapWorld() +, x2c_relays() +, x3c_loadToken() +, x40_loadBuf() +, x44_bufSz(0) +, x48_chainCount(0) +, x60_objectStore(&objStore) +, x64_resFactory(&resFactory) +, x68_curAreaId(kInvalidAreaId) +, x6c_loadedAudioGrpCount(0) +, x70_24_currentAreaNeedsAllocation(true) +, x70_25_loadPaused(false) +, x70_26_skyboxActive(false) +, x70_27_skyboxVisible(false) +, x74_soundGroupData() +, x84_defAudioTrack() +, x94_skyboxWorld() +, xa4_skyboxWorldLoaded() +, xb4_skyboxOverride() +, xc4_neededFx(kEFX_None) +, xc8_globalSfxHandles() { + SObjectTag mlvl('MLVL', mlvlId); + x44_bufSz = gpResourceFactory->ResourceSize(mlvl); + x40_loadBuf = static_cast< char* >(CMemory::Alloc(x44_bufSz, IAllocator::kHI_RoundUpLen)); + x3c_loadToken = resFactory.GetResLoader().LoadResourceAsync(mlvl, x40_loadBuf.get()); +} + +CWorld::~CWorld() { + StopSounds(); + CWorldTransManager* transManager = gpGameState->WorldTransitionManager().GetPtr(); + if (transManager->GetTransType() != CWorldTransManager::kTT_Disabled && + gpMain->GetRestartMode() == CMain::kRM_None) { + CStreamAudioManager::StopOneShot(); + } else { + CStreamAudioManager::StopAll(); + } + UnloadSoundGroups(); + CScriptRoomAcoustics::DisableAuxCallbacks(); +} + +bool CWorld::ScheduleAreaToLoad(CGameArea* area, CStateManager& mgr) { + if (!area->IsPostConstructed()) { + MoveToChain(area, kC_Loading); + return true; + } else { + if (area->GetCurChain() != kC_Alive) { + if (area->GetCurChain() != kC_AliveJudgement) { + x70_24_currentAreaNeedsAllocation = true; + } + MoveToChain(area, kC_Alive); + } + return false; + } +} + +// TOOD nonmatching +void CWorld::TravelToArea(TAreaId aid, CStateManager& mgr, EAreaTravelType type) { + if (aid.Value() < 0 || aid.Value() >= x18_areas.size()) + return; + x70_24_currentAreaNeedsAllocation = false; + x68_curAreaId = aid; + CGameArea* toDeallocateAreas = x4c_chainHeads[0]; + while (toDeallocateAreas) { + if (toDeallocateAreas->Invalidate(&mgr)) { + MoveToChain(toDeallocateAreas, kC_Deallocated); + break; + } + toDeallocateAreas = toDeallocateAreas->GetNext(); + } + + CGameArea* aliveAreas = x4c_chainHeads[3]; + while (aliveAreas) { + CGameArea* aliveArea = aliveAreas; + aliveAreas = aliveAreas->GetNext(); + MoveToChain(aliveArea, kC_AliveJudgement); + } + CGameArea* loadingAreas = x4c_chainHeads[2]; + while (loadingAreas) { + CGameArea* loadingArea = loadingAreas; + loadingAreas = loadingAreas->GetNext(); + MoveToChain(loadingArea, kC_ToDeallocate); + } + + CGameArea* area = Area(aid); + if (area->GetCurChain() != kC_AliveJudgement) + x70_24_currentAreaNeedsAllocation = true; + area->Validate(mgr); + MoveToChain(area, kC_Alive); + area->SetOcclusionState(CGameArea::kOS_Visible); + + CGameArea* otherLoadArea = nullptr; + if (type == kATT_Zero) { + bool otherLoading = false; + for (int i = 0; i < area->GetDockCount(); ++i) { + const CGameArea::Dock& dock = area->GetDock(i); + int dockRefCount = dock.GetDockRefs().size(); + for (int i = 0; i < dockRefCount; ++i) { + if (!dock.ShouldLoadOtherArea(i)) + continue; + TAreaId connArea = dock.GetConnectedAreaId(i); + CGameArea* cArea = Area(connArea); + if (!cArea->IsActive()) + continue; + if (!otherLoading) { + otherLoading = ScheduleAreaToLoad(cArea, mgr); + if (!otherLoading) + continue; + otherLoadArea = cArea; + } else + ScheduleAreaToLoad(cArea, mgr); + } + } + } + CGameArea* judgementAreas = x4c_chainHeads[4]; + while (judgementAreas) { + CGameArea* judgementArea = judgementAreas; + judgementAreas = judgementArea->GetNext(); + MoveToChain(judgementArea, kC_ToDeallocate); + } + + int toStreamCount = 0; + toDeallocateAreas = x4c_chainHeads[0]; + while (toDeallocateAreas) { + toDeallocateAreas->RemoveStaticGeometry(); + toDeallocateAreas = toDeallocateAreas->GetNext(); + ++toStreamCount; + } + + if (!toStreamCount && otherLoadArea && !x70_25_loadPaused) + otherLoadArea->StartStreamIn(mgr); + + GetMapWorld()->SetWhichMapAreasLoaded(*this, aid.Value(), 3); +} + +// TOOD nonmatching +void CWorld::MoveToChain(CGameArea* area, EChain chain) { + if (area->GetCurChain() == chain) { + return; + } + + if (area->GetCurChain() != kC_Invalid) { + if (x4c_chainHeads[area->GetCurChain()] == area) { + x4c_chainHeads[area->GetCurChain()] = area->GetNext(); + } + } + + area->SetChain(x4c_chainHeads[int(chain)], int(chain)); + x4c_chainHeads[int(chain)] = area; +} + +void CWorld::LoadSoundGroups() { + rstl::vector< CAssetId > songAssets = gpTweakManager->GetSongAssetsInWorld(IGetWorldAssetId()); + if (songAssets.empty()) { + return; + } + x74_soundGroupData.reserve(songAssets.size()); + for (rstl::vector< CAssetId >::iterator it = songAssets.begin(); it != songAssets.end(); ++it) { + TToken< CMidiManager::CMidiData > token = gpSimplePool->GetObj(SObjectTag('CSNG', *it)); + x74_soundGroupData.push_back( + CSoundGroupData(token.GetT()->GetGroupId(), token.GetT()->GetSongId())); + } + for (rstl::vector< CSoundGroupData >::iterator it = x74_soundGroupData.begin(); + it != x74_soundGroupData.end(); ++it) { + if (!it->x8_25_loaded) { + LoadSoundGroup(it->x0_groupId, it->x4_agscId, *it); + } + } +} + +void CWorld::LoadSoundGroup(uchar groupId, CAssetId agscId, CSoundGroupData& data) { + data.x8_25_loaded = true; + if (!CAudioSys::SysLoadGroupSet(gpSimplePool, agscId)) { + rstl::string name = CAudioSys::SysGetGroupSetName(agscId); + if (CAudioSys::SysPushGroupIntoARAM(name, groupId)) { + data.x8_24_loadedIntoAram = true; + data.xc_name = name; + ++x6c_loadedAudioGrpCount; + CAudioSys::SysUnloadSampleData(name); + } else { + CAudioSys::SysUnloadGroupSet(name); + } + } +} + +void CWorld::UnloadSoundGroups() { + for (int i = 0; i < x6c_loadedAudioGrpCount; ++i) { + CAudioSys::SysPopGroupFromARAM(); + } + for (rstl::vector< CSoundGroupData >::iterator it = x74_soundGroupData.begin(); + it != x74_soundGroupData.end(); ++it) { + if (it->x8_24_loadedIntoAram) { + CAudioSys::SysUnloadGroupSet(it->xc_name); + } + } +} + +CMapWorld* CWorld::GetMapWorld() const { return x28_mapWorld->GetObject(); } + +CAssetId CWorld::IGetWorldAssetId() const { return GetWorldAssetId(); } + +CAssetId CWorld::IGetStringTableAssetId() const { return xc_strgId; } + +CAssetId CWorld::IGetSaveWorldAssetId() const { return x10_savwId; } + +const CMapWorld* CWorld::IGetMapWorld() const { return GetMapWorld(); } + +CMapWorld* CWorld::IMapWorld() { return GetMapWorld(); } + +const IGameArea* CWorld::IGetAreaAlways(TAreaId id) const { return &GetAreaAlways(id); } + +TAreaId CWorld::IGetCurrentAreaId() const { return x68_curAreaId; } + +bool CWorld::ICheckWorldComplete() { + return CheckWorldComplete(nullptr, kInvalidAreaId, kInvalidAssetId); +} + +rstl::string CWorld::IGetDefaultAudioTrack() const { return x84_defAudioTrack; } + +int CWorld::IGetAreaCount() const { return x18_areas.size(); } + +CDummyWorld::CDummyWorld(CAssetId mlvlId, bool loadMap) +: x4_loadMap(loadMap) +, x8_phase(kP_Loading) +, xc_mlvlId(mlvlId) +#if NONMATCHING +, x10_strgId(kInvalidAssetId) +#endif +, x14_savwId(kInvalidAssetId) +, x18_areas() +, x28_mapWorldId(kInvalidAssetId) +, x2c_mapWorld() +, x30_loadToken() +, x34_loadBuf() +, x38_bufSz(0) +, x3c_curAreaId(kInvalidAreaId) { + SObjectTag mlvl('MLVL', mlvlId); + x38_bufSz = gpResourceFactory->ResourceSize(mlvl); + x34_loadBuf = static_cast< char* >(CMemory::Alloc(x38_bufSz, IAllocator::kHI_RoundUpLen)); + x30_loadToken = gpResourceFactory->GetResLoader().LoadResourceAsync(mlvl, x34_loadBuf.get()); +} + +class CMemoryRelays { +public: + CMemoryRelays(CInputStream& in /*, const int& unk*/); + +private: + rstl::vector< CRelay > x0_relayList; +}; + +CMemoryRelays::CMemoryRelays(CInputStream& in /*, const int& unk*/) { + int count = in.Get< int >(); + x0_relayList.reserve(count); + for (int i = 0; i < count; ++i) { + x0_relayList.push_back(CRelay(in)); + } +} + +CDummyWorld::~CDummyWorld() {} + +// TOOD nonmatching +bool CDummyWorld::ICheckWorldComplete() { + switch (x8_phase) { + case kP_Loading: { + if (!x30_loadToken->IsComplete()) { + return false; + } + + CMemoryInStream r(x34_loadBuf.get(), x38_bufSz); + r.ReadLong(); + uint version = r.Get< uint >(); + x10_strgId = r.Get< CAssetId >(); + + if (version >= 15) { + x14_savwId = r.Get< CAssetId >(); + } + if (version >= 12) { + r.ReadLong(); + } + if (version >= 17) { + // TOOD: this class takes some stack argument in r5 + // but I can't figure out what it is + // int unk; + CMemoryRelays relays(r /*, unk*/); + } + + int areaCount = r.Get< int >(); + r.ReadLong(); + + x18_areas.reserve(areaCount); + for (int i = 0; i < areaCount; ++i) { + x18_areas.push_back(rs_new CDummyGameArea(r, i, version)); + } + + x28_mapWorldId = r.Get< CAssetId >(); + if (x4_loadMap) { + x2c_mapWorld = rs_new TCachedToken< CMapWorld >( + gpSimplePool->GetObj(SObjectTag('MAPW', x28_mapWorldId))); + x2c_mapWorld->Lock(); + } + + r.ReadChar(); + r.ReadLong(); + + if (version > 10) { + int audioGroupCount = r.ReadLong(); + for (int i = 0; i < audioGroupCount; ++i) { + r.ReadLong(); + r.ReadLong(); + } + } + + if (version > 12) { + rstl::string s(r); + } + + SWorldLayers::ReadWorldLayers(r, version, xc_mlvlId); + + x30_loadToken = nullptr; + x34_loadBuf = nullptr; + x38_bufSz = 0; + + if (!x4_loadMap) { + x8_phase = kP_Done; + break; + } else { + x8_phase = kP_LoadingMap; + } + } + case kP_LoadingMap: { + if (!x2c_mapWorld->TryCache()) { + return false; + } + + IMapWorld()->SetWhichMapAreasLoaded(*this, 0, 9999); + x8_phase = kP_LoadingMapAreas; + } + case kP_LoadingMapAreas: { + if (x2c_mapWorld->GetObject()->IsMapAreasStreaming()) { + return false; + } + + x8_phase = kP_Done; + } + case kP_Done: + return true; + default: + break; + } + return false; +} + +CAssetId CDummyWorld::IGetWorldAssetId() const { return xc_mlvlId; } + +CAssetId CDummyWorld::IGetSaveWorldAssetId() const { return x14_savwId; } + +CAssetId CDummyWorld::IGetStringTableAssetId() const { return x10_strgId; } + +const CMapWorld* CDummyWorld::IGetMapWorld() const { return x2c_mapWorld->GetObject(); } + +CMapWorld* CDummyWorld::IMapWorld() { return x2c_mapWorld->GetObject(); } + +const IGameArea* CDummyWorld::IGetAreaAlways(TAreaId id) const { return &*x18_areas[id.Value()]; } + +TAreaId CDummyWorld::IGetCurrentAreaId() const { return x3c_curAreaId; } + +TAreaId CDummyWorld::IGetAreaId(CAssetId id) const { + if (id != kInvalidAssetId) { + for (int i = 0; i < x18_areas.size(); ++i) { + TAreaId aid(i); + if (IGetAreaAlways(aid)->IGetAreaAssetId() == id) { + return aid; + } + } + } +#if NONMATCHING + return kInvalidAreaId; +#else + return TAreaId(-1); +#endif +} diff --git a/src/MetroidPrime/Player/CPlayer.cpp b/src/MetroidPrime/Player/CPlayer.cpp index 6f1b6d0d..2c1ee74f 100644 --- a/src/MetroidPrime/Player/CPlayer.cpp +++ b/src/MetroidPrime/Player/CPlayer.cpp @@ -1669,7 +1669,7 @@ void CPlayer::Think(float dt, CStateManager& mgr) { x794_lastVelocity = GetVelocityWR(); } -void CPlayer::SetFrozenState(CStateManager& stateMgr, CAssetId steamTxtr, ushort sfx, +void CPlayer::SetFrozenState(CStateManager& stateMgr, CAssetId steamTxtr, const ushort sfx, CAssetId iceTxtr) { if (!stateMgr.GetCameraManager()->IsInCinematicCamera() && !GetFrozenState()) { bool showMsg; diff --git a/tools/metaforce_renames.py b/tools/metaforce_renames.py index ed758465..43e17920 100644 --- a/tools/metaforce_renames.py +++ b/tools/metaforce_renames.py @@ -4,6 +4,7 @@ from pathlib import Path _LITERAL_REPLACEMENTS = [ ("std::string_view", "const rstl::string&"), + ("std::string", "rstl::string"), ("std::vector", "rstl::vector"), ("std::list", "rstl::list"), ("std::pair", "rstl::pair"), @@ -102,6 +103,8 @@ _LITERAL_REPLACEMENTS = [ ("EWeaponType::", "kWT_"), ("EFluidState::", "kFS_"), ("EPhazonType::", "kPT_"), + ("EChain::", "kC_"), + ("EPhase::", "kP_"), # CActor ("x34_transform.origin", "GetTranslation()"), @@ -125,6 +128,8 @@ _LITERAL_REPLACEMENTS = [ ("std::fabs", "fabsf"), ("std::sqrt", "sqrtf"), ("clamp(", "CMath::Clamp("), + + ("[[fallthrough]];", ""), ] _RE_REPLACEMENTS = [ # SObjectTag{FOURCC('...'), ...} -> SObjectTag('...', ...)