diff --git a/.gitattributes b/.gitattributes index 04926c6b..430dfc15 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,3 +9,5 @@ **/*.png binary **/*.svg text eol=lf **/*.desktop text eol=lf +assets/widescreen/** filter=lfs diff=lfs merge=lfs -text +assets/hdmusic/** filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e284eee..fe7ce886 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,6 +113,11 @@ jobs: - uses: actions/checkout@v4 + - name: Checkout LFS + if: ${{ matrix.build-assets }} + run: | + git lfs pull + - name: Setup Java (Android) if: ${{ matrix.android }} uses: actions/setup-java@v4 diff --git a/.lfsconfig b/.lfsconfig new file mode 100644 index 00000000..98f14551 --- /dev/null +++ b/.lfsconfig @@ -0,0 +1,2 @@ +[lfs] + url = https://f9622702b3bbcac0705052a10e62a5a5:31adabe20fe975a919bd1909a6f598f37b0d6486225efa84b38da1aaf27cbd35@assets.isle.pizza/a70a70ae5ebee06d2333bb1132711de1.r2.cloudflarestorage.com/assets diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 28b5aa15..54259e63 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -78,8 +78,8 @@ if(DOWNLOAD_DEPENDENCIES) include(FetchContent) FetchContent_Populate( libweaver - URL https://github.com/isledecomp/SIEdit/archive/6da93b2072c41c41d526b8b9df7d4292be1f0f55.tar.gz - URL_MD5 ae59007fcb9efadc06c67621e1e107cb + URL https://github.com/isledecomp/SIEdit/archive/ae447259389f3bf8273c7e7a4844743faf7cbdb8.tar.gz + URL_MD5 dee68424fde8db6d5cef3b9034a8151f ) else() set(libweaver_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libweaver") diff --git a/3rdparty/libweaver b/3rdparty/libweaver index 6da93b20..ae447259 160000 --- a/3rdparty/libweaver +++ b/3rdparty/libweaver @@ -1 +1 @@ -Subproject commit 6da93b2072c41c41d526b8b9df7d4292be1f0f55 +Subproject commit ae447259389f3bf8273c7e7a4844743faf7cbdb8 diff --git a/CMakeLists.txt b/CMakeLists.txt index 47c9b9c8..deebda7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -691,10 +691,6 @@ if(ISLE_BUILD_ASSETS) add_custom_target(build_assets ALL DEPENDS ${GENERATED_ASSETS_DIR}/.stamp ) - - install(DIRECTORY ${GENERATED_ASSETS_DIR}/ - DESTINATION assets - ) endif() if (ISLE_MINIWIN) diff --git a/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp b/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp index 2822e95a..a5bb51b7 100644 --- a/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp +++ b/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp @@ -119,7 +119,12 @@ void MxBackgroundAudioManager::FadeInPendingPresenter() MxS32 compare, volume; if (m_activePresenter == NULL) { - if (m_pendingPresenter) { + if (m_pendingPresenter && m_pendingPresenter->GetCurrentTickleState() >= MxPresenter::e_streaming) { + if (!m_pendingPresenter->IsEnabled()) { + m_pendingPresenter->Enable(TRUE); + m_pendingPresenter->SetTickleState(MxPresenter::e_streaming); + } + if (m_volumeSuppressionAmount != 0) { compare = 30; } @@ -221,6 +226,12 @@ void MxBackgroundAudioManager::StartAction(MxParam& p_param) m_action2.SetObjectId(m_pendingPresenter->GetAction()->GetObjectId()); m_targetVolume = ((MxDSSound*) (m_pendingPresenter->GetAction()))->GetVolume(); m_pendingPresenter->SetVolume(0); + + // Disabling the action here and starting it later once the actively presented music has been faded out. + // This was not necessary in retail because the streaming layer would implicitly not start another action + // before the previous one has ended (since it's all coming from JUKEBOX.SI), however since we now + // allow loading music from multiple SI files this would cause the new music to start immediately. + m_pendingPresenter->GetAction()->SetFlags(m_pendingPresenter->GetAction()->GetFlags() & ~MxDSAction::c_enabled); } // FUNCTION: LEGO1 0x1007f200 @@ -254,7 +265,8 @@ MxResult MxBackgroundAudioManager::PlayMusic( return SUCCESS; } - if (m_action2.GetObjectId() == -1 && m_action1.GetObjectId() != p_action.GetObjectId()) { + if (m_action2.GetObjectId() == -1 && + (m_action1.GetObjectId() != p_action.GetObjectId() || m_action1.GetAtomId() != p_action.GetAtomId())) { MxDSAction action; action.SetAtomId(GetCurrentAction().GetAtomId()); action.SetObjectId(GetCurrentAction().GetObjectId()); diff --git a/LEGO1/lego/legoomni/src/common/legoutils.cpp b/LEGO1/lego/legoomni/src/common/legoutils.cpp index c43e4dc9..f8ef1bab 100644 --- a/LEGO1/lego/legoomni/src/common/legoutils.cpp +++ b/LEGO1/lego/legoomni/src/common/legoutils.cpp @@ -503,6 +503,12 @@ MxBool RemoveFromCurrentWorld(const MxAtomId& p_atomId, MxS32 p_id) { LegoWorld* world = CurrentWorld(); + auto result = + Extension::Call(HandleRemove, SiLoader::StreamObject{p_atomId, p_id}, world).value_or(std::nullopt); + if (result) { + return result.value(); + } + if (world) { MxCore* object = world->Find(p_atomId, p_id); @@ -518,8 +524,6 @@ MxBool RemoveFromCurrentWorld(const MxAtomId& p_atomId, MxS32 p_id) } ((MxPresenter*) object)->EndAction(); - - Extension::Call(RemoveWith, SiLoader::StreamObject{p_atomId, p_id}, world); } return TRUE; @@ -539,6 +543,12 @@ MxBool RemoveFromWorld( { LegoWorld* world = FindWorld(p_worldAtom, p_worldEntityId); + auto result = Extension::Call(HandleRemove, SiLoader::StreamObject{p_entityAtom, p_entityId}, world) + .value_or(std::nullopt); + if (result) { + return result.value(); + } + if (world) { MxCore* object = world->Find(p_entityAtom, p_entityId); @@ -554,8 +564,6 @@ MxBool RemoveFromWorld( } ((MxPresenter*) object)->EndAction(); - - Extension::Call(RemoveWith, SiLoader::StreamObject{p_entityAtom, p_entityId}, world); } return TRUE; diff --git a/LEGO1/lego/legoomni/src/entity/legoworld.cpp b/LEGO1/lego/legoomni/src/entity/legoworld.cpp index 1ba828cb..fe4bbc63 100644 --- a/LEGO1/lego/legoomni/src/entity/legoworld.cpp +++ b/LEGO1/lego/legoomni/src/entity/legoworld.cpp @@ -1,6 +1,7 @@ #include "legoworld.h" #include "anim/legoanim.h" +#include "extensions/siloader.h" #include "legoanimationmanager.h" #include "legoanimpresenter.h" #include "legobuildingmanager.h" @@ -32,6 +33,8 @@ DECOMP_SIZE_ASSERT(LegoEntityListCursor, 0x10) DECOMP_SIZE_ASSERT(LegoCacheSoundList, 0x18) DECOMP_SIZE_ASSERT(LegoCacheSoundListCursor, 0x10) +using namespace Extensions; + // FUNCTION: LEGO1 0x1001ca40 LegoWorld::LegoWorld() : m_pathControllerList(TRUE) { @@ -636,6 +639,12 @@ MxCore* LegoWorld::Find(const char* p_class, const char* p_name) // FUNCTION: BETA10 0x100db3de MxCore* LegoWorld::Find(const MxAtomId& p_atom, MxS32 p_entityId) { + auto result = + Extension::Call(HandleFind, SiLoader::StreamObject{p_atom, p_entityId}, this).value_or(std::nullopt); + if (result) { + return result.value(); + } + LegoEntityListCursor entityCursor(m_entityList); LegoEntity* entity; diff --git a/LEGO1/lego/legoomni/src/main/legomain.cpp b/LEGO1/lego/legoomni/src/main/legomain.cpp index 975933ff..604701eb 100644 --- a/LEGO1/lego/legoomni/src/main/legomain.cpp +++ b/LEGO1/lego/legoomni/src/main/legomain.cpp @@ -474,6 +474,11 @@ LegoWorld* LegoOmni::FindWorld(const MxAtomId& p_atom, MxS32 p_entityid) // STUB: BETA10 0x1008e93e void LegoOmni::DeleteObject(MxDSAction& p_dsAction) { + auto result = Extension::Call(HandleDelete, p_dsAction).value_or(std::nullopt); + if (result && result.value()) { + return; + } + if (p_dsAction.GetAtomId().GetInternal() != NULL) { LegoWorld* world = FindWorld(p_dsAction.GetAtomId(), p_dsAction.GetObjectId()); if (world) { @@ -663,6 +668,13 @@ void LegoOmni::CreateBackgroundAudio() // FUNCTION: BETA10 0x1008f7e0 MxResult LegoOmni::Start(MxDSAction* p_dsAction) { + { + auto result = Extension::Call(HandleStart, *p_dsAction).value_or(std::nullopt); + if (result) { + return result.value(); + } + } + MxResult result = MxOmni::Start(p_dsAction); #ifdef BETA10 this->m_action = *p_dsAction; @@ -673,14 +685,6 @@ MxResult LegoOmni::Start(MxDSAction* p_dsAction) this->m_action.SetObjectId(p_dsAction->GetObjectId()); this->m_action.SetUnknown24(p_dsAction->GetUnknown24()); #endif - - if (result == SUCCESS) { - Extension::Call( - StartWith, - SiLoader::StreamObject{p_dsAction->GetAtomId(), p_dsAction->GetObjectId()} - ); - } - return result; } diff --git a/LEGO1/omni/include/mxdsbuffer.h b/LEGO1/omni/include/mxdsbuffer.h index a988ca57..09f6f826 100644 --- a/LEGO1/omni/include/mxdsbuffer.h +++ b/LEGO1/omni/include/mxdsbuffer.h @@ -91,6 +91,12 @@ class MxDSBuffer : public MxCore { void SetUnk30(MxDSStreamingAction* p_unk0x30) { m_unk0x30 = p_unk0x30; } + void SetSourceBuffer(MxDSBuffer* p_sourceBuffer) + { + m_sourceBuffer = p_sourceBuffer; + m_sourceBuffer->AddRef(NULL); + } + // SYNTHETIC: LEGO1 0x100c6510 // SYNTHETIC: BETA10 0x10158530 // MxDSBuffer::`scalar deleting destructor' @@ -107,6 +113,7 @@ class MxDSBuffer : public MxCore { MxU32 m_writeOffset; // 0x28 MxU32 m_bytesRemaining; // 0x2c MxDSStreamingAction* m_unk0x30; // 0x30 + MxDSBuffer* m_sourceBuffer; }; #endif // MXDSBUFFER_H diff --git a/LEGO1/omni/src/stream/mxdsbuffer.cpp b/LEGO1/omni/src/stream/mxdsbuffer.cpp index 0d49741a..732f9fac 100644 --- a/LEGO1/omni/src/stream/mxdsbuffer.cpp +++ b/LEGO1/omni/src/stream/mxdsbuffer.cpp @@ -28,6 +28,7 @@ MxDSBuffer::MxDSBuffer() m_bytesRemaining = 0; m_mode = e_preallocated; m_unk0x30 = 0; + m_sourceBuffer = NULL; } // FUNCTION: LEGO1 0x100c6530 @@ -36,6 +37,10 @@ MxDSBuffer::~MxDSBuffer() { assert(m_referenceCount == 0); + if (m_sourceBuffer) { + m_sourceBuffer->ReleaseRef(NULL); + } + if (m_pBuffer != NULL) { switch (m_mode) { case e_allocate: @@ -267,6 +272,28 @@ MxResult MxDSBuffer::ParseChunk( return FAILURE; } + // START FIX: Ref-Counting Backpressure for Split Chunks + // + // PROBLEM: When a `DS_CHUNK_SPLIT` is found, the temporary `MxStreamChunk` + // header that holds a reference to the source buffer is immediately + // destroyed. This prematurely releases the reference, causing the source + // buffer's ref-count to drop to zero. + // + // EFFECT: The source buffer is freed immediately instead of being kept + // alive on the m_list0x74 "keep-alive" list. This breaks the natural + // ref-counting backpressure mechanism, as the controller is blind to the + // downstream workload and keeps reading new data from the stream at full + // speed, eventually leading to a memory leak. + // + // SOLUTION: We explicitly link the new reassembly buffer to the original + // source buffer. We then add an artificial reference to the source buffer. + // This reference is designed to be released by the reassembly buffer's + // destructor, ensuring the source buffer is kept alive for the correct + // duration and that the backpressure system functions as intended. + if (p_header->GetBuffer()) { + buffer->SetSourceBuffer(p_header->GetBuffer()); + } + MxU16* flags = MxStreamChunk::IntoFlags(buffer->GetBuffer()); *flags = p_header->GetChunkFlags() & ~DS_CHUNK_SPLIT; @@ -409,9 +436,7 @@ MxU8 MxDSBuffer::ReleaseRef(MxDSChunk*) // FUNCTION: LEGO1 0x100c6ee0 void MxDSBuffer::AddRef(MxDSChunk* p_chunk) { - if (p_chunk) { - m_referenceCount++; - } + m_referenceCount++; } // FUNCTION: LEGO1 0x100c6ef0 diff --git a/assets/hdmusic/Act3Music_HD.wav b/assets/hdmusic/Act3Music_HD.wav new file mode 100644 index 00000000..0480014b --- /dev/null +++ b/assets/hdmusic/Act3Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c3e3080ea1af626bc202d091ead654ef38ac8e27779c2908dca390f3106ced3 +size 14201934 diff --git a/assets/hdmusic/BeachBlvd_Music_HD.wav b/assets/hdmusic/BeachBlvd_Music_HD.wav new file mode 100644 index 00000000..9385be34 --- /dev/null +++ b/assets/hdmusic/BeachBlvd_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1143a85549ca76ec8edccc164e8c54d803d7eb9a5bddd5c0387ce102b5397f2c +size 26919040 diff --git a/assets/hdmusic/Beach_Music_HD.wav b/assets/hdmusic/Beach_Music_HD.wav new file mode 100644 index 00000000..a198fec1 --- /dev/null +++ b/assets/hdmusic/Beach_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e84733243a6d1d3044aab8032cf5d2b5769b1e3e3f0787c3b3bf18689c9f535 +size 22490016 diff --git a/assets/hdmusic/BrickHunt_HD.wav b/assets/hdmusic/BrickHunt_HD.wav new file mode 100644 index 00000000..4aaa8721 --- /dev/null +++ b/assets/hdmusic/BrickHunt_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfbd44517186635ad270a971e9328f65890b94258c456aafda6658830147dce2 +size 33980544 diff --git a/assets/hdmusic/BrickstrChase_HD.wav b/assets/hdmusic/BrickstrChase_HD.wav new file mode 100644 index 00000000..f7222876 --- /dev/null +++ b/assets/hdmusic/BrickstrChase_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a158faeb0b651da3c0663cfa871851ad226dae02577be97d049bc4f84e4d9251 +size 14614784 diff --git a/assets/hdmusic/Cave_Music_HD.wav b/assets/hdmusic/Cave_Music_HD.wav new file mode 100644 index 00000000..b02ed9da --- /dev/null +++ b/assets/hdmusic/Cave_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ae808110329a64ae06a96b08210bea8938f92ac0006b850a62b941541339359 +size 12214400 diff --git a/assets/hdmusic/CentralRoads_Music_HD.wav b/assets/hdmusic/CentralRoads_Music_HD.wav new file mode 100644 index 00000000..43c0e5ee --- /dev/null +++ b/assets/hdmusic/CentralRoads_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:39bd028528e1c9e2c9cea0b090c3c54f78b1889135a863c09080b90d3b13b3d2 +size 34113102 diff --git a/assets/hdmusic/Hospital_Music_HD.wav b/assets/hdmusic/Hospital_Music_HD.wav new file mode 100644 index 00000000..718d46c7 --- /dev/null +++ b/assets/hdmusic/Hospital_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f253c05dcf77d5b7d5109f2e95ac5af85ee7a699a8689d8c276951617bbfd0e6 +size 40701386 diff --git a/assets/hdmusic/InfoCenter_3rd_Floor_Music_HD.wav b/assets/hdmusic/InfoCenter_3rd_Floor_Music_HD.wav new file mode 100644 index 00000000..0320b078 --- /dev/null +++ b/assets/hdmusic/InfoCenter_3rd_Floor_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de7bf7bc1512aa6430b35e94c952c9b815b13ceded533f458ff9806a3915f948 +size 20201550 diff --git a/assets/hdmusic/InformationCenter_Music_HD.wav b/assets/hdmusic/InformationCenter_Music_HD.wav new file mode 100644 index 00000000..f896b392 --- /dev/null +++ b/assets/hdmusic/InformationCenter_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c1ade0044cef699f526898cd44c34f426f3f057312f8d457ebc4b454096bdb0 +size 27256398 diff --git a/assets/hdmusic/JBMusic1_HD.wav b/assets/hdmusic/JBMusic1_HD.wav new file mode 100644 index 00000000..a07f7354 --- /dev/null +++ b/assets/hdmusic/JBMusic1_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c11ed75397b677a069cd9302bec3e1178067b030c42a47ef6853cb9212ff716 +size 22200448 diff --git a/assets/hdmusic/JBMusic2_HD.wav b/assets/hdmusic/JBMusic2_HD.wav new file mode 100644 index 00000000..cdb64575 --- /dev/null +++ b/assets/hdmusic/JBMusic2_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8964ca0fb9698abab90398bfa62f04e52830b647af8b4014a129edd9fbfdae47 +size 28735604 diff --git a/assets/hdmusic/JBMusic3_HD.wav b/assets/hdmusic/JBMusic3_HD.wav new file mode 100644 index 00000000..cf22253b --- /dev/null +++ b/assets/hdmusic/JBMusic3_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98b02da1c3900a9e906b88692ecb2087f2091b4af7303565eaeff242cdf7c313 +size 21653144 diff --git a/assets/hdmusic/JBMusic4_HD.wav b/assets/hdmusic/JBMusic4_HD.wav new file mode 100644 index 00000000..acb73671 --- /dev/null +++ b/assets/hdmusic/JBMusic4_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13c787e61647e22a1f4535d2491cbad5638c835142c075ebe6a39964ff769466 +size 24699000 diff --git a/assets/hdmusic/JBMusic5_HD.wav b/assets/hdmusic/JBMusic5_HD.wav new file mode 100644 index 00000000..69ca50f6 --- /dev/null +++ b/assets/hdmusic/JBMusic5_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b744d204a6bcb3c659f490fc9caa6a2bff96e6c91ab7ba8770508b208f0fffd +size 12828932 diff --git a/assets/hdmusic/JBMusic6_HD.wav b/assets/hdmusic/JBMusic6_HD.wav new file mode 100644 index 00000000..a5235fce --- /dev/null +++ b/assets/hdmusic/JBMusic6_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8779860718e07a6093c979d77ec1723dcef93762bc7759e960e70ee730b66fb3 +size 10059690 diff --git a/assets/hdmusic/Jail_Music_HD.wav b/assets/hdmusic/Jail_Music_HD.wav new file mode 100644 index 00000000..a9f323c3 --- /dev/null +++ b/assets/hdmusic/Jail_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b39644468a172178738aa8854b514580f2b098a810dba127b2f5ae25534fe87e +size 12140672 diff --git a/assets/hdmusic/JetskiRace_Music_HD.wav b/assets/hdmusic/JetskiRace_Music_HD.wav new file mode 100644 index 00000000..eac69605 --- /dev/null +++ b/assets/hdmusic/JetskiRace_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82ccad0d9877ccf915fc050d9b245785cf6bcb2406a6fcb9f6bdb9a75a3fc8c4 +size 11367404 diff --git a/assets/hdmusic/Park_Music_HD.wav b/assets/hdmusic/Park_Music_HD.wav new file mode 100644 index 00000000..83c4f4da --- /dev/null +++ b/assets/hdmusic/Park_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b645676140604d078be9cb1dc7d5f0ee17700878ff50a5280b426616898be64 +size 16089216 diff --git a/assets/hdmusic/PoliceStation_Music_HD.wav b/assets/hdmusic/PoliceStation_Music_HD.wav new file mode 100644 index 00000000..6a4dd58c --- /dev/null +++ b/assets/hdmusic/PoliceStation_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59c5ad73804bf813a852a5486e6ab13739a16542d06c736c28c164694ed424fd +size 10070544 diff --git a/assets/hdmusic/RaceTrackRoad_Music_HD.wav b/assets/hdmusic/RaceTrackRoad_Music_HD.wav new file mode 100644 index 00000000..813d33b3 --- /dev/null +++ b/assets/hdmusic/RaceTrackRoad_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:002c09354642fd8757ea999d62badf0b346f5ab1d4fb6b6ce31e471b0a6bdb1a +size 33341560 diff --git a/assets/hdmusic/ResidentalArea_Music_HD.wav b/assets/hdmusic/ResidentalArea_Music_HD.wav new file mode 100644 index 00000000..923ee0dc --- /dev/null +++ b/assets/hdmusic/ResidentalArea_Music_HD.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f24f80d0979e1794d42000b55e95f0674be196af94c30abfa2a8d971e106a309 +size 15794304 diff --git a/assets/main.cpp b/assets/main.cpp index e1189012..d239e013 100644 --- a/assets/main.cpp +++ b/assets/main.cpp @@ -16,30 +16,182 @@ si::MemoryBuffer mxHd; void CreateWidescreen() { + std::string result = out + "/widescreen.si"; + struct AssetView { + std::string name; + std::string extra; + }; + const AssetView widescreenBitmaps[] = { + {"GaraDoor_Background_Wide", + "World:current, StartWith:\\Lego\\Scripts\\Isle\\Isle;1160, RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1161"} + }; + si::Interleaf si; - std::string file = out + "/widescreen.si"; mxHd.seek(0, si::MemoryBuffer::SeekStart); si.Read(&mxHd); - si::Object GaraDoor_Wide; - const char extra[] = - "World:current, StartWith:\\Lego\\Scripts\\Isle\\Isle;1160, RemoveWith:\\Lego\\Scripts\\Isle\\Isle;1161"; - GaraDoor_Wide.type_ = si::MxOb::Bitmap; - GaraDoor_Wide.flags_ = MxDSAction::c_enabled | MxDSAction::c_bit4; - GaraDoor_Wide.duration_ = -1; - GaraDoor_Wide.loops_ = 1; - GaraDoor_Wide.extra_ = si::bytearray(extra, sizeof(extra)); - GaraDoor_Wide.presenter_ = "MxStillPresenter"; - GaraDoor_Wide.name_ = "GaraDoor_Wide"; - GaraDoor_Wide.filetype_ = si::MxOb::STL; - GaraDoor_Wide.location_.x = -240.0; - GaraDoor_Wide.location_.z = -1.0; - GaraDoor_Wide.up_.y = 1.0; - GaraDoor_Wide.ReplaceWithFile("widescreen/garadoor.bmp"); - si.AppendChild(&GaraDoor_Wide); - depfile << file << ": " << (std::filesystem::current_path() / "widescreen/garadoor.bmp").string() << std::endl; + int i = 0; + for (const AssetView& asset : widescreenBitmaps) { + si::Object* object = new si::Object; + std::string file = std::string("widescreen/") + asset.name + ".bmp"; - si.Write(file.c_str()); + object->id_ = i; + object->type_ = si::MxOb::Bitmap; + object->flags_ = MxDSAction::c_enabled | MxDSAction::c_bit4; + object->duration_ = -1; + object->loops_ = 1; + object->extra_ = si::bytearray(asset.extra.c_str(), asset.extra.length() + 1); + object->presenter_ = "MxStillPresenter"; + object->name_ = asset.name; + object->filetype_ = si::MxOb::STL; + object->location_ = si::Vector3(-240.0, 0.0, -1.0); + object->direction_ = si::Vector3(0, 0, 0); + object->up_ = si::Vector3(0, 1.0, 0); + + if (!object->ReplaceWithFile(file.c_str())) { + abort(); + } + + si.AppendChild(object); + depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl; + i++; + } + + si.Write(result.c_str()); +} + +void CreateHDMusic() +{ + std::string result = out + "/hdmusic.si"; + struct AssetView { + std::string name; + std::string extra; + uint32_t duration; + uint32_t loops; + uint32_t flags; + }; + const AssetView wavAudio[] = { + {"BrickstrChase_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;3", + 82850, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"BrickHunt_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;4", + 192630, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"ResidentalArea_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;5", + 89540, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"BeachBlvd_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;6", + 152600, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"Cave_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;7", + 69240, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"CentralRoads_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;8", + 193380, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"Jail_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;9", + 68820, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"Hospital_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;10", + 211990, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"InformationCenter_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;11", + 154510, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"PoliceStation_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;12", + 57090, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"Park_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;13", + 91210, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"RaceTrackRoad_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;16", + 189000, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"Beach_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;17", + 127490, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"JetskiRace_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;19", + 64440, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"Act3Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;20", + 80510, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3}, + {"JBMusic1_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;55", 125850, 1, MxDSAction::c_enabled}, + {"JBMusic2_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;56", 162900, 1, MxDSAction::c_enabled}, + {"JBMusic3_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;57", 122750, 1, MxDSAction::c_enabled}, + {"JBMusic4_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;58", 140000, 1, MxDSAction::c_enabled}, + {"JBMusic5_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;59", 72720, 1, MxDSAction::c_enabled}, + {"JBMusic6_HD", "Replace:\\Lego\\Scripts\\Isle\\Jukebox;60", 57030, 1, MxDSAction::c_enabled}, + {"InfoCenter_3rd_Floor_Music_HD", + "Replace:\\Lego\\Scripts\\Isle\\Jukebox;61", + 114520, + 10000, + MxDSAction::c_enabled | MxDSAction::c_bit3} + }; + + si::Interleaf si; + mxHd.seek(0, si::MemoryBuffer::SeekStart); + si.Read(&mxHd); + + int i = 0; + for (const AssetView& asset : wavAudio) { + si::Object* object = new si::Object; + std::string file = std::string("hdmusic/") + asset.name + ".wav"; + + object->id_ = i; + object->type_ = si::MxOb::Sound; + object->flags_ = asset.flags; + object->duration_ = asset.duration * asset.loops; + object->loops_ = asset.loops; + object->extra_ = si::bytearray(asset.extra.c_str(), asset.extra.length() + 1); + object->presenter_ = "MxWavePresenter"; + object->name_ = asset.name; + object->filetype_ = si::MxOb::WAV; + object->location_ = si::Vector3(0, 0, 0); + object->direction_ = si::Vector3(0, 0, 1); + object->up_ = si::Vector3(0, 1, 0); + object->volume_ = 79; + + if (!object->ReplaceWithFile(file.c_str())) { + abort(); + } + + si.AppendChild(object); + depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl; + i++; + } + + si.Write(result.c_str()); } int main(int argc, char* argv[]) @@ -54,5 +206,6 @@ int main(int argc, char* argv[]) mxHd.WriteU32(bufferCount); CreateWidescreen(); + CreateHDMusic(); return 0; } diff --git a/assets/widescreen/GaraDoor_Background_Wide.bmp b/assets/widescreen/GaraDoor_Background_Wide.bmp new file mode 100644 index 00000000..8f557e6b --- /dev/null +++ b/assets/widescreen/GaraDoor_Background_Wide.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d245fde39c95f788a9406f2c4bbc94680d566cef925f03883a0824e3485c23d +size 538678 diff --git a/assets/widescreen/garadoor.bmp b/assets/widescreen/garadoor.bmp deleted file mode 100755 index 8fc4c08c..00000000 Binary files a/assets/widescreen/garadoor.bmp and /dev/null differ diff --git a/extensions/include/extensions/siloader.h b/extensions/include/extensions/siloader.h index 3fc7ab0a..ab3e69bc 100644 --- a/extensions/include/extensions/siloader.h +++ b/extensions/include/extensions/siloader.h @@ -7,6 +7,11 @@ #include #include +namespace si +{ +class Core; +} + namespace Extensions { class SiLoader { @@ -15,8 +20,10 @@ class SiLoader { static void Initialize(); static bool Load(); - static bool StartWith(StreamObject p_object); - static bool RemoveWith(StreamObject p_object, LegoWorld* world); + static std::optional HandleFind(StreamObject p_object, LegoWorld* world); + static std::optional HandleStart(MxDSAction& p_action); + static std::optional HandleRemove(StreamObject p_object, LegoWorld* world); + static std::optional HandleDelete(MxDSAction& p_action); static std::map options; static std::vector files; @@ -25,17 +32,23 @@ class SiLoader { private: static std::vector> startWith; static std::vector> removeWith; + static std::vector> replace; static bool LoadFile(const char* p_file); + static void ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom = MxAtomId()); }; #ifdef EXTENSIONS constexpr auto Load = &SiLoader::Load; -constexpr auto StartWith = &SiLoader::StartWith; -constexpr auto RemoveWith = &SiLoader::RemoveWith; +constexpr auto HandleFind = &SiLoader::HandleFind; +constexpr auto HandleStart = &SiLoader::HandleStart; +constexpr auto HandleRemove = &SiLoader::HandleRemove; +constexpr auto HandleDelete = &SiLoader::HandleDelete; #else constexpr decltype(&SiLoader::Load) Load = nullptr; -constexpr decltype(&SiLoader::StartWith) StartWith = nullptr; -constexpr decltype(&SiLoader::RemoveWith) RemoveWith = nullptr; +constexpr decltype(&SiLoader::HandleFind) HandleFind = nullptr; +constexpr decltype(&SiLoader::HandleStart) HandleStart = nullptr; +constexpr decltype(&SiLoader::HandleRemove) HandleRemove = nullptr; +constexpr decltype(&SiLoader::HandleDelete) HandleDelete = nullptr; #endif }; // namespace Extensions diff --git a/extensions/src/siloader.cpp b/extensions/src/siloader.cpp index bfbddd88..ab23b4a7 100644 --- a/extensions/src/siloader.cpp +++ b/extensions/src/siloader.cpp @@ -13,6 +13,7 @@ std::map SiLoader::options; std::vector SiLoader::files; std::vector> SiLoader::startWith; std::vector> SiLoader::removeWith; +std::vector> SiLoader::replace; bool SiLoader::enabled = false; void SiLoader::Initialize() @@ -36,21 +37,52 @@ bool SiLoader::Load() return true; } -bool SiLoader::StartWith(StreamObject p_object) +std::optional SiLoader::HandleFind(StreamObject p_object, LegoWorld* world) { - for (const auto& key : startWith) { + for (const auto& key : replace) { if (key.first == p_object) { + return world->Find(key.second.first, key.second.second); + } + } + + return std::nullopt; +} + +std::optional SiLoader::HandleStart(MxDSAction& p_action) +{ + StreamObject object{p_action.GetAtomId(), p_action.GetObjectId()}; + + for (const auto& key : startWith) { + if (key.first == object) { MxDSAction action; action.SetAtomId(key.second.first); action.SetObjectId(key.second.second); + action.SetUnknown24(p_action.GetUnknown24()); + action.SetNotificationObject(p_action.GetNotificationObject()); + action.SetOrigin(p_action.GetOrigin()); Start(&action); } } - return true; + for (const auto& key : replace) { + if (key.first == object) { + MxDSAction action; + action.SetAtomId(key.second.first); + action.SetObjectId(key.second.second); + action.SetUnknown24(p_action.GetUnknown24()); + action.SetNotificationObject(p_action.GetNotificationObject()); + action.SetOrigin(p_action.GetOrigin()); + + MxResult result = Start(&action); + p_action.SetUnknown24(action.GetUnknown24()); + return result; + } + } + + return std::nullopt; } -bool SiLoader::RemoveWith(StreamObject p_object, LegoWorld* world) +std::optional SiLoader::HandleRemove(StreamObject p_object, LegoWorld* world) { for (const auto& key : removeWith) { if (key.first == p_object) { @@ -58,7 +90,47 @@ bool SiLoader::RemoveWith(StreamObject p_object, LegoWorld* world) } } - return true; + for (const auto& key : replace) { + if (key.first == p_object) { + return RemoveFromWorld(key.second.first, key.second.second, world->GetAtomId(), world->GetEntityId()); + } + } + + return std::nullopt; +} + +std::optional SiLoader::HandleDelete(MxDSAction& p_action) +{ + StreamObject object{p_action.GetAtomId(), p_action.GetObjectId()}; + + for (const auto& key : removeWith) { + if (key.first == object) { + MxDSAction action; + action.SetAtomId(key.second.first); + action.SetObjectId(key.second.second); + action.SetUnknown24(p_action.GetUnknown24()); + action.SetNotificationObject(p_action.GetNotificationObject()); + action.SetOrigin(p_action.GetOrigin()); + DeleteObject(action); + } + } + + for (const auto& key : replace) { + if (key.first == object) { + MxDSAction action; + action.SetAtomId(key.second.first); + action.SetObjectId(key.second.second); + action.SetUnknown24(p_action.GetUnknown24()); + action.SetNotificationObject(p_action.GetNotificationObject()); + action.SetOrigin(p_action.GetOrigin()); + + DeleteObject(action); + p_action.SetUnknown24(action.GetUnknown24()); + return TRUE; + } + } + + return std::nullopt; } bool SiLoader::LoadFile(const char* p_file) @@ -68,10 +140,11 @@ bool SiLoader::LoadFile(const char* p_file) MxString path = MxString(MxOmni::GetHD()) + p_file; path.MapPathToFilesystem(); - if (si.Read(path.GetData()) != si::Interleaf::ERROR_SUCCESS) { + if (si.Read(path.GetData(), si::Interleaf::ObjectsOnly | si::Interleaf::NoInfo) != si::Interleaf::ERROR_SUCCESS) { path = MxString(MxOmni::GetCD()) + p_file; path.MapPathToFilesystem(); - if (si.Read(path.GetData()) != si::Interleaf::ERROR_SUCCESS) { + if (si.Read(path.GetData(), si::Interleaf::ObjectsOnly | si::Interleaf::NoInfo) != + si::Interleaf::ERROR_SUCCESS) { SDL_Log("Could not parse SI file %s", p_file); return false; } @@ -82,7 +155,15 @@ bool SiLoader::LoadFile(const char* p_file) return false; } - for (si::Core* child : si.GetChildren()) { + ParseDirectives(controller->GetAtom(), &si); + return true; +} + +void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom) +{ + for (si::Core* child : p_core->GetChildren()) { + MxAtomId replacedAtom = p_parentReplacedAtom; + if (si::Object* object = dynamic_cast(child)) { if (object->type() != si::MxOb::Null) { std::string extra(object->extra_.data(), object->extra_.size()); @@ -94,7 +175,7 @@ bool SiLoader::LoadFile(const char* p_file) if (SDL_sscanf(directive, "StartWith:%255[^;];%d", atom, &id) == 2) { startWith.emplace_back( StreamObject{MxAtomId{atom, e_lowerCase2}, id}, - StreamObject{controller->GetAtom(), object->id_} + StreamObject{p_atom, object->id_} ); } } @@ -103,13 +184,31 @@ bool SiLoader::LoadFile(const char* p_file) if (SDL_sscanf(directive, "RemoveWith:%255[^;];%d", atom, &id) == 2) { removeWith.emplace_back( StreamObject{MxAtomId{atom, e_lowerCase2}, id}, - StreamObject{controller->GetAtom(), object->id_} + StreamObject{p_atom, object->id_} ); } } + + if (p_parentReplacedAtom.GetInternal()) { + replace.emplace_back( + StreamObject{p_parentReplacedAtom, object->id_}, + StreamObject{p_atom, object->id_} + ); + } + else { + if ((directive = SDL_strstr(extra.c_str(), "Replace:"))) { + if (SDL_sscanf(directive, "Replace:%255[^;];%d", atom, &id) == 2) { + replace.emplace_back( + StreamObject{MxAtomId{atom, e_lowerCase2}, id}, + StreamObject{p_atom, object->id_} + ); + replacedAtom = replace.back().first.first; + } + } + } } } - } - return true; + ParseDirectives(p_atom, child, replacedAtom); + } }