mirror of
https://github.com/zeldaret/botw
synced 2026-07-28 23:26:50 -04:00
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:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user