From 92388bad74ae726c5581b31c23c2090a4b34623f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 17 Jun 2021 12:23:53 +0200 Subject: [PATCH] ksys/evt: Add ResourceFlowchart --- data/uking_functions.csv | 28 +- lib/EventFlow | 2 +- src/KingSystem/Event/CMakeLists.txt | 4 + src/KingSystem/Event/evtActorBindings.h | 1 + src/KingSystem/Event/evtEventResource.cpp | 1 + src/KingSystem/Event/evtEventResource.h | 27 ++ src/KingSystem/Event/evtResourceFlowchart.cpp | 283 ++++++++++++++++++ src/KingSystem/Event/evtResourceFlowchart.h | 51 ++++ .../Resource/Event/resEventFlowBinder.h | 5 + 9 files changed, 387 insertions(+), 15 deletions(-) create mode 100644 src/KingSystem/Event/evtEventResource.cpp create mode 100644 src/KingSystem/Event/evtEventResource.h create mode 100644 src/KingSystem/Event/evtResourceFlowchart.cpp create mode 100644 src/KingSystem/Event/evtResourceFlowchart.h diff --git a/data/uking_functions.csv b/data/uking_functions.csv index 77d72ca6..4951b2d2 100644 --- a/data/uking_functions.csv +++ b/data/uking_functions.csv @@ -75807,20 +75807,20 @@ 0x0000007100dc4cb4,evt::ActorBindings::dtor,160,_ZN4ksys3evt13ActorBindingsD1Ev 0x0000007100dc4d54,evt::ActorBindings::dtorDelete,168,_ZN4ksys3evt13ActorBindingsD0Ev 0x0000007100dc4dfc,evt::ActorBindings::bindActor,448,_ZN4ksys3evt13ActorBindings9bindActorEPKN4evfl8ResActorEPN4sead4HeapE -0x0000007100dc4fbc,ResourceFlowchart::ctor,216, -0x0000007100dc5094,sub_7100DC5094,312, -0x0000007100dc51cc,sub_7100DC51CC,36, -0x0000007100dc51f0,ResourceFlowchart::loadBfevfl,688, -0x0000007100dc54a0,ResourceFlowchart::finishLoad,348, -0x0000007100dc55fc,ResourceFlowchart::initFlowchartContext,644, -0x0000007100dc5880,ResourceFlowchart::buildFlowchart,1488, -0x0000007100dc5e50,evt::bindActorActions,408, -0x0000007100dc5fe8,nullsub_3848,4, -0x0000007100dc5fec,evt::bindActorQueries,408, -0x0000007100dc6184,sub_7100DC6184,140, -0x0000007100dc6210,j__ZdlPv_889,4, -0x0000007100dc6214,j__ZdlPv_890,4, -0x0000007100dc6218,j__ZdlPv_891,4, +0x0000007100dc4fbc,ResourceFlowchart::ctor,216,_ZN4ksys3evt17ResourceFlowchartC1Ev +0x0000007100dc5094,sub_7100DC5094,312,_ZN4ksys3evt17ResourceFlowchartD1Ev +0x0000007100dc51cc,sub_7100DC51CC,36,_ZN4ksys3evt17ResourceFlowchartD0Ev +0x0000007100dc51f0,ResourceFlowchart::loadBfevfl,688,_ZN4ksys3evt17ResourceFlowchart13loadEventFlowEPN4sead4HeapEPNS_3res6HandleE +0x0000007100dc54a0,ResourceFlowchart::finishLoad,348,_ZN4ksys3evt17ResourceFlowchart10finishLoadEv +0x0000007100dc55fc,ResourceFlowchart::initFlowchartContext,644,_ZN4ksys3evt17ResourceFlowchart13setUpBindingsEPNS0_13ActorBindingsEPN4sead4HeapE +0x0000007100dc5880,ResourceFlowchart::buildFlowchart,1488,_ZN4ksys3evt17ResourceFlowchart14buildFlowchartEPN4evfl16FlowchartContextEPN4sead4HeapE +0x0000007100dc5e50,evt::bindActorActions,408,_ZN4ksys3evt16bindActorActionsERN4evfl16FlowchartContextENS_3res21EventFlowActionBinderE +0x0000007100dc5fe8,nullsub_3848,4,_ZN4ksys3res15EventFlowBinderD2Ev +0x0000007100dc5fec,evt::bindActorQueries,408,_ZN4ksys3evt16bindActorQueriesERN4evfl16FlowchartContextENS_3res20EventFlowQueryBinderE +0x0000007100dc6184,sub_7100DC6184,140,_ZNK4ksys3evt17ResourceFlowchart16isResourceLoadedERKN4sead14SafeStringBaseIcEE +0x0000007100dc6210,j__ZdlPv_889,4,_ZN4ksys3res20EventFlowActorBinderD0Ev +0x0000007100dc6214,j__ZdlPv_890,4,_ZN4ksys3res21EventFlowActionBinderD0Ev +0x0000007100dc6218,j__ZdlPv_891,4,_ZN4ksys3res20EventFlowQueryBinderD0Ev 0x0000007100dc621c,ResourceTimeline::ctor,136, 0x0000007100dc62a4,sub_7100DC62A4,180, 0x0000007100dc6358,sub_7100DC6358,176, diff --git a/lib/EventFlow b/lib/EventFlow index 18dcfd78..1bb352ff 160000 --- a/lib/EventFlow +++ b/lib/EventFlow @@ -1 +1 @@ -Subproject commit 18dcfd780faf1d5af5b089b92db5cfd329a306d6 +Subproject commit 1bb352ff3e27137c6345f2f9ca71994da3a7abfc diff --git a/src/KingSystem/Event/CMakeLists.txt b/src/KingSystem/Event/CMakeLists.txt index 776f7b81..96944ad4 100644 --- a/src/KingSystem/Event/CMakeLists.txt +++ b/src/KingSystem/Event/CMakeLists.txt @@ -9,6 +9,8 @@ target_sources(uking PRIVATE evtDemoInfo.h evtEvent.cpp evtEvent.h + evtEventResource.cpp + evtEventResource.h evtInfoData.cpp evtInfoData.h evtManager.cpp @@ -17,4 +19,6 @@ target_sources(uking PRIVATE evtMetadata.h evtOrderParam.cpp evtOrderParam.h + evtResourceFlowchart.cpp + evtResourceFlowchart.h ) diff --git a/src/KingSystem/Event/evtActorBindings.h b/src/KingSystem/Event/evtActorBindings.h index dcafb20d..81d13792 100644 --- a/src/KingSystem/Event/evtActorBindings.h +++ b/src/KingSystem/Event/evtActorBindings.h @@ -25,6 +25,7 @@ public: ActorBinding* bindActor(const evfl::ResActor* res_actor, sead::Heap* heap); int isInitialized() const { return mInitialized; } + int getNumBindings() const { return mBindings.size(); } private: sead::PtrArray mBindings; diff --git a/src/KingSystem/Event/evtEventResource.cpp b/src/KingSystem/Event/evtEventResource.cpp new file mode 100644 index 00000000..6611e08c --- /dev/null +++ b/src/KingSystem/Event/evtEventResource.cpp @@ -0,0 +1 @@ +#include "KingSystem/Event/evtEventResource.h" diff --git a/src/KingSystem/Event/evtEventResource.h b/src/KingSystem/Event/evtEventResource.h new file mode 100644 index 00000000..87ec3acd --- /dev/null +++ b/src/KingSystem/Event/evtEventResource.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace sead { +class Heap; + +} +namespace ksys::evt { + +// TODO +class EventResource {}; + +void* eventFlowAlloc(size_t size, size_t alignment, void* userdata); +void eventFlowFree(void* ptr, void* userdata); + +inline evfl::AllocateArg makeEvflAllocateArg(sead::Heap* heap) { + evfl::AllocateArg arg{}; + arg.alloc = eventFlowAlloc; + arg.free = eventFlowFree; + arg.alloc_userdata = heap; + arg.free_userdata = heap; + return arg; +} + +} // namespace ksys::evt diff --git a/src/KingSystem/Event/evtResourceFlowchart.cpp b/src/KingSystem/Event/evtResourceFlowchart.cpp new file mode 100644 index 00000000..e48cd504 --- /dev/null +++ b/src/KingSystem/Event/evtResourceFlowchart.cpp @@ -0,0 +1,283 @@ +#include "KingSystem/Event/evtResourceFlowchart.h" +#include +#include +#include +#include "KingSystem/Event/evtActorBindings.h" +#include "KingSystem/Event/evtEventResource.h" +#include "KingSystem/Event/evtInfoData.h" +#include "KingSystem/Resource/Event/resEventFlowBinder.h" +#include "KingSystem/Resource/Event/resResourceEventFlow.h" +#include "KingSystem/Resource/resLoadRequest.h" +#include "KingSystem/Resource/resResourceMgrTask.h" +#include "KingSystem/Utils/Byaml/Byaml.h" +#include "KingSystem/Utils/HeapUtil.h" + +namespace ksys::evt { + +ResourceFlowchart::ResourceFlowchart() { + mLoadFailed = false; +} + +ResourceFlowchart::~ResourceFlowchart() { + for (int i = 0; i < mFlowcharts.size(); ++i) + mFlowcharts[i].handle.requestUnload2(); + + mFlowcharts.freeBuffer(); + + if (mMissingFlowcharts.isBufferReady()) { + for (int i = 0; i < mFlowcharts.size(); ++i) + mMissingFlowcharts[i].handle.requestUnload2(); + + mMissingFlowcharts.freeBuffer(); + } +} + +void ResourceFlowchart::loadEventFlow(sead::Heap* heap, res::Handle* pack_handle) { + al::ByamlIter event_info; + al::ByamlIter subfiles; + int num_flowcharts = 1; + if (InfoData::instance()->getEntry(&event_info, mName, mEntryPoint) && + event_info.tryGetIterByKey(&subfiles, "subfile")) { + num_flowcharts = subfiles.getSize() + 1; + } + + mFlowcharts.allocBufferAssert(num_flowcharts, heap); + + res::LoadRequest request; + request.mRequester = "ResourceFlowchart"; + request.mPackHandle = pack_handle; + request._22 = true; + + // Load the main flowchart. + sead::FixedSafeString<128> path; + mFlowcharts[0].loaded = false; + path.format("EventFlow/%s.bfevfl", mName.cstr()); + mFlowcharts[0].handle.requestLoad(path, &request); + + // Load any subfiles. + for (int i = 1; i < num_flowcharts; ++i) { + al::ByamlIter file_entry; + subfiles.tryGetIterByIndex(&file_entry, i - 1); + const char* file_name; + file_entry.tryGetStringByKey(&file_name, "file"); + + mFlowcharts[i].loaded = false; + path.format("EventFlow/%s", file_name); + mFlowcharts[i].handle.requestLoad(path, &request); + } +} + +bool ResourceFlowchart::finishLoad() { + bool ready = true; + + for (int i = 0; i < mFlowcharts.size(); ++i) { + auto& flowchart = mFlowcharts[i]; + if (flowchart.loaded) + continue; + + if (flowchart.handle.isReadyOrNeedsParse()) + flowchart.handle.parseResource(nullptr); + + if (flowchart.handle.isSuccess()) { + auto* evfl_res = sead::DynamicCast(flowchart.handle.getResource()); + if (evfl_res) { + flowchart.res_event_flow_file = evfl_res->getRes(); + flowchart.res_flowchart = flowchart.res_event_flow_file->flowcharts.Get()->Get(); + flowchart.loaded = true; + } + } else if (flowchart.handle.checkLoadStatus()) { + flowchart.loaded = true; + mLoadFailed = true; + } + + if (!flowchart.loaded) + ready = false; + } + + return ready; +} + +long bindActorActions(evfl::FlowchartContext& context, res::EventFlowActionBinder binder) { + u8 any_ok = 0; + u8 any_failed = 0; + + for (auto it = context.GetObjs().begin(); it != context.GetObjs().end(); ++it) { + bool ok; + bool failed; + auto binder_ = binder; + + ok = false; + failed = false; + + auto& evfl_bindings = it->GetActBinder().GetBindings(); + for (auto b = evfl_bindings.begin(); b != evfl_bindings.end(); ++b) { + if (!b->IsUsed() || !b->IsInitialized()) + continue; + + const auto* actor = b->GetActor(); + for (auto a = b->GetActions().begin(); a != b->GetActions().end(); ++a) { + binder_.bind(a, a->res_action, actor, static_cast(b->GetUserData())); + (a->handler ? ok : failed) = true; + } + } + + if (ok) + any_ok = 1; + if (failed) + any_failed = 1; + } + + return (int(any_failed) << 8) | int(any_ok); +} + +long bindActorQueries(evfl::FlowchartContext& context, res::EventFlowQueryBinder binder) { + u8 any_ok = 0; + u8 any_failed = 0; + + for (auto it = context.GetObjs().begin(); it != context.GetObjs().end(); ++it) { + bool ok; + bool failed; + auto binder_ = binder; + + ok = false; + failed = false; + + auto& evfl_bindings = it->GetActBinder().GetBindings(); + for (auto b = evfl_bindings.begin(); b != evfl_bindings.end(); ++b) { + if (!b->IsUsed() || !b->IsInitialized()) + continue; + + const auto* actor = b->GetActor(); + for (auto q = b->GetQueries().begin(); q != b->GetQueries().end(); ++q) { + binder_.bind(q, q->res_query, actor, static_cast(b->GetUserData())); + (q->handler ? ok : failed) = true; + } + } + + if (ok) + any_ok = 1; + if (failed) + any_failed = 1; + } + + return (int(any_failed) << 8) | int(any_ok); +} + +bool ResourceFlowchart::setUpBindings(ActorBindings* bindings, sead::Heap* heap) { + evfl::FlowchartContext context; + if (!buildFlowchart(&context, heap)) + return false; + + // Bind actors. We do it twice: once in order to figure out how many actor bindings + // need to be allocated, and a second time to actually bind actors. + const auto bind_actors = [&] { + for (auto it = context.GetObjs().begin(); it != context.GetObjs().end(); ++it) { + bool ok; + bool failed; + res::EventFlowActorBinder binder{bindings, heap}; + + ok = false; + failed = false; + + auto& evfl_bindings = it->GetActBinder().GetBindings(); + for (auto binding = evfl_bindings.begin(); binding != evfl_bindings.end(); ++binding) { + if (binding->IsUsed() && !binding->IsInitialized()) { + binder.bind(binding, binding->GetActor()); + (binding->IsInitialized() ? ok : failed) = true; + } + } + } + }; + bind_actors(); + bindings->allocBindings(heap); + bind_actors(); + + // Bind actions. + res::EventFlowActionBinder action_binder_1{bindings, heap}; + bindActorActions(context, action_binder_1); + bindings->allocBindingsActions(heap); + res::EventFlowActionBinder action_binder_2{bindings, heap}; + bindActorActions(context, action_binder_2); + + // Bind queries. + res::EventFlowQueryBinder query_binder_1{bindings, heap}; + bindActorQueries(context, query_binder_1); + bindings->allocBindingsQueries(heap); + res::EventFlowQueryBinder query_binder_2{bindings, heap}; + bindActorQueries(context, query_binder_2); + + context.UnbindAll(); + + return bindings->getNumBindings() != 0; +} + +bool ResourceFlowchart::buildFlowchart(evfl::FlowchartContext* context, sead::Heap* heap) { + std::array flowcharts; + int num_flowcharts = 0; + + const auto add_flowcharts = [&] { + for (int i = 0; i < mFlowcharts.size(); ++i) + flowcharts[num_flowcharts++] = mFlowcharts[i].res_flowchart; + }; + add_flowcharts(); + + evfl::FlowchartContext::Builder builder({flowcharts.data(), num_flowcharts}); + + if (!builder.SetEntryPoint(mName.cstr(), mEntryPoint.cstr())) + return false; + + evfl::FlowchartContext::Builder::BuildResult result; + if (builder.Build(&result, context, makeEvflAllocateArg(heap))) + return true; + + // Load any missing flowchart. + // Note: this only works in debug builds; in release builds there is no debug heap + // to allocate the mMissingFlowcharts buffer. + mMissingFlowcharts.freeBuffer(); + static constexpr int NumFallbackRes = 8; + for (int i = 0; i < NumFallbackRes; ++i) { + if (result.result != + evfl::FlowchartContext::Builder::BuildResultType::kResFlowchartNotFound) { + continue; + } + + sead::FormatFixedSafeString<128> path("EventFlow/%s.bfevfl", + result.missing_flowchart_name.data()); + + if (!mMissingFlowcharts.isBufferReady()) + mMissingFlowcharts.allocBufferAssert(NumFallbackRes, util::getDebugHeap()); + + res::ResourceMgrTask::instance()->controlField9c0d88(false); + mMissingFlowcharts[i].handle.load(path, nullptr); + res::ResourceMgrTask::instance()->controlField9c0d88(true); + + /// @bug Bug? This causes all flowcharts in mFlowcharts to be added more than once... + add_flowcharts(); + + for (int flow_idx = 0; flow_idx < i + 1; ++flow_idx) { + auto* flowchart = sead::DynamicCast( + mMissingFlowcharts[flow_idx].handle.getResource()); + flowcharts[num_flowcharts++] = flowchart->getRes()->flowcharts.Get()->Get(); + } + + evfl::FlowchartContext::Builder builder2({flowcharts.data(), num_flowcharts}); + if (!builder2.SetEntryPoint(mName.cstr(), mEntryPoint.cstr())) + continue; + if (!builder2.Build(&result, context, makeEvflAllocateArg(heap))) + continue; + return true; + } + + return false; +} + +bool ResourceFlowchart::isResourceLoaded(const sead::SafeString& path_substring) const { + for (int i = 0; i < mFlowcharts.size(); ++i) { + auto* unit = mFlowcharts[i].handle.getUnit(); + if (unit && unit->getPath().include(path_substring)) + return true; + } + return false; +} + +} // namespace ksys::evt diff --git a/src/KingSystem/Event/evtResourceFlowchart.h b/src/KingSystem/Event/evtResourceFlowchart.h new file mode 100644 index 00000000..4fae0fec --- /dev/null +++ b/src/KingSystem/Event/evtResourceFlowchart.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include "KingSystem/Resource/resHandle.h" + +namespace evfl { +class FlowchartContext; +struct ResEventFlowFile; +struct ResFlowchart; +} // namespace evfl + +namespace ksys::evt { + +class ActorBindings; + +class ResourceFlowchart { +public: + ResourceFlowchart(); + virtual ~ResourceFlowchart(); + + /// Loads event flowchart resource files asynchronously. + void loadEventFlow(sead::Heap* heap, res::Handle* pack_handle); + + /// @return true if the load completed (succeeded or failed), false if it is still ongoing. + bool finishLoad(); + + /// @return whether at least one actor was bound. + bool setUpBindings(ActorBindings* bindings, sead::Heap* heap); + + /// @return whether the build was successful. + bool buildFlowchart(evfl::FlowchartContext* context, sead::Heap* heap); + + bool isResourceLoaded(const sead::SafeString& path_substring) const; + +private: + struct Res { + res::Handle handle; + const evfl::ResEventFlowFile* res_event_flow_file; + const evfl::ResFlowchart* res_flowchart; + bool loaded; + }; + + sead::Buffer mFlowcharts; + sead::Buffer mMissingFlowcharts; + bool mLoadFailed; + sead::FixedSafeString<64> mName; + sead::FixedSafeString<128> mEntryPoint; +}; + +} // namespace ksys::evt diff --git a/src/KingSystem/Resource/Event/resEventFlowBinder.h b/src/KingSystem/Resource/Event/resEventFlowBinder.h index fcdd36be..48257e09 100644 --- a/src/KingSystem/Resource/Event/resEventFlowBinder.h +++ b/src/KingSystem/Resource/Event/resEventFlowBinder.h @@ -18,6 +18,8 @@ public: EventFlowBinder(evt::ActorBindings* bindings, sead::Heap* heap) : mBindings(bindings), mHeap(heap) {} virtual ~EventFlowBinder() = default; + EventFlowBinder(const EventFlowBinder&) = default; + EventFlowBinder& operator=(const EventFlowBinder&) = default; protected: evt::ActorBindings* mBindings; @@ -26,17 +28,20 @@ protected: class EventFlowActorBinder : public EventFlowBinder { public: + using EventFlowBinder::EventFlowBinder; void bind(evfl::ActorBinding* evfl_binding, const evfl::ResActor* evfl_actor); }; class EventFlowActionBinder : public EventFlowBinder { public: + using EventFlowBinder::EventFlowBinder; void bind(evfl::ActorBinding::Action* evfl_binding, const evfl::ResAction* evfl_action, const evfl::ResActor* evfl_actor, evt::ActorBinding* binding); }; class EventFlowQueryBinder : public EventFlowBinder { public: + using EventFlowBinder::EventFlowBinder; void bind(evfl::ActorBinding::Query* evfl_binding, const evfl::ResQuery* evfl_query, const evfl::ResActor* evfl_actor, evt::ActorBinding* binding); };