From e709400dc627924b14ad41692a346220e1523dec Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Wed, 20 Aug 2025 10:21:28 -0700 Subject: [PATCH] Add new directives to SiLoader, allow specifying directives in `.ini` (#688) * WIP Prepend directive * WIP * Add new directives --- .../omni/src/notify/mxnotificationmanager.cpp | 13 ++ LEGO1/omni/src/stream/mxstreamer.cpp | 2 +- extensions/include/extensions/siloader.h | 12 +- extensions/src/siloader.cpp | 220 +++++++++++++++--- 4 files changed, 210 insertions(+), 37 deletions(-) diff --git a/LEGO1/omni/src/notify/mxnotificationmanager.cpp b/LEGO1/omni/src/notify/mxnotificationmanager.cpp index 064f38f3..373c8aef 100644 --- a/LEGO1/omni/src/notify/mxnotificationmanager.cpp +++ b/LEGO1/omni/src/notify/mxnotificationmanager.cpp @@ -2,6 +2,7 @@ #include "compat.h" #include "decomp.h" +#include "extensions/siloader.h" #include "mxautolock.h" #include "mxmisc.h" #include "mxnotificationparam.h" @@ -12,6 +13,8 @@ DECOMP_SIZE_ASSERT(MxNotification, 0x08); DECOMP_SIZE_ASSERT(MxNotificationManager, 0x40); +using namespace Extensions; + // FUNCTION: LEGO1 0x100ac220 MxNotification::MxNotification(MxCore* p_target, const MxNotificationParam& p_param) { @@ -105,6 +108,11 @@ MxResult MxNotificationManager::Tickle() while (m_sendList->size() != 0) { MxNotification* notif = m_sendList->front(); m_sendList->pop_front(); + + if (notif->GetParam()->GetNotification() == c_notificationEndAction) { + Extension::Call(HandleEndAction, (MxEndActionNotificationParam&) *notif->GetParam()); + } + notif->GetTarget()->Notify(*notif->GetParam()); delete notif; } @@ -159,6 +167,11 @@ void MxNotificationManager::FlushPending(MxCore* p_listener) while (pending.size() != 0) { notif = pending.front(); pending.pop_front(); + + if (notif->GetParam()->GetNotification() == c_notificationEndAction) { + Extension::Call(HandleEndAction, (MxEndActionNotificationParam&) *notif->GetParam()); + } + notif->GetTarget()->Notify(*notif->GetParam()); delete notif; } diff --git a/LEGO1/omni/src/stream/mxstreamer.cpp b/LEGO1/omni/src/stream/mxstreamer.cpp index bb5f1cb5..8693ba6e 100644 --- a/LEGO1/omni/src/stream/mxstreamer.cpp +++ b/LEGO1/omni/src/stream/mxstreamer.cpp @@ -66,7 +66,7 @@ MxStreamController* MxStreamer::Open(const char* p_name, MxU16 p_lookupType) MxStreamController* stream = NULL; - if (GetOpenStream(p_name)) { + if ((stream = GetOpenStream(p_name))) { goto done; } diff --git a/extensions/include/extensions/siloader.h b/extensions/include/extensions/siloader.h index ab3e69bc..3f50d08a 100644 --- a/extensions/include/extensions/siloader.h +++ b/extensions/include/extensions/siloader.h @@ -2,6 +2,7 @@ #include "extensions/extensions.h" #include "legoworld.h" +#include "mxactionnotificationparam.h" #include "mxatom.h" #include @@ -24,18 +25,25 @@ class SiLoader { static std::optional HandleStart(MxDSAction& p_action); static std::optional HandleRemove(StreamObject p_object, LegoWorld* world); static std::optional HandleDelete(MxDSAction& p_action); + static MxBool HandleEndAction(MxEndActionNotificationParam& p_param); static std::map options; static std::vector files; + static std::vector directives; static bool enabled; private: static std::vector> startWith; static std::vector> removeWith; static std::vector> replace; + static std::vector> prepend; + static std::vector fullScreenMovie; + static std::vector disable3d; static bool LoadFile(const char* p_file); - static void ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom = MxAtomId()); + static bool LoadDirective(const char* p_directive); + static MxStreamController* OpenStream(const char* p_file); + static void ParseExtra(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom = MxAtomId()); }; #ifdef EXTENSIONS @@ -44,11 +52,13 @@ constexpr auto HandleFind = &SiLoader::HandleFind; constexpr auto HandleStart = &SiLoader::HandleStart; constexpr auto HandleRemove = &SiLoader::HandleRemove; constexpr auto HandleDelete = &SiLoader::HandleDelete; +constexpr auto HandleEndAction = &SiLoader::HandleEndAction; #else constexpr decltype(&SiLoader::Load) Load = 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; +constexpr decltype(&SiLoader::HandleEndAction) HandleEndAction = nullptr; #endif }; // namespace Extensions diff --git a/extensions/src/siloader.cpp b/extensions/src/siloader.cpp index ab23b4a7..3acb667e 100644 --- a/extensions/src/siloader.cpp +++ b/extensions/src/siloader.cpp @@ -9,11 +9,17 @@ using namespace Extensions; +const char prependedMarker[] = ";;prepended;;"; + std::map SiLoader::options; std::vector SiLoader::files; +std::vector SiLoader::directives; std::vector> SiLoader::startWith; std::vector> SiLoader::removeWith; std::vector> SiLoader::replace; +std::vector> SiLoader::prepend; +std::vector SiLoader::fullScreenMovie; +std::vector SiLoader::disable3d; bool SiLoader::enabled = false; void SiLoader::Initialize() @@ -21,11 +27,19 @@ void SiLoader::Initialize() char* files = SDL_strdup(options["si loader:files"].c_str()); char* saveptr; - for (char* file = SDL_strtok_r(files, ",\n", &saveptr); file; file = SDL_strtok_r(NULL, ",\n", &saveptr)) { + for (char* file = SDL_strtok_r(files, ",\n\r ", &saveptr); file; file = SDL_strtok_r(NULL, ",\n\r ", &saveptr)) { SiLoader::files.emplace_back(file); } + char* directives = SDL_strdup(options["si loader:directives"].c_str()); + + for (char* directive = SDL_strtok_r(directives, ",\n\r ", &saveptr); directive; + directive = SDL_strtok_r(NULL, ",\n\r ", &saveptr)) { + SiLoader::directives.emplace_back(directive); + } + SDL_free(files); + SDL_free(directives); } bool SiLoader::Load() @@ -34,6 +48,10 @@ bool SiLoader::Load() LoadFile(file.c_str()); } + for (const auto& directive : directives) { + LoadDirective(directive.c_str()); + } + return true; } @@ -51,34 +69,62 @@ std::optional SiLoader::HandleFind(StreamObject p_object, LegoWorld* wo std::optional SiLoader::HandleStart(MxDSAction& p_action) { StreamObject object{p_action.GetAtomId(), p_action.GetObjectId()}; + auto start = [](const StreamObject& p_object, MxDSAction& p_in, MxDSAction& p_out) -> MxResult { + if (!OpenStream(p_object.first.GetInternal())) { + return FAILURE; + } + + p_out.SetAtomId(p_object.first); + p_out.SetObjectId(p_object.second); + p_out.SetUnknown24(p_in.GetUnknown24()); + p_out.SetNotificationObject(p_in.GetNotificationObject()); + p_out.SetOrigin(p_in.GetOrigin()); + return Start(&p_out); + }; 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); + start(key.second, p_action, 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()); + MxResult result = start(key.second, p_action, action); + + if (result == SUCCESS) { + p_action.SetUnknown24(action.GetUnknown24()); + } - MxResult result = Start(&action); - p_action.SetUnknown24(action.GetUnknown24()); return result; } } + if (p_action.GetExtraLength() == 0 || !SDL_strstr(p_action.GetExtraData(), prependedMarker)) { + for (const auto& key : prepend) { + if (key.first == object) { + MxDSAction action; + MxResult result = start(key.second, p_action, action); + + if (result == SUCCESS) { + p_action.SetUnknown24(action.GetUnknown24()); + } + + return result; + } + } + } + + if (std::find(fullScreenMovie.begin(), fullScreenMovie.end(), object) != fullScreenMovie.end()) { + VideoManager()->EnableFullScreenMovie(TRUE); + } + + if (std::find(disable3d.begin(), disable3d.end(), object) != disable3d.end()) { + VideoManager()->FUN_1007c520(); + } + return std::nullopt; } @@ -102,29 +148,26 @@ std::optional SiLoader::HandleRemove(StreamObject p_object, LegoWorld* w std::optional SiLoader::HandleDelete(MxDSAction& p_action) { StreamObject object{p_action.GetAtomId(), p_action.GetObjectId()}; + auto deleteObject = [](const StreamObject& p_object, MxDSAction& p_in, MxDSAction& p_out) { + p_out.SetAtomId(p_object.first); + p_out.SetObjectId(p_object.second); + p_out.SetUnknown24(p_in.GetUnknown24()); + p_out.SetNotificationObject(p_in.GetNotificationObject()); + p_out.SetOrigin(p_in.GetOrigin()); + DeleteObject(p_out); + }; 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); + deleteObject(key.second, p_action, 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); + deleteObject(key.second, p_action, action); p_action.SetUnknown24(action.GetUnknown24()); return TRUE; } @@ -133,6 +176,39 @@ std::optional SiLoader::HandleDelete(MxDSAction& p_action) return std::nullopt; } +MxBool SiLoader::HandleEndAction(MxEndActionNotificationParam& p_param) +{ + StreamObject object{p_param.GetAction()->GetAtomId(), p_param.GetAction()->GetObjectId()}; + auto start = [](const StreamObject& p_object, MxDSAction& p_in, MxDSAction& p_out) -> MxResult { + if (!OpenStream(p_object.first.GetInternal())) { + return FAILURE; + } + + p_out.SetAtomId(p_object.first); + p_out.SetObjectId(p_object.second); + p_out.SetUnknown24(-1); + p_out.SetNotificationObject(p_in.GetNotificationObject()); + p_out.SetOrigin(p_in.GetOrigin()); + p_out.AppendExtra(sizeof(prependedMarker), prependedMarker); + return Start(&p_out); + }; + + if (std::find(fullScreenMovie.begin(), fullScreenMovie.end(), object) != fullScreenMovie.end()) { + VideoManager()->EnableFullScreenMovie(FALSE); + } + + if (!p_param.GetSender() || !p_param.GetSender()->IsA("MxCompositePresenter")) { + for (const auto& key : prepend) { + if (key.second == object) { + MxDSAction action; + start(key.first, *p_param.GetAction(), action); + } + } + } + + return TRUE; +} + bool SiLoader::LoadFile(const char* p_file) { si::Interleaf si; @@ -150,16 +226,73 @@ bool SiLoader::LoadFile(const char* p_file) } } - if (!(controller = Streamer()->Open(p_file, MxStreamer::e_diskStream))) { - SDL_Log("Could not load SI file %s", p_file); + if (!(controller = OpenStream(p_file))) { return false; } - ParseDirectives(controller->GetAtom(), &si); + ParseExtra(controller->GetAtom(), &si); return true; } -void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom) +bool SiLoader::LoadDirective(const char* p_directive) +{ + char originAtom[256], targetAtom[256]; + uint32_t originId, targetId; + + if (SDL_sscanf( + p_directive, + "StartWith:%255[^:;]%*[:;]%u%*[:;]%255[^:;]%*[:;]%u", + originAtom, + &originId, + targetAtom, + &targetId + ) == 4) { + startWith.emplace_back( + StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId}, + StreamObject{MxAtomId{targetAtom, e_lowerCase2}, targetId} + ); + } + else if (SDL_sscanf(p_directive, "RemoveWith:%255[^:;]%*[:;]%u%*[:;]%255[^:;]%*[:;]%u", originAtom, &originId, targetAtom, &targetId) == 4) { + removeWith.emplace_back( + StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId}, + StreamObject{MxAtomId{targetAtom, e_lowerCase2}, targetId} + ); + } + else if (SDL_sscanf(p_directive, "Replace:%255[^:;]%*[:;]%u%*[:;]%255[^:;]%*[:;]%u", originAtom, &originId, targetAtom, &targetId) == 4) { + replace.emplace_back( + StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId}, + StreamObject{MxAtomId{targetAtom, e_lowerCase2}, targetId} + ); + } + else if (SDL_sscanf(p_directive, "Prepend:%255[^:;]%*[:;]%u%*[:;]%255[^:;]%*[:;]%u", originAtom, &originId, targetAtom, &targetId) == 4) { + prepend.emplace_back( + StreamObject{MxAtomId{targetAtom, e_lowerCase2}, targetId}, + StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId} + ); + } + else if (SDL_sscanf(p_directive, "FullScreenMovie:%255[^:;]%*[:;]%u", originAtom, &originId) == 2) { + fullScreenMovie.emplace_back(StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId}); + } + else if (SDL_sscanf(p_directive, "Disable3d:%255[^:;]%*[:;]%u", originAtom, &originId) == 2) { + disable3d.emplace_back(StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId}); + } + + return true; +} + +MxStreamController* SiLoader::OpenStream(const char* p_file) +{ + MxStreamController* controller; + + if (!(controller = Streamer()->Open(p_file, MxStreamer::e_diskStream))) { + SDL_Log("Could not load SI file %s", p_file); + return nullptr; + } + + return controller; +} + +void SiLoader::ParseExtra(const MxAtomId& p_atom, si::Core* p_core, MxAtomId p_parentReplacedAtom) { for (si::Core* child : p_core->GetChildren()) { MxAtomId replacedAtom = p_parentReplacedAtom; @@ -172,7 +305,7 @@ void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomI uint32_t id; if ((directive = SDL_strstr(extra.c_str(), "StartWith:"))) { - if (SDL_sscanf(directive, "StartWith:%255[^;];%d", atom, &id) == 2) { + if (SDL_sscanf(directive, "StartWith:%255[^:;]%*[:;]%u", atom, &id) == 2) { startWith.emplace_back( StreamObject{MxAtomId{atom, e_lowerCase2}, id}, StreamObject{p_atom, object->id_} @@ -181,7 +314,7 @@ void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomI } if ((directive = SDL_strstr(extra.c_str(), "RemoveWith:"))) { - if (SDL_sscanf(directive, "RemoveWith:%255[^;];%d", atom, &id) == 2) { + if (SDL_sscanf(directive, "RemoveWith:%255[^:;]%*[:;]%u", atom, &id) == 2) { removeWith.emplace_back( StreamObject{MxAtomId{atom, e_lowerCase2}, id}, StreamObject{p_atom, object->id_} @@ -197,7 +330,7 @@ void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomI } else { if ((directive = SDL_strstr(extra.c_str(), "Replace:"))) { - if (SDL_sscanf(directive, "Replace:%255[^;];%d", atom, &id) == 2) { + if (SDL_sscanf(directive, "Replace:%255[^:;]%*[:;]%u", atom, &id) == 2) { replace.emplace_back( StreamObject{MxAtomId{atom, e_lowerCase2}, id}, StreamObject{p_atom, object->id_} @@ -206,9 +339,26 @@ void SiLoader::ParseDirectives(const MxAtomId& p_atom, si::Core* p_core, MxAtomI } } } + + if ((directive = SDL_strstr(extra.c_str(), "Prepend:"))) { + if (SDL_sscanf(directive, "Prepend:%255[^:;]%*[:;]%u", atom, &id) == 2) { + prepend.emplace_back( + StreamObject{p_atom, object->id_}, + StreamObject{MxAtomId{atom, e_lowerCase2}, id} + ); + } + } + + if ((directive = SDL_strstr(extra.c_str(), "FullScreenMovie"))) { + fullScreenMovie.emplace_back(StreamObject{MxAtomId{atom, e_lowerCase2}, id}); + } + + if ((directive = SDL_strstr(extra.c_str(), "Disable3d"))) { + disable3d.emplace_back(StreamObject{MxAtomId{atom, e_lowerCase2}, id}); + } } } - ParseDirectives(p_atom, child, replacedAtom); + ParseExtra(p_atom, child, replacedAtom); } }