From 669c5bd2aee4741371d65c1735dcb34a48833a22 Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 23 Sep 2026 12:08:58 +0300 Subject: [PATCH 01/12] Model CScriptStreamedMusic --- config/G2ME01/splits.txt | 3 + config/G2ME01/symbols.txt | 28 +- configure.py | 1 + include/MetroidPrime/CGameArea.hpp | 7 +- include/MetroidPrime/CInGameTweakManager.hpp | 45 ++++ .../ScriptObjects/CScriptStreamedMusic.hpp | 58 +++++ .../ScriptObjects/CScriptStreamedMusic.cpp | 244 ++++++++++++++++++ 7 files changed, 371 insertions(+), 15 deletions(-) create mode 100644 include/MetroidPrime/CInGameTweakManager.hpp create mode 100644 include/MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp create mode 100644 src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp diff --git a/config/G2ME01/splits.txt b/config/G2ME01/splits.txt index e7a4f33..9381179 100644 --- a/config/G2ME01/splits.txt +++ b/config/G2ME01/splits.txt @@ -78,6 +78,9 @@ MetroidPrime/ScriptObjects/CScriptAreaProperties.cpp: MetroidPrime/CHealthInfo.cpp: .text start:0x8014206C end:0x80142188 +MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp: + .text start:0x8015CD4C end:0x8015DDE8 + MetroidPrime/Player/CGameOptions.cpp: .text start:0x80160C20 end:0x80161D04 diff --git a/config/G2ME01/symbols.txt b/config/G2ME01/symbols.txt index 3f2b062..f255549 100644 --- a/config/G2ME01/symbols.txt +++ b/config/G2ME01/symbols.txt @@ -5798,29 +5798,29 @@ fn_8015C990 = .text:0x8015C990; // type:function size:0x19C align:4 fn_8015CB2C = .text:0x8015CB2C; // type:function size:0x80 align:4 fn_8015CBAC = .text:0x8015CBAC; // type:function size:0xCC align:4 fn_8015CC78 = .text:0x8015CC78; // type:function size:0xD4 -fn_8015CD4C = .text:0x8015CD4C; // type:function size:0x94 align:4 -fn_8015CDE0 = .text:0x8015CDE0; // type:function size:0x2D8 align:4 -fn_8015D0B8 = .text:0x8015D0B8; // type:function size:0x190 +__dt__20CScriptStreamedMusicFv = .text:0x8015CD4C; // type:function size:0x94 align:4 +LoadStreamedAudio__FR13CStateManagerR12CInputStreamRC11CEntityInfo = .text:0x8015CDE0; // type:function size:0x2D8 align:4 +SetStereoPair__20CScriptStreamedMusicFv = .text:0x8015D0B8; // type:function size:0x190 fn_8015D248 = .text:0x8015D248; // type:function size:0x60 fn_8015D2A8 = .text:0x8015D2A8; // type:function size:0xD8 fn_8015D380 = .text:0x8015D380; // type:function size:0xAC align:4 fn_8015D42C = .text:0x8015D42C; // type:function size:0x104 -fn_8015D530 = .text:0x8015D530; // type:function size:0x150 align:4 -fn_8015D680 = .text:0x8015D680; // type:function size:0x120 align:4 +TweakOverride__20CScriptStreamedMusicFR13CStateManager = .text:0x8015D530; // type:function size:0x150 align:4 +PreloadMemoryAudio__20CScriptStreamedMusicFv = .text:0x8015D680; // type:function size:0x120 align:4 fn_8015D7A0 = .text:0x8015D7A0; // type:function size:0x74 -fn_8015D814 = .text:0x8015D814; // type:function size:0x84 align:4 -fn_8015D898 = .text:0x8015D898; // type:function size:0x3C align:4 -fn_8015D8D4 = .text:0x8015D8D4; // type:function size:0x70 align:4 +Think__20CScriptStreamedMusicFfR13CStateManager = .text:0x8015D814; // type:function size:0x84 align:4 +StopStream__20CScriptStreamedMusicFv = .text:0x8015D898; // type:function size:0x3C align:4 +StartStream__20CScriptStreamedMusicFv = .text:0x8015D8D4; // type:function size:0x70 align:4 fn_8015D944 = .text:0x8015D944; // type:function size:0x24 align:4 fn_8015D968 = .text:0x8015D968; // type:function size:0x60 -fn_8015D9C8 = .text:0x8015D9C8; // type:function size:0x34 align:4 +Stop__20CScriptStreamedMusicFv = .text:0x8015D9C8; // type:function size:0x34 align:4 fn_8015D9FC = .text:0x8015D9FC; // type:function size:0x34 -fn_8015DA30 = .text:0x8015DA30; // type:function size:0x7C align:4 -fn_8015DAAC = .text:0x8015DAAC; // type:function size:0x190 align:4 -fn_8015DC3C = .text:0x8015DC3C; // type:function size:0xB4 +Play__20CScriptStreamedMusicFR13CStateManager = .text:0x8015DA30; // type:function size:0x7C align:4 +AcceptScriptMsg__20CScriptStreamedMusicFR13CStateManagerRC10CScriptMsg = .text:0x8015DAAC; // type:function size:0x190 align:4 +IsDSPFile__20CScriptStreamedMusicFRCQ24rstl66basic_string,Q24rstl17rmemory_allocator> = .text:0x8015DC3C; // type:function size:0xB4 fn_8015DCF0 = .text:0x8015DCF0; // type:function size:0x4 -fn_8015DCF4 = .text:0x8015DCF4; // type:function size:0xE4 align:4 -fn_8015DDD8 = .text:0x8015DDD8; // type:function size:0x10 +__ct__20CScriptStreamedMusicF9TUniqueIdRC11CEntityInfoRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RCQ24rstl66basic_string,Q24rstl17rmemory_allocator>bffUibb = .text:0x8015DCF4; // type:function size:0xE4 align:4 +IsOneShot__20CScriptStreamedMusicFb = .text:0x8015DDD8; // type:function size:0x10 fn_8015DDE8 = .text:0x8015DDE8; // type:function size:0x120 align:4 fn_8015DF08 = .text:0x8015DF08; // type:function size:0x4 align:4 fn_8015DF0C = .text:0x8015DF0C; // type:function size:0x32C diff --git a/configure.py b/configure.py index a59e736..962e22d 100644 --- a/configure.py +++ b/configure.py @@ -342,6 +342,7 @@ def MatchingFor(*versions): Object(NonMatching, "MetroidPrime/CHealthInfo.cpp"), Object(NonMatching, "MetroidPrime/Player/CGameOptions.cpp"), Object(NonMatching, "MetroidPrime/ScriptObjects/CScriptSequenceTimer.cpp"), + Object(NonMatching, "MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp"), Object(NonMatching, "MetroidPrime/ScriptLoaderRel.cpp"), Object(NonMatching, "MetroidPrime/ScriptLoader.cpp"), Object(NonMatching, "MetroidPrime/CAxisAngle.cpp"), diff --git a/include/MetroidPrime/CGameArea.hpp b/include/MetroidPrime/CGameArea.hpp index 31ac738..1e03b75 100644 --- a/include/MetroidPrime/CGameArea.hpp +++ b/include/MetroidPrime/CGameArea.hpp @@ -1,6 +1,8 @@ #ifndef _CGAMEAREA #define _CGAMEAREA +#include "Kyoto/SObjectTag.hpp" + class CScriptAreaProperties; class CGameArea { public: @@ -15,6 +17,7 @@ class CGameArea { }; int GetPhase() const { return m_phase; } + CAssetId GetAreaAssetId() const { return m_areaAssetId; } void SetAreaAttributes(CScriptAreaProperties*); bool IsLoaded() const { return m_phase == 0x10; } @@ -23,7 +26,9 @@ class CGameArea { EOcclusionState GetOcclusionState() const { return m_postConstructed->m_occlusionState; } private: - char pad1[0xf4]; + char pad1[0x54]; + CAssetId m_areaAssetId; // 0x54 + char pad2[0x9c]; int m_phase; // 0xf4; CGameArea* m_next; // 0xf8 CGameArea* m_prev; // 0xfc diff --git a/include/MetroidPrime/CInGameTweakManager.hpp b/include/MetroidPrime/CInGameTweakManager.hpp new file mode 100644 index 0000000..1a8aba5 --- /dev/null +++ b/include/MetroidPrime/CInGameTweakManager.hpp @@ -0,0 +1,45 @@ +#ifndef _CINGAMETWEAKMANAGER +#define _CINGAMETWEAKMANAGER + +#include "Kyoto/SObjectTag.hpp" +#include "rstl/string.hpp" + +class CTweakValue { +public: + struct Audio { + float GetFadeIn() const { return x0_fadeIn; } + float GetFadeOut() const { return x4_fadeOut; } + float GetVolume() const { return x8_volume; } + const rstl::string& GetFileName() const { return xc_fileName; } + + private: + float x0_fadeIn; + float x4_fadeOut; + float x8_volume; + rstl::string xc_fileName; + CAssetId x1c_resourceId; + }; + + const Audio& GetAudio() const { return x24_audio; } + +private: + uint x0_type; + rstl::string x4_key; + rstl::string x14_text; + Audio x24_audio; + uint x44_value; +}; + +NESTED_CHECK_SIZEOF(CTweakValue, Audio, 0x20) +CHECK_SIZEOF(CTweakValue, 0x48) + +class CInGameTweakManager { +public: + bool HasTweakValue(const rstl::string& key) const; + const CTweakValue* GetTweakValue(const rstl::string& key) const; + static rstl::string GetIdentifierForMusicEvent(CAssetId areaId, const rstl::string& name); +}; + +extern CInGameTweakManager* gpTweakManager; + +#endif // _CINGAMETWEAKMANAGER diff --git a/include/MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp b/include/MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp new file mode 100644 index 0000000..68aed1d --- /dev/null +++ b/include/MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp @@ -0,0 +1,58 @@ +#ifndef _CSCRIPTSTREAMEDMUSIC +#define _CSCRIPTSTREAMEDMUSIC + +#include "MetroidPrime/CEntity.hpp" + +#include "rstl/optional_object.hpp" + +// The target's four-byte, reference-counted preload handle is not the +// CStaticAudioPlayer used for RSF playback. Its original type name is unknown. +class CStreamedMusicPreload { +public: + CStreamedMusicPreload(const rstl::string& path); + CStreamedMusicPreload(const CStreamedMusicPreload&); + ~CStreamedMusicPreload(); + CStreamedMusicPreload& operator=(const CStreamedMusicPreload&); + bool IsReady() const; + +private: + void* x0_resource; +}; +CHECK_SIZEOF(CStreamedMusicPreload, 0x4) + +class CScriptStreamedMusic : public CEntity { +public: + CScriptStreamedMusic(TUniqueId id, const CEntityInfo& info, const rstl::string& name, + const rstl::string& fileName, bool noStopOnDeactivate, float fadeIn, + float fadeOut, uint volume, bool loop, bool music); + ~CScriptStreamedMusic() override; + CEntity* TypesMatch(int typeId) const override; + void Think(float dt, CStateManager& mgr) override; + void AcceptScriptMsg(CStateManager& mgr, const CScriptMsg& msg) override; + + static bool IsDSPFile(const rstl::string& fileName); + +private: + static int IsOneShot(bool loop); + void StartStream(); + void StopStream(); + void Play(CStateManager& mgr); + void Stop(); + void TweakOverride(CStateManager& mgr); + void PreloadMemoryAudio(); + void SetStereoPair(); + + rstl::string x24_fileName; + bool x34_noStopOnDeactivate : 1; + bool x34_fileIsDsp : 1; + bool x34_loop : 1; + bool x34_music : 1; + bool x34_preloadPending : 1; + float x38_fadeIn; + float x3c_fadeOut; + uint x40_volume; + rstl::optional_object< CStreamedMusicPreload > x44_preload; +}; +CHECK_SIZEOF(CScriptStreamedMusic, 0x4C) + +#endif // _CSCRIPTSTREAMEDMUSIC diff --git a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp new file mode 100644 index 0000000..e86625d --- /dev/null +++ b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp @@ -0,0 +1,244 @@ +#include "MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp" + +#include "Kyoto/Audio/CStreamAudioManager.hpp" +#include "Kyoto/CDvdFile.hpp" +#include "MetroidPrime/CInGameTweakManager.hpp" +#include "MetroidPrime/CMain.hpp" +#include "MetroidPrime/CStateManager.hpp" +#include "MetroidPrime/CWorld.hpp" +#include "MetroidPrime/ScriptLoader.hpp" +#include "MetroidPrime/ScriptLoader/SLdrEditorProperties.hpp" +#include "rstl/StringExtras.hpp" + +#include + +CScriptStreamedMusic::CScriptStreamedMusic(TUniqueId id, const CEntityInfo& info, + const rstl::string& name, const rstl::string& fileName, + bool noStopOnDeactivate, float fadeIn, float fadeOut, + uint volume, bool loop, bool music) +: CEntity(id, info, name, 0) +, x24_fileName(fileName) +, x34_noStopOnDeactivate(noStopOnDeactivate) +, x34_fileIsDsp(IsDSPFile(fileName)) +, x34_loop(loop) +, x34_music(music) +, x34_preloadPending(false) +, x38_fadeIn(fadeIn) +, x3c_fadeOut(fadeOut) +, x40_volume(volume) +, x44_preload() {} + +CScriptStreamedMusic::~CScriptStreamedMusic() {} + +CEntity* CScriptStreamedMusic::TypesMatch(int typeId) const { + return typeId == kET_ScriptStreamedMusic ? const_cast< CScriptStreamedMusic* >(this) + : CEntity::TypesMatch(typeId); +} + +bool CScriptStreamedMusic::IsDSPFile(const rstl::string& fileName) { + return CStringExtras::CompareCaseInsensitive(fileName, rstl::string_l("sw")) == 0 || + CStringExtras::IndexOfSubstring(fileName, rstl::string_l(".dsp")) != -1; +} + +int CScriptStreamedMusic::IsOneShot(bool loop) { return loop ? 0 : 1; } + +void CScriptStreamedMusic::Think(float dt, CStateManager& mgr) { + if (x34_preloadPending && x44_preload->IsReady()) { + SendScriptMsgs(static_cast< EScriptObjectState >(0x41525256), mgr, kInvalidUniqueId, kSM_None); + x34_preloadPending = false; + } +} + +void CScriptStreamedMusic::StartStream() { + if (!x44_preload || x44_preload->IsReady()) { + CStreamAudioManager::Start(IsOneShot(x34_loop), x24_fileName, static_cast< uchar >(x40_volume), + x34_music, x38_fadeIn, x3c_fadeOut); + } +} + +void CScriptStreamedMusic::StopStream() { + CStreamAudioManager::Stop(IsOneShot(x34_loop), x24_fileName); +} + +void CScriptStreamedMusic::SetStereoPair() { + if (x34_fileIsDsp && x24_fileName.find('|', 0) == -1 && x24_fileName.size() >= 5) { + if (CStringExtras::CompareCaseInsensitive( + rstl::string_l(x24_fileName.data() + x24_fileName.size() - 5), + rstl::string_l("L.dsp")) == 0) { + rstl::string right = rstl::string(x24_fileName.begin(), x24_fileName.end() - 5) + "R.dsp"; + if (CDvdFile::FileExists(right.data())) { + x24_fileName = x24_fileName + '|' + right; + } + } + } +} + +void CScriptStreamedMusic::TweakOverride(CStateManager& mgr) { + const CWorld* world = mgr.GetWorld(); + const CGameArea& area = world->GetAreaAlways(GetCurrentAreaId()); + const rstl::string key = + CInGameTweakManager::GetIdentifierForMusicEvent(area.GetAreaAssetId(), rstl::string_l("")); + if (gpTweakManager->HasTweakValue(key)) { + const CTweakValue::Audio& audio = gpTweakManager->GetTweakValue(key)->GetAudio(); + const rstl::string fileName(audio.GetFileName()); + const float fadeIn = audio.GetFadeIn(); + const char volume = static_cast< char >(audio.GetVolume() * 127.f); + const float fadeOut = audio.GetFadeOut(); + + x24_fileName = fileName; + x34_fileIsDsp = IsDSPFile(x24_fileName); + x38_fadeIn = fadeIn; + x40_volume = volume; + x3c_fadeOut = fadeOut; + SetStereoPair(); + } +} + +void CScriptStreamedMusic::PreloadMemoryAudio() { + if (x44_preload) { + x34_preloadPending = true; + return; + } + if (strncmp(x24_fileName.data(), "mem:", 4) == 0) { + const rstl::string path = x24_fileName.substr(4); + if (CDvdFile::FileExists(path.data())) { + x44_preload = CStreamedMusicPreload(path); + x34_preloadPending = true; + } + } +} + +void CScriptStreamedMusic::Stop() { + if (x34_fileIsDsp) { + StopStream(); + } else { + CStreamAudioManager::sub_8036590c(x3c_fadeOut); + } +} + +void CScriptStreamedMusic::Play(CStateManager& mgr) { + if (!GetActive() && gpMain->fn_80008A1C()) { + return; + } + TweakOverride(mgr); + if (x34_fileIsDsp) { + StartStream(); + } else if (x34_noStopOnDeactivate) { + CStreamAudioManager::SetDefaultAudio(x24_fileName, x3c_fadeOut, x38_fadeIn, + static_cast< uchar >(x40_volume)); + } else { + CStreamAudioManager::SetCurrentAudio(x24_fileName, x3c_fadeOut, x38_fadeIn, + static_cast< uchar >(x40_volume)); + } +} + +void CScriptStreamedMusic::AcceptScriptMsg(CStateManager& mgr, const CScriptMsg& msg) { + const EScriptObjectMessage message = msg.GetMessage(); + CEntity::AcceptScriptMsg(mgr, msg); + switch (message) { + case kSM_Play: + if (GetActive()) { + Play(mgr); + } + break; + case kSM_Stop: + if (GetActive()) { + Stop(); + } + break; + case kSM_Deactivate: + if (x34_fileIsDsp || !x34_noStopOnDeactivate) { + Stop(); + } + break; + case kSM_Increment: + if (x34_fileIsDsp) { + CStreamAudioManager::FadeBackIn(IsOneShot(x34_loop), x38_fadeIn); + } else { + CStreamAudioManager::sub_803653f8(x38_fadeIn); + } + break; + case kSM_Decrement: + if (x34_fileIsDsp) { + CStreamAudioManager::TemporaryFadeOut(IsOneShot(x34_loop), x3c_fadeOut); + } else { + CStreamAudioManager::sub_80365424(x3c_fadeOut); + } + break; + default: + if (message == static_cast< EScriptObjectMessage >(0x4c4f4144) && GetActive() && + x34_fileIsDsp) { + PreloadMemoryAudio(); + } + break; + } +} + +struct SLdrStreamedAudio { + SLdrStreamedAudio() + : editorProperties() + , fileName() + , noStopOnDeactivate(false) + , fadeIn(1.f) + , fadeOut(1.f) + , volume(127) + , loopMode(0) + , music(true) { + editorProperties.unknown = 3; + } + + SLdrEditorProperties editorProperties; + rstl::string fileName; + bool noStopOnDeactivate; + float fadeIn; + float fadeOut; + uint volume; + uint loopMode; + bool music; +}; + +CEntity* LoadStreamedAudio(CStateManager& mgr, CInputStream& input, const CEntityInfo& info) { + SLdrStreamedAudio data; + + const int propertyCount = input.ReadUint16(); + for (int i = 0; i < propertyCount; ++i) { + const uint propertyId = input.ReadInt32(); + const uint propertySize = input.ReadUint16(); + switch (propertyId) { + case 0x255a4580: + LoadTypedefSLdrEditorProperties(data.editorProperties, input); + break; + case 0xf6f3de1c: { + const rstl::string value(input); + data.fileName = value; + break; + } + case 0x34b152c4: + data.noStopOnDeactivate = input.ReadBool(); + break; + case 0x90aa341f: + data.fadeIn = input.ReadFloat(); + break; + case 0x7c269ebc: + data.fadeOut = input.ReadFloat(); + break; + case 0x80c66c37: + data.volume = input.ReadInt32(); + break; + case 0x28f82261: + data.loopMode = input.ReadInt32(); + break; + case 0xd3356fe7: + data.music = input.ReadBool(); + break; + default: + input.ReadBytes(nullptr, propertySize); + break; + } + } + + return new CScriptStreamedMusic( + mgr.AllocateUniqueId(), LdrToEntityInfo(info, data.editorProperties), + data.editorProperties.name, data.fileName, data.noStopOnDeactivate, data.fadeIn, data.fadeOut, + data.volume, data.loopMode == 0, data.music); +} From 6705db79095217fb7ce60133c204362703e0d83a Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 23 Sep 2026 12:43:21 +0300 Subject: [PATCH 02/12] Match streamed-music string helpers --- config/G2ME01/symbols.txt | 6 +++--- .../ScriptObjects/CScriptStreamedMusic.cpp | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/config/G2ME01/symbols.txt b/config/G2ME01/symbols.txt index f255549..546e8f5 100644 --- a/config/G2ME01/symbols.txt +++ b/config/G2ME01/symbols.txt @@ -5801,13 +5801,13 @@ fn_8015CC78 = .text:0x8015CC78; // type:function size:0xD4 __dt__20CScriptStreamedMusicFv = .text:0x8015CD4C; // type:function size:0x94 align:4 LoadStreamedAudio__FR13CStateManagerR12CInputStreamRC11CEntityInfo = .text:0x8015CDE0; // type:function size:0x2D8 align:4 SetStereoPair__20CScriptStreamedMusicFv = .text:0x8015D0B8; // type:function size:0x190 -fn_8015D248 = .text:0x8015D248; // type:function size:0x60 -fn_8015D2A8 = .text:0x8015D2A8; // type:function size:0xD8 +__pl__4rstlFRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>c = .text:0x8015D248; // type:function size:0x60 +__ct,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>>__Q24rstl66basic_string,Q24rstl17rmemory_allocator>FQ24rstl127const_linear_iterator,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>Q24rstl127const_linear_iterator,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>RCQ24rstl17rmemory_allocator = .text:0x8015D2A8; // type:function size:0xD8 fn_8015D380 = .text:0x8015D380; // type:function size:0xAC align:4 fn_8015D42C = .text:0x8015D42C; // type:function size:0x104 TweakOverride__20CScriptStreamedMusicFR13CStateManager = .text:0x8015D530; // type:function size:0x150 align:4 PreloadMemoryAudio__20CScriptStreamedMusicFv = .text:0x8015D680; // type:function size:0x120 align:4 -fn_8015D7A0 = .text:0x8015D7A0; // type:function size:0x74 +substr__Q24rstl66basic_string,Q24rstl17rmemory_allocator>CFii = .text:0x8015D7A0; // type:function size:0x74 Think__20CScriptStreamedMusicFfR13CStateManager = .text:0x8015D814; // type:function size:0x84 align:4 StopStream__20CScriptStreamedMusicFv = .text:0x8015D898; // type:function size:0x3C align:4 StartStream__20CScriptStreamedMusicFv = .text:0x8015D8D4; // type:function size:0x70 align:4 diff --git a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp index e86625d..3e61144 100644 --- a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp +++ b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp @@ -60,6 +60,21 @@ void CScriptStreamedMusic::StopStream() { CStreamAudioManager::Stop(IsOneShot(x34_loop), x24_fileName); } +template <> +template <> +rstl::basic_string< char >::basic_string(rstl::basic_string< char >::const_iterator first, + rstl::basic_string< char >::const_iterator last, + const rstl::rmemory_allocator&) { + const int len = last - first; + internal_allocate(len + 1); + int i = 0; + for (const_iterator it = first; it != last; ++it, ++i) { + const_cast< char& >(x0_ptr[i]) = *it; + } + const_cast< char& >(x0_ptr[i]) = char_traits< char >::eos(); + x8_size = len; +} + void CScriptStreamedMusic::SetStereoPair() { if (x34_fileIsDsp && x24_fileName.find('|', 0) == -1 && x24_fileName.size() >= 5) { if (CStringExtras::CompareCaseInsensitive( @@ -94,6 +109,12 @@ void CScriptStreamedMusic::TweakOverride(CStateManager& mgr) { } } +template <> +rstl::string rstl::basic_string< char >::substr(int pos, int count) const { + const pair< const_iterator, const_iterator > range = range_iterator(pos, count); + return basic_string(range.first, range.second); +} + void CScriptStreamedMusic::PreloadMemoryAudio() { if (x44_preload) { x34_preloadPending = true; From db7adb901f2676c8c33e02958336685a5fe7d542 Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 23 Sep 2026 13:32:34 +0300 Subject: [PATCH 03/12] Use shared preload handle for streamed music --- .../ScriptObjects/CScriptStreamedMusic.hpp | 18 ++---------------- .../ScriptObjects/CScriptStreamedMusic.cpp | 2 +- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/include/MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp b/include/MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp index 68aed1d..6a8dde4 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp @@ -1,25 +1,11 @@ #ifndef _CSCRIPTSTREAMEDMUSIC #define _CSCRIPTSTREAMEDMUSIC +#include "Kyoto/Streams/CFilePreload.hpp" #include "MetroidPrime/CEntity.hpp" #include "rstl/optional_object.hpp" -// The target's four-byte, reference-counted preload handle is not the -// CStaticAudioPlayer used for RSF playback. Its original type name is unknown. -class CStreamedMusicPreload { -public: - CStreamedMusicPreload(const rstl::string& path); - CStreamedMusicPreload(const CStreamedMusicPreload&); - ~CStreamedMusicPreload(); - CStreamedMusicPreload& operator=(const CStreamedMusicPreload&); - bool IsReady() const; - -private: - void* x0_resource; -}; -CHECK_SIZEOF(CStreamedMusicPreload, 0x4) - class CScriptStreamedMusic : public CEntity { public: CScriptStreamedMusic(TUniqueId id, const CEntityInfo& info, const rstl::string& name, @@ -51,7 +37,7 @@ class CScriptStreamedMusic : public CEntity { float x38_fadeIn; float x3c_fadeOut; uint x40_volume; - rstl::optional_object< CStreamedMusicPreload > x44_preload; + rstl::optional_object< CFilePreload > x44_preload; }; CHECK_SIZEOF(CScriptStreamedMusic, 0x4C) diff --git a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp index 3e61144..89f9fcb 100644 --- a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp +++ b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp @@ -123,7 +123,7 @@ void CScriptStreamedMusic::PreloadMemoryAudio() { if (strncmp(x24_fileName.data(), "mem:", 4) == 0) { const rstl::string path = x24_fileName.substr(4); if (CDvdFile::FileExists(path.data())) { - x44_preload = CStreamedMusicPreload(path); + x44_preload = CFilePreload(path); x34_preloadPending = true; } } From abbff6d17333bffa0e9623570fe5260753436c27 Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 23 Sep 2026 13:49:24 +0300 Subject: [PATCH 04/12] Add signed-byte fast cast --- include/Kyoto/Basics/CCast.hpp | 11 +++++++++++ include/dolphin/os/OSFastCast.h | 1 + 2 files changed, 12 insertions(+) diff --git a/include/Kyoto/Basics/CCast.hpp b/include/Kyoto/Basics/CCast.hpp index 41f4b41..1dd0e2c 100644 --- a/include/Kyoto/Basics/CCast.hpp +++ b/include/Kyoto/Basics/CCast.hpp @@ -17,6 +17,16 @@ inline uchar ToUint8(register float in) { return *ptr; } +inline char ToInt8(register float in) { + char a; + register char* ptr = &a; + + asm { + psq_st in, 0(ptr), 1, OS_FASTCAST_S8 + } + return *ptr; +} + inline uchar ToUint8(int c) { return static_cast(c); } inline float ToReal32(register const uchar& in) { @@ -52,6 +62,7 @@ inline float LtoF(int in) { return static_cast< float >(in); } #else inline uchar ToUint8(float in) { return static_cast< uchar >(in); } +inline char ToInt8(float in) { return static_cast< char >(in); } inline uchar ToUint8(int c) { return static_cast(c); } inline float ToReal32(uchar in) { return static_cast< float >(in); } inline short FtoS(float in) { return static_cast< short >(in); } diff --git a/include/dolphin/os/OSFastCast.h b/include/dolphin/os/OSFastCast.h index b6ca50d..c64b5f9 100644 --- a/include/dolphin/os/OSFastCast.h +++ b/include/dolphin/os/OSFastCast.h @@ -13,6 +13,7 @@ extern "C" { #define OS_FASTCAST_U8 2 #define OS_FASTCAST_U16 3 +#define OS_FASTCAST_S8 4 #define OS_FASTCAST_S16 5 // clang-format off static inline void OSInitFastCast(void) { From 0746ae1a039478cac0c06d5038bf562be44df97e Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 23 Sep 2026 13:49:24 +0300 Subject: [PATCH 05/12] Improve CScriptStreamedMusic matching --- config/G2ME01/splits.txt | 3 + .../ScriptObjects/CScriptStreamedMusic.cpp | 72 ++++++++++--------- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/config/G2ME01/splits.txt b/config/G2ME01/splits.txt index 9381179..6b2b8f0 100644 --- a/config/G2ME01/splits.txt +++ b/config/G2ME01/splits.txt @@ -80,6 +80,9 @@ MetroidPrime/CHealthInfo.cpp: MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp: .text start:0x8015CD4C end:0x8015DDE8 + .rodata start:0x803A95F8 end:0x803A9620 + .data start:0x803B5430 end:0x803B5450 + .sdata2 start:0x8041C460 end:0x8041C468 MetroidPrime/Player/CGameOptions.cpp: .text start:0x80160C20 end:0x80161D04 diff --git a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp index 89f9fcb..cf4ca0b 100644 --- a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp +++ b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp @@ -1,6 +1,7 @@ #include "MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp" #include "Kyoto/Audio/CStreamAudioManager.hpp" +#include "Kyoto/Basics/CCast.hpp" #include "Kyoto/CDvdFile.hpp" #include "MetroidPrime/CInGameTweakManager.hpp" #include "MetroidPrime/CMain.hpp" @@ -12,6 +13,8 @@ #include +extern "C" void fn_8015DCF0(CScriptStreamedMusic*); + CScriptStreamedMusic::CScriptStreamedMusic(TUniqueId id, const CEntityInfo& info, const rstl::string& name, const rstl::string& fileName, bool noStopOnDeactivate, float fadeIn, float fadeOut, @@ -26,7 +29,11 @@ CScriptStreamedMusic::CScriptStreamedMusic(TUniqueId id, const CEntityInfo& info , x38_fadeIn(fadeIn) , x3c_fadeOut(fadeOut) , x40_volume(volume) -, x44_preload() {} +, x44_preload() { + fn_8015DCF0(this); +} + +extern "C" void fn_8015DCF0(CScriptStreamedMusic*) {} CScriptStreamedMusic::~CScriptStreamedMusic() {} @@ -75,29 +82,34 @@ rstl::basic_string< char >::basic_string(rstl::basic_string< char >::const_itera x8_size = len; } -void CScriptStreamedMusic::SetStereoPair() { - if (x34_fileIsDsp && x24_fileName.find('|', 0) == -1 && x24_fileName.size() >= 5) { - if (CStringExtras::CompareCaseInsensitive( - rstl::string_l(x24_fileName.data() + x24_fileName.size() - 5), - rstl::string_l("L.dsp")) == 0) { - rstl::string right = rstl::string(x24_fileName.begin(), x24_fileName.end() - 5) + "R.dsp"; - if (CDvdFile::FileExists(right.data())) { - x24_fileName = x24_fileName + '|' + right; - } +template <> +rstl::string rstl::basic_string< char >::substr(int pos, int count) const { + const pair< const_iterator, const_iterator > range = range_iterator(pos, count); + return basic_string(range.first, range.second); +} + +void CScriptStreamedMusic::PreloadMemoryAudio() { + if (x44_preload) { + x34_preloadPending = true; + return; + } + if (strncmp(x24_fileName.data(), "mem:", 4) == 0) { + const rstl::string path = x24_fileName.substr(4); + if (CDvdFile::FileExists(path.data())) { + x44_preload = CFilePreload(path); + x34_preloadPending = true; } } } void CScriptStreamedMusic::TweakOverride(CStateManager& mgr) { - const CWorld* world = mgr.GetWorld(); - const CGameArea& area = world->GetAreaAlways(GetCurrentAreaId()); - const rstl::string key = - CInGameTweakManager::GetIdentifierForMusicEvent(area.GetAreaAssetId(), rstl::string_l("")); + const rstl::string key = CInGameTweakManager::GetIdentifierForMusicEvent( + mgr.GetWorld()->GetAreaAlways(GetCurrentAreaId()).GetAreaAssetId(), rstl::string_l("")); if (gpTweakManager->HasTweakValue(key)) { const CTweakValue::Audio& audio = gpTweakManager->GetTweakValue(key)->GetAudio(); const rstl::string fileName(audio.GetFileName()); const float fadeIn = audio.GetFadeIn(); - const char volume = static_cast< char >(audio.GetVolume() * 127.f); + const char volume = CCast::ToInt8(audio.GetVolume() * 127.f); const float fadeOut = audio.GetFadeOut(); x24_fileName = fileName; @@ -105,26 +117,20 @@ void CScriptStreamedMusic::TweakOverride(CStateManager& mgr) { x38_fadeIn = fadeIn; x40_volume = volume; x3c_fadeOut = fadeOut; + fn_8015DCF0(this); SetStereoPair(); } } -template <> -rstl::string rstl::basic_string< char >::substr(int pos, int count) const { - const pair< const_iterator, const_iterator > range = range_iterator(pos, count); - return basic_string(range.first, range.second); -} - -void CScriptStreamedMusic::PreloadMemoryAudio() { - if (x44_preload) { - x34_preloadPending = true; - return; - } - if (strncmp(x24_fileName.data(), "mem:", 4) == 0) { - const rstl::string path = x24_fileName.substr(4); - if (CDvdFile::FileExists(path.data())) { - x44_preload = CFilePreload(path); - x34_preloadPending = true; +void CScriptStreamedMusic::SetStereoPair() { + if (x34_fileIsDsp && x24_fileName.find('|', 0) == -1 && x24_fileName.size() >= 5) { + if (CStringExtras::CompareCaseInsensitive( + rstl::string_l(x24_fileName.data() + x24_fileName.size() - 5), + rstl::string_l("L.dsp")) == 0) { + rstl::string right = rstl::string(x24_fileName.begin(), x24_fileName.end() - 5) + "R.dsp"; + if (CDvdFile::FileExists(right.data())) { + x24_fileName = x24_fileName + '|' + right; + } } } } @@ -200,8 +206,8 @@ struct SLdrStreamedAudio { : editorProperties() , fileName() , noStopOnDeactivate(false) - , fadeIn(1.f) - , fadeOut(1.f) + , fadeIn(0.25f) + , fadeOut(0.25f) , volume(127) , loopMode(0) , music(true) { From 1234e6c8822f617a43d602fdf378b6421ee01aa0 Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 23 Sep 2026 13:51:33 +0300 Subject: [PATCH 06/12] Match CScriptStreamedMusic preload --- src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp index cf4ca0b..25c511a 100644 --- a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp +++ b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp @@ -93,7 +93,8 @@ void CScriptStreamedMusic::PreloadMemoryAudio() { x34_preloadPending = true; return; } - if (strncmp(x24_fileName.data(), "mem:", 4) == 0) { + const char* fileName = x24_fileName.data(); + if (strncmp(fileName, "mem:", strlen("mem:")) == 0) { const rstl::string path = x24_fileName.substr(4); if (CDvdFile::FileExists(path.data())) { x44_preload = CFilePreload(path); From 1b8bc42c60d2bb89ca8dadbbfb052c54b4b2456e Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 23 Sep 2026 14:07:32 +0300 Subject: [PATCH 07/12] Recover CScriptStreamedMusic playback helpers --- config/G2ME01/symbols.txt | 6 ++-- .../ScriptObjects/CScriptStreamedMusic.hpp | 3 ++ .../ScriptObjects/CScriptStreamedMusic.cpp | 29 +++++++++++++++---- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/config/G2ME01/symbols.txt b/config/G2ME01/symbols.txt index 546e8f5..a78f139 100644 --- a/config/G2ME01/symbols.txt +++ b/config/G2ME01/symbols.txt @@ -5811,10 +5811,10 @@ substr__Q24rstl66basic_string,Q24rstl17rmemory_allocat Think__20CScriptStreamedMusicFfR13CStateManager = .text:0x8015D814; // type:function size:0x84 align:4 StopStream__20CScriptStreamedMusicFv = .text:0x8015D898; // type:function size:0x3C align:4 StartStream__20CScriptStreamedMusicFv = .text:0x8015D8D4; // type:function size:0x70 align:4 -fn_8015D944 = .text:0x8015D944; // type:function size:0x24 align:4 -fn_8015D968 = .text:0x8015D968; // type:function size:0x60 +StopNonDsp__20CScriptStreamedMusicFv = .text:0x8015D944; // type:function size:0x24 align:4 +PlayNonDsp__20CScriptStreamedMusicFv = .text:0x8015D968; // type:function size:0x60 Stop__20CScriptStreamedMusicFv = .text:0x8015D9C8; // type:function size:0x34 align:4 -fn_8015D9FC = .text:0x8015D9FC; // type:function size:0x34 +PlayAudio__20CScriptStreamedMusicFv = .text:0x8015D9FC; // type:function size:0x34 Play__20CScriptStreamedMusicFR13CStateManager = .text:0x8015DA30; // type:function size:0x7C align:4 AcceptScriptMsg__20CScriptStreamedMusicFR13CStateManagerRC10CScriptMsg = .text:0x8015DAAC; // type:function size:0x190 align:4 IsDSPFile__20CScriptStreamedMusicFRCQ24rstl66basic_string,Q24rstl17rmemory_allocator> = .text:0x8015DC3C; // type:function size:0xB4 diff --git a/include/MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp b/include/MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp index 6a8dde4..08a4052 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptStreamedMusic.hpp @@ -15,6 +15,7 @@ class CScriptStreamedMusic : public CEntity { CEntity* TypesMatch(int typeId) const override; void Think(float dt, CStateManager& mgr) override; void AcceptScriptMsg(CStateManager& mgr, const CScriptMsg& msg) override; + void PlayAudio(); static bool IsDSPFile(const rstl::string& fileName); @@ -22,6 +23,8 @@ class CScriptStreamedMusic : public CEntity { static int IsOneShot(bool loop); void StartStream(); void StopStream(); + void StopNonDsp(); + void PlayNonDsp(); void Play(CStateManager& mgr); void Stop(); void TweakOverride(CStateManager& mgr); diff --git a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp index 25c511a..469bf9b 100644 --- a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp +++ b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp @@ -136,11 +136,32 @@ void CScriptStreamedMusic::SetStereoPair() { } } +void CScriptStreamedMusic::StopNonDsp() { + CStreamAudioManager::sub_8036590c(x3c_fadeOut); +} + +void CScriptStreamedMusic::PlayNonDsp() { + const char volume = x40_volume; + if (x34_noStopOnDeactivate) { + CStreamAudioManager::SetDefaultAudio(x24_fileName, x3c_fadeOut, x38_fadeIn, volume); + } else { + CStreamAudioManager::SetCurrentAudio(x24_fileName, x3c_fadeOut, x38_fadeIn, volume); + } +} + void CScriptStreamedMusic::Stop() { if (x34_fileIsDsp) { StopStream(); } else { - CStreamAudioManager::sub_8036590c(x3c_fadeOut); + StopNonDsp(); + } +} + +void CScriptStreamedMusic::PlayAudio() { + if (x34_fileIsDsp) { + StartStream(); + } else { + PlayNonDsp(); } } @@ -151,12 +172,8 @@ void CScriptStreamedMusic::Play(CStateManager& mgr) { TweakOverride(mgr); if (x34_fileIsDsp) { StartStream(); - } else if (x34_noStopOnDeactivate) { - CStreamAudioManager::SetDefaultAudio(x24_fileName, x3c_fadeOut, x38_fadeIn, - static_cast< uchar >(x40_volume)); } else { - CStreamAudioManager::SetCurrentAudio(x24_fileName, x3c_fadeOut, x38_fadeIn, - static_cast< uchar >(x40_volume)); + PlayNonDsp(); } } From 9eca4213ec22317c5ba32f938ff9768d22cf9f8c Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 23 Sep 2026 14:15:49 +0300 Subject: [PATCH 08/12] Match CScriptStreamedMusic play gate --- include/MetroidPrime/CEntity.hpp | 1 + src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/MetroidPrime/CEntity.hpp b/include/MetroidPrime/CEntity.hpp index c50fc4f..3e68efb 100644 --- a/include/MetroidPrime/CEntity.hpp +++ b/include/MetroidPrime/CEntity.hpp @@ -34,6 +34,7 @@ class CEntity { TAreaId GetCurrentAreaId() const { return m_areaId; } const bool GetActive() const { return m_active; } bool IsScriptingBlocked() const { return m_scriptingBlocked; } + bool GetEditorFlag2() const { return m_entityUnknown; } // might be fake? rstl::vector< SConnection >& ConnectionList() { return m_conns; } diff --git a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp index 469bf9b..adae486 100644 --- a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp +++ b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp @@ -166,7 +166,7 @@ void CScriptStreamedMusic::PlayAudio() { } void CScriptStreamedMusic::Play(CStateManager& mgr) { - if (!GetActive() && gpMain->fn_80008A1C()) { + if (!GetEditorFlag2() && gpMain->fn_80008A1C()) { return; } TweakOverride(mgr); From 1ab4a3d28e918c0f87f422fe3dc385aeabb31454 Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 23 Sep 2026 14:23:55 +0300 Subject: [PATCH 09/12] Recover CScriptStreamedMusic message dispatch --- include/MetroidPrime/CEntityInfo.hpp | 1 + .../ScriptObjects/CScriptStreamedMusic.cpp | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/MetroidPrime/CEntityInfo.hpp b/include/MetroidPrime/CEntityInfo.hpp index 362408a..2665a58 100644 --- a/include/MetroidPrime/CEntityInfo.hpp +++ b/include/MetroidPrime/CEntityInfo.hpp @@ -60,6 +60,7 @@ enum EScriptObjectMessage { kSM_Start = 0x53545254, kSM_Stop = 0x53544f50, kSM_Play = 0x504c4159, + kSM_Load = 0x4c4f4144, kSM_Activate = 0x41435456, kSM_Deactivate = 0x44435456, kSM_ToggleActive = 0x54435456, diff --git a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp index adae486..23aa962 100644 --- a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp +++ b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp @@ -186,13 +186,18 @@ void CScriptStreamedMusic::AcceptScriptMsg(CStateManager& mgr, const CScriptMsg& Play(mgr); } break; + case kSM_Load: + if (GetActive() && x34_fileIsDsp) { + PreloadMemoryAudio(); + } + break; case kSM_Stop: if (GetActive()) { Stop(); } break; case kSM_Deactivate: - if (x34_fileIsDsp || !x34_noStopOnDeactivate) { + if ((!x34_fileIsDsp && !x34_noStopOnDeactivate) || x34_fileIsDsp) { Stop(); } break; @@ -211,10 +216,6 @@ void CScriptStreamedMusic::AcceptScriptMsg(CStateManager& mgr, const CScriptMsg& } break; default: - if (message == static_cast< EScriptObjectMessage >(0x4c4f4144) && GetActive() && - x34_fileIsDsp) { - PreloadMemoryAudio(); - } break; } } From 70a2695cbda6a2474c3ed40f5a091fd3737c938f Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 23 Sep 2026 14:49:54 +0300 Subject: [PATCH 10/12] Recover CScriptStreamedMusic string search --- config/G2ME01/symbols.txt | 4 +-- .../ScriptObjects/CScriptStreamedMusic.cpp | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/config/G2ME01/symbols.txt b/config/G2ME01/symbols.txt index a78f139..d9a1abe 100644 --- a/config/G2ME01/symbols.txt +++ b/config/G2ME01/symbols.txt @@ -5803,8 +5803,8 @@ LoadStreamedAudio__FR13CStateManagerR12CInputStreamRC11CEntityInfo = .text:0x801 SetStereoPair__20CScriptStreamedMusicFv = .text:0x8015D0B8; // type:function size:0x190 __pl__4rstlFRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>c = .text:0x8015D248; // type:function size:0x60 __ct,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>>__Q24rstl66basic_string,Q24rstl17rmemory_allocator>FQ24rstl127const_linear_iterator,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>Q24rstl127const_linear_iterator,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>RCQ24rstl17rmemory_allocator = .text:0x8015D2A8; // type:function size:0xD8 -fn_8015D380 = .text:0x8015D380; // type:function size:0xAC align:4 -fn_8015D42C = .text:0x8015D42C; // type:function size:0x104 +find__Q24rstl66basic_string,Q24rstl17rmemory_allocator>CFci = .text:0x8015D380; // type:function size:0xAC align:4 +internal_search,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>,PCc>__Q24rstl66basic_string,Q24rstl17rmemory_allocator>FQ24rstl127const_linear_iterator,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>Q24rstl127const_linear_iterator,Q24rstl17rmemory_allocator>,Q24rstl17rmemory_allocator>PCcPCc = .text:0x8015D42C; // type:function size:0x104 TweakOverride__20CScriptStreamedMusicFR13CStateManager = .text:0x8015D530; // type:function size:0x150 align:4 PreloadMemoryAudio__20CScriptStreamedMusicFv = .text:0x8015D680; // type:function size:0x120 align:4 substr__Q24rstl66basic_string,Q24rstl17rmemory_allocator>CFii = .text:0x8015D7A0; // type:function size:0x74 diff --git a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp index 23aa962..7258335 100644 --- a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp +++ b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp @@ -123,6 +123,37 @@ void CScriptStreamedMusic::TweakOverride(CStateManager& mgr) { } } +template <> +template <> +int rstl::basic_string< char >::internal_search< rstl::string::const_iterator, const char* >( + rstl::string::const_iterator first, rstl::string::const_iterator last, + const char* searchFirst, const char* searchLast) { + if (searchFirst == searchLast) { + return 0; + } + int index = 0; + for (const_iterator it = first; it != last; ++it, ++index) { + if (char_traits< char >::eq(*it, *searchFirst)) { + const_iterator next = it; + ++next; + const char* search = searchFirst + 1; + const char* nextData = &*next; + while (search != searchLast && next != last) { + if (!char_traits< char >::eq(*nextData, *search)) { + break; + } + ++next; + ++nextData; + ++search; + } + if (search == searchLast) { + return index; + } + } + } + return -1; +} + void CScriptStreamedMusic::SetStereoPair() { if (x34_fileIsDsp && x24_fileName.find('|', 0) == -1 && x24_fileName.size() >= 5) { if (CStringExtras::CompareCaseInsensitive( From 4ba9d63d112eac85558f3903879e1c1ad035cc3a Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 23 Sep 2026 15:00:43 +0300 Subject: [PATCH 11/12] Match streamed music string range constructor --- .../ScriptObjects/CScriptStreamedMusic.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp index 7258335..e09de24 100644 --- a/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp +++ b/src/MetroidPrime/ScriptObjects/CScriptStreamedMusic.cpp @@ -67,15 +67,24 @@ void CScriptStreamedMusic::StopStream() { CStreamAudioManager::Stop(IsOneShot(x34_loop), x24_fileName); } +namespace rstl { +// Keep the iterator-range constructor's distance calculation inline in this TU. +template <> +inline long distance< string::const_iterator >(string::const_iterator first, + string::const_iterator last) { + return last - first; +} +} // namespace rstl + template <> template <> rstl::basic_string< char >::basic_string(rstl::basic_string< char >::const_iterator first, rstl::basic_string< char >::const_iterator last, const rstl::rmemory_allocator&) { - const int len = last - first; + const int len = rstl::distance(first, last); internal_allocate(len + 1); int i = 0; - for (const_iterator it = first; it != last; ++it, ++i) { + for (const_iterator it = first; it != last; it = it + 1, ++i) { const_cast< char& >(x0_ptr[i]) = *it; } const_cast< char& >(x0_ptr[i]) = char_traits< char >::eos(); From 459e2825bb6e617af1ad6ab6a73a1ac4b974138f Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 23 Sep 2026 15:12:55 +0300 Subject: [PATCH 12/12] Correct entity and actor virtual slots --- config/G2ME01/symbols.txt | 2 +- include/MetroidPrime/CActor.hpp | 11 ++++++----- include/MetroidPrime/CEntity.hpp | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/config/G2ME01/symbols.txt b/config/G2ME01/symbols.txt index d9a1abe..e2d6366 100644 --- a/config/G2ME01/symbols.txt +++ b/config/G2ME01/symbols.txt @@ -18148,7 +18148,7 @@ lbl_803B53A0 = .data:0x803B53A0; // type:object size:0x40 lbl_803B53E0 = .data:0x803B53E0; // type:object size:0x20 lbl_803B5400 = .data:0x803B5400; // type:object size:0x10 lbl_803B5410 = .data:0x803B5410; // type:object size:0x20 -lbl_803B5430 = .data:0x803B5430; // type:object size:0x20 +__vt__20CScriptStreamedMusic = .data:0x803B5430; // type:object size:0x20 lbl_803B5450 = .data:0x803B5450; // type:object size:0x18 lbl_803B5468 = .data:0x803B5468; // type:object size:0x98 lbl_803B5500 = .data:0x803B5500; // type:object size:0x80 diff --git a/include/MetroidPrime/CActor.hpp b/include/MetroidPrime/CActor.hpp index 5d88c8f..f0ef7e9 100644 --- a/include/MetroidPrime/CActor.hpp +++ b/include/MetroidPrime/CActor.hpp @@ -61,6 +61,7 @@ class CActor : public CEntity { void AcceptScriptMsg(CStateManager& mgr, const CScriptMsg&) override; void SetActive(const bool active) override; + virtual void UnkVtable20(); // G2ME01 slot +0x20; original name unknown virtual void PreRender(CStateManager&, const CFrustumPlanes&); virtual void AddToRenderer(const CStateManager&) const; virtual void Render(const CStateManager&) const; @@ -88,13 +89,14 @@ class CActor : public CEntity { virtual CAABox GetSortingBounds(const CStateManager&) const; virtual void DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt); + virtual CScannableObjectInfo* GetScannableObjectInfo() const; + virtual void ProcessSoundEvent(int sfxId, float weight, int flags, float fallOff, float maxDist, + uchar minVol, uchar maxVol, const CVector3f& toListener, + const CVector3f& position, int aid, CStateManager& mgr, + bool translateId); CAdvancementDeltas UpdateAnimation(float dt, CStateManager& mgr, bool advTree); - void ProcessSoundEvent(int sfxId, float weight, int flags, float fallOff, float maxDist, - uchar minVol, uchar maxVol, const CVector3f& toListener, - const CVector3f& position, int aid, CStateManager& mgr, bool translateId); - void UpdateSfxEmitters(); void RemoveEmitter(); void SetModelData(const CModelData& modelData, CStateManager& mgr); @@ -125,7 +127,6 @@ class CActor : public CEntity { void SetActorLights(rstl::auto_ptr< CActorLights > lights); void SetInFluid(bool b, TUniqueId uid); - CScannableObjectInfo* GetScannableObjectInfo() const; void MoveScannableObjectInfoToActor(CActor* actor, CStateManager& mgr); /// ???? diff --git a/include/MetroidPrime/CEntity.hpp b/include/MetroidPrime/CEntity.hpp index 3e68efb..664273f 100644 --- a/include/MetroidPrime/CEntity.hpp +++ b/include/MetroidPrime/CEntity.hpp @@ -17,7 +17,7 @@ class CEntity { virtual void PreThink(float dt, CStateManager& mgr); virtual void Think(float dt, CStateManager& mgr); virtual void AcceptScriptMsg(CStateManager& mgr, const CScriptMsg&); - virtual void SendActive(CStateManager& mgr, bool active); + void SendActive(CStateManager& mgr, bool active); virtual void SetActive(const bool active); CEntity(TUniqueId id, const CEntityInfo& info, const rstl::string& name, uint inGrave);