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"
This commit is contained in:
Léo Lam
2022-03-21 19:25:20 +01:00
parent ffcc7f659e
commit 18c60323a9
457 changed files with 52182 additions and 16 deletions
+125
View File
@@ -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
@@ -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
+208
View File
@@ -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
+208
View File
@@ -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
+29
View File
@@ -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
+187
View File
@@ -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
@@ -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
+136
View File
@@ -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
+80
View File
@@ -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
+78
View File
@@ -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
+45
View File
@@ -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
+287
View File
@@ -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
+153
View File
@@ -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
+149
View File
@@ -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
+25
View File
@@ -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
+115
View File
@@ -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
+100
View File
@@ -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
+28
View File
@@ -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
@@ -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
+62
View File
@@ -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
+75
View File
@@ -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
+58
View File
@@ -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
+20
View File
@@ -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
+79
View File
@@ -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
+20
View File
@@ -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;