diff options
| author | Léo Lam <leo@leolam.fr> | 2022-03-21 19:25:20 +0100 |
|---|---|---|
| committer | Léo Lam <leo@leolam.fr> | 2022-03-21 21:31:42 +0100 |
| commit | 18c60323a9bbc7594c287d18d6e87a1ae5d60006 (patch) | |
| tree | 387c2c412b5ea13493c5d942c85b8b046255431b /lib/EventFlow | |
| parent | ffcc7f659ebc9bc9d149e52bec553b906bb47369 (diff) | |
Switch to subrepos
git subrepo clone https://github.com/open-ead/sead lib/sead
subrepo:
subdir: "lib/sead"
merged: "1b66e825d"
upstream:
origin: "https://github.com/open-ead/sead"
branch: "master"
commit: "1b66e825d"
git-subrepo:
version: "0.4.3"
origin: "https://github.com/ingydotnet/git-subrepo"
commit: "2f68596"
git subrepo clone (merge) https://github.com/open-ead/nnheaders lib/NintendoSDK
subrepo:
subdir: "lib/NintendoSDK"
merged: "9ee21399f"
upstream:
origin: "https://github.com/open-ead/nnheaders"
branch: "master"
commit: "9ee21399f"
git-subrepo:
version: "0.4.3"
origin: "ssh://git@github.com/ingydotnet/git-subrepo"
commit: "2f68596"
git subrepo clone https://github.com/open-ead/agl lib/agl
subrepo:
subdir: "lib/agl"
merged: "7c063271b"
upstream:
origin: "https://github.com/open-ead/agl"
branch: "master"
commit: "7c063271b"
git-subrepo:
version: "0.4.3"
origin: "ssh://git@github.com/ingydotnet/git-subrepo"
commit: "2f68596"
git subrepo clone https://github.com/open-ead/EventFlow lib/EventFlow
subrepo:
subdir: "lib/EventFlow"
merged: "c35d21b34"
upstream:
origin: "https://github.com/open-ead/EventFlow"
branch: "master"
commit: "c35d21b34"
git-subrepo:
version: "0.4.3"
origin: "ssh://git@github.com/ingydotnet/git-subrepo"
commit: "2f68596"
Diffstat (limited to 'lib/EventFlow')
46 files changed, 5578 insertions, 0 deletions
diff --git a/lib/EventFlow b/lib/EventFlow deleted file mode 160000 -Subproject c35d21b34397bec6dd3c67a649fc66b68839341 diff --git a/lib/EventFlow/.clang-format b/lib/EventFlow/.clang-format new file mode 100644 index 00000000..cea20307 --- /dev/null +++ b/lib/EventFlow/.clang-format @@ -0,0 +1,75 @@ +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 100 +CommentPragmas: '^ (IWYU pragma:|NOLINT)' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ForEachMacros: [] +IncludeCategories: + - Regex: '^<[Ww]indows\.h>$' + Priority: 1 + - Regex: '^<' + Priority: 2 + - Regex: '^"' + Priority: 3 +IndentCaseLabels: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: c++17 +TabWidth: 4 +UseTab: Never +WhitespaceSensitiveMacros: ["ORE_ENUM"] +... diff --git a/lib/EventFlow/.gitignore b/lib/EventFlow/.gitignore new file mode 100644 index 00000000..343c32e3 --- /dev/null +++ b/lib/EventFlow/.gitignore @@ -0,0 +1,31 @@ +__pycache__/ +*.pyc +*.egg-info/ +*.dist-info/ +*.so +*.dll +dist/ +build/ +bin/ +.mypy_cache/ +.benchmarks/ + +.idea/ +.vscode/ + +*.id0 +*.id1 +*.id2 +*.idb +*.i64 +*.nam +*.til + +main.elf + +perf.mData +perf.mData.old +.gdb_history + +.DS_Store +tools/aarch64-none-elf-objdump
\ No newline at end of file diff --git a/lib/EventFlow/.gitrepo b/lib/EventFlow/.gitrepo new file mode 100644 index 00000000..b5109f06 --- /dev/null +++ b/lib/EventFlow/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = https://github.com/open-ead/EventFlow + branch = master + commit = c35d21b34397bec6dd3c67a649fc66b68839341e + parent = ffcc7f659ebc9bc9d149e52bec553b906bb47369 + method = merge + cmdver = 0.4.3 diff --git a/lib/EventFlow/CMakeLists.txt b/lib/EventFlow/CMakeLists.txt new file mode 100644 index 00000000..394c6ffa --- /dev/null +++ b/lib/EventFlow/CMakeLists.txt @@ -0,0 +1,56 @@ +project(EventFlow CXX ASM) + +option(EVFL_VER_LABO "Build Labo's version of this library" OFF) + +add_library(evfl OBJECT + include/evfl/Action.h + include/evfl/EvflAllocator.h + include/evfl/Flowchart.h + include/evfl/Param.h + include/evfl/Query.h + include/evfl/ResActor.h + include/evfl/ResEventFlowFile.h + include/evfl/ResFlowchart.h + include/evfl/ResTimeline.h + include/evfl/TimelineObj.h + include/ore/Allocator.h + include/ore/Array.h + include/ore/BinaryFile.h + include/ore/BitUtils.h + include/ore/Buffer.h + include/ore/EnumUtil.h + include/ore/IntrusiveList.h + include/ore/IterRange.h + include/ore/RelocationTable.h + include/ore/ResDic.h + include/ore/ResEndian.h + include/ore/ResMetaData.h + include/ore/StringPool.h + include/ore/StringView.h + include/ore/Types.h + src/evfl/Action.cpp + src/evfl/Param.cpp + src/evfl/Flowchart.cpp + src/evfl/FlowchartObj.cpp + src/evfl/TimelineObj.cpp + src/evfl/ResEventFlowFile.cpp + src/evfl/ResTimeline.cpp + src/evfl/ResActor.cpp + src/evfl/ResFlowchart.cpp + src/ore/BitUtils.cpp + src/ore/EnumUtil.cpp + src/ore/BinaryFile.cpp + src/ore/RelocationTable.cpp + src/ore/StringPool.cpp + src/ore/ResDic.cpp + src/ore/ResMetaData.cpp +) + +target_compile_options(evfl PRIVATE -fno-exceptions) +target_compile_options(evfl PRIVATE -fno-strict-aliasing) +target_compile_options(evfl PRIVATE -Wno-invalid-offsetof) +target_include_directories(evfl PUBLIC include/) + +if(EVFL_VER_LABO) + target_compile_definitions(evfl PUBLIC EVFL_VER_LABO=1) +endif() diff --git a/lib/EventFlow/include/evfl/Action.h b/lib/EventFlow/include/evfl/Action.h new file mode 100644 index 00000000..88dea6dd --- /dev/null +++ b/lib/EventFlow/include/evfl/Action.h @@ -0,0 +1,125 @@ +#pragma once + +#include <evfl/Param.h> +#include <ore/EnumUtil.h> +#include <ore/IntrusiveList.h> +#include <utility> + +namespace evfl { + +class FlowchartContext; +class FlowchartContextNode; +class FlowchartObj; +struct ResClip; +struct ResEvent; +struct ResOneshot; +class TimelineObj; +class VariablePack; + +ORE_VALUED_ENUM(TriggerType, kFlowchart = 0, kClipEnter = 1, kClipLeave = 2, kOneshot = 3, + kNormal = 0, kEnter = 1, kLeave = 2) + +class ActionDoneHandler { +public: + ActionDoneHandler() = default; + ActionDoneHandler(FlowchartObj* obj, FlowchartContext* context, int node_idx); + explicit ActionDoneHandler(TimelineObj* obj) : m_is_flowchart(false) { m_timeline_obj = obj; } + ~ActionDoneHandler() { m_list_node.Erase(); } + + ActionDoneHandler(const ActionDoneHandler&) = delete; + auto operator=(const ActionDoneHandler&) = delete; + + ActionDoneHandler(ActionDoneHandler&& other) noexcept { *this = std::move(other); } + + ActionDoneHandler& operator=(ActionDoneHandler&& other) noexcept { + m_context = other.m_context; + m_node_idx = other.m_node_idx; + m_obj = other.m_obj; + m_node_counter = other.m_node_counter; + m_handled = other.m_handled; + m_is_flowchart = other.m_is_flowchart; + m_list_node = std::move(other.m_list_node); + + other.m_context = nullptr; + other.m_node_idx = -1; + other.m_node_counter = -1; + other.m_obj = nullptr; + other.m_handled = false; + other.m_is_flowchart = true; + + return *this; + } + + FlowchartContextNode* GetContextNode(); + void InvokeFromFlowchartImpl(); + void InvokeFromTimelineImpl(); + bool IsWaitingJoin(); + bool CancelWaiting(); + + void Reset() { + m_context = nullptr; + m_obj = nullptr; + m_node_idx = -1; + m_node_counter = -1; + m_handled = false; + m_is_flowchart = true; + } + + static constexpr size_t GetListNodeOffset() { return offsetof(ActionDoneHandler, m_list_node); } + +private: + friend class FlowchartContext; + + ore::IntrusiveListNode m_list_node; + FlowchartContext* m_context = nullptr; + int m_node_idx = -1; + int m_node_counter = -1; + union { + FlowchartObj* m_obj = nullptr; + TimelineObj* m_timeline_obj; + }; + bool m_handled = false; + bool m_is_flowchart = true; +}; + +struct ActionArg { + ActionArg(FlowchartContext* context, int node_idx, void* actor_user_data_, + void* action_user_data_, VariablePack* variable_pack_, const ResEvent* event_) + : param_accessor(context, node_idx), actor_user_data(actor_user_data_), + action_user_data(action_user_data_), flowchart_ctx(context), + variable_pack(variable_pack_), res(event_), timeline_time_delta(0.0), + trigger_type(TriggerType::kFlowchart) {} + + ActionArg(const ResClip* clip, void* actor_user_data_, void* action_user_data_, + float timeline_time_delta_, TriggerType::Type type, const ore::ResMetaData* params) + : param_accessor(params), actor_user_data(actor_user_data_), + action_user_data(action_user_data_), res(clip), timeline_time_delta(timeline_time_delta_), + trigger_type(type) {} + + ActionArg(const ResOneshot* oneshot, void* actor_user_data_, void* action_user_data_, + float timeline_time_delta_, const ore::ResMetaData* params) + : param_accessor(params), actor_user_data(actor_user_data_), + action_user_data(action_user_data_), res(oneshot), + timeline_time_delta(timeline_time_delta_), trigger_type(TriggerType::kOneshot) { + res.oneshot = oneshot; + } + + ParamAccessor param_accessor; + void* actor_user_data = nullptr; + void* action_user_data = nullptr; + FlowchartContext* flowchart_ctx = nullptr; + VariablePack* variable_pack = nullptr; + union Res { + explicit Res(const ResEvent* e) : event(e) {} + explicit Res(const ResClip* c) : clip(c) {} + explicit Res(const ResOneshot* o) : oneshot(o) {} + + const ResEvent* event; + const ResClip* clip; + const ResOneshot* oneshot; + } res; + float timeline_time_delta{}; + TriggerType::Type trigger_type{}; +}; + +} // namespace evfl diff --git a/lib/EventFlow/include/evfl/EvflAllocator.h b/lib/EventFlow/include/evfl/EvflAllocator.h new file mode 100644 index 00000000..e81d4441 --- /dev/null +++ b/lib/EventFlow/include/evfl/EvflAllocator.h @@ -0,0 +1,31 @@ +#pragma once + +#include <ore/Allocator.h> + +namespace evfl { + +struct AllocateArg { + void* (*alloc)(size_t size, size_t alignment, void* userdata); + void (*free)(void* ptr, void* userdata); + void* alloc_userdata; + void* free_userdata; +}; + +class EvflAllocator : public ore::Allocator { +public: + EvflAllocator() = default; + explicit EvflAllocator(AllocateArg arg) : m_arg(arg) {} + + void* AllocImpl(size_t size, size_t alignment) override { + return m_arg.alloc(size, alignment, m_arg.alloc_userdata); + } + + void FreeImpl(void* ptr) override { m_arg.free(ptr, m_arg.free_userdata); } + + AllocateArg GetArg() const { return m_arg; } + +private: + AllocateArg m_arg; +}; + +} // namespace evfl diff --git a/lib/EventFlow/include/evfl/Flowchart.h b/lib/EventFlow/include/evfl/Flowchart.h new file mode 100644 index 00000000..4bc40a17 --- /dev/null +++ b/lib/EventFlow/include/evfl/Flowchart.h @@ -0,0 +1,208 @@ +#pragma once + +#include <evfl/EvflAllocator.h> +#include <evfl/ResActor.h> +#include <ore/Array.h> +#include <ore/Buffer.h> +#include <ore/EnumUtil.h> +#include <ore/IntrusiveList.h> +#include <ore/IterRange.h> +#include <ore/StringView.h> +#include <ore/Types.h> + +namespace ore { +class Allocator; +class BitArray; +} // namespace ore + +namespace evfl { + +class MetaDataPack; +struct ResEntryPoint; +struct ResEvent; +struct ResFlowchart; +class VariablePack; + +ORE_ENUM(SubFlowCallbackType, kEnter, kLeave) + +class FlowchartObj { +public: + class Builder { + public: + Builder(const ResFlowchart* flowchart, ore::BitArray* visited_entry_points) + : m_flowchart(flowchart), m_entry_points_mask(visited_entry_points) {} + + bool Build(FlowchartObj* obj, ore::Allocator* allocator, + ore::IterRange<const ResFlowchart* const*> flowcharts); + + private: + const ResFlowchart* m_flowchart{}; + ore::BitArray* m_entry_points_mask{}; + ActBinder::Builder m_act_binder_builder{}; + }; + +#ifdef MATCHING_HACK_NX_CLANG + [[gnu::always_inline]] +#endif + ~FlowchartObj() = default; + + const ResFlowchart* GetFlowchart() const { return m_flowchart; } + const ActBinder& GetActBinder() const { return m_act_binder; } + ActBinder& GetActBinder() { return m_act_binder; } + +private: + const ResFlowchart* m_flowchart{}; + ActBinder m_act_binder; +}; + +class FlowchartContextNode { +public: + ORE_ENUM(State, kInvalid, kFree, kNotInvoked, kInvoked, kDone, kWaiting) + + FlowchartContextNode() { Reset(); } + + bool IsInvalidOrFree() const { return m_state == State::kInvalid || m_state == State::kFree; } + FlowchartObj* GetObj() const { return m_obj; } + VariablePack* GetVariablePack() const { return m_variable_pack; } + int GetNodeCounter() const { return m_node_counter; } + u16 GetEventIdx() const { return m_event_idx; } + u16 GetNextNodeIdx() const { return m_next_node_idx; } + State::Type GetState() const { return m_state; } + + void Reset() { + m_node_counter = -1; + m_obj = nullptr; + m_event_idx = -1; + m_next_node_idx = -1; + m_idx = -1; + m_state = State::kInvalid; + m_variable_pack = nullptr; + m_owns_variable_pack = false; + } + +private: + friend class ActionDoneHandler; + friend class FlowchartContext; + + FlowchartObj* m_obj; + VariablePack* m_variable_pack; + int m_node_counter; + u16 m_event_idx; + u16 m_next_node_idx; + u16 m_idx; + ore::SizedEnum<State::Type, u8> m_state; + bool m_owns_variable_pack; +}; + +class FlowchartContext { +public: + class Builder { + public: + using FlowchartRange = ore::IterRange<const ResFlowchart* const*>; + ORE_ENUM(BuildResultType, kSuccess, kInvalidOperation, kResFlowchartNotFound, kEntryPointNotFound) + + struct BuildResult { + BuildResultType::Type result; + /// Indicates which flowchart was required yet couldn't be found. + ore::StringView missing_flowchart_name{}; + /// Indicates which entry point was required yet couldn't be found. + ore::StringView missing_entry_point_name{}; + }; + + Builder() = default; + explicit Builder(FlowchartRange flowcharts, int flowchart_idx = 0) + : m_flowcharts(flowcharts), m_flowchart_idx(flowchart_idx) {} + + bool SetEntryPoint(const ore::StringView& flowchart_name, + const ore::StringView& entry_point_name); + bool SetEntryPoint(BuildResult* result, const ore::StringView& flowchart_name, + const ore::StringView& entry_point_name); + + bool Build(FlowchartContext* context, AllocateArg allocate_arg); + bool Build(BuildResult* result, FlowchartContext* context, AllocateArg allocate_arg); + + private: + bool BuildImpl(BuildResult* result, FlowchartRange flowcharts, FlowchartContext* context, + AllocateArg allocate_arg, ore::Buffer flowchart_obj_buffer); + + FlowchartRange m_flowcharts{}; + int m_flowchart_idx = 0; + int m_entry_point_idx = -1; + }; + + FlowchartContext(); + ~FlowchartContext() { + Dispose(); + m_objs.DestructElements(); + } + + FlowchartContext(const FlowchartContext&) = delete; + auto operator=(const FlowchartContext&) = delete; + + void Start(MetaDataPack* pack); + int AllocNode(); + void AllocVariablePack(FlowchartContextNode& node, const ResEntryPoint& entry_point); + void ProcessContext(); + FlowchartObj* FindFlowchartObj(ore::StringView name); + const FlowchartObj* FindFlowchartObj(ore::StringView name) const; + void UnbindAll(); + void Clear(); + void FreeVariablePack(FlowchartContextNode& node); + void CopyVariablePack(FlowchartContextNode& src, FlowchartContextNode& dst); + bool ProcessContextNode(int node_idx); + ActorBinding* TrackBackArgumentActor(int node_idx, const ore::StringView& name); + bool IsUsing(const ResFlowchart* flowchart) const; + bool IsPlaying(const ResFlowchart* flowchart) const; + const ore::Array<ActorBinding>* GetUsedResActors(ore::StringView flowchart_name) const; + + FlowchartContextNode& GetNode(int idx) { return m_nodes[idx]; } + const FlowchartContextNode& GetNode(int idx) const { return m_nodes[idx]; } + MetaDataPack* GetMetaDataPack() const { return m_metadata_pack; } + ore::Array<FlowchartObj>& GetObjs() { return m_objs; } + const ore::Array<FlowchartObj>& GetObjs() const { return m_objs; } + +private: + void Dispose() { + m_obj_idx = -1; + m_active_entry_point_idx = -1; + m_metadata_pack = nullptr; + m_objs.Clear(&m_allocator); + for (auto& node : m_nodes) + FreeVariablePack(node); + m_nodes.Reset(); + } + + void UpdateNodeCounter(int node_idx) { + auto& node = GetNode(node_idx); + node.m_node_counter = ++s_GlobalCounter; + } + + void CallSubFlowCallback(const ResFlowchart* flowchart, const ResEvent* event, + SubFlowCallbackType::Type type) { +#ifdef EVFL_VER_LABO + if (m_on_sub_flow_callback) + m_on_sub_flow_callback(this, flowchart, event, type); +#endif + } + + static int s_GlobalCounter; + + EvflAllocator m_allocator; + ore::Array<FlowchartObj> m_objs; + ore::DynArrayList<FlowchartContextNode> m_nodes; + ore::IntrusiveList<ActionDoneHandler> m_handlers; +#ifdef EVFL_VER_LABO + void (*m_on_sub_flow_callback)(FlowchartContext* context, const ResFlowchart* flowchart, + const ResEvent* event, SubFlowCallbackType::Type type) = nullptr; +#endif + MetaDataPack* m_metadata_pack = nullptr; + void* _78 = nullptr; + int m_next_node_idx = 0; + int m_num_allocated_nodes = 0; + int m_obj_idx = -1; + int m_active_entry_point_idx = -1; + bool m_is_processing = false; + u8 _91 = 0; +}; + +} // namespace evfl diff --git a/lib/EventFlow/include/evfl/Param.h b/lib/EventFlow/include/evfl/Param.h new file mode 100644 index 00000000..439aba28 --- /dev/null +++ b/lib/EventFlow/include/evfl/Param.h @@ -0,0 +1,208 @@ +#pragma once + +#include <evfl/EvflAllocator.h> +#include <ore/Array.h> +#include <ore/BinaryFile.h> +#include <ore/Buffer.h> +#include <ore/EnumUtil.h> +#include <ore/IterRange.h> +#include <ore/ResMetaData.h> +#include <ore/StringView.h> +#include <ore/Types.h> + +namespace ore { +struct ResDic; +struct ResMetaData; +} // namespace ore + +namespace evfl { + +class FlowchartContext; +class FlowchartObj; +struct ResEntryPoint; + +class MetaDataPack { +public: + ORE_ENUM(DataType, kInt, kBool, kFloat, kString, kWString) + + struct Entry { + union Value { + bool b; + int i; + float f; + const char* str; + const wchar_t* wstr; + }; + + bool IsKey(const ore::StringView& other) const { return ore::StringView(key) == other; } + + const char* key; + Value value; + DataType::Type type; + }; + + class Builder { + public: + void CalcMemSize(); + bool Build(MetaDataPack* pack, ore::Buffer buffer); + void SetNumEntries(int num) { m_num_entries = num; } + int GetRequiredSize() const { return m_required_size; } + + private: + int m_num_entries = 16; + int m_required_size = 0; + int m_alignment_real = 16; + int m_entries_byte_size = 0; + int m_alignment = 16; + int _14 = 0; + int m_buffer_offset = -1; + }; + + void AddInt(const char* key, int value); + void AddBool(const char* key, bool value); + void AddFloat(const char* key, float value); + void AddStringPtr(const char* key, const char* value); + void AddWStringPtr(const char* key, const wchar_t* value); + + bool FindInt(int* value, const ore::StringView& key) const; + bool FindBool(bool* value, const ore::StringView& key) const; + bool FindFloat(float* value, const ore::StringView& key) const; + bool FindString(ore::StringView* value, const ore::StringView& key) const; + bool FindWString(ore::WStringView* value, const ore::StringView& key) const; + + ore::ResMetaData::DataType::Type GetType(const ore::StringView& key) const; + +private: + Entry* Find(const ore::StringView& key) const; + Entry& AddEntry() { return m_entries[m_entries_num++]; } + + ore::Array<Entry> GetEntries() const { return {m_entries, m_entries_num}; } + + ore::Buffer m_buffer{}; + Entry* m_entries{}; + int m_entries_num{}; + int m_entries_capacity{}; +}; + +class VariablePack { +public: + using VariableType = ore::ResMetaData::DataType::Type; + + struct Entry { + union Value { + void* dummy; + int i; + float f; + ore::DynArrayList<int>* int_array; + ore::DynArrayList<float>* float_array; + }; + + Value value; + VariableType type; + }; + + VariablePack(); + ~VariablePack(); + VariablePack(const VariablePack&) = delete; + auto operator=(const VariablePack&) = delete; + + void Init(AllocateArg arg, const ResEntryPoint* entry_point); + + Entry* GetVariableEntry(const ore::StringView& name); + const Entry* GetVariableEntry(const ore::StringView& name) const; + VariableType GetVariableType(const ore::StringView& name) const; + ore::StringView GetVariableName(int idx) const; + int GetVariableCount() const; + + bool Contains(const ore::StringView& name) const; + + bool FindInt(int* value, const ore::StringView& name) const; + bool FindBool(bool* value, const ore::StringView& name) const; + bool FindFloat(float* value, const ore::StringView& name) const; + bool FindIntList(ore::DynArrayList<int>** value, const ore::StringView& name) const; + bool FindFloatList(ore::DynArrayList<float>** value, const ore::StringView& name) const; + + int GetInt(const ore::StringView& name) const; + bool GetBool(const ore::StringView& name) const; + float GetFloat(const ore::StringView& name) const; + ore::DynArrayList<int>* GetIntList(const ore::StringView& name) const; + ore::DynArrayList<float>* GetFloatList(const ore::StringView& name) const; + + void SetInt(const ore::StringView& name, int value); + void SetFloat(const ore::StringView& name, float value); + void SetBool(const ore::StringView& name, bool value); + +private: + void Dispose(); + ore::Allocator* GetAllocator() { return &m_allocator; } + + const ore::ResDic* m_names = nullptr; + ore::Array<Entry> m_variables; + EvflAllocator m_allocator; +}; + +class ParamAccessor { +public: + using Type = ore::ResMetaData::DataType::Type; + using IntRange = ore::IterRange<const int*>; + using FloatRange = ore::IterRange<const float*>; + using StringRange = ore::IterRange<const ore::BinTPtr<ore::BinString>*>; + using WStringRange = ore::IterRange<const ore::BinTPtr<ore::BinWString>*>; + + explicit ParamAccessor(const ore::ResMetaData* metadata); + explicit ParamAccessor(const FlowchartContext* context, int node_idx); + + const ore::ResMetaData* GetFrontResMetaData() const; + int GetParamCount() const; + ore::StringView GetParamName(int idx) const; + Type GetParamType(int idx) const; + + /// @param out_metadata Must be nonnull. + /// @param out_metadata_pack Must be nonnull. + /// @param out_variable_pack Must be nonnull. + /// @param out_obj May be null. + /// @param argument Name of the argument to search for. + ore::StringView TrackBackArgument(const ore::ResMetaData** out_metadata, + const MetaDataPack** out_metadata_pack, + const VariablePack** out_variable_pack, + FlowchartObj** out_obj, + const ore::StringView& argument) const; + + int GetInt(int idx) const; + bool FindInt(int* value, const ore::StringView& name) const; + + bool GetBool(int idx) const; + bool FindBool(bool* value, const ore::StringView& name) const; + + float GetFloat(int idx) const; + bool FindFloat(float* value, const ore::StringView& name) const; + + ore::StringView GetString(int idx) const; + bool FindString(ore::StringView* value, const ore::StringView& name) const; + + ore::WStringView GetWString(int idx) const; + bool FindWString(ore::WStringView* value, const ore::StringView& name) const; + + IntRange GetIntArray(int idx) const; + bool FindIntArray(IntRange* value, const ore::StringView& name) const; + + FloatRange GetFloatArray(int idx) const; + bool FindFloatArray(FloatRange* value, const ore::StringView& name) const; + + StringRange GetStringArray(int idx) const; + bool FindStringArray(StringRange* value, const ore::StringView& name) const; + + WStringRange GetWStringArray(int idx) const; + bool FindWStringArray(WStringRange* value, const ore::StringView& name) const; + + bool FindActorIdentifier(ore::StringView* actor_name, ore::StringView* actor_sub_name, + const ore::StringView& name) const; + +private: + const ore::ResMetaData* m_metadata = nullptr; + const FlowchartContext* m_context = nullptr; + int m_node_idx = -1; + u32 m_node_counter = -1; +}; + +} // namespace evfl diff --git a/lib/EventFlow/include/evfl/Query.h b/lib/EventFlow/include/evfl/Query.h new file mode 100644 index 00000000..979907c7 --- /dev/null +++ b/lib/EventFlow/include/evfl/Query.h @@ -0,0 +1,29 @@ +#pragma once + +#include <evfl/Param.h> +#include <ore/EnumUtil.h> + +namespace evfl { + +class FlowchartContext; +struct ResEvent; +class VariablePack; + +ORE_ENUM(QueryValueType, kBool, kInt, kFloat, kString, kConst) + +struct QueryArg { + QueryArg(FlowchartContext* context, int node_idx, void* actor_user_data_, + void* query_user_data_, const ResEvent* event_, VariablePack* variable_pack_) + : actor_user_data(actor_user_data_), query_user_data(query_user_data_), main_event(event_), + flowchart_ctx(context), variable_pack(variable_pack_), param_accessor(context, node_idx) { + } + + void* actor_user_data; + void* query_user_data; + const ResEvent* main_event; + FlowchartContext* flowchart_ctx; + VariablePack* variable_pack; + ParamAccessor param_accessor; +}; + +} // namespace evfl diff --git a/lib/EventFlow/include/evfl/ResActor.h b/lib/EventFlow/include/evfl/ResActor.h new file mode 100644 index 00000000..5a43e1a4 --- /dev/null +++ b/lib/EventFlow/include/evfl/ResActor.h @@ -0,0 +1,187 @@ +#pragma once + +#include <cstddef> +#include <ore/Array.h> +#include <ore/BinaryFile.h> +#include <ore/IterRange.h> +#include <utility> + +namespace ore { +struct ResEndian; +struct ResMetaData; +} // namespace ore + +namespace evfl { + +struct ActionArg; +class ActionDoneHandler; +class FlowchartContext; +class FlowchartContextNode; +class FlowchartObj; +struct QueryArg; + +struct ResAction { + ore::BinTPtr<ore::BinString> name; +}; + +struct ResQuery { + ore::BinTPtr<ore::BinString> name; +}; + +struct ResActor { + bool HasArgumentName() const { return !argument_name.Get()->empty(); } + + ore::BinTPtr<ore::BinString> name; + ore::BinTPtr<ore::BinString> secondary_name; + ore::BinTPtr<ore::BinString> argument_name; + ore::BinTPtr<ResAction> actions; + ore::BinTPtr<ResQuery> queries; + ore::BinTPtr<ore::ResMetaData> params; + u16 num_actions; + u16 num_queries; + /// Entry point index for assicated entry point (0xffff if none) + u16 entry_point_idx; + // TODO: Cut number? This is set to 1 for flowcharts but other values have been seen + // for timeline actors. + u8 cut_number; +}; + +using ActionHandler = void (*)(const ActionArg& arg, ActionDoneHandler done_handler); +using QueryHandler = int (*)(const QueryArg& arg); + +class ActorBinding { +public: + struct Action { + ActionHandler handler{}; + void* user_data{}; + const ResAction* res_action{}; + }; + + struct Query { + QueryHandler handler{}; + void* user_data{}; + const ResQuery* res_query{}; + }; + + ActorBinding() = default; + + void Register(const ResAction* action); + void Register(const ResQuery* query); + + // Similar to std::find_if. Returns m_actions.end() if the specified action is not found. + Action* GetAction(const ore::StringView& name); + const Action* GetAction(const ore::StringView& name) const; + Query* GetQuery(const ore::StringView& name); + const Query* GetQuery(const ore::StringView& name) const; + + ore::DynArrayList<Action>& GetActions() { return m_actions; } + ore::DynArrayList<Query>& GetQueries() { return m_queries; } + const ore::DynArrayList<Action>& GetActions() const { return m_actions; } + const ore::DynArrayList<Query>& GetQueries() const { return m_queries; } + auto ActionsEnd() const { return m_actions.end(); } + auto QueriesEnd() const { return m_queries.end(); } + + auto GetActor() const { return m_actor; } + + void* GetUserData() const { return m_user_data; } + void SetUserData(void* user_data) { m_user_data = user_data; } + + bool IsInitialized() const { return m_initialized; } + bool IsUsed() const { return m_is_used; } + void SetInitialized(bool initialized) { m_initialized = initialized; } + void SetIsUsed(bool used) { m_is_used = used; } + + void UnbindAll() { + m_user_data = nullptr; + m_initialized = false; + for (auto it = m_actions.begin(); it != m_actions.end(); ++it) { + it->handler = nullptr; + it->user_data = nullptr; + } + for (auto it = m_queries.begin(); it != m_queries.end(); ++it) { + it->handler = nullptr; + it->user_data = nullptr; + } + } + +private: + friend class ActBinder; + + ore::DynArrayList<Action> m_actions{}; + ore::DynArrayList<Query> m_queries{}; + void* m_user_data{}; + const ResActor* m_actor{}; + bool m_initialized{}; + bool m_is_used{}; +}; + +class ActBinder { +public: + class Builder { + public: + bool Build(evfl::ActBinder* binder, ore::Allocator* allocator, + ore::IterRange<const ResActor*> actors); + }; + + ActBinder() = default; + ActBinder(const ActBinder&) = delete; + auto operator=(const ActBinder&) = delete; + +#ifdef MATCHING_HACK_NX_CLANG + [[gnu::always_inline]] +#endif + ~ActBinder() { + Reset(); + } + + u32 GetEventUsedActorCount() const { return m_event_used_actor_count; } + const ore::Array<ActorBinding>* GetUsedResActors() const; + + ore::Array<ActorBinding>& GetBindings() { return m_bindings; } + const ore::Array<ActorBinding>& GetBindings() const { return m_bindings; } + void IncrementNumActors() { ++m_event_used_actor_count; } + void SetIsUsed() { m_is_used = true; } + bool IsUsed() const { return m_is_used; } + + void RegisterAction(int actor_idx, const evfl::ResAction* action) { + auto& binding = GetBindings()[actor_idx]; + if (!binding.IsUsed() && binding.GetActor()->argument_name.Get()->empty()) { + IncrementNumActors(); + binding.SetIsUsed(true); + } + binding.Register(action); + } + + void RegisterQuery(int actor_idx, const evfl::ResQuery* query) { + auto& binding = GetBindings()[actor_idx]; + if (!binding.IsUsed() && binding.GetActor()->argument_name.Get()->empty()) { + IncrementNumActors(); + binding.SetIsUsed(true); + } + binding.Register(query); + } + + void UnbindAll() { + for (auto it = m_bindings.begin(); it != m_bindings.end(); ++it) + it->UnbindAll(); + } + + void Reset() { + m_event_used_actor_count = 0; + if (auto* data = m_bindings.data()) { + m_bindings.ClearWithoutFreeing(); + m_allocator->Free(data); + } + m_allocator = nullptr; + } + +private: + u32 m_event_used_actor_count{}; + ore::Allocator* m_allocator{}; + ore::SelfDestructingArray<ActorBinding> m_bindings{}; + bool m_is_used{}; +}; + +void SwapEndian(ore::ResEndian* endian, ResActor* actor); + +} // namespace evfl diff --git a/lib/EventFlow/include/evfl/ResEventFlowFile.h b/lib/EventFlow/include/evfl/ResEventFlowFile.h new file mode 100644 index 00000000..30d8217c --- /dev/null +++ b/lib/EventFlow/include/evfl/ResEventFlowFile.h @@ -0,0 +1,36 @@ +#pragma once + +#include <ore/BinaryFile.h> +#include <ore/Types.h> + +namespace ore { +struct ResDic; +struct ResEndian; +} // namespace ore + +namespace evfl { + +struct ResFlowchart; +struct ResTimeline; + +struct ResEventFlowFile { + /// data must be a pointer to a buffer of size >= 0x20. + static bool IsValid(void* data); + /// data must be a valid ResEventFlowFile. + static ResEventFlowFile* ResCast(void* data); + + void Relocate(); + void Unrelocate(); + + ore::BinaryFileHeader header; + u16 num_flowcharts; + u16 num_timelines; + ore::BinTPtr<ore::BinTPtr<ResFlowchart>> flowcharts; + ore::BinTPtr<ore::ResDic> flowchart_names; + ore::BinTPtr<ore::BinTPtr<ResTimeline>> timelines; + ore::BinTPtr<ore::ResDic> timeline_names; +}; + +void SwapEndian(ore::ResEndian* endian, ResEventFlowFile* file); + +} // namespace evfl diff --git a/lib/EventFlow/include/evfl/ResFlowchart.h b/lib/EventFlow/include/evfl/ResFlowchart.h new file mode 100644 index 00000000..99517bd2 --- /dev/null +++ b/lib/EventFlow/include/evfl/ResFlowchart.h @@ -0,0 +1,136 @@ +#pragma once + +#include <ore/BinaryFile.h> +#include <ore/EnumUtil.h> +#include <ore/ResDic.h> +#include <ore/ResMetaData.h> +#include <ore/StringView.h> + +namespace ore { +struct ResEndian; +} + +namespace evfl { + +struct ResActor; + +struct ResCase { + u32 value; + u16 event_idx; +}; + +struct ResEvent { + ORE_ENUM(EventType, kAction, kSwitch, kFork, kJoin, kSubFlow) + + ore::BinTPtr<ore::BinString> name; + ore::SizedEnum<EventType::Type, u8> type; + + union { + // Action, Join, Sub flow + u16 next_event_idx; + // Switch + u16 num_cases; + // Fork + u16 num_forks; + }; + + union { + // Action, Switch + u16 actor_idx; + // Fork + u16 join_event_idx; + }; + + union { + // Action + u16 actor_action_idx; + // Switch + u16 actor_query_idx; + }; + + union { + // Action, Switch, Sub flow + ore::BinTPtr<ore::ResMetaData> params; + // Fork + ore::BinTPtr<u16> fork_event_indices; + }; + + union { + // Switch + ore::BinTPtr<ResCase> cases; + // Sub flow + ore::BinTPtr<ore::BinString> sub_flow_flowchart; + }; + + union { + // Sub flow + ore::BinTPtr<ore::BinString> sub_flow_entry_point; + }; +}; + +struct ResVariableDef { + union Value { + // Also used for booleans. Anything that is != 0 is treated as true. + int i; + float f; + ore::BinTPtr<int> int_array; + ore::BinTPtr<float> float_array; + }; + + Value value; + u16 num; + ore::SizedEnum<ore::ResMetaData::DataType::Type, u8> type; +}; + +struct ResEntryPoint { + ore::BinTPtr<u16> sub_flow_event_indices; + ore::BinTPtr<ore::ResDic> variable_defs_names; + ore::BinTPtr<ResVariableDef> variable_defs; + u16 num_sub_flow_event_indices; + u16 num_variable_defs; + u16 main_event_idx; +}; + +struct ResFlowchart { + int CountEvent(ResEvent::EventType::Type type) const; + + const ResEntryPoint* GetEntryPoint(const ore::StringView& entry_point_name) const { + const int idx = entry_point_names.Get()->FindIndex(entry_point_name); + if (idx == -1) + return nullptr; + + return entry_points.Get() + idx; + } + + ore::StringView GetEntryPointName(int idx) const { + return entry_point_names.Get()->GetEntries()[1 + idx].GetKey(); + } + + /// 'EVFL' + u32 magic; + /// String pool offset (relative to this structure) + u32 string_pool_offset; + u32 reserved_8; + u32 reserved_c; + u16 num_actors; + u16 num_actions; + u16 num_queries; + u16 num_events; + u16 num_entry_points; + u16 reserved_1a; + u16 reserved_1c; + u16 reserved_1e; + ore::BinTPtr<ore::BinString> name; + ore::BinTPtr<ResActor> actors; + ore::BinTPtr<ResEvent> events; + ore::BinTPtr<ore::ResDic> entry_point_names; + ore::BinTPtr<ResEntryPoint> entry_points; +}; + +void SwapEndian(ore::ResEndian* endian, ResCase* case_); +void SwapEndian(ore::ResEndian* endian, ResEvent* event); +void SwapEndian(ore::ResEndian* endian, ResEntryPoint* entry); +void SwapEndian(ore::ResEndian* endian, ResVariableDef* def); +void SwapEndian(ore::ResEndian* endian, ResFlowchart* flowchart); + +} // namespace evfl diff --git a/lib/EventFlow/include/evfl/ResTimeline.h b/lib/EventFlow/include/evfl/ResTimeline.h new file mode 100644 index 00000000..f6a45ff8 --- /dev/null +++ b/lib/EventFlow/include/evfl/ResTimeline.h @@ -0,0 +1,80 @@ +#pragma once + +#include <ore/BinaryFile.h> +#include <ore/EnumUtil.h> +#include <ore/Types.h> + +namespace ore { +struct ResEndian; +struct ResMetaData; +} // namespace ore + +namespace evfl { + +struct ResActor; + +struct ResTrigger { + u16 clip_index; + u8 trigger_type; +}; + +struct ResCut { + float start_time; + ore::BinTPtr<ore::BinString> name; + ore::BinTPtr<ore::ResMetaData> params; +}; + +struct ResClip { + float start_time; + float duration; + u16 actor_index; + u16 actor_action_index; + u8 _c; + ore::BinTPtr<ore::ResMetaData> params; +}; + +struct ResOneshot { + float time; + u16 actor_index; + u16 actor_action_index; + u32 _8; + u32 _c; + ore::BinTPtr<ore::ResMetaData> params; +}; + +struct ResSubtimeline { + ore::BinTPtr<ore::BinString> name; +}; + +struct ResTimeline { + /// 'TLIN' + u32 magic; + /// String pool offset (relative to this structure) + int string_pool_offset; + u32 reserved_8; + u32 reserved_c; + float duration; + u16 num_actors; + u16 num_actions; + u16 num_clips; + u16 num_oneshots; + u16 num_subtimelines; + u16 num_cuts; + ore::BinTPtr<ore::BinString> name; + ore::BinTPtr<ResActor> actors; + ore::BinTPtr<ResClip> clips; + ore::BinTPtr<ResOneshot> oneshots; + ore::BinTPtr<ResTrigger> triggers; + ore::BinTPtr<ResSubtimeline> subtimelines; + ore::BinTPtr<ResCut> cuts; + ore::BinTPtr<ore::ResMetaData> params; +}; + +void SwapEndian(ore::ResEndian* endian, ResTrigger* trigger); +void SwapEndian(ore::ResEndian* endian, ResCut* cut); +void SwapEndian(ore::ResEndian* endian, ResClip* clip); +void SwapEndian(ore::ResEndian* endian, ResOneshot* oneshot); +void SwapEndian(ore::ResEndian* endian, ResSubtimeline* subtimeline); +void SwapEndian(ore::ResEndian* endian, ResTimeline* timeline); + +} // namespace evfl diff --git a/lib/EventFlow/include/evfl/TimelineObj.h b/lib/EventFlow/include/evfl/TimelineObj.h new file mode 100644 index 00000000..5d1ab38a --- /dev/null +++ b/lib/EventFlow/include/evfl/TimelineObj.h @@ -0,0 +1,78 @@ +#pragma once + +#include <evfl/EvflAllocator.h> +#include <evfl/ResActor.h> +#include <ore/Array.h> +#include <ore/EnumUtil.h> +#include <ore/Types.h> + +namespace evfl { + +struct ResTimeline; + +ORE_ENUM(TimelineState, kNotStarted, kPlaying, kStop, kPause) + +class TimelineObj { +public: + class Builder { + public: + explicit Builder(const ResTimeline* timeline) : m_timeline(timeline) {} + + bool Build(TimelineObj* obj, AllocateArg allocate_arg); + + private: + const ResTimeline* m_timeline = nullptr; + ActBinder::Builder m_act_binder_builder; + }; + + TimelineObj(); + + void Calc(); + void Reset(); + void SetState(TimelineState::Type state); + void Start(float start_time); + void JumpTimeTo(float time); + void AdvanceTimeTo(float time); + bool RegisterSubtimeline(TimelineObj* obj); + + ActBinder& GetActBinder() { return m_act_binder; } + const ActBinder& GetActBinder() const { return m_act_binder; } + const ore::Array<TimelineObj*>& GetSubTimelines() const { return m_sub_timelines; } + const ResTimeline* GetTimeline() const { return m_timeline; } + float GetTime() const { return m_time; } + float GetNewTime() const { return m_new_time; } + int GetLastTriggerIdx() const { return m_last_trigger_idx; } + int GetLastOneshotIdx() const { return m_last_oneshot_idx; } + int GetPlayCounter() const { return m_play_counter; } + bool IsStarted() const { return m_started; } + bool IsJumpedTime() const { return m_jumped_time; } + TimelineState::Type GetState() const { return m_state; } + +private: + void CalcImpl(); + void JumpTimeToImpl(float time); + void AdvanceTimeToImpl(float time); + + static int s_GlobalPlayCounter; + + void Finalize() { + m_timeline = nullptr; + m_act_binder.Reset(); + m_sub_timelines.Clear(&m_allocator); + } + + EvflAllocator m_allocator; + ore::Array<TimelineObj*> m_sub_timelines; + const ResTimeline* m_timeline{}; + ActBinder m_act_binder{}; + float m_time{}; + float m_new_time{}; + int m_last_trigger_idx = -1; + int m_last_oneshot_idx = -1; + int m_play_counter = 0; + bool m_started = false; + bool m_jumped_time = false; + ore::SizedEnum<TimelineState::Type, u8> m_state = TimelineState::kNotStarted; +}; + +} // namespace evfl diff --git a/lib/EventFlow/include/ore/Allocator.h b/lib/EventFlow/include/ore/Allocator.h new file mode 100644 index 00000000..23f6cd81 --- /dev/null +++ b/lib/EventFlow/include/ore/Allocator.h @@ -0,0 +1,45 @@ +#pragma once + +#include <cstddef> +#include <memory> +#include <ore/Types.h> + +namespace ore { + +class Allocator { +public: + Allocator() = default; + virtual ~Allocator() = default; + + void* New(size_t size, size_t alignment = alignof(std::max_align_t)) { + return AllocImpl(size, alignment); + } + + template <typename T> + T* New(size_t alignment = alignof(std::max_align_t)) { + auto* buffer = AllocImpl(sizeof(T), alignment); + if (buffer) + return new (buffer) T; + return static_cast<T*>(buffer); + } + + template <typename T> + void Delete(T* ptr) { + std::destroy_at(ptr); + Free(ptr); + } + + template <typename T> + void DeleteAndNull(T*& ptr) { + std::destroy_at(ptr); + Free(ptr); + ptr = nullptr; + } + + void Free(void* ptr) { FreeImpl(ptr); } + + virtual void* AllocImpl(size_t size, size_t alignment) = 0; + virtual void FreeImpl(void* ptr) = 0; +}; + +} // namespace ore diff --git a/lib/EventFlow/include/ore/Array.h b/lib/EventFlow/include/ore/Array.h new file mode 100644 index 00000000..342ce62d --- /dev/null +++ b/lib/EventFlow/include/ore/Array.h @@ -0,0 +1,287 @@ +#pragma once + +#include <algorithm> +#include <iterator> +#include <memory> +#include <ore/Allocator.h> +#include <ore/Buffer.h> +#include <ore/IterRange.h> +#include <type_traits> + +namespace ore { + +// This is like a std::span, not a fixed-size array like std::array. +// Elements will NOT be automatically freed. +template <typename T> +class Array { +public: + Array() = default; + Array(T* data, int size) : m_data(data), m_size(size) {} + + T* data() const { return m_data; } + int size() const { return m_size; } + + auto begin() const { return data(); } + auto end() const { return data() + size(); } + + T& operator[](int idx) { return m_data[idx]; } + const T& operator[](int idx) const { return m_data[idx]; } + + T& front() { return m_data[0]; } + const T& front() const { return m_data[0]; } + + T& back() { return m_data[m_size - 1]; } + const T& back() const { return m_data[m_size - 1]; } + + void SetBuffer(void* new_buffer, int num) { + DestructElements(); + m_data = static_cast<T*>(new_buffer); + m_size = num; + } + + void SetBuffer(int num, Allocator* allocator) { + auto* new_buffer = allocator->AllocImpl(num * int(sizeof(T)), alignof(std::max_align_t)); + SetBuffer(new_buffer, num); + } + + void ConstructElements(int num, Allocator* allocator) { + SetBuffer(num, allocator); + DefaultConstructElements(); + } + + void ConstructElements(void* new_buffer, int num) { + SetBuffer(new_buffer, num); + DefaultConstructElements(); + } + + void ConstructElements(Buffer buffer) { + DestructElements(); + m_data = reinterpret_cast<T*>(buffer.data); + m_size = buffer.size / int(sizeof(T)); + DefaultConstructElements(); + } + + void DestructElements() { std::destroy(begin(), end()); } + + void ClearWithoutFreeing() { + DestructElements(); + m_data = nullptr; + m_size = 0; + } + + void Clear(Allocator* allocator) { + if (!m_data) + return; + auto* data = m_data; + ClearWithoutFreeing(); + allocator->Free(data); + } + + void DefaultConstructElements() { + for (auto it = begin(), e = end(); it != e;) + new (it++) T; + } + + void UninitializedDefaultConstructElements() { + std::uninitialized_default_construct(begin(), end()); + } + +private: + T* m_data{}; + int m_size{}; +}; + +template <typename T> +class SelfDestructingArray : public Array<T> { +public: + ~SelfDestructingArray() { this->DestructElements(); } +}; + +template <typename T> +class ArrayListBase { +public: + ArrayListBase() : m_data(), m_size(), m_capacity() {} + ArrayListBase(T* data, int capacity) { + m_size = 0; + m_data = data; + m_capacity = capacity; + } + ~ArrayListBase() { clear(); } + + ArrayListBase(const ArrayListBase&) = delete; + auto operator=(const ArrayListBase&) = delete; + + T* begin() { return m_data; } + const T* begin() const { return m_data; } + + T* end() { return m_data + m_size; } + const T* end() const { return m_data + m_size; } + + T* data() { return m_data; } + const T* data() const { return m_data; } + + int size() const { return m_size; } + int capacity() const { return m_capacity; } + + T& operator[](int idx) { return m_data[idx]; } + const T& operator[](int idx) const { return m_data[idx]; } + + T& front() { return m_data[0]; } + const T& front() const { return m_data[0]; } + + T& back() { return m_data[m_size - 1]; } + const T& back() const { return m_data[m_size - 1]; } + + template <typename... Args> + T& emplace_back(Args&&... args) { + auto* item = new (&m_data[m_size++]) T(std::forward<Args>(args)...); + return *item; + } + + void push_back(const T& item) { new (&m_data[m_size++]) T(item); } + + void pop_back() { + std::destroy_at(&back()); + --m_size; + } + + void clear() { + std::destroy(begin(), end()); + m_size = 0; + } + + T* m_data; + int m_size; + int m_capacity; +}; + +template <typename T, int N> +class FixedArrayList : public ArrayListBase<T> { +public: + FixedArrayList() : ArrayListBase<T>(reinterpret_cast<T*>(m_storage), N) {} + +private: + std::aligned_storage_t<sizeof(T), alignof(T)> m_storage[N]; +}; + +// This is like a std::vector. +template <typename T> +class DynArrayList : public ArrayListBase<T> { +public: + DynArrayList() = default; + explicit DynArrayList(Allocator* allocator) : m_allocator(allocator) {} + + ~DynArrayList() { + clear(); + m_allocator = nullptr; + this->m_size = 0; + } + + void Reset() { + clear(); + m_allocator = nullptr; + } + + DynArrayList(const DynArrayList&) = delete; + auto operator=(const DynArrayList&) = delete; + + void Init(Allocator* allocator, int initial_capacity = 1) { + clear(); + m_allocator = allocator; + Reallocate(initial_capacity); + } + + template <typename... Args> + T& emplace_back(Args&&... args) { + GrowIfNeeded(); + return ArrayListBase<T>::emplace_back(std::forward<Args>(args)...); + } + + void push_back(const T& item) { + GrowIfNeeded(); + return ArrayListBase<T>::push_back(item); + } + + void clear() { + std::destroy(this->begin(), this->end()); + auto* data = this->m_data; + this->m_data = nullptr; + this->m_size = 0; + this->m_capacity = 0; + Free(data); + } + + template <typename InputIterator> + void OverwriteWith(InputIterator src_begin, InputIterator src_end) { + const int src_size = std::distance(src_begin, src_end); + if (src_size > this->m_capacity) { + this->m_size = 0; + Reallocate(2 * src_size); + } + this->m_size = src_size; + std::uninitialized_copy(src_begin, src_end, this->begin()); + } + + /// Quadratic complexity; only use this for small copies. + template <typename Range> + void DeduplicateCopy(const Range& range) { + for (auto it = range.begin(), end = range.end(); it != end; ++it) { + auto value = *it; + if (std::find_if(range.begin(), it, [&](const auto& v) { return value == v; }) == it) + this->emplace_back(value); + } + } + + /// Resize the array so that it contains `new_size` elements. + /// + /// - If the new size is greater than the current size, new elements are added and + /// default initialized. Iterators may be invalidated. + /// - If the new size is less than the current size, excess elements are destroyed. + /// + /// @param new_size The new size of the array. + void Resize(int new_size) { + if (this->m_capacity < new_size) + Reallocate(new_size); + + if (this->m_size < new_size) { + std::uninitialized_default_construct(this->m_data + this->m_size, + this->m_data + new_size); + } else { + std::destroy(this->m_data + new_size, this->m_data + this->m_size); + } + + this->m_size = new_size; + } + +private: + void GrowIfNeeded() { + if (this->m_size < this->m_capacity) + return; + Reallocate(2 * this->m_size + 2); + } + + void Reallocate(int new_capacity) { + const int num_bytes = sizeof(T) * new_capacity; + auto* new_buffer = + static_cast<T*>(m_allocator->AllocImpl(num_bytes, alignof(std::max_align_t))); + auto* old_buffer = this->m_data; + auto* capacity = &this->m_capacity; + UninitializedCopyTo(new_buffer); + this->m_data = new_buffer; + *capacity = new_capacity; + Free(old_buffer); + } + + void UninitializedCopyTo(T* destination) const { + std::uninitialized_copy(this->begin(), this->end(), destination); + } + + void Free(void* ptr) { + if (ptr) + m_allocator->Free(ptr); + } + + Allocator* m_allocator{}; +}; + +} // namespace ore diff --git a/lib/EventFlow/include/ore/BinaryFile.h b/lib/EventFlow/include/ore/BinaryFile.h new file mode 100644 index 00000000..c2d62587 --- /dev/null +++ b/lib/EventFlow/include/ore/BinaryFile.h @@ -0,0 +1,153 @@ +#pragma once + +#include <ore/StringView.h> +#include <ore/Types.h> +#include <type_traits> +#include <utility> + +namespace ore { + +template <typename T> +constexpr T AlignUpToPowerOf2(T val, int base) { + return val + base - 1 & static_cast<unsigned int>(-base); +} + +struct RelocationTable; + +struct BinaryBlockHeader { + BinaryBlockHeader* FindNextBlock(int type); + const BinaryBlockHeader* FindNextBlock(int type) const; + BinaryBlockHeader* GetNextBlock(); + const BinaryBlockHeader* GetNextBlock() const; + void SetNextBlock(BinaryBlockHeader* block); + + u32 magic; + int next_block_offset; +}; + +struct BinaryFileHeader { + bool IsValid(s64 magic_, int ver_major_, int ver_minor_, int ver_patch_, int ver_sub_) const; + bool IsSignatureValid(s64 magic_) const; + bool IsVersionValid(int major, int minor, int patch, int sub) const; + bool IsEndianReverse() const; + bool IsEndianValid() const; + + bool IsAlignmentValid() const; + int GetAlignment() const; + void SetAlignment(int alignment_); + + bool IsRelocated() const; + void SetRelocated(bool relocated); + + void SetByteOrderMark(); + + int GetFileSize() const; + void SetFileSize(int size); + + StringView GetFileName() const; + void SetFileName(const StringView& name); + + RelocationTable* GetRelocationTable(); + void SetRelocationTable(RelocationTable* table); + + BinaryBlockHeader* GetFirstBlock(); + const BinaryBlockHeader* GetFirstBlock() const; + void SetFirstBlock(BinaryBlockHeader* block); + + BinaryBlockHeader* FindFirstBlock(int type); + const BinaryBlockHeader* FindFirstBlock(int type) const; + + u64 magic; + u8 ver_major; + u8 ver_minor; + u8 ver_patch; + u8 ver_sub; + s16 bom; + u8 alignment; + u8 _f; + int file_name_offset; + u16 relocation_flags; + u16 first_block_offset; + int relocation_table_offset; + int file_size; +}; + +template <typename T> +struct BinTString { + // Make it impossible to accidentally construct a (partial, broken) copy. + BinTString(const BinTString&) = delete; + auto operator=(const BinTString&) = delete; + + T* data() { return chars; } + const T* data() const { return chars; } + + T& operator[](size_t idx) { return data()[idx]; } + const T& operator[](size_t idx) const { return data()[idx]; } + + auto begin() { return data(); } + auto begin() const { return data(); } + + auto end() { return data() + length; } + auto end() const { return data() + length; } + + bool empty() const { return length == 0; } + + // NOLINTNEXTLINE(google-explicit-constructor) + operator TStringView<T>() const { return {data(), length}; } + + BinTString* NextString() { return const_cast<BinTString*>(std::as_const(*this).NextString()); } + + const BinTString* NextString() const { + // XXX: this shouldn't have to be a separate case. + if constexpr (std::is_same_v<T, wchar_t>) { + const auto offset = ((2 + (4 * (length + 1) - 1)) & -4) + 2; + return reinterpret_cast<const BinTString*>(reinterpret_cast<const char*>(this) + + offset); + + } else { + // + 1 for the null terminator + const auto offset = offsetof(BinTString, chars) + sizeof(T) * (length + 1); + return reinterpret_cast<const BinTString*>( + reinterpret_cast<const char*>(this) + + AlignUpToPowerOf2(offset, alignof(BinTString))); + } + } + + u16 length; + T chars[1]; +}; + +using BinString = BinTString<char>; +using BinWString = BinTString<wchar_t>; + +template <typename T> +struct BinTPtr { + void Clear() { offset_or_ptr = 0; } + void Set(T* ptr) { offset_or_ptr = reinterpret_cast<u64>(ptr); } + + // Only use this after relocation. + T* Get() { return reinterpret_cast<T*>(offset_or_ptr); } + const T* Get() const { return reinterpret_cast<const T*>(offset_or_ptr); } + + void SetOffset(void* base, void* ptr) { + offset_or_ptr = static_cast<int>(ptr ? uintptr_t(ptr) - uintptr_t(base) : 0); + } + + u64 GetOffset() const { return offset_or_ptr; } + + T* ToPtr(void* base) const { + const auto offset = static_cast<int>(offset_or_ptr); + if (offset == 0) + return nullptr; + return reinterpret_cast<T*>(reinterpret_cast<char*>(base) + offset); + } + + void Relocate(void* base) { Set(ToPtr(base)); } + void Unrelocate(void* base) { SetOffset(base, Get()); } + + u64 offset_or_ptr; +}; + +static_assert(sizeof(u64) >= sizeof(void*)); + +} // namespace ore diff --git a/lib/EventFlow/include/ore/BitUtils.h b/lib/EventFlow/include/ore/BitUtils.h new file mode 100644 index 00000000..fd907f9c --- /dev/null +++ b/lib/EventFlow/include/ore/BitUtils.h @@ -0,0 +1,149 @@ +#pragma once + +#include <ore/Allocator.h> +#include <ore/Types.h> + +namespace ore { + +constexpr int PopCount(u32 x) { + x = x - ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0F0F0F0F; + x += (x >> 8); + x += (x >> 16); + return int(x & 0x3f); +} + +constexpr int PopCount(u64 x) { + x = x - ((x >> 1) & 0x5555555555555555); + x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333); + x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0F; + x += (x >> 8); + x += (x >> 16); + x += (x >> 32); + return int(x & 0x7f); +} + +constexpr int CountTrailingZeros(u32 x) { + return PopCount((x & -x) - 1); +} + +constexpr int CountTrailingZeros(u64 x) { + return PopCount((x & -x) - 1); +} + +namespace detail { +template <typename T> +constexpr T AlignUpToPowerOf2(T val, int base) { + return val + base - 1 & static_cast<unsigned int>(-base); +} +} // namespace detail + +class BitArray { +public: + using Word = size_t; + static constexpr int NumBitsPerWord = sizeof(Word) * 8; + static constexpr int ClearMask = ~(NumBitsPerWord - 1); + static constexpr int ShiftAmount = CountTrailingZeros(u32(NumBitsPerWord)); + + class TestIter { + public: + TestIter(const Word* start, const Word* end); + TestIter& operator++(); + + int operator*() const { return m_bit; } + bool operator==(const TestIter& other) const { return m_bit == other.m_bit; } + bool operator!=(const TestIter& other) const { return !operator==(other); } + + private: + void SetInvalid() { + m_bit = -1; + m_current_word = nullptr; + m_last_word = nullptr; + m_next = 0; + } + + int m_bit; + const Word* m_current_word; + const Word* m_last_word; + Word m_next; + }; + + /// Same as TestIter but clears bits after iterating over them. + class TestClearIter { + public: + TestClearIter(Word* start, Word* end); + TestClearIter& operator++(); + int operator*() const { return m_bit; } + bool operator==(const TestClearIter& other) const { return m_bit == other.m_bit; } + bool operator!=(const TestClearIter& other) const { return !operator==(other); } + + private: + void SetInvalid() { + m_bit = -1; + m_current_word = nullptr; + m_last_word = nullptr; + m_next = 0; + } + + int m_bit; + Word* m_current_word; + Word* m_last_word; + Word m_next; + }; + + constexpr BitArray() = default; + constexpr BitArray(void* buffer, int num_bits) { SetData(buffer, num_bits); } + constexpr BitArray(ore::Allocator* allocator, int num_bits) { + AllocateBuffer(allocator, num_bits); + } + + void SetData(void* buffer, int num_bits) { + m_words = reinterpret_cast<Word*>(buffer); + m_num_bits = num_bits; + } + + void AllocateBuffer(ore::Allocator* allocator, int num_bits) { + SetData(allocator->New(GetRequiredBufferSize(num_bits)), num_bits); + SetAllOff(); + } + + void FreeBufferIfNeeded(ore::Allocator* allocator) { + if (m_words) + allocator->Delete(m_words); + } + + void FreeBuffer(ore::Allocator* allocator) { allocator->Delete(m_words); } + + bool Test(int bit) const { + return (GetWord(bit) & (Word(1) << (Word(bit) % NumBitsPerWord))) != 0; + } + void Set(int bit) { GetWord(bit) |= Word(1) << (Word(bit) % NumBitsPerWord); } + void Clear(int bit) { GetWord(bit) &= ~(Word(1) << (Word(bit) % NumBitsPerWord)); } + + void SetAllOn(); + void SetAllOff(); + TestIter BeginTest() const; + TestIter EndTest() const; + TestClearIter BeginTestClear(); + TestClearIter EndTestClear(); + + static int GetRequiredBufferSize(int num_bits) { + return sizeof(Word) * (detail::AlignUpToPowerOf2(num_bits, NumBitsPerWord) >> ShiftAmount); + } + +private: + Word& GetWord(int bit) const { return m_words[bit >> ShiftAmount]; } + int GetNumWords() const { return int((m_num_bits + NumBitsPerWord - 1) >> ShiftAmount); } + + void Fill(int num, Word value) { + auto* it = m_words; + for (int i = num - 1; i >= 0; --i) + *it++ = value; + } + + Word* m_words{}; + int m_num_bits{}; +}; + +} // namespace ore diff --git a/lib/EventFlow/include/ore/Buffer.h b/lib/EventFlow/include/ore/Buffer.h new file mode 100644 index 00000000..0952ab6c --- /dev/null +++ b/lib/EventFlow/include/ore/Buffer.h @@ -0,0 +1,25 @@ +#pragma once + +#include <ore/Allocator.h> +#include <ore/Types.h> + +namespace ore { + +struct Buffer { + template <typename T> + void Allocate(Allocator* allocator, int num) { + size = sizeof(T) * num; + data = static_cast<char*>(allocator->New(size)); + } + + void Free(Allocator* allocator) { + allocator->Free(data); + data = nullptr; + size = 0; + } + + char* data; + int size; +}; + +} // namespace ore diff --git a/lib/EventFlow/include/ore/EnumUtil.h b/lib/EventFlow/include/ore/EnumUtil.h new file mode 100644 index 00000000..e0c2d953 --- /dev/null +++ b/lib/EventFlow/include/ore/EnumUtil.h @@ -0,0 +1,115 @@ +#pragma once + +#include <iterator> +#include <ore/IterRange.h> +#include <ore/StringView.h> +#include <type_traits> + +namespace ore { + +namespace detail::EnumUtil { + +int FindIndex(int value, const IterRange<const int*>& values); +void Parse(const IterRange<StringView*>& out, StringView definition); + +constexpr int CountValues(const char* text_all, size_t text_all_len) { + int count = 1; + for (size_t i = 0; i < text_all_len; ++i) { + if (text_all[i] == ',') + ++count; + } + return count; +} + +} // namespace detail::EnumUtil + +template <class T> +struct Enum { +public: + Enum() { T::Init(); } + + static Enum<T>& Info() { return s_Info; } + + StringView name{}; + IterRange<StringView*> members{}; + +private: + static inline Enum<T> s_Info{}; +}; + +#define ORE_ENUM(NAME, ...) \ + class NAME { \ + public: \ + enum Type { __VA_ARGS__ }; \ + \ + static void Init() { \ + static ore::StringView names[cCount]; \ + ore::detail::EnumUtil::Parse(ore::IterRange<ore::StringView*>(names), cTextAll); \ + ore::Enum<NAME>::Info().name = #NAME; \ + ore::Enum<NAME>::Info().members = ore::IterRange<ore::StringView*>(names); \ + } \ + \ + static constexpr int Size() { return cCount; } \ + static constexpr Type Invalid() { return Type(Size()); } \ + \ + private: \ + static constexpr const char* cTextAll = #__VA_ARGS__; \ + static constexpr size_t cTextAllLen = sizeof(#__VA_ARGS__); \ + static constexpr int cCount = ore::detail::EnumUtil::CountValues(cTextAll, cTextAllLen); \ + }; + +// FIXME +template <class T> +class ValuedEnum { +public: + ValuedEnum() { T::Init(); } + + static Enum<T>& Info() { return s_Info; } + + StringView name{}; + IterRange<StringView*> members{}; + +private: + static inline Enum<T> s_Info{}; +}; + +#define ORE_VALUED_ENUM(NAME, ...) \ + class NAME { \ + public: \ + enum Type { __VA_ARGS__ }; \ + \ + static void Init() { \ + static ore::StringView names[cCount]; \ + ore::detail::EnumUtil::Parse(ore::IterRange<ore::StringView*>(names), cTextAll); \ + ore::ValuedEnum<NAME>::Info().name = #NAME; \ + ore::ValuedEnum<NAME>::Info().members = ore::IterRange<ore::StringView*>(names); \ + } \ + \ + static constexpr int Size() { return cCount; } \ + static constexpr Type Invalid() { return Type(Size()); } \ + \ + private: \ + static constexpr const char* cTextAll = #__VA_ARGS__; \ + static constexpr size_t cTextAllLen = sizeof(#__VA_ARGS__); \ + static constexpr int cCount = ore::detail::EnumUtil::CountValues(cTextAll, cTextAllLen); \ + }; + +/// For storing an enum with a particular storage size when specifying the underlying type of the +/// enum is not an option. +template <typename Enum, typename Storage> +struct SizedEnum { + static_assert(std::is_enum<Enum>()); + static_assert(!std::is_enum<Storage>()); + + constexpr SizedEnum() = default; + constexpr SizedEnum(Enum value) { *this = value; } + constexpr operator Enum() const { return static_cast<Enum>(mValue); } + constexpr SizedEnum& operator=(Enum value) { + mValue = static_cast<Storage>(value); + return *this; + } + + Storage mValue; +}; + +} // namespace ore diff --git a/lib/EventFlow/include/ore/IntrusiveList.h b/lib/EventFlow/include/ore/IntrusiveList.h new file mode 100644 index 00000000..33f4e257 --- /dev/null +++ b/lib/EventFlow/include/ore/IntrusiveList.h @@ -0,0 +1,100 @@ +#pragma once + +#include <utility> + +namespace ore { + +class IntrusiveListNode { +public: + constexpr explicit IntrusiveListNode() { m_prev = m_next = this; } + + IntrusiveListNode(const IntrusiveListNode&) = delete; + auto operator=(const IntrusiveListNode&) = delete; + + IntrusiveListNode(IntrusiveListNode&& other) noexcept { *this = std::move(other); } + IntrusiveListNode& operator=(IntrusiveListNode&& other) noexcept { + auto* prev = other.m_prev; + other.m_prev = this; + prev->m_next = this; + m_prev = prev; + m_next = &other; + other.Erase(); + return *this; + } + + IntrusiveListNode* Prev() const { return m_prev; } + IntrusiveListNode* Next() const { return m_next; } + bool IsLinked() const { return Prev() || Next(); } + + void Erase() { + auto* next = m_next; + auto* next_prev = next->m_prev; + m_prev->m_next = next; + next->m_prev = m_prev; + // This is a circular list. + next_prev->m_next = this; + m_prev = next_prev; + } + + void InsertFront(IntrusiveListNode* node) { + auto* prev = node->m_prev; + node->m_prev = m_prev; + prev->m_next = this; + m_prev->m_next = node; + m_prev = prev; + } + +private: + template <typename T> + friend class IntrusiveList; + + IntrusiveListNode* m_prev{}; + IntrusiveListNode* m_next{}; +}; + +template <typename T> +class IntrusiveList { +public: + void SetOffset(int offset) { m_offset = offset; } + + bool Empty() const { return m_node.m_next == &m_node; } + T* Front() { return NodeToItemWithNullCheck(m_node.m_next); } + T* Back() { return NodeToItemWithNullCheck(m_node.m_prev); } + const T* Front() const { return NodeToItemWithNullCheck(m_node.m_next); } + const T* Back() const { return NodeToItemWithNullCheck(m_node.m_prev); } + + void Erase(T* item) { ItemToNode(item)->Erase(); } + + void InsertFront(T* item) { m_node.InsertFront(ItemToNode(item)); } + +private: + IntrusiveListNode* ItemToNode(T* item) const { + return reinterpret_cast<IntrusiveListNode*>(reinterpret_cast<char*>(item) + m_offset); + } + + const IntrusiveListNode* ItemToNode(const T* item) const { + return reinterpret_cast<const IntrusiveListNode*>(reinterpret_cast<const char*>(item) + + m_offset); + } + + T* NodeToItem(IntrusiveListNode* node) const { + return reinterpret_cast<T*>(reinterpret_cast<char*>(node) - m_offset); + } + + const T* NodeToItem(const IntrusiveListNode* node) const { + return reinterpret_cast<const T*>(reinterpret_cast<const char*>(node) - m_offset); + } + + T* NodeToItemWithNullCheck(IntrusiveListNode* node) const { + return node == &m_node ? nullptr : NodeToItem(node); + } + + const T* NodeToItemWithNullCheck(const IntrusiveListNode* node) const { + return node == &m_node ? nullptr : NodeToItem(node); + } + + IntrusiveListNode m_node; + int m_offset = -1; +}; + +} // namespace ore diff --git a/lib/EventFlow/include/ore/IterRange.h b/lib/EventFlow/include/ore/IterRange.h new file mode 100644 index 00000000..338628ca --- /dev/null +++ b/lib/EventFlow/include/ore/IterRange.h @@ -0,0 +1,28 @@ +#pragma once + +#include <iterator> + +namespace ore { + +template <typename T> +class IterRange { +public: + constexpr IterRange() = default; + constexpr IterRange(const T& begin_, const T& end_) : m_begin(begin_), m_end(end_) {} + template <typename Other> + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr IterRange(Other& x) : IterRange(std::begin(x), std::end(x)) {} + + constexpr IterRange(const T& begin, int size) : m_begin(begin), m_end(begin + size) {} + + const auto& begin() const { return m_begin; } + const auto& end() const { return m_end; } + + int size() const { return end() - begin(); } + +private: + T m_begin{}; + T m_end{}; +}; + +} // namespace ore diff --git a/lib/EventFlow/include/ore/RelocationTable.h b/lib/EventFlow/include/ore/RelocationTable.h new file mode 100644 index 00000000..4593abb3 --- /dev/null +++ b/lib/EventFlow/include/ore/RelocationTable.h @@ -0,0 +1,50 @@ +#pragma once + +#include <ore/Types.h> + +namespace ore { + +struct RelocationTable { + struct Section { + struct Entry { + /// Offset to pointers to relocate + int pointers_offset; + /// Bit field that determines which pointers need to be relocated + /// (next to 32 contiguous pointers starting from the listed offset) + u32 mask; + }; + + void SetPtr(void* ptr_); + void* GetPtr() const; + void* GetPtrInFile(void* base) const; + void* GetBasePtr(void* base) const; + u32 GetSize() const; + + u64 ptr; + int offset; + int size; + int first_entry_idx; + int num_entries; + }; + + u32 magic; + int table_start_offset; + int num_sections; + Section sections[1]; + + Section* GetSections() { return sections; } + const Section* GetSections() const { return sections; } + + Section::Entry* GetEntries() { + return reinterpret_cast<Section::Entry*>(GetSections() + num_sections); + } + const Section::Entry* GetEntries() const { + return reinterpret_cast<const Section::Entry*>(GetSections() + num_sections); + } + + void Relocate(); + void Unrelocate(); + static int CalcSize(int num_sections, int num_entries); +}; + +} // namespace ore diff --git a/lib/EventFlow/include/ore/ResDic.h b/lib/EventFlow/include/ore/ResDic.h new file mode 100644 index 00000000..d9a686f6 --- /dev/null +++ b/lib/EventFlow/include/ore/ResDic.h @@ -0,0 +1,62 @@ +#pragma once + +#include <ore/BinaryFile.h> +#include <ore/StringView.h> +#include <ore/Types.h> + +namespace ore { + +struct ResEndian; + +struct ResDicEntry { + StringView GetKey() const { return *name.Get(); } + + // Bits 3-7: index of the byte that should be checked + // Bits 0-2: index of the bit in that byte + int compact_bit_idx; + u16 next_indices[2]; + BinTPtr<BinString> name; +}; + +struct ResDic { + static int FindRefBit(const StringView& str1, const StringView& str2); + + const ResDicEntry* FindEntry(const StringView& key) const { + auto* prev = &entries[0]; + auto* entry = &entries[prev->next_indices[0]]; + while (prev->compact_bit_idx < entry->compact_bit_idx) { + const int bit_idx = entry->compact_bit_idx; + long bit = 0; + if (u32(key.length()) > u32(bit_idx >> 3)) + bit = ((key[key.length() + -((bit_idx >> 3) + 1)] >> (bit_idx & 7))) & 1; + + prev = entry; + entry = &entries[prev->next_indices[bit]]; + } + return entry; + } + + /// Returns the index for the specified key or -1 if it cannot be found. + int FindIndex(const StringView& key) const { + const auto* entry = FindEntry(key); + const auto entry_name = entry->GetKey(); + bool ok = [&] { return StringView(key.data(), key.length()) == entry_name; }(); + if (!ok) + return -1; + return static_cast<int>(entry - &GetEntries()[1]); + } + + /// Entry 0 is the root entry. + ResDicEntry* GetEntries() { return entries; } + /// Entry 0 is the root entry. + const ResDicEntry* GetEntries() const { return entries; } + + u32 magic; + int num_entries; + ResDicEntry entries[1]; + // Followed by ResDicEntry[num_entries]. +}; + +void SwapEndian(ResEndian* endian, ResDic* dic); + +} // namespace ore diff --git a/lib/EventFlow/include/ore/ResEndian.h b/lib/EventFlow/include/ore/ResEndian.h new file mode 100644 index 00000000..dc38e111 --- /dev/null +++ b/lib/EventFlow/include/ore/ResEndian.h @@ -0,0 +1,75 @@ +#pragma once + +#ifdef _MSC_VER +#include <stdlib.h> +#endif + +#include <cstring> +#include <ore/Types.h> + +namespace ore { + +[[nodiscard]] inline u8 SwapEndian(u8 x) { + return x; +} + +[[nodiscard]] inline u16 SwapEndian(u16 x) { +#ifdef _MSC_VER + return _byteswap_ushort(x); +#else + return __builtin_bswap16(x); +#endif +} + +[[nodiscard]] inline u32 SwapEndian(u32 x) { +#ifdef _MSC_VER + return _byteswap_ulong(x); +#else + return __builtin_bswap32(x); +#endif +} + +[[nodiscard]] inline u64 SwapEndian(u64 x) { +#ifdef _MSC_VER + return _byteswap_uint64(x); +#else + return __builtin_bswap64(x); +#endif +} + +[[nodiscard]] inline s8 SwapEndian(s8 x) { + return SwapEndian(u8(x)); +} + +[[nodiscard]] inline s16 SwapEndian(s16 x) { + return SwapEndian(u16(x)); +} + +[[nodiscard]] inline s32 SwapEndian(s32 x) { + return SwapEndian(u32(x)); +} + +[[nodiscard]] inline s64 SwapEndian(s64 x) { + return SwapEndian(u64(x)); +} + +[[nodiscard]] inline f32 SwapEndian(f32 x) { + static_assert(sizeof(u32) == sizeof(f32)); + u32 i; + std::memcpy(&i, &x, sizeof(i)); + i = SwapEndian(i); + std::memcpy(&x, &i, sizeof(i)); + return x; +} + +template <typename T> +inline void SwapEndian(T* value) { + *value = SwapEndian(*value); +} + +struct ResEndian { + char* base; + bool is_serializing; +}; + +} // namespace ore diff --git a/lib/EventFlow/include/ore/ResMetaData.h b/lib/EventFlow/include/ore/ResMetaData.h new file mode 100644 index 00000000..60e84cd0 --- /dev/null +++ b/lib/EventFlow/include/ore/ResMetaData.h @@ -0,0 +1,58 @@ +#pragma once + +#include <ore/BinaryFile.h> +#include <ore/EnumUtil.h> +#include <ore/ResDic.h> +#include <ore/StringView.h> +#include <ore/Types.h> + +namespace ore { + +struct ResDic; +struct ResEndian; + +struct ResMetaData { + struct ActorIdentifier { + BinTPtr<BinString> name; + BinTPtr<BinString> sub_name; + }; + + union Value { + BinTPtr<ResMetaData> container; + // Also used for booleans. Anything that is != 0 is treated as true. + int i; + float f; + BinTPtr<BinString> str; + BinTPtr<BinWString> wstr; + ActorIdentifier actor; + }; + + ORE_ENUM(DataType, kArgument, kContainer, kInt, kBool, kFloat, kString, kWString, kIntArray, kBoolArray, kFloatArray, kStringArray, kWStringArray, kActorIdentifier) + + /// @warning Only usable if type == kContainer. + const ResMetaData* Get(const StringView& key, DataType::Type expected_type) const { + const int idx = dictionary.Get()->FindIndex(key); + if (idx == -1) + return nullptr; + + const auto* meta = (&value.container + idx)->Get(); + if (meta->type != expected_type) + return nullptr; + + return meta; + } + + SizedEnum<DataType::Type, u8> type; + u16 num_items; + BinTPtr<ResDic> dictionary; + Value value; +}; + +// XXX: is this unused? +struct ResUserData { + ORE_ENUM(DataType, kInt, kFloat, kString, kWString, kStream) +}; + +void SwapEndian(ResEndian* endian, ResMetaData* res); + +} // namespace ore diff --git a/lib/EventFlow/include/ore/StringPool.h b/lib/EventFlow/include/ore/StringPool.h new file mode 100644 index 00000000..2777ce2b --- /dev/null +++ b/lib/EventFlow/include/ore/StringPool.h @@ -0,0 +1,20 @@ +#pragma once + +#include <ore/BinaryFile.h> +#include <ore/Types.h> + +namespace ore { + +struct StringPool : BinaryBlockHeader { + int GetLength() const; + void SetLength(int len); + + BinString* GetFirstString() { return dummy_string.NextString(); } + + u32 reserved_8; + u32 reserved_c; + int length; + BinString dummy_string; +}; + +} // namespace ore diff --git a/lib/EventFlow/include/ore/StringView.h b/lib/EventFlow/include/ore/StringView.h new file mode 100644 index 00000000..f23d47f5 --- /dev/null +++ b/lib/EventFlow/include/ore/StringView.h @@ -0,0 +1,79 @@ +#pragma once + +#include <algorithm> +#include <ore/Types.h> +#include <string> + +namespace ore { + +template <typename T> +constexpr size_t StringLength(const T* str) { + if (str == nullptr || str[0] == 0) + return 0; + + size_t len = 0; + while (*str++ != 0) + ++len; + +#ifdef MATCHING_HACK_NX_CLANG + __builtin_assume(len <= 0xffffffff); +#endif + return len; +} + +template <typename T> +class TStringView { +public: + // Annoyingly enough, this cannot be defaulted (otherwise Clang will not dynamically + // initialize static StringView variables). + TStringView() {} + + constexpr TStringView(const T* data, size_t len) : m_data(data), m_len(len) {} + + /// @param data A null-terminated string. Must not be nullptr. + // NOLINTNEXTLINE(google-explicit-constructor) + TStringView(const T* data) : m_data(data), m_len(StringLength(data)) {} + + constexpr const T* data() const { return m_data; } + constexpr int size() const { return m_len; } + constexpr int length() const { return m_len; } + constexpr bool empty() const { return size() == 0; } + + constexpr auto begin() const { return m_data; } + constexpr auto cbegin() const { return m_data; } + constexpr auto end() const { return m_data + m_len; } + constexpr auto cend() const { return m_data + m_len; } + + const T& operator[](size_t idx) const { return m_data[idx]; } + + static int Compare(TStringView lhs, TStringView rhs) { + const T* s1 = lhs.data(); + const T* s2 = rhs.data(); + int len = std::min(lhs.size(), rhs.size()); + if (len < 1) + return lhs.size() - rhs.size(); + while (len-- > 0) { + if (*s1 == 0 || *s1 != *s2) + return *s1 - *s2; + ++s1, ++s2; + } + return lhs.size() - rhs.size(); + } + + int Compare(TStringView rhs) const { return Compare(*this, rhs); } + + friend bool operator==(TStringView lhs, TStringView rhs) { + return lhs.size() == rhs.size() && Compare(lhs, rhs) == 0; + } + + friend bool operator!=(TStringView lhs, TStringView rhs) { return !operator==(lhs, rhs); } + +private: + const T* m_data{}; + u32 m_len{}; +}; + +using StringView = TStringView<char>; +using WStringView = TStringView<wchar_t>; + +} // namespace ore diff --git a/lib/EventFlow/include/ore/Types.h b/lib/EventFlow/include/ore/Types.h new file mode 100644 index 00000000..601d9dd6 --- /dev/null +++ b/lib/EventFlow/include/ore/Types.h @@ -0,0 +1,20 @@ +#pragma once + +#include <cstddef> +#include <cstdint> + +using u8 = std::uint8_t; +using u16 = std::uint16_t; +using u32 = std::uint32_t; +using u64 = std::uint64_t; + +using s8 = std::int8_t; +using s16 = std::int16_t; +using s32 = std::int32_t; +using s64 = std::int64_t; + +using f32 = float; +using f64 = double; + +using char16 = char16_t; +using size_t = std::size_t; diff --git a/lib/EventFlow/src/evfl/Action.cpp b/lib/EventFlow/src/evfl/Action.cpp new file mode 100644 index 00000000..5fe7a490 --- /dev/null +++ b/lib/EventFlow/src/evfl/Action.cpp @@ -0,0 +1,56 @@ +#include <evfl/Action.h> +#include <evfl/Flowchart.h> + +namespace evfl { + +ActionDoneHandler::ActionDoneHandler(FlowchartObj* obj, FlowchartContext* context, int node_idx) + : m_context(context) { + m_node_idx = node_idx; + m_obj = obj; + m_node_counter = context->GetNode(node_idx).GetNodeCounter(); +} + +FlowchartContextNode* ActionDoneHandler::GetContextNode() { + if (!m_context) + return nullptr; + + auto& node = m_context->GetNode(m_node_idx); + if (node.GetNodeCounter() != m_node_counter) + return nullptr; + + return &node; +} + +void ActionDoneHandler::InvokeFromFlowchartImpl() { + auto* node = GetContextNode(); + if (!node) + return; + + node->m_state = FlowchartContextNode::State::kDone; + m_context->ProcessContext(); +} + +void ActionDoneHandler::InvokeFromTimelineImpl() {} + +bool ActionDoneHandler::IsWaitingJoin() { + if (!m_context) + return false; + + const auto& node = m_context->GetNode(m_node_idx); + if (node.GetNodeCounter() != m_node_counter) + return false; + + return node.GetState() == FlowchartContextNode::State::kWaiting; +} + +bool ActionDoneHandler::CancelWaiting() { + auto* node = GetContextNode(); + if (!node) + return false; + + node->m_state = FlowchartContextNode::State::kInvoked; + m_handled = false; + return true; +} + +} // namespace evfl diff --git a/lib/EventFlow/src/evfl/Flowchart.cpp b/lib/EventFlow/src/evfl/Flowchart.cpp new file mode 100644 index 00000000..84c0af05 --- /dev/null +++ b/lib/EventFlow/src/evfl/Flowchart.cpp @@ -0,0 +1,694 @@ +#include <algorithm> +#include <evfl/Action.h> +#include <evfl/Flowchart.h> +#include <evfl/Param.h> +#include <evfl/Query.h> +#include <evfl/ResFlowchart.h> +#include <iterator> +#include <ore/Array.h> +#include <ore/BitUtils.h> +#include <ore/IterRange.h> +#include <ore/ResDic.h> +#include <ore/StringView.h> + +namespace evfl { + +int FlowchartContext::s_GlobalCounter{}; + +FlowchartContext::FlowchartContext() { + m_handlers.SetOffset(ActionDoneHandler::GetListNodeOffset()); +} + +void FlowchartContext::Start(MetaDataPack* pack) { + auto& obj = m_objs[m_obj_idx]; + const auto& entry_point = obj.GetFlowchart()->entry_points.Get()[m_active_entry_point_idx]; + + const auto main_event_idx = entry_point.main_event_idx; + if (main_event_idx == 0xffff) + return; + + m_metadata_pack = pack; + + const int node_idx = AllocNode(); + auto& node = m_nodes[node_idx]; + node.m_obj = &obj; + node.m_event_idx = main_event_idx; + node.m_next_node_idx = -1; + node.m_idx = node_idx; + node.m_state = FlowchartContextNode::State::kNotInvoked; + AllocVariablePack(node, entry_point); + + ProcessContext(); +} + +int FlowchartContext::AllocNode() { + int idx = m_next_node_idx; + + if (idx == 0xffff) { + idx = m_nodes.size(); + m_nodes.Resize(2 * m_nodes.size()); + for (int i = idx, n = m_nodes.size(); i < n - 1; ++i) { + m_nodes[i].m_next_node_idx = i + 1; + m_nodes[i].m_state = FlowchartContextNode::State::kFree; + } + } + + auto& node = m_nodes[idx]; + m_next_node_idx = node.m_next_node_idx; + node.m_event_idx = -1; + node.m_next_node_idx = -1; + node.m_idx = -1; + node.m_owns_variable_pack = false; + node.m_obj = nullptr; + node.m_variable_pack = nullptr; + node.m_node_counter = ++s_GlobalCounter; + node.m_state = FlowchartContextNode::State::kInvalid; + ++m_num_allocated_nodes; + return idx; +} + +void FlowchartContext::AllocVariablePack(FlowchartContextNode& node, + const ResEntryPoint& entry_point) { + FreeVariablePack(node); + + if (entry_point.num_variable_defs != 0) { + auto* pack = m_allocator.New<VariablePack>(); + pack->Init(m_allocator.GetArg(), &entry_point); + node.m_variable_pack = pack; + node.m_owns_variable_pack = true; + } +} + +void FlowchartContext::ProcessContext() { + if (m_is_processing) { + _91 = 1; + } else { + m_is_processing = true; + do { + _91 = 0; + for (int i = 0, n = m_nodes.size(); i < n; ++i) { + if (!m_nodes[i].IsInvalidOrFree()) + _91 = (ProcessContextNode(i) | (_91 != 0)) & 1; + } + } while (_91); + m_is_processing = false; + } +} + +FlowchartObj* FlowchartContext::FindFlowchartObj(ore::StringView name) { + auto it = std::find_if(m_objs.begin(), m_objs.end(), [&](const FlowchartObj& obj) { + return name == *obj.GetFlowchart()->name.Get(); + }); + return it == m_objs.end() ? nullptr : it; +} + +const FlowchartObj* FlowchartContext::FindFlowchartObj(ore::StringView name) const { + auto it = std::find_if(m_objs.begin(), m_objs.end(), [&](const FlowchartObj& obj) { + return name == *obj.GetFlowchart()->name.Get(); + }); + return it == m_objs.end() ? nullptr : it; +} + +void FlowchartContext::UnbindAll() { + Clear(); + for (auto it = m_objs.begin(); it != m_objs.end(); ++it) + it->GetActBinder().UnbindAll(); +} + +void FlowchartContext::Clear() { + for (int i = 0, n = m_nodes.size(); i < n; ++i) { + FreeVariablePack(m_nodes[i]); + m_nodes[i].Reset(); + if (i < n - 1) { + m_nodes[i].m_next_node_idx = i + 1; + m_nodes[i].m_state = FlowchartContextNode::State::kFree; + } + } + + m_next_node_idx = 0; + m_num_allocated_nodes = 0; + m_metadata_pack = nullptr; + + while (!m_handlers.Empty()) { + auto* handler = m_handlers.Front(); + handler->m_list_node.Erase(); + handler->Reset(); + } +} + +void FlowchartContext::FreeVariablePack(FlowchartContextNode& node) { + if (node.m_variable_pack && node.m_owns_variable_pack) + m_allocator.DeleteAndNull(node.m_variable_pack); + + node.m_variable_pack = nullptr; + node.m_owns_variable_pack = false; +} + +void FlowchartContext::CopyVariablePack(FlowchartContextNode& src, FlowchartContextNode& dst) { + FreeVariablePack(dst); + dst.m_variable_pack = src.m_variable_pack; + dst.m_owns_variable_pack = false; +} + +bool FlowchartContext::ProcessContextNode(int node_idx) { + using State = FlowchartContextNode::State; + auto& node = GetNode(node_idx); + while (true) { + auto* obj = node.m_obj; + const auto* flowchart = obj->GetFlowchart(); + const auto& event = flowchart->events.Get()[node.m_event_idx]; + + int next_event_idx = -1; + + switch (event.type) { + case ResEvent::EventType::kAction: { + switch (node.m_state) { + case State::kNotInvoked: { + node.m_state = State::kInvoked; + + const auto actor_idx = event.actor_idx; +#ifdef EVFL_VER_LABO + const auto& actor = flowchart->actors.Get()[actor_idx]; +#else + const auto& actor = obj->GetFlowchart()->actors.Get()[actor_idx]; +#endif + const auto* actions = actor.actions.Get(); + const auto* arg_name = actor.argument_name.Get(); + const auto action_idx = event.actor_action_idx; + + const auto* binding = &obj->GetActBinder().GetBindings()[actor_idx]; + if (!arg_name->empty()) { + binding = TrackBackArgumentActor(node_idx, *arg_name); + if (!binding) + return false; + } + + const auto* action = binding->GetAction(*actions[action_idx].name.Get()); + const ActionArg arg(this, node_idx, binding->GetUserData(), action->user_data, + node.m_variable_pack, &event); + ActionDoneHandler done_handler(obj, this, node_idx); + m_handlers.InsertFront(&done_handler); + action->handler(arg, std::move(done_handler)); + return false; + } // action case State::kNotInvoked + case State::kDone: + next_event_idx = event.next_event_idx; + break; + default: + return false; + } + break; + } // case ResEvent::EventType::kAction + + case ResEvent::EventType::kSwitch: { + if (node.m_state != State::kNotInvoked) + return false; + + const auto actor_idx = event.actor_idx; + const auto& actor = flowchart->actors.Get()[actor_idx]; + const auto* queries = actor.queries.Get(); + const auto* arg_name = actor.argument_name.Get(); + const auto query_idx = event.actor_query_idx; + + const auto* binding = &obj->GetActBinder().GetBindings()[actor_idx]; + if (!arg_name->empty()) { + binding = TrackBackArgumentActor(node_idx, *arg_name); + if (!binding) + return false; + } + + const auto* query = binding->GetQuery(*queries[query_idx].name.Get()); + const QueryArg arg(this, node_idx, binding->GetUserData(), query->user_data, &event, + node.m_variable_pack); + const int result = query->handler(arg); + + next_event_idx = 0xffff; + ore::Array<const ResCase> cases{event.cases.Get(), event.num_cases}; + for (const auto& case_ : cases) { + if (case_.value == result) { + next_event_idx = case_.event_idx; + break; + } + } + break; + } // case ResEvent::EventType::kSwitch + + case ResEvent::EventType::kFork: { + if (node.m_state != State::kNotInvoked) + return false; + + node.m_event_idx = event.join_event_idx; + node.m_state = State::kInvoked; + UpdateNodeCounter(node_idx); + + int prev_fork_node_idx = -1; + int first_fork_node_idx = -1; + int last_fork_node_idx = -1; + + const ore::Array<const u16> forks{event.fork_event_indices.Get(), event.num_forks}; + for (const auto& fork : forks) { + last_fork_node_idx = AllocNode(); + auto& fork_node = GetNode(last_fork_node_idx); + fork_node.m_obj = obj; + fork_node.m_event_idx = fork; + fork_node.m_next_node_idx = node_idx; + fork_node.m_idx = prev_fork_node_idx; + fork_node.m_state = State::kNotInvoked; + if (first_fork_node_idx == -1) + first_fork_node_idx = last_fork_node_idx; + CopyVariablePack(node, fork_node); + prev_fork_node_idx = last_fork_node_idx; + } + + GetNode(first_fork_node_idx).m_idx = u16(last_fork_node_idx); + return true; + } // case ResEvent::EventType::kFork + + case ResEvent::EventType::kJoin: { + if (node.m_state != State::kDone) + return false; + next_event_idx = event.next_event_idx; + break; + } // case ResEvent::EventType::kJoin + + case ResEvent::EventType::kSubFlow: { + bool called; + bool valid_parameters; + bool tried_invoking = false; + switch (node.m_state) { + case State::kNotInvoked: { + tried_invoking = true; + node.m_state = State::kInvoked; + + const ore::StringView sub_flow_flowchart = *event.sub_flow_flowchart.Get(); + if (!sub_flow_flowchart.empty()) { + obj = FindFlowchartObj(sub_flow_flowchart); + if (obj == nullptr) { + called = false; + valid_parameters = false; + break; + } + } + + const int entry_point_idx = obj->GetFlowchart()->entry_point_names.Get()->FindIndex( + *event.sub_flow_entry_point.Get()); + if (entry_point_idx == -1) { + called = false; + valid_parameters = false; + break; + } + + const auto& entry_point = obj->GetFlowchart()->entry_points.Get()[entry_point_idx]; + + const u16 main_event_idx = entry_point.main_event_idx; + if (main_event_idx == 0xffff) { + node.m_state = State::kDone; + called = false; + valid_parameters = true; + break; + } + + // Optimization: if this is a tail call, we don't need to allocate a new node. + if (event.next_event_idx == 0xffff && event.params.Get() == nullptr) { + node.m_obj = obj; + node.m_event_idx = main_event_idx; + node.m_state = State::kNotInvoked; + AllocVariablePack(node, entry_point); + UpdateNodeCounter(node_idx); + } else { + const auto sub_flow_node_idx = AllocNode(); + auto& sub_flow_node = GetNode(sub_flow_node_idx); + sub_flow_node.m_obj = obj; + sub_flow_node.m_event_idx = main_event_idx; + sub_flow_node.m_next_node_idx = node_idx; + sub_flow_node.m_idx = sub_flow_node_idx; + sub_flow_node.m_state = State::kNotInvoked; + AllocVariablePack(sub_flow_node, entry_point); + } + CallSubFlowCallback(flowchart, &event, SubFlowCallbackType::kEnter); + called = true; + /// @bug valid_parameters should have been initialized to true here. + /// This bug causes LLVM to generate dumb code like + /// mov w8, wzr; mov w9, wzr; orr w8, w8, w9 (for the failure cases above) + /// or more worryingly: + /// mov w8, #1; orr w8, w8, w9 + /// where w9 is actually undefined! +#ifdef AVOID_UB + valid_parameters = true; +#endif + break; + } // subflow case State::kNotInvoked + case State::kInvoked: + return false; + case State::kDone: + next_event_idx = event.next_event_idx; + CallSubFlowCallback(flowchart, &event, SubFlowCallbackType::kLeave); + break; + default: + break; + } + if (tried_invoking) + return valid_parameters || called; + break; + } // case ResEvent::EventType::kSubFlow + } + + // We are checking for 0xffff (a 0xffff that comes from the ResEvent data), *not* -1. + if (next_event_idx == 0xffff) { + bool ready = false; + auto* node_2 = &node; + if (node.m_idx != node_idx) { + do { + node_2 = &GetNode(node_2->m_idx); + ready |= node_2->m_state != State::kWaiting; + } while (node_2->m_idx != node_idx); + } + + if (!ready) { + if (node.m_next_node_idx != 0xffff) + GetNode(node.m_next_node_idx).m_state = State::kDone; + + int next_node_idx = node_idx; + int next; + do { + auto& node_to_free = GetNode(next_node_idx); + next = node_to_free.m_idx; + FreeVariablePack(node_to_free); + node_to_free.Reset(); + node_to_free.m_next_node_idx = m_next_node_idx; + node_to_free.m_state = State::kFree; + m_next_node_idx = next_node_idx; + --m_num_allocated_nodes; + next_node_idx = next; + } while (next != node_idx); + return true; + } + + if (event.type == ResEvent::EventType::kAction) { + node.m_state = State::kWaiting; + return true; + } + + node_2->m_idx = node.m_idx; + auto& node_to_free = GetNode(node_idx); + FreeVariablePack(node_to_free); + node_to_free.Reset(); + node_to_free.m_next_node_idx = m_next_node_idx; + node_to_free.m_state = State::kFree; + m_next_node_idx = node_idx; + --m_num_allocated_nodes; + return true; + } + + node.m_event_idx = next_event_idx; + node.m_state = State::kNotInvoked; + UpdateNodeCounter(node_idx); + } +} + +ActorBinding* FlowchartContext::TrackBackArgumentActor(int node_idx, const ore::StringView& name) { + if (node_idx == -1) + return nullptr; + + const ore::ResMetaData* metadata; + const MetaDataPack* metadata_pack; + const VariablePack* variable_pack; + FlowchartObj* obj; + const ParamAccessor accessor{this, GetNode(node_idx).GetNextNodeIdx()}; + const auto real_name = + accessor.TrackBackArgument(&metadata, &metadata_pack, &variable_pack, &obj, name); + + if (real_name.empty() || !metadata || !obj) + return nullptr; + + const auto* param = metadata->Get(real_name, ore::ResMetaData::DataType::kActorIdentifier); + if (!param) + return nullptr; + + const ore::StringView actor_name = *param->value.actor.name.Get(); + const ore::StringView actor_sub_name = *param->value.actor.sub_name.Get(); + + auto actor = obj->GetFlowchart()->actors.Get(); + for (int i = 0; i < int(obj->GetFlowchart()->num_actors); ++i, ++actor) { + if (*actor->name.Get() == actor_name && *actor->secondary_name.Get() == actor_sub_name) { + if (!actor->argument_name.Get()->empty()) + return nullptr; + return &obj->GetActBinder().GetBindings()[i]; + } + } + + return nullptr; +} + +bool FlowchartContext::IsUsing(const ResFlowchart* flowchart) const { + auto* obj = FindFlowchartObj(*flowchart->name.Get()); + return obj && obj->GetActBinder().IsUsed(); +} + +// NON_MATCHING: if (((state | 4) & 7) != 4) -- extremely weird check +bool FlowchartContext::IsPlaying(const ResFlowchart* flowchart) const { + int state = 2; + for (int i = 0, n = m_nodes.size(); i < n; ++i) { + if (m_nodes[i].IsInvalidOrFree()) { + state = 4; + } else { + const auto flowchart_name = ore::StringView(*flowchart->name.Get()); + const auto node_flowchart_name = + ore::StringView(*m_nodes[i].m_obj->GetFlowchart()->name.Get()); + if (node_flowchart_name == flowchart_name) { + state = 1; + } else { + state = 0; + } + } + + if (((state | 4) & 7) != 4) + break; + } + return state != 2; +} + +const ore::Array<ActorBinding>* +FlowchartContext::GetUsedResActors(ore::StringView flowchart_name) const { + auto* obj = FindFlowchartObj(flowchart_name); + if (!obj) + return nullptr; + return obj->GetActBinder().GetUsedResActors(); +} + +/// Recursively checks subflow calls in the specified entry point for missing flowcharts +/// or entry points. +/// @param result Optional. +/// @param visited Visited entry points (one BitArray per flowchart) +/// @param flowchart_idx Index of the flowchart to which the entry point belongs. +/// @param entry_point_idx Index of the entry point to be checked. +/// @returns true on success, false on failure +bool CheckSubFlowCalls(FlowchartContext::Builder::BuildResult* result, + const ore::IterRange<const ResFlowchart* const*>& flowcharts, + ore::Array<ore::BitArray>& visited, int flowchart_idx, int entry_point_idx) { + if (visited[flowchart_idx].Test(entry_point_idx)) + return true; + visited[flowchart_idx].Set(entry_point_idx); + + const auto* flowchart = *std::next(flowcharts.begin(), flowchart_idx); + const auto entry_point_name = + flowchart->entry_point_names.Get()->GetEntries()[1 + entry_point_idx].GetKey(); + const auto* entry_point = flowchart->GetEntryPoint(entry_point_name); + + for (u16 i = 0; i != entry_point->num_sub_flow_event_indices; ++i) { + const auto& event = flowchart->events.Get()[entry_point->sub_flow_event_indices.Get()[i]]; + ore::StringView sub_flow_flowchart = *event.sub_flow_flowchart.Get(); + const ore::StringView sub_flow_entry_point = *event.sub_flow_entry_point.Get(); + + auto sub_flowchart_idx = flowchart_idx; + auto* sub_flowchart_res = flowchart; + + if (!sub_flow_flowchart.empty()) { + const auto it = + std::find_if(flowcharts.begin(), flowcharts.end(), [=](const ResFlowchart* f) { + return sub_flow_flowchart == *f->name.Get(); + }); + + if (it == flowcharts.end()) { + if (result) { + result->result = + FlowchartContext::Builder::BuildResultType::kResFlowchartNotFound; + result->missing_flowchart_name = sub_flow_flowchart; + result->missing_entry_point_name = {}; + } + return false; + } + + sub_flowchart_idx = std::distance(flowcharts.begin(), it); + sub_flowchart_res = *it; + + } else { + sub_flow_flowchart = *flowchart->name.Get(); + } + + const int sub_entry_point_idx = + sub_flowchart_res->entry_point_names.Get()->FindIndex(sub_flow_entry_point); + + if (sub_entry_point_idx == -1) { + if (result) { + result->result = FlowchartContext::Builder::BuildResultType::kEntryPointNotFound; + result->missing_flowchart_name = sub_flow_flowchart; + result->missing_entry_point_name = sub_flow_entry_point; + } + return false; + } + + if (!CheckSubFlowCalls(result, flowcharts, visited, sub_flowchart_idx, sub_entry_point_idx)) + return false; + } + + if (result) { + result->result = FlowchartContext::Builder::BuildResultType::kSuccess; + result->missing_flowchart_name = {}; + result->missing_entry_point_name = {}; + } + return true; +} + +bool FlowchartContext::Builder::SetEntryPoint(const ore::StringView& flowchart_name, + const ore::StringView& entry_point_name) { + return SetEntryPoint(nullptr, flowchart_name, entry_point_name); +} + +bool FlowchartContext::Builder::SetEntryPoint(BuildResult* result, + const ore::StringView& flowchart_name, + const ore::StringView& entry_point_name) { + const auto* flowchart_it = + std::find_if(m_flowcharts.begin(), m_flowcharts.end(), [=](const ResFlowchart* flowchart) { + return flowchart_name == *flowchart->name.Get(); + }); + + if (flowchart_it == m_flowcharts.end()) { + if (result) { + result->result = BuildResultType::kResFlowchartNotFound; + result->missing_flowchart_name = flowchart_name; + result->missing_entry_point_name = {}; + } + return false; + } + + const auto entry_point_idx = + (*flowchart_it)->entry_point_names.Get()->FindIndex(entry_point_name); + + if (entry_point_idx == -1) { + if (result) { + result->result = BuildResultType::kEntryPointNotFound; + result->missing_flowchart_name = flowchart_name; + result->missing_entry_point_name = entry_point_name; + } + return false; + } + + m_flowchart_idx = std::distance(m_flowcharts.begin(), flowchart_it); + m_entry_point_idx = entry_point_idx; + if (result) { + result->result = BuildResultType::kSuccess; + result->missing_flowchart_name = {}; + result->missing_entry_point_name = {}; + } + return true; +} + +bool FlowchartContext::Builder::BuildImpl(BuildResult* result, FlowchartRange flowcharts, + FlowchartContext* context, AllocateArg allocate_arg, + ore::Buffer flowchart_obj_buffer) { + context->m_nodes.Reset(); + EvflAllocator allocator{allocate_arg}; + context->m_allocator = allocator; + + const int num_flowcharts = flowcharts.size(); + + ore::Array<ore::BitArray> visited_entry_points; + visited_entry_points.SetBuffer(num_flowcharts, &allocator); + visited_entry_points.UninitializedDefaultConstructElements(); + + const auto clean_up_visited_entry_points = [&] { + if (auto* data = visited_entry_points.data()) { + for (auto it = data; it != data + visited_entry_points.size(); ++it) + it->FreeBufferIfNeeded(&allocator); + + visited_entry_points.DestructElements(); + allocator.Free(data); + } + }; + + for (int i = 0; i < num_flowcharts; ++i) { + visited_entry_points[i].AllocateBuffer(&allocator, + (flowcharts.begin()[i])->num_entry_points); + } + + if (!CheckSubFlowCalls(result, flowcharts, visited_entry_points, m_flowchart_idx, + m_entry_point_idx)) { + clean_up_visited_entry_points(); + return false; + } + + context->m_objs.ConstructElements(flowchart_obj_buffer); + + for (int i = 0; i < num_flowcharts; ++i) { + auto* obj = &context->m_objs[i]; + FlowchartObj::Builder obj_builder{flowcharts.begin()[i], &visited_entry_points[i]}; + + if (!obj_builder.Build(obj, &context->m_allocator, flowcharts)) { + clean_up_visited_entry_points(); + context->m_objs.ClearWithoutFreeing(); + + if (result) { + const auto* flowchart = flowcharts.begin()[m_flowchart_idx]; + const ore::StringView name = *flowchart->name.Get(); + const ore::StringView ep_name = flowchart->GetEntryPointName(m_entry_point_idx); + result->result = BuildResultType::kInvalidOperation; + result->missing_flowchart_name = name; + result->missing_entry_point_name = ep_name; + } + return false; + } + } + + context->m_obj_idx = m_flowchart_idx; + context->m_active_entry_point_idx = m_entry_point_idx; + context->m_nodes.Init(&context->m_allocator, 16); + context->m_nodes.Resize(16); + context->Clear(); + clean_up_visited_entry_points(); + + if (result) { + result->result = BuildResultType::kSuccess; + result->missing_flowchart_name = {}; + result->missing_entry_point_name = {}; + } + return true; +} + +bool FlowchartContext::Builder::Build(FlowchartContext* context, AllocateArg allocate_arg) { + return Build(nullptr, context, allocate_arg); +} + +bool FlowchartContext::Builder::Build(BuildResult* result, FlowchartContext* context, + AllocateArg allocate_arg) { + context->Dispose(); + + EvflAllocator allocator{allocate_arg}; + + ore::DynArrayList<const ResFlowchart*> flowcharts{&allocator}; + flowcharts.Init(&allocator); + flowcharts.DeduplicateCopy(m_flowcharts); + + FlowchartRange range{flowcharts}; + ore::Buffer obj_buffer{}; + obj_buffer.Allocate<FlowchartObj>(&allocator, flowcharts.size()); + + if (!BuildImpl(result, range, context, allocate_arg, obj_buffer)) { + obj_buffer.Free(&allocator); + return false; + } + + return true; +} + +} // namespace evfl diff --git a/lib/EventFlow/src/evfl/FlowchartObj.cpp b/lib/EventFlow/src/evfl/FlowchartObj.cpp new file mode 100644 index 00000000..b0306b59 --- /dev/null +++ b/lib/EventFlow/src/evfl/FlowchartObj.cpp @@ -0,0 +1,243 @@ +#include <algorithm> +#include <evfl/Flowchart.h> +#include <evfl/ResActor.h> +#include <evfl/ResFlowchart.h> +#include <ore/Allocator.h> +#include <ore/Array.h> +#include <ore/BitUtils.h> +#include <ore/IterRange.h> +#include <ore/ResMetaData.h> +#include <ore/Types.h> + +namespace evfl { + +namespace { + +void RegisterBindings(FlowchartObj* obj, ore::BitArray* visited_events, int event_idx) { + const ResActor* actors = obj->GetFlowchart()->actors.Get(); + const ResEvent* events = obj->GetFlowchart()->events.Get(); + + while (event_idx != 0xffff && !visited_events->Test(event_idx)) { + visited_events->Set(event_idx); + const ResEvent& event = events[event_idx]; + const auto event_type = event.type; + + switch (event_type.mValue) { + case ResEvent::EventType::kAction: + if (actors[event.actor_idx].argument_name.Get()->empty()) { + auto* action = actors[event.actor_idx].actions.Get() + event.actor_action_idx; + obj->GetActBinder().RegisterAction(event.actor_idx, action); + } + break; + case ResEvent::EventType::kSwitch: + if (actors[event.actor_idx].argument_name.Get()->empty()) { + auto* query = actors[event.actor_idx].queries.Get() + event.actor_query_idx; + obj->GetActBinder().RegisterQuery(event.actor_idx, query); + } + break; + default: + break; + } + + // Process the next event. + + switch (event_type) { + case ResEvent::EventType::kAction: + case ResEvent::EventType::kJoin: + case ResEvent::EventType::kSubFlow: + event_idx = event.next_event_idx; + break; + case ResEvent::EventType::kSwitch: { + ore::Array<const ResCase> cases{event.cases.Get(), event.num_cases}; + std::for_each(cases.begin(), cases.end(), [&](const ResCase& case_) { + RegisterBindings(obj, visited_events, case_.event_idx); + }); + return; + } + case ResEvent::EventType::kFork: { + ore::Array<const u16> forks{event.fork_event_indices.Get(), event.num_forks}; + std::for_each(forks.begin(), forks.end(), + [&](u16 fork) { RegisterBindings(obj, visited_events, fork); }); + event_idx = event.join_event_idx; + break; + } + } + } +} + +struct ActorArgumentInfo { + bool operator==(const ActorArgumentInfo& rhs) const { + return entry_point_idx == rhs.entry_point_idx && flowchart == rhs.flowchart && + rhs.GetActorArgumentName() == GetActorArgumentName(); + } + bool operator!=(const ActorArgumentInfo& rhs) const { return !(*this == rhs); } + + ore::StringView GetActorArgumentName() const { + return ore::StringView(actor_argument_name, actor_argument_name_len); + } + + const ResFlowchart* flowchart; + int entry_point_idx; + const char* actor_argument_name; + size_t actor_argument_name_len; +}; + +// NON_MATCHING: the if checks are reordered to hell for some reason +const ResActor* FindActor(const ActorArgumentInfo& entry) { + ore::Array<const ResActor> actors{entry.flowchart->actors.Get(), entry.flowchart->num_actors}; + for (const auto& actor : actors) { + if (actor.entry_point_idx != entry.entry_point_idx) + continue; + if (actor.argument_name.Get()->empty()) + continue; + if (*actor.argument_name.Get() != entry.GetActorArgumentName()) + continue; + return &actor; + } + return nullptr; +} + +void RegisterBindingsForArguments(ActBinder& binder, int actor_idx, + const ore::IterRange<const ResFlowchart* const*>& flowcharts, + ore::ArrayListBase<ActorArgumentInfo>& processed, + const ActorArgumentInfo& entry) { + if (std::find(processed.begin(), processed.end(), entry) != processed.end()) + return; + + processed.emplace_back(entry); + + if (auto* actor = FindActor(entry)) { + ore::Array<const ResAction> actions{actor->actions.Get(), actor->num_actions}; + for (const auto& action : actions) + binder.RegisterAction(actor_idx, &action); + + ore::Array<const ResQuery> queries{actor->queries.Get(), actor->num_queries}; + for (const auto& query : queries) + binder.RegisterQuery(actor_idx, &query); + } + + const auto& entry_point = entry.flowchart->entry_points.Get()[entry.entry_point_idx]; + ore::Array<const u16> sub_flow_event_indices{entry_point.sub_flow_event_indices.Get(), + entry_point.num_sub_flow_event_indices}; + for (auto sub_flow_event_idx : sub_flow_event_indices) { + const auto& event = entry.flowchart->events.Get()[sub_flow_event_idx]; + const ore::StringView sub_flow_flowchart = *event.sub_flow_flowchart.Get(); + const ore::StringView sub_flow_entry_point = *event.sub_flow_entry_point.Get(); + + auto* params = event.params.Get(); + if (!params) + continue; + + for (int i = 0; i < params->num_items; ++i) { + auto* param = (¶ms->value.container + i)->Get(); + if (param->type != ore::ResMetaData::DataType::kArgument) + continue; + if (entry.GetActorArgumentName() != *param->value.str.Get()) + continue; + + const ResFlowchart* flowchart = entry.flowchart; + if (!sub_flow_flowchart.empty()) { + flowchart = + *std::find_if(flowcharts.begin(), flowcharts.end(), [=](const ResFlowchart* f) { + return sub_flow_flowchart == *f->name.Get(); + }); + } + + const auto entry_point_idx = + flowchart->entry_point_names.Get()->FindIndex(sub_flow_entry_point); + const auto actor_argument_name = params->dictionary.Get()->GetEntries()[1 + i].GetKey(); + + ActorArgumentInfo arg; + arg.flowchart = flowchart; + arg.entry_point_idx = entry_point_idx; + arg.actor_argument_name = actor_argument_name.data(); + arg.actor_argument_name_len = actor_argument_name.size(); + RegisterBindingsForArguments(binder, actor_idx, flowcharts, processed, arg); + } + } + + processed.pop_back(); +} + +void RegisterBindingsForActorIdentifiers(FlowchartObj* obj, + ore::IterRange<const ResFlowchart* const*> flowcharts, + int entry_point_idx) { + auto* flowchart = obj->GetFlowchart(); + const auto& entry_point = flowchart->entry_points.Get()[entry_point_idx]; + + ore::IterRange<const u16*> sub_flow_event_indices{entry_point.sub_flow_event_indices.Get(), + entry_point.num_sub_flow_event_indices}; + const ResEvent* events = flowchart->events.Get(); + ore::IterRange<const ResActor*> actors{flowchart->actors.Get(), flowchart->num_actors}; + + for (auto sub_flow_event_idx : sub_flow_event_indices) { + const auto& event = events[sub_flow_event_idx]; + + auto* params = event.params.Get(); + if (!params) + continue; + + const ore::StringView sub_flow_flowchart = *event.sub_flow_flowchart.Get(); + const ore::StringView sub_flow_entry_point = *event.sub_flow_entry_point.Get(); + const ResFlowchart* arg_flowchart = flowchart; + if (!sub_flow_flowchart.empty()) { + arg_flowchart = + *std::find_if(flowcharts.begin(), flowcharts.end(), [=](const ResFlowchart* f) { + return sub_flow_flowchart == *f->name.Get(); + }); + } + + for (int i = 0; i < params->num_items; ++i) { + auto* param = (¶ms->value.container + i)->Get(); + if (param->type != ore::ResMetaData::DataType::kActorIdentifier) + continue; + + const ore::StringView param_name = + params->dictionary.Get()->GetEntries()[1 + i].GetKey(); + const ore::StringView name = *param->value.actor.name.Get(); + const ore::StringView sub_name = *param->value.actor.sub_name.Get(); + auto* actor = std::find_if(actors.begin(), actors.end(), [&](const ResActor& a) { + return name == *a.name.Get() && sub_name == *a.secondary_name.Get(); + }); + const int actor_idx = std::distance(actors.begin(), actor); + const int arg_entry_point_idx = + arg_flowchart->entry_point_names.Get()->FindIndex(sub_flow_entry_point); + + ore::FixedArrayList<ActorArgumentInfo, 64> processed; + ActorArgumentInfo arg; + arg.flowchart = arg_flowchart; + arg.entry_point_idx = arg_entry_point_idx; + arg.actor_argument_name = param_name.data(); + arg.actor_argument_name_len = param_name.size(); + RegisterBindingsForArguments(obj->GetActBinder(), actor_idx, flowcharts, processed, + arg); + } + } +} + +} // namespace + +bool FlowchartObj::Builder::Build(FlowchartObj* obj, ore::Allocator* allocator, + ore::IterRange<const ResFlowchart* const*> flowcharts) { + obj->m_flowchart = m_flowchart; + m_act_binder_builder.Build(&obj->GetActBinder(), allocator, + {m_flowchart->actors.Get(), m_flowchart->num_actors}); + + const int num_events = m_flowchart->num_events; + ore::BitArray visited_events{allocator, num_events}; + + auto* entry_points = obj->m_flowchart->entry_points.Get(); + auto entry_point_it = m_entry_points_mask->BeginTest(); + const auto entry_point_end = m_entry_points_mask->EndTest(); + while (entry_point_it != entry_point_end) { + RegisterBindings(obj, &visited_events, entry_points[*entry_point_it].main_event_idx); + RegisterBindingsForActorIdentifiers(obj, flowcharts, *entry_point_it); + obj->GetActBinder().SetIsUsed(); + ++entry_point_it; + } + + visited_events.FreeBuffer(allocator); + return true; +} + +} // namespace evfl diff --git a/lib/EventFlow/src/evfl/Param.cpp b/lib/EventFlow/src/evfl/Param.cpp new file mode 100644 index 00000000..596ca7ee --- /dev/null +++ b/lib/EventFlow/src/evfl/Param.cpp @@ -0,0 +1,714 @@ +#include <algorithm> +#include <evfl/Flowchart.h> +#include <evfl/Param.h> +#include <evfl/ResFlowchart.h> +#include <ore/BinaryFile.h> +#include <ore/ResDic.h> + +namespace evfl { + +namespace { + +constexpr ore::ResMetaData::DataType::Type +ConvertMetaDataPackTypeToMDType(MetaDataPack::DataType::Type type) { + if (u32(type) < u32(MetaDataPack::DataType::Invalid())) + return ore::ResMetaData::DataType::Type(ore::ResMetaData::DataType::kInt + type); + +#ifdef MATCHING_HACK_NX_CLANG + // Force a branch to be generated (instead of CSEL) + __builtin_assume(type >= 0); +#endif + return ore::ResMetaData::DataType::Invalid(); +} + +} // namespace + +void MetaDataPack::AddInt(const char* key, int value) { + auto& entry = AddEntry(); + entry.key = key; + entry.value.i = value; + entry.type = DataType::kInt; +} + +// NON_MATCHING: ??? +void MetaDataPack::AddBool(const char* key, bool value) { + auto& entry = AddEntry(); + entry.key = key; + entry.value.i = value; + entry.type = DataType::kBool; +} + +void MetaDataPack::AddFloat(const char* key, float value) { + auto& entry = AddEntry(); + entry.key = key; + entry.value.f = value; + entry.type = DataType::kFloat; +} + +void MetaDataPack::AddStringPtr(const char* key, const char* value) { + auto& entry = AddEntry(); + entry.key = key; + entry.value.str = value; + entry.type = DataType::kString; +} + +void MetaDataPack::AddWStringPtr(const char* key, const wchar_t* value) { + auto& entry = AddEntry(); + entry.key = key; + entry.value.wstr = value; + entry.type = DataType::kWString; +} + +MetaDataPack::Entry* MetaDataPack::Find(const ore::StringView& key) const { + for (auto& entry : GetEntries()) { + if (entry.IsKey(key)) + return &entry; + } + return nullptr; +} + +bool MetaDataPack::FindInt(int* value, const ore::StringView& key) const { + auto* entry = Find(key); + if (!entry || entry->type != DataType::kInt) + return false; + *value = entry->value.i; + return true; +} + +bool MetaDataPack::FindBool(bool* value, const ore::StringView& key) const { + auto* entry = Find(key); + if (!entry || entry->type != DataType::kBool) + return false; + *value = entry->value.i == 1; + return true; +} + +bool MetaDataPack::FindFloat(float* value, const ore::StringView& key) const { + auto* entry = Find(key); + if (!entry || entry->type != DataType::kFloat) + return false; + *value = entry->value.f; + return true; +} + +bool MetaDataPack::FindString(ore::StringView* value, const ore::StringView& key) const { + auto* entry = Find(key); + if (!entry || entry->type != DataType::kString) + return false; + *value = entry->value.str; + return true; +} + +bool MetaDataPack::FindWString(ore::WStringView* value, const ore::StringView& key) const { + auto* entry = Find(key); + if (!entry || entry->type != DataType::kWString) + return false; + *value = entry->value.wstr; + return true; +} + +ore::ResMetaData::DataType::Type MetaDataPack::GetType(const ore::StringView& key) const { + auto* entry = Find(key); + return ConvertMetaDataPackTypeToMDType(entry ? entry->type : DataType::Invalid()); +} + +// NON_MATCHING: two add operands swapped +void MetaDataPack::Builder::CalcMemSize() { + m_entries_byte_size = sizeof(Entry) * m_num_entries; + m_required_size = 0; + m_alignment_real = 16; + if (m_num_entries != 0) { + m_alignment_real = std::max(16, m_alignment); + m_buffer_offset = ore::AlignUpToPowerOf2(_14, m_alignment) - _14; + m_required_size = m_buffer_offset + m_entries_byte_size; + } +} + +bool MetaDataPack::Builder::Build(MetaDataPack* pack, ore::Buffer buffer) { + if (m_alignment_real <= 0) + return false; + if (m_required_size > buffer.size) + return false; + pack->m_buffer.data = buffer.data; + pack->m_buffer.size = buffer.size; + const int byte_size = m_entries_byte_size; + pack->m_entries = reinterpret_cast<Entry*>(buffer.data + m_buffer_offset); + pack->m_entries_capacity = byte_size / int(sizeof(Entry)); + pack->m_entries_num = 0; + return true; +} + +ParamAccessor::ParamAccessor(const ore::ResMetaData* metadata) : m_metadata(metadata) {} + +ParamAccessor::ParamAccessor(const FlowchartContext* context, int node_idx) + : m_context(context), m_node_idx(node_idx) { + m_node_counter = context->GetNode(node_idx).GetNodeCounter(); +} + +const ore::ResMetaData* ParamAccessor::GetFrontResMetaData() const { + if (m_node_idx == -1) + return m_metadata; + + const auto& node = m_context->GetNode(m_node_idx); + const auto& event = node.GetObj()->GetFlowchart()->events.Get()[node.GetEventIdx()]; + return event.params.Get(); +} + +int ParamAccessor::GetParamCount() const { + auto* meta = GetFrontResMetaData(); + return meta ? meta->num_items : 0; +} + +ore::StringView ParamAccessor::GetParamName(int idx) const { + auto* meta = GetFrontResMetaData(); + return meta->dictionary.Get()->GetEntries()[1 + idx].GetKey(); +} + +ParamAccessor::Type ParamAccessor::GetParamType(int idx) const { + Type type = ore::ResMetaData::DataType::Invalid(); + + const ore::ResMetaData* metadata; + const MetaDataPack* metadata_pack; + const VariablePack* variable_pack; + const auto param_name = GetParamName(idx); + const auto real_name = + TrackBackArgument(&metadata, &metadata_pack, &variable_pack, nullptr, param_name); + + if (metadata) { + const int entry_idx = metadata->dictionary.Get()->FindIndex(real_name); + if (entry_idx == -1) + return ore::ResMetaData::DataType::Invalid(); + type = (&metadata->value.container + entry_idx)->Get()->type; + } else if (variable_pack) { + type = variable_pack->GetVariableType(real_name); + } else if (metadata_pack) { + type = metadata_pack->GetType(real_name); + } + + if (type == ore::ResMetaData::DataType::Invalid()) + return ore::ResMetaData::DataType::Invalid(); + + return type; +} + +ore::StringView ParamAccessor::TrackBackArgument(const ore::ResMetaData** out_metadata, + const MetaDataPack** out_metadata_pack, + const VariablePack** out_variable_pack, + FlowchartObj** out_obj, + const ore::StringView& argument) const { + *out_metadata = nullptr; + *out_metadata_pack = nullptr; + *out_variable_pack = nullptr; + if (out_obj) + *out_obj = nullptr; + + if (m_node_idx == -1) { + *out_metadata = m_metadata; + return argument; + } + + auto ret = argument; + + for (int i = m_node_idx; i != 0xffff; i = m_context->GetNode(i).GetNextNodeIdx()) { + const auto& node = m_context->GetNode(i); + const auto* variable_pack = node.GetVariablePack(); + const auto* events = node.GetObj()->GetFlowchart()->events.Get(); + const auto& event = events[node.GetEventIdx()]; + + if (!((event.type == ResEvent::EventType::kAction && i == m_node_idx) || + (event.type == ResEvent::EventType::kSwitch && i == m_node_idx) || + event.type == ResEvent::EventType::kSubFlow)) { + continue; + } + + const auto* params = event.params.Get(); + if (!params) + return {}; + + const auto* param = params->Get(ret, Type::kArgument); + if (param) { + ret = *param->value.str.Get(); + if (variable_pack && variable_pack->Contains(ret)) { + *out_variable_pack = variable_pack; + return ret; + } + continue; + } + + *out_metadata = params; + if (out_obj) + *out_obj = node.GetObj(); + return ret; + } + + *out_metadata_pack = m_context->GetMetaDataPack(); + if (*out_metadata_pack == nullptr) + return {}; + return ret; +} + +int ParamAccessor::GetInt(int idx) const { + int value; + FindInt(&value, GetParamName(idx)); + return value; +} + +bool ParamAccessor::FindInt(int* value, const ore::StringView& name) const { + const ore::ResMetaData* metadata; + const MetaDataPack* metadata_pack; + const VariablePack* variable_pack; + const auto real_name = + TrackBackArgument(&metadata, &metadata_pack, &variable_pack, nullptr, name); + + if (metadata) { + const auto* entry = metadata->Get(real_name, Type::kInt); + if (entry != nullptr) { + *value = entry->value.i; + return true; + } + } + + if (variable_pack && variable_pack->FindInt(value, real_name)) + return true; + + if (metadata_pack && metadata_pack->FindInt(value, real_name)) + return true; + + return false; +} + +bool ParamAccessor::GetBool(int idx) const { + bool value; + FindBool(&value, GetParamName(idx)); + return value; +} + +bool ParamAccessor::FindBool(bool* value, const ore::StringView& name) const { + const ore::ResMetaData* metadata; + const MetaDataPack* metadata_pack; + const VariablePack* variable_pack; + const auto real_name = + TrackBackArgument(&metadata, &metadata_pack, &variable_pack, nullptr, name); + + if (metadata) { + const auto* entry = metadata->Get(real_name, Type::kBool); + if (entry != nullptr) { + *value = entry->value.i != 0; + return true; + } + } + + if (variable_pack && variable_pack->FindBool(value, real_name)) + return true; + + if (metadata_pack && metadata_pack->FindBool(value, real_name)) + return true; + + return false; +} + +float ParamAccessor::GetFloat(int idx) const { + float value; + FindFloat(&value, GetParamName(idx)); + return value; +} + +bool ParamAccessor::FindFloat(float* value, const ore::StringView& name) const { + const ore::ResMetaData* metadata; + const MetaDataPack* metadata_pack; + const VariablePack* variable_pack; + const auto real_name = + TrackBackArgument(&metadata, &metadata_pack, &variable_pack, nullptr, name); + + if (metadata) { + const auto* entry = metadata->Get(real_name, Type::kFloat); + if (entry != nullptr) { + *value = entry->value.f; + return true; + } + } + + if (variable_pack && variable_pack->FindFloat(value, real_name)) + return true; + + if (metadata_pack && metadata_pack->FindFloat(value, real_name)) + return true; + + return false; +} + +ore::StringView ParamAccessor::GetString(int idx) const { + ore::StringView value; + FindString(&value, GetParamName(idx)); + return value; +} + +bool ParamAccessor::FindString(ore::StringView* value, const ore::StringView& name) const { + const ore::ResMetaData* metadata; + const MetaDataPack* metadata_pack; + const VariablePack* variable_pack; + const auto real_name = + TrackBackArgument(&metadata, &metadata_pack, &variable_pack, nullptr, name); + + if (metadata) { + const auto* entry = metadata->Get(real_name, Type::kString); + if (entry) { + *value = *entry->value.str.Get(); + return true; + } + return false; + } + + if (metadata_pack) + return metadata_pack->FindString(value, real_name); + + return false; +} + +ore::WStringView ParamAccessor::GetWString(int idx) const { + ore::WStringView value; + FindWString(&value, GetParamName(idx)); + return value; +} + +bool ParamAccessor::FindWString(ore::WStringView* value, const ore::StringView& name) const { + const ore::ResMetaData* metadata; + const MetaDataPack* metadata_pack; + const VariablePack* variable_pack; + const auto real_name = + TrackBackArgument(&metadata, &metadata_pack, &variable_pack, nullptr, name); + + if (metadata) { + const auto* entry = metadata->Get(real_name, Type::kWString); + if (entry) { + *value = *entry->value.wstr.Get(); + return true; + } + return false; + } + + if (metadata_pack) + return metadata_pack->FindWString(value, real_name); + + return false; +} + +ParamAccessor::IntRange ParamAccessor::GetIntArray(int idx) const { + IntRange value; + FindIntArray(&value, GetParamName(idx)); + return value; +} + +bool ParamAccessor::FindIntArray(ParamAccessor::IntRange* value, + const ore::StringView& name) const { + const ore::ResMetaData* metadata; + const MetaDataPack* metadata_pack; + const VariablePack* variable_pack; + const auto real_name = + TrackBackArgument(&metadata, &metadata_pack, &variable_pack, nullptr, name); + + if (metadata) { + const auto* entry = metadata->Get(real_name, Type::kIntArray); + if (entry) { + *value = IntRange{&entry->value.i, entry->num_items}; + return true; + } + } + + if (variable_pack) { + ore::DynArrayList<int>* list; + if (variable_pack->FindIntList(&list, real_name)) { + *value = IntRange{*list}; + return true; + } + } + + return false; +} + +ParamAccessor::FloatRange ParamAccessor::GetFloatArray(int idx) const { + FloatRange value; + FindFloatArray(&value, GetParamName(idx)); + return value; +} + +bool ParamAccessor::FindFloatArray(ParamAccessor::FloatRange* value, + const ore::StringView& name) const { + const ore::ResMetaData* metadata; + const MetaDataPack* metadata_pack; + const VariablePack* variable_pack; + const auto real_name = + TrackBackArgument(&metadata, &metadata_pack, &variable_pack, nullptr, name); + + if (metadata) { + const auto* entry = metadata->Get(real_name, Type::kFloatArray); + if (entry) { + *value = FloatRange{&entry->value.f, entry->num_items}; + return true; + } + } + + if (variable_pack) { + ore::DynArrayList<float>* list; + if (variable_pack->FindFloatList(&list, real_name)) { + *value = FloatRange{*list}; + return true; + } + } + + return false; +} + +ParamAccessor::StringRange ParamAccessor::GetStringArray(int idx) const { + StringRange value; + FindStringArray(&value, GetParamName(idx)); + return value; +} + +bool ParamAccessor::FindStringArray(ParamAccessor::StringRange* value, + const ore::StringView& name) const { + const ore::ResMetaData* metadata; + const MetaDataPack* metadata_pack; + const VariablePack* variable_pack; + const auto real_name = + TrackBackArgument(&metadata, &metadata_pack, &variable_pack, nullptr, name); + + if (metadata) { + const auto* entry = metadata->Get(real_name, Type::kStringArray); + if (entry) { + *value = StringRange{&entry->value.str, entry->num_items}; + return true; + } + } + + return false; +} + +ParamAccessor::WStringRange ParamAccessor::GetWStringArray(int idx) const { + WStringRange value; + FindWStringArray(&value, GetParamName(idx)); + return value; +} + +bool ParamAccessor::FindWStringArray(ParamAccessor::WStringRange* value, + const ore::StringView& name) const { + const ore::ResMetaData* metadata; + const MetaDataPack* metadata_pack; + const VariablePack* variable_pack; + const auto real_name = + TrackBackArgument(&metadata, &metadata_pack, &variable_pack, nullptr, name); + + if (metadata) { + const auto* entry = metadata->Get(real_name, Type::kWStringArray); + if (entry) { + *value = WStringRange{&entry->value.wstr, entry->num_items}; + return true; + } + } + + return false; +} + +VariablePack::VariableType VariablePack::GetVariableType(const ore::StringView& name) const { + if (!Contains(name)) + return ore::ResMetaData::DataType::Invalid(); + return GetVariableEntry(name)->type; +} + +bool VariablePack::FindInt(int* value, const ore::StringView& name) const { + if (GetVariableType(name) != ore::ResMetaData::DataType::kInt) + return false; + *value = GetVariableEntry(name)->value.i; + return true; +} + +bool VariablePack::FindBool(bool* value, const ore::StringView& name) const { + if (GetVariableType(name) != ore::ResMetaData::DataType::kBool) + return false; + *value = GetVariableEntry(name)->value.i; + return true; +} + +bool VariablePack::FindFloat(float* value, const ore::StringView& name) const { + if (GetVariableType(name) != ore::ResMetaData::DataType::kFloat) + return false; + *value = GetVariableEntry(name)->value.f; + return true; +} + +bool VariablePack::FindIntList(ore::DynArrayList<int>** value, const ore::StringView& name) const { + if (GetVariableType(name) != ore::ResMetaData::DataType::kIntArray) + return false; + *value = GetVariableEntry(name)->value.int_array; + return true; +} + +bool VariablePack::FindFloatList(ore::DynArrayList<float>** value, + const ore::StringView& name) const { + if (GetVariableType(name) != ore::ResMetaData::DataType::kFloatArray) + return false; + *value = GetVariableEntry(name)->value.float_array; + return true; +} + +bool ParamAccessor::FindActorIdentifier(ore::StringView* actor_name, + ore::StringView* actor_sub_name, + const ore::StringView& name) const { + const ore::ResMetaData* metadata; + const MetaDataPack* metadata_pack; + const VariablePack* variable_pack; + const auto real_name = + TrackBackArgument(&metadata, &metadata_pack, &variable_pack, nullptr, name); + + if (metadata) { + const auto* entry = metadata->Get(real_name, Type::kActorIdentifier); + if (entry) { + *actor_name = *entry->value.actor.name.Get(); + *actor_sub_name = *entry->value.actor.sub_name.Get(); + return true; + } + } + + return false; +} + +bool VariablePack::Contains(const ore::StringView& name) const { + return m_names->FindIndex(name) != -1; +} + +VariablePack::VariablePack() = default; + +VariablePack::~VariablePack() { + Dispose(); +} + +void VariablePack::Dispose() { + auto* variables = m_variables.data(); + if (!variables) + return; + + for (auto& variable : m_variables) { + switch (variable.type) { + case ore::ResMetaData::DataType::kIntArray: + if (variable.value.int_array) + GetAllocator()->DeleteAndNull(variable.value.int_array); + break; + case ore::ResMetaData::DataType::kFloatArray: + if (variable.value.float_array) + GetAllocator()->DeleteAndNull(variable.value.float_array); + break; + default: + break; + } + } + GetAllocator()->FreeImpl(variables); +} + +void VariablePack::Init(AllocateArg arg, const ResEntryPoint* entry_point) { + Dispose(); + m_names = entry_point->variable_defs_names.Get(); + m_allocator = evfl::EvflAllocator{arg}; + m_variables.ConstructElements(m_names->num_entries, GetAllocator()); + + const ResVariableDef* def = entry_point->variable_defs.Get(); + for (auto& variable : m_variables) { + variable.type = def->type; + variable.value = {}; + switch (variable.type) { + case ore::ResMetaData::DataType::kArgument: + case ore::ResMetaData::DataType::kContainer: + break; + case ore::ResMetaData::DataType::kInt: + case ore::ResMetaData::DataType::kBool: + variable.value.i = def->value.i; + break; + case ore::ResMetaData::DataType::kFloat: + variable.value.f = def->value.f; + break; + case ore::ResMetaData::DataType::kString: + case ore::ResMetaData::DataType::kWString: + break; + case ore::ResMetaData::DataType::kIntArray: { + variable.value.int_array = GetAllocator()->New<ore::DynArrayList<int>>(); + variable.value.int_array->Init(GetAllocator(), 2); + ore::Array<const int> values{def->value.int_array.Get(), def->num}; + variable.value.int_array->OverwriteWith(values.begin(), values.end()); + break; + } + case ore::ResMetaData::DataType::kBoolArray: + break; + case ore::ResMetaData::DataType::kFloatArray: { + variable.value.float_array = GetAllocator()->New<ore::DynArrayList<float>>(); + variable.value.float_array->Init(GetAllocator(), 2); + ore::Array<const float> values{def->value.float_array.Get(), def->num}; + variable.value.float_array->OverwriteWith(values.begin(), values.end()); + break; + } + case ore::ResMetaData::DataType::kStringArray: + case ore::ResMetaData::DataType::kWStringArray: + case ore::ResMetaData::DataType::kActorIdentifier: + break; + } + ++def; + } +} + +const VariablePack::Entry* VariablePack::GetVariableEntry(const ore::StringView& name) const { + return &m_variables[m_names->FindIndex(name)]; +} + +ore::StringView VariablePack::GetVariableName(int idx) const { + return *m_names->GetEntries()[1 + idx].name.Get(); +} + +int VariablePack::GetVariableCount() const { + return m_names->num_entries; +} + +VariablePack::Entry* VariablePack::GetVariableEntry(const ore::StringView& name) { + return &m_variables[m_names->FindIndex(name)]; +} + +void VariablePack::SetInt(const ore::StringView& name, int value) { + m_variables[m_names->FindIndex(name)].value.i = value; +} + +void VariablePack::SetFloat(const ore::StringView& name, float value) { + m_variables[m_names->FindIndex(name)].value.f = value; +} + +void VariablePack::SetBool(const ore::StringView& name, bool value) { + m_variables[m_names->FindIndex(name)].value.i = value; +} + +int VariablePack::GetInt(const ore::StringView& name) const { + int value; + FindInt(&value, name); + return value; +} + +bool VariablePack::GetBool(const ore::StringView& name) const { + bool value; + FindBool(&value, name); + return value; +} + +float VariablePack::GetFloat(const ore::StringView& name) const { + float value; + FindFloat(&value, name); + return value; +} + +ore::DynArrayList<int>* VariablePack::GetIntList(const ore::StringView& name) const { + ore::DynArrayList<int>* value; + FindIntList(&value, name); + return value; +} + +ore::DynArrayList<float>* VariablePack::GetFloatList(const ore::StringView& name) const { + ore::DynArrayList<float>* value; + FindFloatList(&value, name); + return value; +} + +} // namespace evfl diff --git a/lib/EventFlow/src/evfl/ResActor.cpp b/lib/EventFlow/src/evfl/ResActor.cpp new file mode 100644 index 00000000..053ec21e --- /dev/null +++ b/lib/EventFlow/src/evfl/ResActor.cpp @@ -0,0 +1,89 @@ +#include <algorithm> +#include <evfl/ResActor.h> +#include <ore/ResEndian.h> +#include <ore/ResMetaData.h> + +namespace evfl { + +void ActorBinding::Register(const ResAction* action) { + if (GetAction(*action->name.Get()) != m_actions.end()) + return; + + Action entry; + entry.res_action = action; + m_actions.emplace_back(entry); +} + +void ActorBinding::Register(const ResQuery* query) { + if (GetQuery(*query->name.Get()) != m_queries.end()) + return; + + Query entry; + entry.res_query = query; + m_queries.emplace_back(entry); +} + +ActorBinding::Action* ActorBinding::GetAction(const ore::StringView& name) { + return std::find_if(m_actions.begin(), m_actions.end(), [name](const Action& action) { + return name == *action.res_action->name.Get(); + }); +} + +const ActorBinding::Action* ActorBinding::GetAction(const ore::StringView& name) const { + return std::find_if(m_actions.begin(), m_actions.end(), [name](const Action& action) { + return name == *action.res_action->name.Get(); + }); +} + +ActorBinding::Query* ActorBinding::GetQuery(const ore::StringView& name) { + return std::find_if(m_queries.begin(), m_queries.end(), [name](const Query& query) { + return name == *query.res_query->name.Get(); + }); +} + +const ActorBinding::Query* ActorBinding::GetQuery(const ore::StringView& name) const { + return std::find_if(m_queries.begin(), m_queries.end(), [name](const Query& query) { + return name == *query.res_query->name.Get(); + }); +} + +bool ActBinder::Builder::Build(evfl::ActBinder* binder, ore::Allocator* allocator, + ore::IterRange<const ResActor*> actors) { + binder->m_event_used_actor_count = 0; + binder->m_allocator = allocator; + binder->m_bindings.ConstructElements(actors.size(), allocator); + + auto it = actors.begin(); + for (int i = 0; i < actors.size(); ++i) { + auto& binding = binder->m_bindings[i]; + binding.m_actor = it; + binding.m_actions.Init(binder->m_allocator); + binding.m_queries.Init(binder->m_allocator); + ++it; + } + + return true; +} + +const ore::Array<ActorBinding>* ActBinder::GetUsedResActors() const { + return &m_bindings; +} + +void SwapEndian(ore::ResEndian* endian, ResActor* actor) { + using ore::SwapEndian; + if (endian->is_serializing) { + if (auto* params = actor->params.ToPtr(endian->base)) + SwapEndian(endian, params); + SwapEndian(&actor->num_actions); + SwapEndian(&actor->num_queries); + SwapEndian(&actor->entry_point_idx); + } else { + SwapEndian(&actor->num_actions); + SwapEndian(&actor->num_queries); + SwapEndian(&actor->entry_point_idx); + if (auto* params = actor->params.ToPtr(endian->base)) + SwapEndian(endian, params); + } +} + +} // namespace evfl diff --git a/lib/EventFlow/src/evfl/ResEventFlowFile.cpp b/lib/EventFlow/src/evfl/ResEventFlowFile.cpp new file mode 100644 index 00000000..4fac653c --- /dev/null +++ b/lib/EventFlow/src/evfl/ResEventFlowFile.cpp @@ -0,0 +1,112 @@ +#include <evfl/ResEventFlowFile.h> +#include <evfl/ResFlowchart.h> +#include <evfl/ResTimeline.h> +#include <ore/Array.h> +#include <ore/RelocationTable.h> +#include <ore/ResDic.h> +#include <ore/ResEndian.h> +#include <ore/StringPool.h> +#include <string_view> + +namespace evfl { + +using ore::SwapEndian; + +template <typename T> +constexpr T MakeMagic(std::string_view magic) { + T result = 0; + for (size_t i = 0; i < magic.length(); ++i) + result |= T(magic[i]) << (8 * i); + return result; +} + +bool ResEventFlowFile::IsValid(void* data) { + return static_cast<ore::BinaryFileHeader*>(data)->IsValid(MakeMagic<u64>("BFEVFL"), 0, 3, 0, 0); +} + +ResEventFlowFile* ResEventFlowFile::ResCast(void* data) { + auto* file = static_cast<ResEventFlowFile*>(data); + file->Relocate(); + return file; +} + +void ResEventFlowFile::Relocate() { + if (header.IsRelocated()) + return; + + auto* table = header.GetRelocationTable(); + table->Relocate(); + header.SetRelocated(true); +} + +void ResEventFlowFile::Unrelocate() { + if (!header.IsRelocated()) + return; + + auto* table = header.GetRelocationTable(); + table->Unrelocate(); + header.SetRelocated(false); +} + +static void SwapEndian(ore::ResEndian* endian, ore::StringPool* pool) { + ore::BinString* str = pool->GetFirstString(); + const int num_strings = pool->GetLength(); + if (endian->is_serializing) { + for (int i = 0; i < num_strings; ++i) { + auto* next = str->NextString(); + SwapEndian(&str->length); + str = next; + } + } else { + for (int i = 0; i < num_strings; ++i) { + SwapEndian(&str->length); + str = str->NextString(); + } + } +} + +template <typename T> +static void SwapEndian(ore::ResEndian* endian, ore::BinTPtr<T>* ptr) { + if (auto* value = ptr->ToPtr(endian->base)) + SwapEndian(endian, value); +} + +static void SwapEndianForFileData(ore::ResEndian* endian, ResEventFlowFile* file) { + ore::Array<ore::BinTPtr<ResFlowchart>> flowcharts{file->flowcharts.ToPtr(endian->base), + file->num_flowcharts}; + for (auto& flowchart : flowcharts) + SwapEndian(endian, &flowchart); + + if (auto* flowchart_names = file->flowchart_names.ToPtr(endian->base)) + SwapEndian(endian, flowchart_names); + + ore::Array<ore::BinTPtr<ResTimeline>> timelines{file->timelines.ToPtr(endian->base), + file->num_timelines}; + for (auto& timeline : timelines) + SwapEndian(endian, &timeline); + + if (auto* timeline_names = file->timeline_names.ToPtr(endian->base)) + SwapEndian(endian, timeline_names); + + auto* string_pool = + static_cast<ore::StringPool*>(file->header.FindFirstBlock(MakeMagic<u32>("STR "))); + SwapEndian(endian, string_pool); +} + +void SwapEndian(ore::ResEndian* endian, ResEventFlowFile* file) { + const auto swap_fields = [&] { + SwapEndian(&file->header.bom); + SwapEndian(&file->num_flowcharts); + SwapEndian(&file->num_timelines); + }; + + if (endian->is_serializing) { + SwapEndianForFileData(endian, file); + swap_fields(); + } else { + swap_fields(); + SwapEndianForFileData(endian, file); + } +} + +} // namespace evfl diff --git a/lib/EventFlow/src/evfl/ResFlowchart.cpp b/lib/EventFlow/src/evfl/ResFlowchart.cpp new file mode 100644 index 00000000..da23e457 --- /dev/null +++ b/lib/EventFlow/src/evfl/ResFlowchart.cpp @@ -0,0 +1,191 @@ +#include <algorithm> +#include <evfl/ResActor.h> +#include <evfl/ResFlowchart.h> +#include <ore/Array.h> +#include <ore/ResDic.h> +#include <ore/ResEndian.h> + +namespace evfl { + +using ore::SwapEndian; + +static void SwapEndianForFlowchartData(ore::ResEndian* endian, ResFlowchart* flowchart) { + ore::Array<ResActor> actors{flowchart->actors.ToPtr(endian->base), flowchart->num_actors}; + for (auto& actor : actors) + SwapEndian(endian, &actor); + + ore::Array<ResEvent> events{flowchart->events.ToPtr(endian->base), flowchart->num_events}; + for (auto& event : events) + SwapEndian(endian, &event); + + if (auto* names = flowchart->entry_point_names.ToPtr(endian->base)) + SwapEndian(endian, names); + + ore::Array<ResEntryPoint> entry_points{flowchart->entry_points.ToPtr(endian->base), + flowchart->num_entry_points}; + for (auto& entry : entry_points) + SwapEndian(endian, &entry); +} + +static void SwapEndianForFlowchartFields(ore::ResEndian* endian, ResFlowchart* flowchart) { + SwapEndian(&flowchart->num_actors); + SwapEndian(&flowchart->num_actions); + SwapEndian(&flowchart->num_queries); + SwapEndian(&flowchart->num_events); + SwapEndian(&flowchart->num_entry_points); +} + +void SwapEndian(ore::ResEndian* endian, ResFlowchart* flowchart) { + if (endian->is_serializing) { + SwapEndianForFlowchartData(endian, flowchart); + SwapEndianForFlowchartFields(endian, flowchart); + } else { + SwapEndianForFlowchartFields(endian, flowchart); + SwapEndianForFlowchartData(endian, flowchart); + } +} + +int ResFlowchart::CountEvent(ResEvent::EventType::Type type) const { + ore::Array<const ResEvent> array{events.Get(), num_events}; + return std::count_if(array.begin(), array.end(), + [type](const ResEvent& event) { return event.type == type; }); +} + +void SwapEndian(ore::ResEndian* endian, ResCase* case_) { + SwapEndian(&case_->event_idx); + SwapEndian(&case_->value); +} + +static void SwapEndianForEventData(ore::ResEndian* endian, ResEvent* event) { + switch (event->type) { + case ResEvent::EventType::kAction: + if (auto* params = event->params.ToPtr(endian->base)) + SwapEndian(endian, params); + break; + case ResEvent::EventType::kSwitch: { + ore::Array<ResCase> cases{event->cases.ToPtr(endian->base), event->num_cases}; + for (auto& case_ : cases) + SwapEndian(endian, &case_); + if (auto* params = event->params.ToPtr(endian->base)) + SwapEndian(endian, params); + break; + } + case ResEvent::EventType::kFork: { + ore::Array<u16> forks{event->fork_event_indices.ToPtr(endian->base), event->num_forks}; + for (auto& fork : forks) + SwapEndian(&fork); + break; + } + case ResEvent::EventType::kJoin: + break; + case ResEvent::EventType::kSubFlow: + if (auto* params = event->params.ToPtr(endian->base)) + SwapEndian(endian, params); + break; + } +} + +static void SwapEndianForEventFields(ore::ResEndian* endian, ResEvent* event) { + switch (event->type) { + case ResEvent::EventType::kAction: + SwapEndian(&event->next_event_idx); + SwapEndian(&event->actor_idx); + SwapEndian(&event->actor_action_idx); + break; + case ResEvent::EventType::kSwitch: + SwapEndian(&event->next_event_idx); + SwapEndian(&event->actor_idx); + SwapEndian(&event->actor_query_idx); + break; + case ResEvent::EventType::kFork: + SwapEndian(&event->num_forks); + SwapEndian(&event->join_event_idx); + break; + case ResEvent::EventType::kJoin: + case ResEvent::EventType::kSubFlow: + SwapEndian(&event->next_event_idx); + break; + } +} + +void SwapEndian(ore::ResEndian* endian, ResEvent* event) { + if (endian->is_serializing) { + SwapEndianForEventData(endian, event); + SwapEndianForEventFields(endian, event); + } else { + SwapEndianForEventFields(endian, event); + SwapEndianForEventData(endian, event); + } +} + +static void SwapEndianImpl(ore::ResEndian* endian, ResEntryPoint* entry) { + ore::Array<u16> sub_flow_event_indices{entry->sub_flow_event_indices.ToPtr(endian->base), + entry->num_sub_flow_event_indices}; + for (auto& x : sub_flow_event_indices) + SwapEndian(&x); + + auto* variable_def_names = entry->variable_defs_names.ToPtr(endian->base); + if (variable_def_names) + SwapEndian(endian, variable_def_names); + + ore::Array<ResVariableDef> variable_defs{entry->variable_defs.ToPtr(endian->base), + entry->num_variable_defs}; + for (auto& def : variable_defs) + SwapEndian(endian, &def); +} + +void SwapEndian(ore::ResEndian* endian, ResEntryPoint* entry) { + if (endian->is_serializing) { + SwapEndianImpl(endian, entry); + SwapEndian(&entry->main_event_idx); + SwapEndian(&entry->num_sub_flow_event_indices); + SwapEndian(&entry->num_variable_defs); + } else { + SwapEndian(&entry->main_event_idx); + SwapEndian(&entry->num_sub_flow_event_indices); + SwapEndian(&entry->num_variable_defs); + SwapEndianImpl(endian, entry); + } +} + +static void SwapEndianImpl(ore::ResEndian* endian, ResVariableDef* def) { + switch (def->type) { + case ore::ResMetaData::DataType::kIntArray: { + const auto num = def->num; + ore::Array<int> array{def->value.int_array.ToPtr(endian->base), num}; + for (auto& x : array) + SwapEndian(&x); + break; + } + case ore::ResMetaData::DataType::kFloatArray: { + const auto num = def->num; + ore::Array<float> array{def->value.float_array.ToPtr(endian->base), num}; + for (auto& x : array) + SwapEndian(reinterpret_cast<u32*>(&x)); + break; + } + default: + break; + } +} + +static bool IsScalarVariableDef(ResVariableDef* def) { + using Type = ore::ResMetaData::DataType::Type; + return def->type == Type::kInt || def->type == Type::kBool || def->type == Type::kFloat; +} + +void SwapEndian(ore::ResEndian* endian, ResVariableDef* def) { + if (endian->is_serializing) { + SwapEndianImpl(endian, def); + SwapEndian(&def->num); + if (IsScalarVariableDef(def)) + SwapEndian(&def->value.i); + } else { + SwapEndian(&def->num); + if (IsScalarVariableDef(def)) + SwapEndian(&def->value.i); + SwapEndianImpl(endian, def); + } +} + +} // namespace evfl diff --git a/lib/EventFlow/src/evfl/ResTimeline.cpp b/lib/EventFlow/src/evfl/ResTimeline.cpp new file mode 100644 index 00000000..a81c9bf1 --- /dev/null +++ b/lib/EventFlow/src/evfl/ResTimeline.cpp @@ -0,0 +1,118 @@ +#include <evfl/ResActor.h> +#include <evfl/ResTimeline.h> +#include <ore/Array.h> +#include <ore/ResEndian.h> +#include <ore/ResMetaData.h> + +namespace evfl { + +using ore::SwapEndian; + +void SwapEndian(ore::ResEndian* endian, ResTrigger* trigger) { + SwapEndian(&trigger->clip_index); +} + +void SwapEndian(ore::ResEndian* endian, ResCut* cut) { + if (endian->is_serializing) { + if (auto* params = cut->params.ToPtr(endian->base)) + SwapEndian(endian, params); + SwapEndian(&cut->start_time); + } else { + SwapEndian(&cut->start_time); + if (auto* params = cut->params.ToPtr(endian->base)) + SwapEndian(endian, params); + } +} + +void SwapEndian(ore::ResEndian* endian, ResClip* clip) { + const auto swap_fields = [&] { + SwapEndian(&clip->start_time); + SwapEndian(&clip->duration); + SwapEndian(&clip->actor_index); + SwapEndian(&clip->actor_action_index); + }; + + const auto swap_params = [&] { + if (auto* params = clip->params.ToPtr(endian->base)) + SwapEndian(endian, params); + }; + + if (endian->is_serializing) { + swap_params(); + swap_fields(); + } else { + swap_fields(); + swap_params(); + } +} + +void SwapEndian(ore::ResEndian* endian, ResOneshot* oneshot) { + const auto swap_fields = [&] { + SwapEndian(&oneshot->time); + SwapEndian(&oneshot->actor_index); + SwapEndian(&oneshot->actor_action_index); + }; + + const auto swap_params = [&] { + if (auto* params = oneshot->params.ToPtr(endian->base)) + SwapEndian(endian, params); + }; + + if (endian->is_serializing) { + swap_params(); + swap_fields(); + } else { + swap_fields(); + swap_params(); + } +} + +void SwapEndian(ore::ResEndian* endian, ResSubtimeline* subtimeline) {} + +static void SwapEndianForTimelineData(ore::ResEndian* endian, ResTimeline* timeline) { + ore::Array<ResClip> clips{timeline->clips.ToPtr(endian->base), timeline->num_clips}; + for (auto& clip : clips) + SwapEndian(endian, &clip); + + ore::Array<ResOneshot> oneshots{timeline->oneshots.ToPtr(endian->base), timeline->num_oneshots}; + for (auto& oneshot : oneshots) + SwapEndian(endian, &oneshot); + + ore::Array<ResActor> actors{timeline->actors.ToPtr(endian->base), timeline->num_actors}; + for (auto& actor : actors) + SwapEndian(endian, &actor); + + const int num_triggers = timeline->num_clips * 2; + ore::Array<ResTrigger> triggers{timeline->triggers.ToPtr(endian->base), num_triggers}; + for (auto& trigger : triggers) + SwapEndian(endian, &trigger); + + ore::Array<ResCut> cuts{timeline->cuts.ToPtr(endian->base), timeline->num_cuts}; + for (auto& cut : cuts) + SwapEndian(endian, &cut); + + if (auto* params = timeline->params.ToPtr(endian->base)) + SwapEndian(endian, params); +} + +void SwapEndian(ore::ResEndian* endian, ResTimeline* timeline) { + const auto swap_fields = [&] { + SwapEndian(&timeline->duration); + SwapEndian(&timeline->num_actors); + SwapEndian(&timeline->num_actions); + SwapEndian(&timeline->num_clips); + SwapEndian(&timeline->num_oneshots); + SwapEndian(&timeline->num_subtimelines); + SwapEndian(&timeline->num_cuts); + }; + + if (endian->is_serializing) { + SwapEndianForTimelineData(endian, timeline); + swap_fields(); + } else { + swap_fields(); + SwapEndianForTimelineData(endian, timeline); + } +} + +} // namespace evfl diff --git a/lib/EventFlow/src/evfl/TimelineObj.cpp b/lib/EventFlow/src/evfl/TimelineObj.cpp new file mode 100644 index 00000000..14e89d8b --- /dev/null +++ b/lib/EventFlow/src/evfl/TimelineObj.cpp @@ -0,0 +1,235 @@ +#include <evfl/Action.h> +#include <evfl/ResActor.h> +#include <evfl/ResTimeline.h> +#include <evfl/TimelineObj.h> +#include <utility> + +namespace evfl { + +int TimelineObj::s_GlobalPlayCounter{}; + +// NON_MATCHING: didn't bother matching, clearly equivalent +TimelineObj::TimelineObj() = default; + +void TimelineObj::Calc() { + CalcImpl(); + for (auto* timeline : m_sub_timelines) { + if (timeline) + timeline->Calc(); + } +} + +void TimelineObj::Reset() { + m_play_counter = ++s_GlobalPlayCounter; + m_started = false; + m_state = TimelineState::kNotStarted; + for (auto* timeline : m_sub_timelines) { + if (timeline) + timeline->Reset(); + } +} + +void TimelineObj::SetState(TimelineState::Type state) { + m_state = state; +} + +void TimelineObj::Start(float start_time) { + if (m_started) + return; + + m_started = true; + m_state = TimelineState::kPlaying; + m_time = -1.0; + m_last_trigger_idx = -1; + m_last_oneshot_idx = -1; + JumpTimeTo(start_time); + Calc(); + + for (auto* timeline : m_sub_timelines) { + if (timeline && !timeline->m_started) + timeline->Start(start_time); + } +} + +void TimelineObj::JumpTimeTo(float time) { + JumpTimeToImpl(time); +} + +void TimelineObj::AdvanceTimeTo(float time) { + AdvanceTimeToImpl(time); +} + +namespace { + +TriggerType::Type ReverseTriggerType(TriggerType::Type clip_trigger_type) { + switch (clip_trigger_type) { + case TriggerType::kClipEnter: + return TriggerType::kClipLeave; + case TriggerType::kClipLeave: + return TriggerType::kClipEnter; + case TriggerType::kOneshot: + return TriggerType::kOneshot; + default: + return {}; + } +} + +float GetTriggerTime(TriggerType::Type type, const ResClip& clip) { + switch (type) { + case TriggerType::kEnter: + return clip.start_time; + case TriggerType::kLeave: + return clip.start_time + clip.duration; + default: + return 0.0; + } +} + +float GetTriggerTimeReverse(TriggerType::Type type, const ResClip& clip) { + switch (type) { + case TriggerType::kEnter: + return clip.start_time + clip.duration; + case TriggerType::kLeave: + return clip.start_time; + default: + return 0.0; + } +} + +} // namespace + +// NON_MATCHING: reorderings for the action binding stuff +void TimelineObj::CalcImpl() { + if (m_time == m_new_time) + return; + + const int direction = m_time < m_new_time ? 1 : -1; + const auto time_max = m_time < m_new_time ? m_new_time : m_time; + const auto time_min = m_time < m_new_time ? m_time : m_new_time; + + ore::Array<const ResTrigger> triggers{m_timeline->triggers.Get(), 2 * m_timeline->num_clips}; + ore::Array<const ResClip> clips{m_timeline->clips.Get(), m_timeline->num_clips}; + + for (int trigger_idx = (m_time < m_new_time) + m_last_trigger_idx; + u32(trigger_idx) < u32(triggers.size()); trigger_idx += direction) { + const auto& trigger = triggers[trigger_idx]; + const auto& clip = clips[trigger.clip_index]; + const auto trigger_type = TriggerType::Type(trigger.trigger_type); + + const float trigger_time = GetTriggerTime(trigger_type, clip); + if (trigger_time <= time_min || time_max < trigger_time) + break; + + m_last_trigger_idx = trigger_idx; + + if (m_jumped_time) { + const float rev_trigger_time = GetTriggerTimeReverse(trigger_type, clip); + if (time_min < rev_trigger_time && rev_trigger_time <= time_max) + continue; + } + + const auto actor_idx = clip.actor_index; + const auto& bindings = m_act_binder.GetBindings(); + const auto* actions = m_timeline->actors.Get()[actor_idx].actions.Get(); + const auto& binding = bindings[actor_idx]; + const ore::StringView name = *actions[clip.actor_action_index].name.Get(); + const auto* action = binding.GetAction(name); + + auto real_trigger_type = trigger_type; + if (m_jumped_time && direction < 0) + real_trigger_type = ReverseTriggerType(real_trigger_type); + + const ActionArg arg(&clip, binding.GetUserData(), action->user_data, + m_new_time - trigger_time, real_trigger_type, clip.params.Get()); + ActionDoneHandler handler{this}; + action->handler(arg, std::move(handler)); + } + + ore::Array<const ResOneshot> oneshots{m_timeline->oneshots.Get(), m_timeline->num_oneshots}; + for (int oneshot_idx = (m_time < m_new_time) + m_last_oneshot_idx; + u32(oneshot_idx) < u32(oneshots.size()); oneshot_idx += direction) { + const auto& oneshot = oneshots[oneshot_idx]; + const auto trigger_time = oneshot.time; + if (trigger_time < time_min || time_max < trigger_time) + break; + + m_last_oneshot_idx = oneshot_idx; + + if (m_jumped_time && trigger_time != m_new_time) + continue; + + const auto actor_idx = oneshot.actor_index; + const auto& bindings = m_act_binder.GetBindings(); + const auto& binding = bindings[actor_idx]; + const auto* actions = m_timeline->actors.Get()[actor_idx].actions.Get(); + const ore::StringView name = *actions[oneshot.actor_action_index].name.Get(); + const auto* action = binding.GetAction(name); + + const ActionArg arg(&oneshot, binding.GetUserData(), action->user_data, + m_new_time - trigger_time, oneshot.params.Get()); + ActionDoneHandler handler{this}; + action->handler(arg, std::move(handler)); + } + + m_time = m_new_time; +} + +void TimelineObj::AdvanceTimeToImpl(float time) { + m_new_time = time; + m_jumped_time = false; + for (auto* timeline : m_sub_timelines) { + if (timeline) + timeline->AdvanceTimeToImpl(time); + } +} + +void TimelineObj::JumpTimeToImpl(float time) { + m_new_time = time; + m_jumped_time = true; + for (auto* timeline : m_sub_timelines) { + if (timeline) + timeline->JumpTimeToImpl(time); + } +} + +bool TimelineObj::RegisterSubtimeline(TimelineObj* obj) { + ore::Array<const ResSubtimeline> subtimelines{m_timeline->subtimelines.Get(), + m_timeline->num_subtimelines}; + for (int i = 0; i < subtimelines.size(); ++i) { + if (ore::StringView(*obj->m_timeline->name.Get()) == *subtimelines[i].name.Get()) { + m_sub_timelines[i] = obj; + return true; + } + } + return false; +} + +// NON_MATCHING: std::fill using obj->m_sub_timelines.size() rather than the byte size +bool TimelineObj::Builder::Build(TimelineObj* obj, AllocateArg allocate_arg) { + if (!obj) + return false; + + if (!allocate_arg.alloc || !allocate_arg.free) + return false; + + obj->Finalize(); + + EvflAllocator allocator{allocate_arg}; + obj->m_allocator = allocator; + + m_act_binder_builder.Build(&obj->m_act_binder, &obj->m_allocator, + {m_timeline->actors.Get(), m_timeline->num_actors}); + + auto& bindings = obj->m_act_binder.GetBindings(); + for (auto it = bindings.begin(); it != bindings.end(); ++it) + it->SetIsUsed(true); + + obj->m_timeline = m_timeline; + obj->m_allocator = allocator; + obj->m_sub_timelines.ConstructElements(m_timeline->num_subtimelines, &allocator); + std::fill(obj->m_sub_timelines.begin(), obj->m_sub_timelines.end(), nullptr); + + return true; +} + +} // namespace evfl diff --git a/lib/EventFlow/src/ore/BinaryFile.cpp b/lib/EventFlow/src/ore/BinaryFile.cpp new file mode 100644 index 00000000..f705306b --- /dev/null +++ b/lib/EventFlow/src/ore/BinaryFile.cpp @@ -0,0 +1,182 @@ +#include <cstdint> +#include <ore/BinaryFile.h> +#include <ore/BitUtils.h> + +namespace ore { + +bool BinaryFileHeader::IsValid(s64 magic_, int ver_major_, int ver_minor_, int ver_patch_, + int ver_sub_) const { + bool valid = true; + valid &= int(ver_major) == ver_major_ && int(ver_minor) == ver_minor_ && + magic == magic_ & int(ver_patch) <= ver_patch_; + valid &= IsEndianReverse() || IsEndianValid(); + valid &= IsAlignmentValid(); + return valid; +} + +bool BinaryFileHeader::IsSignatureValid(s64 magic_) const { + return magic == magic_; +} + +bool BinaryFileHeader::IsVersionValid(int major, int minor, int patch, int sub) const { + if (int(ver_major) != major) + return false; + if (int(ver_minor) != minor) + return false; + if (int(ver_patch) > patch) + return false; + return true; +} + +bool BinaryFileHeader::IsEndianReverse() const { + return bom == s16(0xFFFE); +} + +bool BinaryFileHeader::IsEndianValid() const { + return bom == s16(0xFEFF); +} + +bool BinaryFileHeader::IsAlignmentValid() const { + return (std::uintptr_t(this) & (GetAlignment() - 1)) == 0; +} + +int BinaryFileHeader::GetAlignment() const { + return 1 << alignment; +} + +static constexpr u32 FlagRelocated = 1 << 0; + +bool BinaryFileHeader::IsRelocated() const { + return relocation_flags & FlagRelocated; +} + +void BinaryFileHeader::SetRelocated(bool relocated) { + if (relocated) + relocation_flags |= FlagRelocated; + else + relocation_flags &= ~FlagRelocated; +} + +void BinaryFileHeader::SetByteOrderMark() { + bom = s16(0xFEFF); +} + +int BinaryFileHeader::GetFileSize() const { + return file_size; +} + +void BinaryFileHeader::SetFileSize(int size) { + file_size = size; +} + +void BinaryFileHeader::SetAlignment(int alignment_) { + alignment = CountTrailingZeros(u32(alignment_)); +} + +StringView BinaryFileHeader::GetFileName() const { + StringView name; + if (file_name_offset != 0) + name = reinterpret_cast<const char*>(this) + file_name_offset; + return name; +} + +void BinaryFileHeader::SetFileName(const StringView& name) { + if (name.empty()) { + file_name_offset = 0; + } else { + file_name_offset = int(intptr_t(name.data()) - intptr_t(this)); +#ifdef MATCHING_HACK_NX_CLANG + asm(""); +#endif + } +} + +RelocationTable* BinaryFileHeader::GetRelocationTable() { + if (relocation_table_offset == 0) + return nullptr; + return reinterpret_cast<RelocationTable*>(reinterpret_cast<char*>(this) + + relocation_table_offset); +} + +void BinaryFileHeader::SetRelocationTable(RelocationTable* table) { + if (table == nullptr) { + relocation_table_offset = 0; + } else { + relocation_table_offset = int(intptr_t(table) - intptr_t(this)); +#ifdef MATCHING_HACK_NX_CLANG + asm(""); +#endif + } +} + +BinaryBlockHeader* BinaryFileHeader::GetFirstBlock() { + if (first_block_offset == 0) + return nullptr; + return reinterpret_cast<BinaryBlockHeader*>(reinterpret_cast<char*>(this) + first_block_offset); +} + +const BinaryBlockHeader* BinaryFileHeader::GetFirstBlock() const { + if (first_block_offset == 0) + return nullptr; + return reinterpret_cast<const BinaryBlockHeader*>(reinterpret_cast<const char*>(this) + + first_block_offset); +} + +BinaryBlockHeader* BinaryFileHeader::FindFirstBlock(int type) { + auto* block = GetFirstBlock(); + if (!block || block->magic == type) + return block; + return block->FindNextBlock(type); +} + +const BinaryBlockHeader* BinaryFileHeader::FindFirstBlock(int type) const { + auto* block = GetFirstBlock(); + if (!block || block->magic == type) + return block; + return block->FindNextBlock(type); +} + +void BinaryFileHeader::SetFirstBlock(BinaryBlockHeader* block) { + if (block == nullptr) + first_block_offset = 0; + else + first_block_offset = int(intptr_t(block) - intptr_t(this)); +} + +BinaryBlockHeader* BinaryBlockHeader::FindNextBlock(int type) { + auto* block = this; + do + block = block->GetNextBlock(); + while (block && block->magic != type); + return block; +} + +const BinaryBlockHeader* BinaryBlockHeader::FindNextBlock(int type) const { + auto* block = this; + do + block = block->GetNextBlock(); + while (block && block->magic != type); + return block; +} + +BinaryBlockHeader* BinaryBlockHeader::GetNextBlock() { + if (next_block_offset == 0) + return nullptr; + return reinterpret_cast<BinaryBlockHeader*>(reinterpret_cast<char*>(this) + next_block_offset); +} + +const BinaryBlockHeader* BinaryBlockHeader::GetNextBlock() const { + if (next_block_offset == 0) + return nullptr; + return reinterpret_cast<const BinaryBlockHeader*>(reinterpret_cast<const char*>(this) + + next_block_offset); +} + +void BinaryBlockHeader::SetNextBlock(BinaryBlockHeader* block) { + if (block == nullptr) + next_block_offset = 0; + else + next_block_offset = int(intptr_t(block) - intptr_t(this)); +} + +} // namespace ore diff --git a/lib/EventFlow/src/ore/BitUtils.cpp b/lib/EventFlow/src/ore/BitUtils.cpp new file mode 100644 index 00000000..e392bd13 --- /dev/null +++ b/lib/EventFlow/src/ore/BitUtils.cpp @@ -0,0 +1,115 @@ +#include <ore/BitUtils.h> + +namespace ore { + +void BitArray::SetAllOn() { + const int num = m_num_bits >> ShiftAmount; + Fill(num, Word(-1)); + + u32 remainder = u32(m_num_bits) % NumBitsPerWord; + if (remainder != 0) + m_words[num] = (1ul << remainder) - 1; +} + +void BitArray::SetAllOff() { + Fill(GetNumWords(), Word(0)); +} + +BitArray::TestIter BitArray::BeginTest() const { + return TestIter(m_words, m_words + GetNumWords()); +} + +BitArray::TestIter BitArray::EndTest() const { + return TestIter(nullptr, nullptr); +} + +BitArray::TestClearIter BitArray::BeginTestClear() { + return TestClearIter(m_words, m_words + GetNumWords()); +} + +BitArray::TestClearIter BitArray::EndTestClear() { + return TestClearIter(nullptr, nullptr); +} + +BitArray::TestIter::TestIter(const BitArray::Word* start, const BitArray::Word* end) { + for (auto* it = start; it != end; ++it) { + if (*it != 0) { + auto idx = CountTrailingZeros(*it); + idx += 8 * int(intptr_t(it) - intptr_t(start)) & ClearMask; + m_bit = idx; + m_current_word = it; + m_last_word = end; + m_next = *it & (*it - 1); + return; + } + } + + SetInvalid(); +} + +BitArray::TestIter& BitArray::TestIter::operator++() { + m_bit &= ClearMask; + + // Fast path: we still have bits in the current word + if (m_next != 0) { + m_bit += CountTrailingZeros(m_next); + m_next &= m_next - 1; + return *this; + } + + // Find the next nonzero word and the first set bit in it + ++m_current_word; + for (; m_current_word != m_last_word; ++m_current_word) { + m_bit += NumBitsPerWord; + if (*m_current_word == 0) + continue; + m_bit += CountTrailingZeros(*m_current_word); + m_next = *m_current_word & (*m_current_word - 1); + return *this; + } + + SetInvalid(); + return *this; +} + +BitArray::TestClearIter::TestClearIter(BitArray::Word* start, BitArray::Word* end) { + for (auto* it = start; it != end; ++it) { + if (*it != 0) { + auto idx = CountTrailingZeros(*it); + idx += 8 * int(intptr_t(it) - intptr_t(start)) & ClearMask; + m_bit = idx; + m_current_word = it; + m_last_word = end; + m_next = *it & (*it - 1); + return; + } + } + + SetInvalid(); +} + +BitArray::TestClearIter& BitArray::TestClearIter::operator++() { + m_bit &= ClearMask; + + if (m_next != 0) { + m_bit += CountTrailingZeros(m_next); + m_next &= m_next - 1; + return *this; + } + + *m_current_word = 0; + ++m_current_word; + for (; m_current_word != m_last_word; *m_current_word = 0, ++m_current_word) { + m_bit += NumBitsPerWord; + if (*m_current_word == 0) + continue; + m_bit += CountTrailingZeros(*m_current_word); + m_next = *m_current_word & (*m_current_word - 1); + return *this; + } + + SetInvalid(); + return *this; +} + +} // namespace ore diff --git a/lib/EventFlow/src/ore/EnumUtil.cpp b/lib/EventFlow/src/ore/EnumUtil.cpp new file mode 100644 index 00000000..71c1cc95 --- /dev/null +++ b/lib/EventFlow/src/ore/EnumUtil.cpp @@ -0,0 +1,13 @@ +#include <algorithm> +#include <ore/EnumUtil.h> + +namespace ore { + +int detail::EnumUtil::FindIndex(int value, const IterRange<const int*>& values) { + auto it = std::find_if(values.begin(), values.end(), [value](int x) { return value == x; }); + if (it == values.end()) + return -1; + return static_cast<int>(it - values.begin()); +} + +} // namespace ore diff --git a/lib/EventFlow/src/ore/RelocationTable.cpp b/lib/EventFlow/src/ore/RelocationTable.cpp new file mode 100644 index 00000000..b58625fb --- /dev/null +++ b/lib/EventFlow/src/ore/RelocationTable.cpp @@ -0,0 +1,106 @@ +#include <cstring> +#include <ore/RelocationTable.h> + +namespace ore { + +namespace { + +struct BitFlag32 { + explicit BitFlag32(u32 flags) : m_flags(flags) {} + bool operator[](int idx) const { return m_flags & (1 << idx); } + + u32 m_flags{}; +}; + +} // namespace + +void RelocationTable::Section::SetPtr(void* ptr_) { + ptr = reinterpret_cast<u64>(ptr_); +} + +void* RelocationTable::Section::GetPtr() const { + return reinterpret_cast<void*>(ptr); +} + +void* RelocationTable::Section::GetPtrInFile(void* base) const { + return static_cast<char*>(base) + offset; +} + +void* RelocationTable::Section::GetBasePtr(void* base) const { + if (ptr) + base = reinterpret_cast<void*>(ptr - offset); + return base; +} + +u32 RelocationTable::Section::GetSize() const { + return size; +} + +void RelocationTable::Relocate() { + char* const table_base = reinterpret_cast<char*>(this) - table_start_offset; + const auto* entries = GetEntries(); + const int num = num_sections; + + for (int section_idx = 0; section_idx < num; ++section_idx) { + const auto& section = GetSections()[section_idx]; + + auto* base = static_cast<char*>(section.GetBasePtr(table_base)); + const int idx0 = section.first_entry_idx; + const int end = idx0 + section.num_entries; + + for (int idx = idx0; idx < end; ++idx) { + const auto& entry = entries[idx]; + const auto pointers_offset = entry.pointers_offset; + const BitFlag32 mask{entry.mask}; + + auto* pointer_ptr = reinterpret_cast<u64*>(table_base + pointers_offset); + for (int i = 0; i < 32; ++i, ++pointer_ptr) { + if (!mask[i]) + continue; + const auto offset = static_cast<int>(*pointer_ptr); + void* ptr = offset == 0 ? nullptr : reinterpret_cast<void*>(base + offset); + std::memcpy(pointer_ptr, &ptr, sizeof(ptr)); + } + } + } +} + +void RelocationTable::Unrelocate() { + char* const table_base = reinterpret_cast<char*>(this) - table_start_offset; + const auto* entries = GetEntries(); + const int num = num_sections; + + for (int section_idx = 0; section_idx < num; ++section_idx) { + auto& section = GetSections()[section_idx]; + + auto* base = static_cast<char*>(section.GetBasePtr(table_base)); + section.SetPtr(nullptr); + const int idx0 = section.first_entry_idx; + const int end = idx0 + section.num_entries; + + for (int idx = idx0; idx < end; ++idx) { + const auto& entry = entries[idx]; + const auto pointers_offset = entry.pointers_offset; + const BitFlag32 mask{entry.mask}; + + auto* pointer_ptr = reinterpret_cast<void**>(table_base + pointers_offset); + for (int i = 0; i < 32; ++i, ++pointer_ptr) { + if (!mask[i]) + continue; + void* ptr = *pointer_ptr; + u64 offset = static_cast<int>(ptr == nullptr ? 0 : intptr_t(ptr) - intptr_t(base)); + std::memcpy(pointer_ptr, &offset, sizeof(offset)); + } + } + } +} + +int RelocationTable::CalcSize(int num_sections, int num_entries) { + int size = 0; + size += offsetof(RelocationTable, sections); + size += sizeof(Section) * num_sections; + size += sizeof(Section::Entry) * num_entries; + return size; +} + +} // namespace ore diff --git a/lib/EventFlow/src/ore/ResDic.cpp b/lib/EventFlow/src/ore/ResDic.cpp new file mode 100644 index 00000000..019ffe61 --- /dev/null +++ b/lib/EventFlow/src/ore/ResDic.cpp @@ -0,0 +1,50 @@ +#include <algorithm> +#include <ore/ResDic.h> +#include <ore/ResEndian.h> + +namespace ore { + +int ResDic::FindRefBit(const StringView& str1, const StringView& str2) { + const auto len1 = str1.size(); + const auto len2 = str2.size(); + const auto len = std::max(len1, len2); + + for (int bit_idx = 0; bit_idx < 8 * len; ++bit_idx) { + const int idx = bit_idx >> 3; + + int bit1 = 0; + if (len1 > idx) + bit1 = str1[len1 + -(idx + 1)] >> (bit_idx % 8) & 1; + + int bit2 = 0; + if (len2 > idx) + bit2 = str2[len2 + -(idx + 1)] >> (bit_idx % 8) & 1; + + if (bit1 != bit2) + return bit_idx; + } + + return -1; +} + +void SwapEndian(ResEndian* endian, ResDic* dic) { + const auto swap_entries = [&] { + const int num_entries = dic->num_entries + 1; + for (int i = 0; i < num_entries; ++i) { + ResDicEntry& entry = dic->GetEntries()[i]; + SwapEndian(&entry.compact_bit_idx); + SwapEndian(&entry.next_indices[0]); + SwapEndian(&entry.next_indices[1]); + } + }; + + if (endian->is_serializing) { + swap_entries(); + SwapEndian(&dic->num_entries); + } else { + SwapEndian(&dic->num_entries); + swap_entries(); + } +} + +} // namespace ore diff --git a/lib/EventFlow/src/ore/ResMetaData.cpp b/lib/EventFlow/src/ore/ResMetaData.cpp new file mode 100644 index 00000000..2587f3d6 --- /dev/null +++ b/lib/EventFlow/src/ore/ResMetaData.cpp @@ -0,0 +1,89 @@ +#include <ore/ResDic.h> +#include <ore/ResEndian.h> +#include <ore/ResMetaData.h> + +namespace ore { + +static void SwapEndianImpl(ResEndian* endian, ResMetaData* res) { + auto* dictionary = res->dictionary.ToPtr(endian->base); + if (dictionary) + SwapEndian(endian, dictionary); + + switch (res->type) { + case ResMetaData::DataType::kArgument: + case ResMetaData::DataType::kString: + case ResMetaData::DataType::kStringArray: + case ResMetaData::DataType::kActorIdentifier: { + if (res->num_items == 0) + break; + BinString* str = res->value.str.ToPtr(endian->base); + if (endian->is_serializing) { + for (int i = 0, n = res->num_items; i < n; ++i) { + auto* next = str->NextString(); + SwapEndian(&str->length); + str = next; + } + } else { + for (int i = 0, n = res->num_items; i < n; ++i) { + SwapEndian(&str->length); + str = str->NextString(); + } + } + break; + } + case ResMetaData::DataType::kContainer: { + for (int i = 0, n = res->num_items; i < n; ++i) { + ResMetaData* ptr = (&res->value.container + i)->ToPtr(endian->base); + if (ptr) + SwapEndian(endian, ptr); + } + break; + } + case ResMetaData::DataType::kInt: + case ResMetaData::DataType::kBool: + case ResMetaData::DataType::kFloat: + case ResMetaData::DataType::kIntArray: + case ResMetaData::DataType::kFloatArray: + for (int i = 0, n = res->num_items; i < n; ++i) { + SwapEndian(&res->value.i + i); + } + break; + case ResMetaData::DataType::kWString: + case ResMetaData::DataType::kWStringArray: { + if (res->num_items == 0) + break; + BinWString* str = res->value.wstr.ToPtr(endian->base); + if (endian->is_serializing) { + for (int i = 0, n = res->num_items; i < n; ++i) { + for (auto& c : *str) + c = static_cast<wchar_t>(SwapEndian(static_cast<u32>(c))); + auto* next = str->NextString(); + SwapEndian(&str->length); + str = next; + } + } else { + for (int i = 0, n = res->num_items; i < n; ++i) { + SwapEndian(&str->length); + for (auto& c : *str) + c = static_cast<wchar_t>(SwapEndian(static_cast<u32>(c))); + str = str->NextString(); + } + } + break; + } + case ResMetaData::DataType::kBoolArray: + break; + } +} + +void SwapEndian(ResEndian* endian, ResMetaData* res) { + if (endian->is_serializing) { + SwapEndianImpl(endian, res); + SwapEndian(&res->num_items); + } else { + SwapEndian(&res->num_items); + SwapEndianImpl(endian, res); + } +} + +} // namespace ore diff --git a/lib/EventFlow/src/ore/StringPool.cpp b/lib/EventFlow/src/ore/StringPool.cpp new file mode 100644 index 00000000..6b45338a --- /dev/null +++ b/lib/EventFlow/src/ore/StringPool.cpp @@ -0,0 +1,13 @@ +#include <ore/StringPool.h> + +namespace ore { + +int StringPool::GetLength() const { + return length; +} + +void StringPool::SetLength(int len) { + length = len; +} + +} // namespace ore |
