Switch to subrepos

git subrepo clone https://github.com/open-ead/sead lib/sead

subrepo:
  subdir:   "lib/sead"
  merged:   "1b66e825d"
upstream:
  origin:   "https://github.com/open-ead/sead"
  branch:   "master"
  commit:   "1b66e825d"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"

git subrepo clone (merge) https://github.com/open-ead/nnheaders lib/NintendoSDK

subrepo:
  subdir:   "lib/NintendoSDK"
  merged:   "9ee21399f"
upstream:
  origin:   "https://github.com/open-ead/nnheaders"
  branch:   "master"
  commit:   "9ee21399f"
git-subrepo:
  version:  "0.4.3"
  origin:   "ssh://git@github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"

git subrepo clone https://github.com/open-ead/agl lib/agl

subrepo:
  subdir:   "lib/agl"
  merged:   "7c063271b"
upstream:
  origin:   "https://github.com/open-ead/agl"
  branch:   "master"
  commit:   "7c063271b"
git-subrepo:
  version:  "0.4.3"
  origin:   "ssh://git@github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"

git subrepo clone https://github.com/open-ead/EventFlow lib/EventFlow

subrepo:
  subdir:   "lib/EventFlow"
  merged:   "c35d21b34"
upstream:
  origin:   "https://github.com/open-ead/EventFlow"
  branch:   "master"
  commit:   "c35d21b34"
git-subrepo:
  version:  "0.4.3"
  origin:   "ssh://git@github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"
This commit is contained in:
Léo Lam 2022-03-21 19:25:20 +01:00
parent ffcc7f659e
commit 18c60323a9
No known key found for this signature in database
GPG Key ID: 0DF30F9081000741
457 changed files with 52182 additions and 16 deletions

12
.gitmodules vendored
View File

@ -1,15 +1,3 @@
[submodule "lib/sead"]
path = lib/sead
url = https://github.com/open-ead/sead
[submodule "lib/NintendoSDK"]
path = lib/NintendoSDK
url = https://github.com/open-ead/nnheaders
[submodule "lib/agl"]
path = lib/agl
url = https://github.com/open-ead/agl
[submodule "lib/EventFlow"]
path = lib/EventFlow
url = https://github.com/open-ead/EventFlow
[submodule "tools/common"]
path = tools/common
url = https://github.com/open-ead/nx-decomp-tools

View File

@ -220,6 +220,41 @@ This project sometimes uses small hacks to force particular code to be generated
* `MATCHING_HACK_NX_CLANG`: Hacks for Switch, when compiling with Clang.
## Modifying library code
Changes to the following libraries must be PR'd/submitted to their own repository:
* sead: https://github.com/open-ead/sead
* NintendoSDK: https://github.com/open-ead/nnheaders
* agl: https://github.com/open-ead/agl
* EventFlow: https://github.com/open-ead/EventFlow
The recommended workflow is to commit your changes as usual in the BotW repo, and then do a "subrepo push" to
your fork of the library repo once you are ready to open a pull request.
Example:
```
echo test > lib/sead/test_file
git add -A
git commit -m "test"
git subrepo push lib/sead -r <your fork> -b <new branch name in your fork>
```
You can then open one PR in the BotW repo and another one in the library repo.
Once the library repo PR has been merged, a maintainer will let you know what you should do
to update the library subrepo in the BotW repo.
Follow the [instructions here](https://github.com/ingydotnet/git-subrepo#installation) to install the git-subrepo command
if you do not already have it.
### Updating the library repos
Library subrepos can be updated with e.g. `git subrepo pull lib/sead`. This will pull the latest changes from the
library repository and git-subrepo will automatically create a commit in the BotW repo to reflect the changes.
Do not attempt to amend the commit message -- doing so could break the subrepo.
## Project tools
* Check all decompiled functions for issues: `tools/check`

@ -1 +0,0 @@
Subproject commit c35d21b34397bec6dd3c67a649fc66b68839341e

View File

@ -0,0 +1,75 @@
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 100
CommentPragmas: '^ (IWYU pragma:|NOLINT)'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros: []
IncludeCategories:
- Regex: '^<[Ww]indows\.h>$'
Priority: 1
- Regex: '^<'
Priority: 2
- Regex: '^"'
Priority: 3
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++17
TabWidth: 4
UseTab: Never
WhitespaceSensitiveMacros: ["ORE_ENUM"]
...

31
lib/EventFlow/.gitignore vendored Normal file
View File

@ -0,0 +1,31 @@
__pycache__/
*.pyc
*.egg-info/
*.dist-info/
*.so
*.dll
dist/
build/
bin/
.mypy_cache/
.benchmarks/
.idea/
.vscode/
*.id0
*.id1
*.id2
*.idb
*.i64
*.nam
*.til
main.elf
perf.mData
perf.mData.old
.gdb_history
.DS_Store
tools/aarch64-none-elf-objdump

12
lib/EventFlow/.gitrepo Normal file
View File

@ -0,0 +1,12 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = https://github.com/open-ead/EventFlow
branch = master
commit = c35d21b34397bec6dd3c67a649fc66b68839341e
parent = ffcc7f659ebc9bc9d149e52bec553b906bb47369
method = merge
cmdver = 0.4.3

View File

@ -0,0 +1,56 @@
project(EventFlow CXX ASM)
option(EVFL_VER_LABO "Build Labo's version of this library" OFF)
add_library(evfl OBJECT
include/evfl/Action.h
include/evfl/EvflAllocator.h
include/evfl/Flowchart.h
include/evfl/Param.h
include/evfl/Query.h
include/evfl/ResActor.h
include/evfl/ResEventFlowFile.h
include/evfl/ResFlowchart.h
include/evfl/ResTimeline.h
include/evfl/TimelineObj.h
include/ore/Allocator.h
include/ore/Array.h
include/ore/BinaryFile.h
include/ore/BitUtils.h
include/ore/Buffer.h
include/ore/EnumUtil.h
include/ore/IntrusiveList.h
include/ore/IterRange.h
include/ore/RelocationTable.h
include/ore/ResDic.h
include/ore/ResEndian.h
include/ore/ResMetaData.h
include/ore/StringPool.h
include/ore/StringView.h
include/ore/Types.h
src/evfl/Action.cpp
src/evfl/Param.cpp
src/evfl/Flowchart.cpp
src/evfl/FlowchartObj.cpp
src/evfl/TimelineObj.cpp
src/evfl/ResEventFlowFile.cpp
src/evfl/ResTimeline.cpp
src/evfl/ResActor.cpp
src/evfl/ResFlowchart.cpp
src/ore/BitUtils.cpp
src/ore/EnumUtil.cpp
src/ore/BinaryFile.cpp
src/ore/RelocationTable.cpp
src/ore/StringPool.cpp
src/ore/ResDic.cpp
src/ore/ResMetaData.cpp
)
target_compile_options(evfl PRIVATE -fno-exceptions)
target_compile_options(evfl PRIVATE -fno-strict-aliasing)
target_compile_options(evfl PRIVATE -Wno-invalid-offsetof)
target_include_directories(evfl PUBLIC include/)
if(EVFL_VER_LABO)
target_compile_definitions(evfl PUBLIC EVFL_VER_LABO=1)
endif()

View File

@ -0,0 +1,125 @@
#pragma once
#include <evfl/Param.h>
#include <ore/EnumUtil.h>
#include <ore/IntrusiveList.h>
#include <utility>
namespace evfl {
class FlowchartContext;
class FlowchartContextNode;
class FlowchartObj;
struct ResClip;
struct ResEvent;
struct ResOneshot;
class TimelineObj;
class VariablePack;
ORE_VALUED_ENUM(TriggerType, kFlowchart = 0, kClipEnter = 1, kClipLeave = 2, kOneshot = 3,
kNormal = 0, kEnter = 1, kLeave = 2)
class ActionDoneHandler {
public:
ActionDoneHandler() = default;
ActionDoneHandler(FlowchartObj* obj, FlowchartContext* context, int node_idx);
explicit ActionDoneHandler(TimelineObj* obj) : m_is_flowchart(false) { m_timeline_obj = obj; }
~ActionDoneHandler() { m_list_node.Erase(); }
ActionDoneHandler(const ActionDoneHandler&) = delete;
auto operator=(const ActionDoneHandler&) = delete;
ActionDoneHandler(ActionDoneHandler&& other) noexcept { *this = std::move(other); }
ActionDoneHandler& operator=(ActionDoneHandler&& other) noexcept {
m_context = other.m_context;
m_node_idx = other.m_node_idx;
m_obj = other.m_obj;
m_node_counter = other.m_node_counter;
m_handled = other.m_handled;
m_is_flowchart = other.m_is_flowchart;
m_list_node = std::move(other.m_list_node);
other.m_context = nullptr;
other.m_node_idx = -1;
other.m_node_counter = -1;
other.m_obj = nullptr;
other.m_handled = false;
other.m_is_flowchart = true;
return *this;
}
FlowchartContextNode* GetContextNode();
void InvokeFromFlowchartImpl();
void InvokeFromTimelineImpl();
bool IsWaitingJoin();
bool CancelWaiting();
void Reset() {
m_context = nullptr;
m_obj = nullptr;
m_node_idx = -1;
m_node_counter = -1;
m_handled = false;
m_is_flowchart = true;
}
static constexpr size_t GetListNodeOffset() { return offsetof(ActionDoneHandler, m_list_node); }
private:
friend class FlowchartContext;
ore::IntrusiveListNode m_list_node;
FlowchartContext* m_context = nullptr;
int m_node_idx = -1;
int m_node_counter = -1;
union {
FlowchartObj* m_obj = nullptr;
TimelineObj* m_timeline_obj;
};
bool m_handled = false;
bool m_is_flowchart = true;
};
struct ActionArg {
ActionArg(FlowchartContext* context, int node_idx, void* actor_user_data_,
void* action_user_data_, VariablePack* variable_pack_, const ResEvent* event_)
: param_accessor(context, node_idx), actor_user_data(actor_user_data_),
action_user_data(action_user_data_), flowchart_ctx(context),
variable_pack(variable_pack_), res(event_), timeline_time_delta(0.0),
trigger_type(TriggerType::kFlowchart) {}
ActionArg(const ResClip* clip, void* actor_user_data_, void* action_user_data_,
float timeline_time_delta_, TriggerType::Type type, const ore::ResMetaData* params)
: param_accessor(params), actor_user_data(actor_user_data_),
action_user_data(action_user_data_), res(clip), timeline_time_delta(timeline_time_delta_),
trigger_type(type) {}
ActionArg(const ResOneshot* oneshot, void* actor_user_data_, void* action_user_data_,
float timeline_time_delta_, const ore::ResMetaData* params)
: param_accessor(params), actor_user_data(actor_user_data_),
action_user_data(action_user_data_), res(oneshot),
timeline_time_delta(timeline_time_delta_), trigger_type(TriggerType::kOneshot) {
res.oneshot = oneshot;
}
ParamAccessor param_accessor;
void* actor_user_data = nullptr;
void* action_user_data = nullptr;
FlowchartContext* flowchart_ctx = nullptr;
VariablePack* variable_pack = nullptr;
union Res {
explicit Res(const ResEvent* e) : event(e) {}
explicit Res(const ResClip* c) : clip(c) {}
explicit Res(const ResOneshot* o) : oneshot(o) {}
const ResEvent* event;
const ResClip* clip;
const ResOneshot* oneshot;
} res;
float timeline_time_delta{};
TriggerType::Type trigger_type{};
};
} // namespace evfl

View File

@ -0,0 +1,31 @@
#pragma once
#include <ore/Allocator.h>
namespace evfl {
struct AllocateArg {
void* (*alloc)(size_t size, size_t alignment, void* userdata);
void (*free)(void* ptr, void* userdata);
void* alloc_userdata;
void* free_userdata;
};
class EvflAllocator : public ore::Allocator {
public:
EvflAllocator() = default;
explicit EvflAllocator(AllocateArg arg) : m_arg(arg) {}
void* AllocImpl(size_t size, size_t alignment) override {
return m_arg.alloc(size, alignment, m_arg.alloc_userdata);
}
void FreeImpl(void* ptr) override { m_arg.free(ptr, m_arg.free_userdata); }
AllocateArg GetArg() const { return m_arg; }
private:
AllocateArg m_arg;
};
} // namespace evfl

View File

@ -0,0 +1,208 @@
#pragma once
#include <evfl/EvflAllocator.h>
#include <evfl/ResActor.h>
#include <ore/Array.h>
#include <ore/Buffer.h>
#include <ore/EnumUtil.h>
#include <ore/IntrusiveList.h>
#include <ore/IterRange.h>
#include <ore/StringView.h>
#include <ore/Types.h>
namespace ore {
class Allocator;
class BitArray;
} // namespace ore
namespace evfl {
class MetaDataPack;
struct ResEntryPoint;
struct ResEvent;
struct ResFlowchart;
class VariablePack;
ORE_ENUM(SubFlowCallbackType, kEnter, kLeave)
class FlowchartObj {
public:
class Builder {
public:
Builder(const ResFlowchart* flowchart, ore::BitArray* visited_entry_points)
: m_flowchart(flowchart), m_entry_points_mask(visited_entry_points) {}
bool Build(FlowchartObj* obj, ore::Allocator* allocator,
ore::IterRange<const ResFlowchart* const*> flowcharts);
private:
const ResFlowchart* m_flowchart{};
ore::BitArray* m_entry_points_mask{};
ActBinder::Builder m_act_binder_builder{};
};
#ifdef MATCHING_HACK_NX_CLANG
[[gnu::always_inline]]
#endif
~FlowchartObj() = default;
const ResFlowchart* GetFlowchart() const { return m_flowchart; }
const ActBinder& GetActBinder() const { return m_act_binder; }
ActBinder& GetActBinder() { return m_act_binder; }
private:
const ResFlowchart* m_flowchart{};
ActBinder m_act_binder;
};
class FlowchartContextNode {
public:
ORE_ENUM(State, kInvalid, kFree, kNotInvoked, kInvoked, kDone, kWaiting)
FlowchartContextNode() { Reset(); }
bool IsInvalidOrFree() const { return m_state == State::kInvalid || m_state == State::kFree; }
FlowchartObj* GetObj() const { return m_obj; }
VariablePack* GetVariablePack() const { return m_variable_pack; }
int GetNodeCounter() const { return m_node_counter; }
u16 GetEventIdx() const { return m_event_idx; }
u16 GetNextNodeIdx() const { return m_next_node_idx; }
State::Type GetState() const { return m_state; }
void Reset() {
m_node_counter = -1;
m_obj = nullptr;
m_event_idx = -1;
m_next_node_idx = -1;
m_idx = -1;
m_state = State::kInvalid;
m_variable_pack = nullptr;
m_owns_variable_pack = false;
}
private:
friend class ActionDoneHandler;
friend class FlowchartContext;
FlowchartObj* m_obj;
VariablePack* m_variable_pack;
int m_node_counter;
u16 m_event_idx;
u16 m_next_node_idx;
u16 m_idx;
ore::SizedEnum<State::Type, u8> m_state;
bool m_owns_variable_pack;
};
class FlowchartContext {
public:
class Builder {
public:
using FlowchartRange = ore::IterRange<const ResFlowchart* const*>;
ORE_ENUM(BuildResultType, kSuccess, kInvalidOperation, kResFlowchartNotFound, kEntryPointNotFound)
struct BuildResult {
BuildResultType::Type result;
/// Indicates which flowchart was required yet couldn't be found.
ore::StringView missing_flowchart_name{};
/// Indicates which entry point was required yet couldn't be found.
ore::StringView missing_entry_point_name{};
};
Builder() = default;
explicit Builder(FlowchartRange flowcharts, int flowchart_idx = 0)
: m_flowcharts(flowcharts), m_flowchart_idx(flowchart_idx) {}
bool SetEntryPoint(const ore::StringView& flowchart_name,
const ore::StringView& entry_point_name);
bool SetEntryPoint(BuildResult* result, const ore::StringView& flowchart_name,
const ore::StringView& entry_point_name);
bool Build(FlowchartContext* context, AllocateArg allocate_arg);
bool Build(BuildResult* result, FlowchartContext* context, AllocateArg allocate_arg);
private:
bool BuildImpl(BuildResult* result, FlowchartRange flowcharts, FlowchartContext* context,
AllocateArg allocate_arg, ore::Buffer flowchart_obj_buffer);
FlowchartRange m_flowcharts{};
int m_flowchart_idx = 0;
int m_entry_point_idx = -1;
};
FlowchartContext();
~FlowchartContext() {
Dispose();
m_objs.DestructElements();
}
FlowchartContext(const FlowchartContext&) = delete;
auto operator=(const FlowchartContext&) = delete;
void Start(MetaDataPack* pack);
int AllocNode();
void AllocVariablePack(FlowchartContextNode& node, const ResEntryPoint& entry_point);
void ProcessContext();
FlowchartObj* FindFlowchartObj(ore::StringView name);
const FlowchartObj* FindFlowchartObj(ore::StringView name) const;
void UnbindAll();
void Clear();
void FreeVariablePack(FlowchartContextNode& node);
void CopyVariablePack(FlowchartContextNode& src, FlowchartContextNode& dst);
bool ProcessContextNode(int node_idx);
ActorBinding* TrackBackArgumentActor(int node_idx, const ore::StringView& name);
bool IsUsing(const ResFlowchart* flowchart) const;
bool IsPlaying(const ResFlowchart* flowchart) const;
const ore::Array<ActorBinding>* GetUsedResActors(ore::StringView flowchart_name) const;
FlowchartContextNode& GetNode(int idx) { return m_nodes[idx]; }
const FlowchartContextNode& GetNode(int idx) const { return m_nodes[idx]; }
MetaDataPack* GetMetaDataPack() const { return m_metadata_pack; }
ore::Array<FlowchartObj>& GetObjs() { return m_objs; }
const ore::Array<FlowchartObj>& GetObjs() const { return m_objs; }
private:
void Dispose() {
m_obj_idx = -1;
m_active_entry_point_idx = -1;
m_metadata_pack = nullptr;
m_objs.Clear(&m_allocator);
for (auto& node : m_nodes)
FreeVariablePack(node);
m_nodes.Reset();
}
void UpdateNodeCounter(int node_idx) {
auto& node = GetNode(node_idx);
node.m_node_counter = ++s_GlobalCounter;
}
void CallSubFlowCallback(const ResFlowchart* flowchart, const ResEvent* event,
SubFlowCallbackType::Type type) {
#ifdef EVFL_VER_LABO
if (m_on_sub_flow_callback)
m_on_sub_flow_callback(this, flowchart, event, type);
#endif
}
static int s_GlobalCounter;
EvflAllocator m_allocator;
ore::Array<FlowchartObj> m_objs;
ore::DynArrayList<FlowchartContextNode> m_nodes;
ore::IntrusiveList<ActionDoneHandler> m_handlers;
#ifdef EVFL_VER_LABO
void (*m_on_sub_flow_callback)(FlowchartContext* context, const ResFlowchart* flowchart,
const ResEvent* event, SubFlowCallbackType::Type type) = nullptr;
#endif
MetaDataPack* m_metadata_pack = nullptr;
void* _78 = nullptr;
int m_next_node_idx = 0;
int m_num_allocated_nodes = 0;
int m_obj_idx = -1;
int m_active_entry_point_idx = -1;
bool m_is_processing = false;
u8 _91 = 0;
};
} // namespace evfl

View File

@ -0,0 +1,208 @@
#pragma once
#include <evfl/EvflAllocator.h>
#include <ore/Array.h>
#include <ore/BinaryFile.h>
#include <ore/Buffer.h>
#include <ore/EnumUtil.h>
#include <ore/IterRange.h>
#include <ore/ResMetaData.h>
#include <ore/StringView.h>
#include <ore/Types.h>
namespace ore {
struct ResDic;
struct ResMetaData;
} // namespace ore
namespace evfl {
class FlowchartContext;
class FlowchartObj;
struct ResEntryPoint;
class MetaDataPack {
public:
ORE_ENUM(DataType, kInt, kBool, kFloat, kString, kWString)
struct Entry {
union Value {
bool b;
int i;
float f;
const char* str;
const wchar_t* wstr;
};
bool IsKey(const ore::StringView& other) const { return ore::StringView(key) == other; }
const char* key;
Value value;
DataType::Type type;
};
class Builder {
public:
void CalcMemSize();
bool Build(MetaDataPack* pack, ore::Buffer buffer);
void SetNumEntries(int num) { m_num_entries = num; }
int GetRequiredSize() const { return m_required_size; }
private:
int m_num_entries = 16;
int m_required_size = 0;
int m_alignment_real = 16;
int m_entries_byte_size = 0;
int m_alignment = 16;
int _14 = 0;
int m_buffer_offset = -1;
};
void AddInt(const char* key, int value);
void AddBool(const char* key, bool value);
void AddFloat(const char* key, float value);
void AddStringPtr(const char* key, const char* value);
void AddWStringPtr(const char* key, const wchar_t* value);
bool FindInt(int* value, const ore::StringView& key) const;
bool FindBool(bool* value, const ore::StringView& key) const;
bool FindFloat(float* value, const ore::StringView& key) const;
bool FindString(ore::StringView* value, const ore::StringView& key) const;
bool FindWString(ore::WStringView* value, const ore::StringView& key) const;
ore::ResMetaData::DataType::Type GetType(const ore::StringView& key) const;
private:
Entry* Find(const ore::StringView& key) const;
Entry& AddEntry() { return m_entries[m_entries_num++]; }
ore::Array<Entry> GetEntries() const { return {m_entries, m_entries_num}; }
ore::Buffer m_buffer{};
Entry* m_entries{};
int m_entries_num{};
int m_entries_capacity{};
};
class VariablePack {
public:
using VariableType = ore::ResMetaData::DataType::Type;
struct Entry {
union Value {
void* dummy;
int i;
float f;
ore::DynArrayList<int>* int_array;
ore::DynArrayList<float>* float_array;
};
Value value;
VariableType type;
};
VariablePack();
~VariablePack();
VariablePack(const VariablePack&) = delete;
auto operator=(const VariablePack&) = delete;
void Init(AllocateArg arg, const ResEntryPoint* entry_point);
Entry* GetVariableEntry(const ore::StringView& name);
const Entry* GetVariableEntry(const ore::StringView& name) const;
VariableType GetVariableType(const ore::StringView& name) const;
ore::StringView GetVariableName(int idx) const;
int GetVariableCount() const;
bool Contains(const ore::StringView& name) const;
bool FindInt(int* value, const ore::StringView& name) const;
bool FindBool(bool* value, const ore::StringView& name) const;
bool FindFloat(float* value, const ore::StringView& name) const;
bool FindIntList(ore::DynArrayList<int>** value, const ore::StringView& name) const;
bool FindFloatList(ore::DynArrayList<float>** value, const ore::StringView& name) const;
int GetInt(const ore::StringView& name) const;
bool GetBool(const ore::StringView& name) const;
float GetFloat(const ore::StringView& name) const;
ore::DynArrayList<int>* GetIntList(const ore::StringView& name) const;
ore::DynArrayList<float>* GetFloatList(const ore::StringView& name) const;
void SetInt(const ore::StringView& name, int value);
void SetFloat(const ore::StringView& name, float value);
void SetBool(const ore::StringView& name, bool value);
private:
void Dispose();
ore::Allocator* GetAllocator() { return &m_allocator; }
const ore::ResDic* m_names = nullptr;
ore::Array<Entry> m_variables;
EvflAllocator m_allocator;
};
class ParamAccessor {
public:
using Type = ore::ResMetaData::DataType::Type;
using IntRange = ore::IterRange<const int*>;
using FloatRange = ore::IterRange<const float*>;
using StringRange = ore::IterRange<const ore::BinTPtr<ore::BinString>*>;
using WStringRange = ore::IterRange<const ore::BinTPtr<ore::BinWString>*>;
explicit ParamAccessor(const ore::ResMetaData* metadata);
explicit ParamAccessor(const FlowchartContext* context, int node_idx);
const ore::ResMetaData* GetFrontResMetaData() const;
int GetParamCount() const;
ore::StringView GetParamName(int idx) const;
Type GetParamType(int idx) const;
/// @param out_metadata Must be nonnull.
/// @param out_metadata_pack Must be nonnull.
/// @param out_variable_pack Must be nonnull.
/// @param out_obj May be null.
/// @param argument Name of the argument to search for.
ore::StringView TrackBackArgument(const ore::ResMetaData** out_metadata,
const MetaDataPack** out_metadata_pack,
const VariablePack** out_variable_pack,
FlowchartObj** out_obj,
const ore::StringView& argument) const;
int GetInt(int idx) const;
bool FindInt(int* value, const ore::StringView& name) const;
bool GetBool(int idx) const;
bool FindBool(bool* value, const ore::StringView& name) const;
float GetFloat(int idx) const;
bool FindFloat(float* value, const ore::StringView& name) const;
ore::StringView GetString(int idx) const;
bool FindString(ore::StringView* value, const ore::StringView& name) const;
ore::WStringView GetWString(int idx) const;
bool FindWString(ore::WStringView* value, const ore::StringView& name) const;
IntRange GetIntArray(int idx) const;
bool FindIntArray(IntRange* value, const ore::StringView& name) const;
FloatRange GetFloatArray(int idx) const;
bool FindFloatArray(FloatRange* value, const ore::StringView& name) const;
StringRange GetStringArray(int idx) const;
bool FindStringArray(StringRange* value, const ore::StringView& name) const;
WStringRange GetWStringArray(int idx) const;
bool FindWStringArray(WStringRange* value, const ore::StringView& name) const;
bool FindActorIdentifier(ore::StringView* actor_name, ore::StringView* actor_sub_name,
const ore::StringView& name) const;
private:
const ore::ResMetaData* m_metadata = nullptr;
const FlowchartContext* m_context = nullptr;
int m_node_idx = -1;
u32 m_node_counter = -1;
};
} // namespace evfl

View File

@ -0,0 +1,29 @@
#pragma once
#include <evfl/Param.h>
#include <ore/EnumUtil.h>
namespace evfl {
class FlowchartContext;
struct ResEvent;
class VariablePack;
ORE_ENUM(QueryValueType, kBool, kInt, kFloat, kString, kConst)
struct QueryArg {
QueryArg(FlowchartContext* context, int node_idx, void* actor_user_data_,
void* query_user_data_, const ResEvent* event_, VariablePack* variable_pack_)
: actor_user_data(actor_user_data_), query_user_data(query_user_data_), main_event(event_),
flowchart_ctx(context), variable_pack(variable_pack_), param_accessor(context, node_idx) {
}
void* actor_user_data;
void* query_user_data;
const ResEvent* main_event;
FlowchartContext* flowchart_ctx;
VariablePack* variable_pack;
ParamAccessor param_accessor;
};
} // namespace evfl

View File

@ -0,0 +1,187 @@
#pragma once
#include <cstddef>
#include <ore/Array.h>
#include <ore/BinaryFile.h>
#include <ore/IterRange.h>
#include <utility>
namespace ore {
struct ResEndian;
struct ResMetaData;
} // namespace ore
namespace evfl {
struct ActionArg;
class ActionDoneHandler;
class FlowchartContext;
class FlowchartContextNode;
class FlowchartObj;
struct QueryArg;
struct ResAction {
ore::BinTPtr<ore::BinString> name;
};
struct ResQuery {
ore::BinTPtr<ore::BinString> name;
};
struct ResActor {
bool HasArgumentName() const { return !argument_name.Get()->empty(); }
ore::BinTPtr<ore::BinString> name;
ore::BinTPtr<ore::BinString> secondary_name;
ore::BinTPtr<ore::BinString> argument_name;
ore::BinTPtr<ResAction> actions;
ore::BinTPtr<ResQuery> queries;
ore::BinTPtr<ore::ResMetaData> params;
u16 num_actions;
u16 num_queries;
/// Entry point index for assicated entry point (0xffff if none)
u16 entry_point_idx;
// TODO: Cut number? This is set to 1 for flowcharts but other values have been seen
// for timeline actors.
u8 cut_number;
};
using ActionHandler = void (*)(const ActionArg& arg, ActionDoneHandler done_handler);
using QueryHandler = int (*)(const QueryArg& arg);
class ActorBinding {
public:
struct Action {
ActionHandler handler{};
void* user_data{};
const ResAction* res_action{};
};
struct Query {
QueryHandler handler{};
void* user_data{};
const ResQuery* res_query{};
};
ActorBinding() = default;
void Register(const ResAction* action);
void Register(const ResQuery* query);
// Similar to std::find_if. Returns m_actions.end() if the specified action is not found.
Action* GetAction(const ore::StringView& name);
const Action* GetAction(const ore::StringView& name) const;
Query* GetQuery(const ore::StringView& name);
const Query* GetQuery(const ore::StringView& name) const;
ore::DynArrayList<Action>& GetActions() { return m_actions; }
ore::DynArrayList<Query>& GetQueries() { return m_queries; }
const ore::DynArrayList<Action>& GetActions() const { return m_actions; }
const ore::DynArrayList<Query>& GetQueries() const { return m_queries; }
auto ActionsEnd() const { return m_actions.end(); }
auto QueriesEnd() const { return m_queries.end(); }
auto GetActor() const { return m_actor; }
void* GetUserData() const { return m_user_data; }
void SetUserData(void* user_data) { m_user_data = user_data; }
bool IsInitialized() const { return m_initialized; }
bool IsUsed() const { return m_is_used; }
void SetInitialized(bool initialized) { m_initialized = initialized; }
void SetIsUsed(bool used) { m_is_used = used; }
void UnbindAll() {
m_user_data = nullptr;
m_initialized = false;
for (auto it = m_actions.begin(); it != m_actions.end(); ++it) {
it->handler = nullptr;
it->user_data = nullptr;
}
for (auto it = m_queries.begin(); it != m_queries.end(); ++it) {
it->handler = nullptr;
it->user_data = nullptr;
}
}
private:
friend class ActBinder;
ore::DynArrayList<Action> m_actions{};
ore::DynArrayList<Query> m_queries{};
void* m_user_data{};
const ResActor* m_actor{};
bool m_initialized{};
bool m_is_used{};
};
class ActBinder {
public:
class Builder {
public:
bool Build(evfl::ActBinder* binder, ore::Allocator* allocator,
ore::IterRange<const ResActor*> actors);
};
ActBinder() = default;
ActBinder(const ActBinder&) = delete;
auto operator=(const ActBinder&) = delete;
#ifdef MATCHING_HACK_NX_CLANG
[[gnu::always_inline]]
#endif
~ActBinder() {
Reset();
}
u32 GetEventUsedActorCount() const { return m_event_used_actor_count; }
const ore::Array<ActorBinding>* GetUsedResActors() const;
ore::Array<ActorBinding>& GetBindings() { return m_bindings; }
const ore::Array<ActorBinding>& GetBindings() const { return m_bindings; }
void IncrementNumActors() { ++m_event_used_actor_count; }
void SetIsUsed() { m_is_used = true; }
bool IsUsed() const { return m_is_used; }
void RegisterAction(int actor_idx, const evfl::ResAction* action) {
auto& binding = GetBindings()[actor_idx];
if (!binding.IsUsed() && binding.GetActor()->argument_name.Get()->empty()) {
IncrementNumActors();
binding.SetIsUsed(true);
}
binding.Register(action);
}
void RegisterQuery(int actor_idx, const evfl::ResQuery* query) {
auto& binding = GetBindings()[actor_idx];
if (!binding.IsUsed() && binding.GetActor()->argument_name.Get()->empty()) {
IncrementNumActors();
binding.SetIsUsed(true);
}
binding.Register(query);
}
void UnbindAll() {
for (auto it = m_bindings.begin(); it != m_bindings.end(); ++it)
it->UnbindAll();
}
void Reset() {
m_event_used_actor_count = 0;
if (auto* data = m_bindings.data()) {
m_bindings.ClearWithoutFreeing();
m_allocator->Free(data);
}
m_allocator = nullptr;
}
private:
u32 m_event_used_actor_count{};
ore::Allocator* m_allocator{};
ore::SelfDestructingArray<ActorBinding> m_bindings{};
bool m_is_used{};
};
void SwapEndian(ore::ResEndian* endian, ResActor* actor);
} // namespace evfl

View File

@ -0,0 +1,36 @@
#pragma once
#include <ore/BinaryFile.h>
#include <ore/Types.h>
namespace ore {
struct ResDic;
struct ResEndian;
} // namespace ore
namespace evfl {
struct ResFlowchart;
struct ResTimeline;
struct ResEventFlowFile {
/// data must be a pointer to a buffer of size >= 0x20.
static bool IsValid(void* data);
/// data must be a valid ResEventFlowFile.
static ResEventFlowFile* ResCast(void* data);
void Relocate();
void Unrelocate();
ore::BinaryFileHeader header;
u16 num_flowcharts;
u16 num_timelines;
ore::BinTPtr<ore::BinTPtr<ResFlowchart>> flowcharts;
ore::BinTPtr<ore::ResDic> flowchart_names;
ore::BinTPtr<ore::BinTPtr<ResTimeline>> timelines;
ore::BinTPtr<ore::ResDic> timeline_names;
};
void SwapEndian(ore::ResEndian* endian, ResEventFlowFile* file);
} // namespace evfl

View File

@ -0,0 +1,136 @@
#pragma once
#include <ore/BinaryFile.h>
#include <ore/EnumUtil.h>
#include <ore/ResDic.h>
#include <ore/ResMetaData.h>
#include <ore/StringView.h>
namespace ore {
struct ResEndian;
}
namespace evfl {
struct ResActor;
struct ResCase {
u32 value;
u16 event_idx;
};
struct ResEvent {
ORE_ENUM(EventType, kAction, kSwitch, kFork, kJoin, kSubFlow)
ore::BinTPtr<ore::BinString> name;
ore::SizedEnum<EventType::Type, u8> type;
union {
// Action, Join, Sub flow
u16 next_event_idx;
// Switch
u16 num_cases;
// Fork
u16 num_forks;
};
union {
// Action, Switch
u16 actor_idx;
// Fork
u16 join_event_idx;
};
union {
// Action
u16 actor_action_idx;
// Switch
u16 actor_query_idx;
};
union {
// Action, Switch, Sub flow
ore::BinTPtr<ore::ResMetaData> params;
// Fork
ore::BinTPtr<u16> fork_event_indices;
};
union {
// Switch
ore::BinTPtr<ResCase> cases;
// Sub flow
ore::BinTPtr<ore::BinString> sub_flow_flowchart;
};
union {
// Sub flow
ore::BinTPtr<ore::BinString> sub_flow_entry_point;
};
};
struct ResVariableDef {
union Value {
// Also used for booleans. Anything that is != 0 is treated as true.
int i;
float f;
ore::BinTPtr<int> int_array;
ore::BinTPtr<float> float_array;
};
Value value;
u16 num;
ore::SizedEnum<ore::ResMetaData::DataType::Type, u8> type;
};
struct ResEntryPoint {
ore::BinTPtr<u16> sub_flow_event_indices;
ore::BinTPtr<ore::ResDic> variable_defs_names;
ore::BinTPtr<ResVariableDef> variable_defs;
u16 num_sub_flow_event_indices;
u16 num_variable_defs;
u16 main_event_idx;
};
struct ResFlowchart {
int CountEvent(ResEvent::EventType::Type type) const;
const ResEntryPoint* GetEntryPoint(const ore::StringView& entry_point_name) const {
const int idx = entry_point_names.Get()->FindIndex(entry_point_name);
if (idx == -1)
return nullptr;
return entry_points.Get() + idx;
}
ore::StringView GetEntryPointName(int idx) const {
return entry_point_names.Get()->GetEntries()[1 + idx].GetKey();
}
/// 'EVFL'
u32 magic;
/// String pool offset (relative to this structure)
u32 string_pool_offset;
u32 reserved_8;
u32 reserved_c;
u16 num_actors;
u16 num_actions;
u16 num_queries;
u16 num_events;
u16 num_entry_points;
u16 reserved_1a;
u16 reserved_1c;
u16 reserved_1e;
ore::BinTPtr<ore::BinString> name;
ore::BinTPtr<ResActor> actors;
ore::BinTPtr<ResEvent> events;
ore::BinTPtr<ore::ResDic> entry_point_names;
ore::BinTPtr<ResEntryPoint> entry_points;
};
void SwapEndian(ore::ResEndian* endian, ResCase* case_);
void SwapEndian(ore::ResEndian* endian, ResEvent* event);
void SwapEndian(ore::ResEndian* endian, ResEntryPoint* entry);
void SwapEndian(ore::ResEndian* endian, ResVariableDef* def);
void SwapEndian(ore::ResEndian* endian, ResFlowchart* flowchart);
} // namespace evfl

View File

@ -0,0 +1,80 @@
#pragma once
#include <ore/BinaryFile.h>
#include <ore/EnumUtil.h>
#include <ore/Types.h>
namespace ore {
struct ResEndian;
struct ResMetaData;
} // namespace ore
namespace evfl {
struct ResActor;
struct ResTrigger {
u16 clip_index;
u8 trigger_type;
};
struct ResCut {
float start_time;
ore::BinTPtr<ore::BinString> name;
ore::BinTPtr<ore::ResMetaData> params;
};
struct ResClip {
float start_time;
float duration;
u16 actor_index;
u16 actor_action_index;
u8 _c;
ore::BinTPtr<ore::ResMetaData> params;
};
struct ResOneshot {
float time;
u16 actor_index;
u16 actor_action_index;
u32 _8;
u32 _c;
ore::BinTPtr<ore::ResMetaData> params;
};
struct ResSubtimeline {
ore::BinTPtr<ore::BinString> name;
};
struct ResTimeline {
/// 'TLIN'
u32 magic;
/// String pool offset (relative to this structure)
int string_pool_offset;
u32 reserved_8;
u32 reserved_c;
float duration;
u16 num_actors;
u16 num_actions;
u16 num_clips;
u16 num_oneshots;
u16 num_subtimelines;
u16 num_cuts;
ore::BinTPtr<ore::BinString> name;
ore::BinTPtr<ResActor> actors;
ore::BinTPtr<ResClip> clips;
ore::BinTPtr<ResOneshot> oneshots;
ore::BinTPtr<ResTrigger> triggers;
ore::BinTPtr<ResSubtimeline> subtimelines;
ore::BinTPtr<ResCut> cuts;
ore::BinTPtr<ore::ResMetaData> params;
};
void SwapEndian(ore::ResEndian* endian, ResTrigger* trigger);
void SwapEndian(ore::ResEndian* endian, ResCut* cut);
void SwapEndian(ore::ResEndian* endian, ResClip* clip);
void SwapEndian(ore::ResEndian* endian, ResOneshot* oneshot);
void SwapEndian(ore::ResEndian* endian, ResSubtimeline* subtimeline);
void SwapEndian(ore::ResEndian* endian, ResTimeline* timeline);
} // namespace evfl

View File

@ -0,0 +1,78 @@
#pragma once
#include <evfl/EvflAllocator.h>
#include <evfl/ResActor.h>
#include <ore/Array.h>
#include <ore/EnumUtil.h>
#include <ore/Types.h>
namespace evfl {
struct ResTimeline;
ORE_ENUM(TimelineState, kNotStarted, kPlaying, kStop, kPause)
class TimelineObj {
public:
class Builder {
public:
explicit Builder(const ResTimeline* timeline) : m_timeline(timeline) {}
bool Build(TimelineObj* obj, AllocateArg allocate_arg);
private:
const ResTimeline* m_timeline = nullptr;
ActBinder::Builder m_act_binder_builder;
};
TimelineObj();
void Calc();
void Reset();
void SetState(TimelineState::Type state);
void Start(float start_time);
void JumpTimeTo(float time);
void AdvanceTimeTo(float time);
bool RegisterSubtimeline(TimelineObj* obj);
ActBinder& GetActBinder() { return m_act_binder; }
const ActBinder& GetActBinder() const { return m_act_binder; }
const ore::Array<TimelineObj*>& GetSubTimelines() const { return m_sub_timelines; }
const ResTimeline* GetTimeline() const { return m_timeline; }
float GetTime() const { return m_time; }
float GetNewTime() const { return m_new_time; }
int GetLastTriggerIdx() const { return m_last_trigger_idx; }
int GetLastOneshotIdx() const { return m_last_oneshot_idx; }
int GetPlayCounter() const { return m_play_counter; }
bool IsStarted() const { return m_started; }
bool IsJumpedTime() const { return m_jumped_time; }
TimelineState::Type GetState() const { return m_state; }
private:
void CalcImpl();
void JumpTimeToImpl(float time);
void AdvanceTimeToImpl(float time);
static int s_GlobalPlayCounter;
void Finalize() {
m_timeline = nullptr;
m_act_binder.Reset();
m_sub_timelines.Clear(&m_allocator);
}
EvflAllocator m_allocator;
ore::Array<TimelineObj*> m_sub_timelines;
const ResTimeline* m_timeline{};
ActBinder m_act_binder{};
float m_time{};
float m_new_time{};
int m_last_trigger_idx = -1;
int m_last_oneshot_idx = -1;
int m_play_counter = 0;
bool m_started = false;
bool m_jumped_time = false;
ore::SizedEnum<TimelineState::Type, u8> m_state = TimelineState::kNotStarted;
};
} // namespace evfl

View File

@ -0,0 +1,45 @@
#pragma once
#include <cstddef>
#include <memory>
#include <ore/Types.h>
namespace ore {
class Allocator {
public:
Allocator() = default;
virtual ~Allocator() = default;
void* New(size_t size, size_t alignment = alignof(std::max_align_t)) {
return AllocImpl(size, alignment);
}
template <typename T>
T* New(size_t alignment = alignof(std::max_align_t)) {
auto* buffer = AllocImpl(sizeof(T), alignment);
if (buffer)
return new (buffer) T;
return static_cast<T*>(buffer);
}
template <typename T>
void Delete(T* ptr) {
std::destroy_at(ptr);
Free(ptr);
}
template <typename T>
void DeleteAndNull(T*& ptr) {
std::destroy_at(ptr);
Free(ptr);
ptr = nullptr;
}
void Free(void* ptr) { FreeImpl(ptr); }
virtual void* AllocImpl(size_t size, size_t alignment) = 0;
virtual void FreeImpl(void* ptr) = 0;
};
} // namespace ore

View File

@ -0,0 +1,287 @@
#pragma once
#include <algorithm>
#include <iterator>
#include <memory>
#include <ore/Allocator.h>
#include <ore/Buffer.h>
#include <ore/IterRange.h>
#include <type_traits>
namespace ore {
// This is like a std::span, not a fixed-size array like std::array.
// Elements will NOT be automatically freed.
template <typename T>
class Array {
public:
Array() = default;
Array(T* data, int size) : m_data(data), m_size(size) {}
T* data() const { return m_data; }
int size() const { return m_size; }
auto begin() const { return data(); }
auto end() const { return data() + size(); }
T& operator[](int idx) { return m_data[idx]; }
const T& operator[](int idx) const { return m_data[idx]; }
T& front() { return m_data[0]; }
const T& front() const { return m_data[0]; }
T& back() { return m_data[m_size - 1]; }
const T& back() const { return m_data[m_size - 1]; }
void SetBuffer(void* new_buffer, int num) {
DestructElements();
m_data = static_cast<T*>(new_buffer);
m_size = num;
}
void SetBuffer(int num, Allocator* allocator) {
auto* new_buffer = allocator->AllocImpl(num * int(sizeof(T)), alignof(std::max_align_t));
SetBuffer(new_buffer, num);
}
void ConstructElements(int num, Allocator* allocator) {
SetBuffer(num, allocator);
DefaultConstructElements();
}
void ConstructElements(void* new_buffer, int num) {
SetBuffer(new_buffer, num);
DefaultConstructElements();
}
void ConstructElements(Buffer buffer) {
DestructElements();
m_data = reinterpret_cast<T*>(buffer.data);
m_size = buffer.size / int(sizeof(T));
DefaultConstructElements();
}
void DestructElements() { std::destroy(begin(), end()); }
void ClearWithoutFreeing() {
DestructElements();
m_data = nullptr;
m_size = 0;
}
void Clear(Allocator* allocator) {
if (!m_data)
return;
auto* data = m_data;
ClearWithoutFreeing();
allocator->Free(data);
}
void DefaultConstructElements() {
for (auto it = begin(), e = end(); it != e;)
new (it++) T;
}
void UninitializedDefaultConstructElements() {
std::uninitialized_default_construct(begin(), end());
}
private:
T* m_data{};
int m_size{};
};
template <typename T>
class SelfDestructingArray : public Array<T> {
public:
~SelfDestructingArray() { this->DestructElements(); }
};
template <typename T>
class ArrayListBase {
public:
ArrayListBase() : m_data(), m_size(), m_capacity() {}
ArrayListBase(T* data, int capacity) {
m_size = 0;
m_data = data;
m_capacity = capacity;
}
~ArrayListBase() { clear(); }
ArrayListBase(const ArrayListBase&) = delete;
auto operator=(const ArrayListBase&) = delete;
T* begin() { return m_data; }
const T* begin() const { return m_data; }
T* end() { return m_data + m_size; }
const T* end() const { return m_data + m_size; }
T* data() { return m_data; }
const T* data() const { return m_data; }
int size() const { return m_size; }
int capacity() const { return m_capacity; }
T& operator[](int idx) { return m_data[idx]; }
const T& operator[](int idx) const { return m_data[idx]; }
T& front() { return m_data[0]; }
const T& front() const { return m_data[0]; }
T& back() { return m_data[m_size - 1]; }
const T& back() const { return m_data[m_size - 1]; }
template <typename... Args>
T& emplace_back(Args&&... args) {
auto* item = new (&m_data[m_size++]) T(std::forward<Args>(args)...);
return *item;
}
void push_back(const T& item) { new (&m_data[m_size++]) T(item); }
void pop_back() {
std::destroy_at(&back());
--m_size;
}
void clear() {
std::destroy(begin(), end());
m_size = 0;
}
T* m_data;
int m_size;
int m_capacity;
};
template <typename T, int N>
class FixedArrayList : public ArrayListBase<T> {
public:
FixedArrayList() : ArrayListBase<T>(reinterpret_cast<T*>(m_storage), N) {}
private:
std::aligned_storage_t<sizeof(T), alignof(T)> m_storage[N];
};
// This is like a std::vector.
template <typename T>
class DynArrayList : public ArrayListBase<T> {
public:
DynArrayList() = default;
explicit DynArrayList(Allocator* allocator) : m_allocator(allocator) {}
~DynArrayList() {
clear();
m_allocator = nullptr;
this->m_size = 0;
}
void Reset() {
clear();
m_allocator = nullptr;
}
DynArrayList(const DynArrayList&) = delete;
auto operator=(const DynArrayList&) = delete;
void Init(Allocator* allocator, int initial_capacity = 1) {
clear();
m_allocator = allocator;
Reallocate(initial_capacity);
}
template <typename... Args>
T& emplace_back(Args&&... args) {
GrowIfNeeded();
return ArrayListBase<T>::emplace_back(std::forward<Args>(args)...);
}
void push_back(const T& item) {
GrowIfNeeded();
return ArrayListBase<T>::push_back(item);
}
void clear() {
std::destroy(this->begin(), this->end());
auto* data = this->m_data;
this->m_data = nullptr;
this->m_size = 0;
this->m_capacity = 0;
Free(data);
}
template <typename InputIterator>
void OverwriteWith(InputIterator src_begin, InputIterator src_end) {
const int src_size = std::distance(src_begin, src_end);
if (src_size > this->m_capacity) {
this->m_size = 0;
Reallocate(2 * src_size);
}
this->m_size = src_size;
std::uninitialized_copy(src_begin, src_end, this->begin());
}
/// Quadratic complexity; only use this for small copies.
template <typename Range>
void DeduplicateCopy(const Range& range) {
for (auto it = range.begin(), end = range.end(); it != end; ++it) {
auto value = *it;
if (std::find_if(range.begin(), it, [&](const auto& v) { return value == v; }) == it)
this->emplace_back(value);
}
}
/// Resize the array so that it contains `new_size` elements.
///
/// - If the new size is greater than the current size, new elements are added and
/// default initialized. Iterators may be invalidated.
/// - If the new size is less than the current size, excess elements are destroyed.
///
/// @param new_size The new size of the array.
void Resize(int new_size) {
if (this->m_capacity < new_size)
Reallocate(new_size);
if (this->m_size < new_size) {
std::uninitialized_default_construct(this->m_data + this->m_size,
this->m_data + new_size);
} else {
std::destroy(this->m_data + new_size, this->m_data + this->m_size);
}
this->m_size = new_size;
}
private:
void GrowIfNeeded() {
if (this->m_size < this->m_capacity)
return;
Reallocate(2 * this->m_size + 2);
}
void Reallocate(int new_capacity) {
const int num_bytes = sizeof(T) * new_capacity;
auto* new_buffer =
static_cast<T*>(m_allocator->AllocImpl(num_bytes, alignof(std::max_align_t)));
auto* old_buffer = this->m_data;
auto* capacity = &this->m_capacity;
UninitializedCopyTo(new_buffer);
this->m_data = new_buffer;
*capacity = new_capacity;
Free(old_buffer);
}
void UninitializedCopyTo(T* destination) const {
std::uninitialized_copy(this->begin(), this->end(), destination);
}
void Free(void* ptr) {
if (ptr)
m_allocator->Free(ptr);
}
Allocator* m_allocator{};
};
} // namespace ore

View File

@ -0,0 +1,153 @@
#pragma once
#include <ore/StringView.h>
#include <ore/Types.h>
#include <type_traits>
#include <utility>
namespace ore {
template <typename T>
constexpr T AlignUpToPowerOf2(T val, int base) {
return val + base - 1 & static_cast<unsigned int>(-base);
}
struct RelocationTable;
struct BinaryBlockHeader {
BinaryBlockHeader* FindNextBlock(int type);
const BinaryBlockHeader* FindNextBlock(int type) const;
BinaryBlockHeader* GetNextBlock();
const BinaryBlockHeader* GetNextBlock() const;
void SetNextBlock(BinaryBlockHeader* block);
u32 magic;
int next_block_offset;
};
struct BinaryFileHeader {
bool IsValid(s64 magic_, int ver_major_, int ver_minor_, int ver_patch_, int ver_sub_) const;
bool IsSignatureValid(s64 magic_) const;
bool IsVersionValid(int major, int minor, int patch, int sub) const;
bool IsEndianReverse() const;
bool IsEndianValid() const;
bool IsAlignmentValid() const;
int GetAlignment() const;
void SetAlignment(int alignment_);
bool IsRelocated() const;
void SetRelocated(bool relocated);
void SetByteOrderMark();
int GetFileSize() const;
void SetFileSize(int size);
StringView GetFileName() const;
void SetFileName(const StringView& name);
RelocationTable* GetRelocationTable();
void SetRelocationTable(RelocationTable* table);
BinaryBlockHeader* GetFirstBlock();
const BinaryBlockHeader* GetFirstBlock() const;
void SetFirstBlock(BinaryBlockHeader* block);
BinaryBlockHeader* FindFirstBlock(int type);
const BinaryBlockHeader* FindFirstBlock(int type) const;
u64 magic;
u8 ver_major;
u8 ver_minor;
u8 ver_patch;
u8 ver_sub;
s16 bom;
u8 alignment;
u8 _f;
int file_name_offset;
u16 relocation_flags;
u16 first_block_offset;
int relocation_table_offset;
int file_size;
};
template <typename T>
struct BinTString {
// Make it impossible to accidentally construct a (partial, broken) copy.
BinTString(const BinTString&) = delete;
auto operator=(const BinTString&) = delete;
T* data() { return chars; }
const T* data() const { return chars; }
T& operator[](size_t idx) { return data()[idx]; }
const T& operator[](size_t idx) const { return data()[idx]; }
auto begin() { return data(); }
auto begin() const { return data(); }
auto end() { return data() + length; }
auto end() const { return data() + length; }
bool empty() const { return length == 0; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator TStringView<T>() const { return {data(), length}; }
BinTString* NextString() { return const_cast<BinTString*>(std::as_const(*this).NextString()); }
const BinTString* NextString() const {
// XXX: this shouldn't have to be a separate case.
if constexpr (std::is_same_v<T, wchar_t>) {
const auto offset = ((2 + (4 * (length + 1) - 1)) & -4) + 2;
return reinterpret_cast<const BinTString*>(reinterpret_cast<const char*>(this) +
offset);
} else {
// + 1 for the null terminator
const auto offset = offsetof(BinTString, chars) + sizeof(T) * (length + 1);
return reinterpret_cast<const BinTString*>(
reinterpret_cast<const char*>(this) +
AlignUpToPowerOf2(offset, alignof(BinTString)));
}
}
u16 length;
T chars[1];
};
using BinString = BinTString<char>;
using BinWString = BinTString<wchar_t>;
template <typename T>
struct BinTPtr {
void Clear() { offset_or_ptr = 0; }
void Set(T* ptr) { offset_or_ptr = reinterpret_cast<u64>(ptr); }
// Only use this after relocation.
T* Get() { return reinterpret_cast<T*>(offset_or_ptr); }
const T* Get() const { return reinterpret_cast<const T*>(offset_or_ptr); }
void SetOffset(void* base, void* ptr) {
offset_or_ptr = static_cast<int>(ptr ? uintptr_t(ptr) - uintptr_t(base) : 0);
}
u64 GetOffset() const { return offset_or_ptr; }
T* ToPtr(void* base) const {
const auto offset = static_cast<int>(offset_or_ptr);
if (offset == 0)
return nullptr;
return reinterpret_cast<T*>(reinterpret_cast<char*>(base) + offset);
}
void Relocate(void* base) { Set(ToPtr(base)); }
void Unrelocate(void* base) { SetOffset(base, Get()); }
u64 offset_or_ptr;
};
static_assert(sizeof(u64) >= sizeof(void*));
} // namespace ore

View File

@ -0,0 +1,149 @@
#pragma once
#include <ore/Allocator.h>
#include <ore/Types.h>
namespace ore {
constexpr int PopCount(u32 x) {
x = x - ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x + (x >> 4)) & 0x0F0F0F0F;
x += (x >> 8);
x += (x >> 16);
return int(x & 0x3f);
}
constexpr int PopCount(u64 x) {
x = x - ((x >> 1) & 0x5555555555555555);
x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333);
x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0F;
x += (x >> 8);
x += (x >> 16);
x += (x >> 32);
return int(x & 0x7f);
}
constexpr int CountTrailingZeros(u32 x) {
return PopCount((x & -x) - 1);
}
constexpr int CountTrailingZeros(u64 x) {
return PopCount((x & -x) - 1);
}
namespace detail {
template <typename T>
constexpr T AlignUpToPowerOf2(T val, int base) {
return val + base - 1 & static_cast<unsigned int>(-base);
}
} // namespace detail
class BitArray {
public:
using Word = size_t;
static constexpr int NumBitsPerWord = sizeof(Word) * 8;
static constexpr int ClearMask = ~(NumBitsPerWord - 1);
static constexpr int ShiftAmount = CountTrailingZeros(u32(NumBitsPerWord));
class TestIter {
public:
TestIter(const Word* start, const Word* end);
TestIter& operator++();
int operator*() const { return m_bit; }
bool operator==(const TestIter& other) const { return m_bit == other.m_bit; }
bool operator!=(const TestIter& other) const { return !operator==(other); }
private:
void SetInvalid() {
m_bit = -1;
m_current_word = nullptr;
m_last_word = nullptr;
m_next = 0;
}
int m_bit;
const Word* m_current_word;
const Word* m_last_word;
Word m_next;
};
/// Same as TestIter but clears bits after iterating over them.
class TestClearIter {
public:
TestClearIter(Word* start, Word* end);
TestClearIter& operator++();
int operator*() const { return m_bit; }
bool operator==(const TestClearIter& other) const { return m_bit == other.m_bit; }
bool operator!=(const TestClearIter& other) const { return !operator==(other); }
private:
void SetInvalid() {
m_bit = -1;
m_current_word = nullptr;
m_last_word = nullptr;
m_next = 0;
}
int m_bit;
Word* m_current_word;
Word* m_last_word;
Word m_next;
};
constexpr BitArray() = default;
constexpr BitArray(void* buffer, int num_bits) { SetData(buffer, num_bits); }
constexpr BitArray(ore::Allocator* allocator, int num_bits) {
AllocateBuffer(allocator, num_bits);
}
void SetData(void* buffer, int num_bits) {
m_words = reinterpret_cast<Word*>(buffer);
m_num_bits = num_bits;
}
void AllocateBuffer(ore::Allocator* allocator, int num_bits) {
SetData(allocator->New(GetRequiredBufferSize(num_bits)), num_bits);
SetAllOff();
}
void FreeBufferIfNeeded(ore::Allocator* allocator) {
if (m_words)
allocator->Delete(m_words);
}
void FreeBuffer(ore::Allocator* allocator) { allocator->Delete(m_words); }
bool Test(int bit) const {
return (GetWord(bit) & (Word(1) << (Word(bit) % NumBitsPerWord))) != 0;
}
void Set(int bit) { GetWord(bit) |= Word(1) << (Word(bit) % NumBitsPerWord); }
void Clear(int bit) { GetWord(bit) &= ~(Word(1) << (Word(bit) % NumBitsPerWord)); }
void SetAllOn();
void SetAllOff();
TestIter BeginTest() const;
TestIter EndTest() const;
TestClearIter BeginTestClear();
TestClearIter EndTestClear();
static int GetRequiredBufferSize(int num_bits) {
return sizeof(Word) * (detail::AlignUpToPowerOf2(num_bits, NumBitsPerWord) >> ShiftAmount);
}
private:
Word& GetWord(int bit) const { return m_words[bit >> ShiftAmount]; }
int GetNumWords() const { return int((m_num_bits + NumBitsPerWord - 1) >> ShiftAmount); }
void Fill(int num, Word value) {
auto* it = m_words;
for (int i = num - 1; i >= 0; --i)
*it++ = value;
}
Word* m_words{};
int m_num_bits{};
};
} // namespace ore

View File

@ -0,0 +1,25 @@
#pragma once
#include <ore/Allocator.h>
#include <ore/Types.h>
namespace ore {
struct Buffer {
template <typename T>
void Allocate(Allocator* allocator, int num) {
size = sizeof(T) * num;
data = static_cast<char*>(allocator->New(size));
}
void Free(Allocator* allocator) {
allocator->Free(data);
data = nullptr;
size = 0;
}
char* data;
int size;
};
} // namespace ore

View File

@ -0,0 +1,115 @@
#pragma once
#include <iterator>
#include <ore/IterRange.h>
#include <ore/StringView.h>
#include <type_traits>
namespace ore {
namespace detail::EnumUtil {
int FindIndex(int value, const IterRange<const int*>& values);
void Parse(const IterRange<StringView*>& out, StringView definition);
constexpr int CountValues(const char* text_all, size_t text_all_len) {
int count = 1;
for (size_t i = 0; i < text_all_len; ++i) {
if (text_all[i] == ',')
++count;
}
return count;
}
} // namespace detail::EnumUtil
template <class T>
struct Enum {
public:
Enum() { T::Init(); }
static Enum<T>& Info() { return s_Info; }
StringView name{};
IterRange<StringView*> members{};
private:
static inline Enum<T> s_Info{};
};
#define ORE_ENUM(NAME, ...) \
class NAME { \
public: \
enum Type { __VA_ARGS__ }; \
\
static void Init() { \
static ore::StringView names[cCount]; \
ore::detail::EnumUtil::Parse(ore::IterRange<ore::StringView*>(names), cTextAll); \
ore::Enum<NAME>::Info().name = #NAME; \
ore::Enum<NAME>::Info().members = ore::IterRange<ore::StringView*>(names); \
} \
\
static constexpr int Size() { return cCount; } \
static constexpr Type Invalid() { return Type(Size()); } \
\
private: \
static constexpr const char* cTextAll = #__VA_ARGS__; \
static constexpr size_t cTextAllLen = sizeof(#__VA_ARGS__); \
static constexpr int cCount = ore::detail::EnumUtil::CountValues(cTextAll, cTextAllLen); \
};
// FIXME
template <class T>
class ValuedEnum {
public:
ValuedEnum() { T::Init(); }
static Enum<T>& Info() { return s_Info; }
StringView name{};
IterRange<StringView*> members{};
private:
static inline Enum<T> s_Info{};
};
#define ORE_VALUED_ENUM(NAME, ...) \
class NAME { \
public: \
enum Type { __VA_ARGS__ }; \
\
static void Init() { \
static ore::StringView names[cCount]; \
ore::detail::EnumUtil::Parse(ore::IterRange<ore::StringView*>(names), cTextAll); \
ore::ValuedEnum<NAME>::Info().name = #NAME; \
ore::ValuedEnum<NAME>::Info().members = ore::IterRange<ore::StringView*>(names); \
} \
\
static constexpr int Size() { return cCount; } \
static constexpr Type Invalid() { return Type(Size()); } \
\
private: \
static constexpr const char* cTextAll = #__VA_ARGS__; \
static constexpr size_t cTextAllLen = sizeof(#__VA_ARGS__); \
static constexpr int cCount = ore::detail::EnumUtil::CountValues(cTextAll, cTextAllLen); \
};
/// For storing an enum with a particular storage size when specifying the underlying type of the
/// enum is not an option.
template <typename Enum, typename Storage>
struct SizedEnum {
static_assert(std::is_enum<Enum>());
static_assert(!std::is_enum<Storage>());
constexpr SizedEnum() = default;
constexpr SizedEnum(Enum value) { *this = value; }
constexpr operator Enum() const { return static_cast<Enum>(mValue); }
constexpr SizedEnum& operator=(Enum value) {
mValue = static_cast<Storage>(value);
return *this;
}
Storage mValue;
};
} // namespace ore

View File

@ -0,0 +1,100 @@
#pragma once
#include <utility>
namespace ore {
class IntrusiveListNode {
public:
constexpr explicit IntrusiveListNode() { m_prev = m_next = this; }
IntrusiveListNode(const IntrusiveListNode&) = delete;
auto operator=(const IntrusiveListNode&) = delete;
IntrusiveListNode(IntrusiveListNode&& other) noexcept { *this = std::move(other); }
IntrusiveListNode& operator=(IntrusiveListNode&& other) noexcept {
auto* prev = other.m_prev;
other.m_prev = this;
prev->m_next = this;
m_prev = prev;
m_next = &other;
other.Erase();
return *this;
}
IntrusiveListNode* Prev() const { return m_prev; }
IntrusiveListNode* Next() const { return m_next; }
bool IsLinked() const { return Prev() || Next(); }
void Erase() {
auto* next = m_next;
auto* next_prev = next->m_prev;
m_prev->m_next = next;
next->m_prev = m_prev;
// This is a circular list.
next_prev->m_next = this;
m_prev = next_prev;
}
void InsertFront(IntrusiveListNode* node) {
auto* prev = node->m_prev;
node->m_prev = m_prev;
prev->m_next = this;
m_prev->m_next = node;
m_prev = prev;
}
private:
template <typename T>
friend class IntrusiveList;
IntrusiveListNode* m_prev{};
IntrusiveListNode* m_next{};
};
template <typename T>
class IntrusiveList {
public:
void SetOffset(int offset) { m_offset = offset; }
bool Empty() const { return m_node.m_next == &m_node; }
T* Front() { return NodeToItemWithNullCheck(m_node.m_next); }
T* Back() { return NodeToItemWithNullCheck(m_node.m_prev); }
const T* Front() const { return NodeToItemWithNullCheck(m_node.m_next); }
const T* Back() const { return NodeToItemWithNullCheck(m_node.m_prev); }
void Erase(T* item) { ItemToNode(item)->Erase(); }
void InsertFront(T* item) { m_node.InsertFront(ItemToNode(item)); }
private:
IntrusiveListNode* ItemToNode(T* item) const {
return reinterpret_cast<IntrusiveListNode*>(reinterpret_cast<char*>(item) + m_offset);
}
const IntrusiveListNode* ItemToNode(const T* item) const {
return reinterpret_cast<const IntrusiveListNode*>(reinterpret_cast<const char*>(item) +
m_offset);
}
T* NodeToItem(IntrusiveListNode* node) const {
return reinterpret_cast<T*>(reinterpret_cast<char*>(node) - m_offset);
}
const T* NodeToItem(const IntrusiveListNode* node) const {
return reinterpret_cast<const T*>(reinterpret_cast<const char*>(node) - m_offset);
}
T* NodeToItemWithNullCheck(IntrusiveListNode* node) const {
return node == &m_node ? nullptr : NodeToItem(node);
}
const T* NodeToItemWithNullCheck(const IntrusiveListNode* node) const {
return node == &m_node ? nullptr : NodeToItem(node);
}
IntrusiveListNode m_node;
int m_offset = -1;
};
} // namespace ore

View File

@ -0,0 +1,28 @@
#pragma once
#include <iterator>
namespace ore {
template <typename T>
class IterRange {
public:
constexpr IterRange() = default;
constexpr IterRange(const T& begin_, const T& end_) : m_begin(begin_), m_end(end_) {}
template <typename Other>
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr IterRange(Other& x) : IterRange(std::begin(x), std::end(x)) {}
constexpr IterRange(const T& begin, int size) : m_begin(begin), m_end(begin + size) {}
const auto& begin() const { return m_begin; }
const auto& end() const { return m_end; }
int size() const { return end() - begin(); }
private:
T m_begin{};
T m_end{};
};
} // namespace ore

View File

@ -0,0 +1,50 @@
#pragma once
#include <ore/Types.h>
namespace ore {
struct RelocationTable {
struct Section {
struct Entry {
/// Offset to pointers to relocate
int pointers_offset;
/// Bit field that determines which pointers need to be relocated
/// (next to 32 contiguous pointers starting from the listed offset)
u32 mask;
};
void SetPtr(void* ptr_);
void* GetPtr() const;
void* GetPtrInFile(void* base) const;
void* GetBasePtr(void* base) const;
u32 GetSize() const;
u64 ptr;
int offset;
int size;
int first_entry_idx;
int num_entries;
};
u32 magic;
int table_start_offset;
int num_sections;
Section sections[1];
Section* GetSections() { return sections; }
const Section* GetSections() const { return sections; }
Section::Entry* GetEntries() {
return reinterpret_cast<Section::Entry*>(GetSections() + num_sections);
}
const Section::Entry* GetEntries() const {
return reinterpret_cast<const Section::Entry*>(GetSections() + num_sections);
}
void Relocate();
void Unrelocate();
static int CalcSize(int num_sections, int num_entries);
};
} // namespace ore

View File

@ -0,0 +1,62 @@
#pragma once
#include <ore/BinaryFile.h>
#include <ore/StringView.h>
#include <ore/Types.h>
namespace ore {
struct ResEndian;
struct ResDicEntry {
StringView GetKey() const { return *name.Get(); }
// Bits 3-7: index of the byte that should be checked
// Bits 0-2: index of the bit in that byte
int compact_bit_idx;
u16 next_indices[2];
BinTPtr<BinString> name;
};
struct ResDic {
static int FindRefBit(const StringView& str1, const StringView& str2);
const ResDicEntry* FindEntry(const StringView& key) const {
auto* prev = &entries[0];
auto* entry = &entries[prev->next_indices[0]];
while (prev->compact_bit_idx < entry->compact_bit_idx) {
const int bit_idx = entry->compact_bit_idx;
long bit = 0;
if (u32(key.length()) > u32(bit_idx >> 3))
bit = ((key[key.length() + -((bit_idx >> 3) + 1)] >> (bit_idx & 7))) & 1;
prev = entry;
entry = &entries[prev->next_indices[bit]];
}
return entry;
}
/// Returns the index for the specified key or -1 if it cannot be found.
int FindIndex(const StringView& key) const {
const auto* entry = FindEntry(key);
const auto entry_name = entry->GetKey();
bool ok = [&] { return StringView(key.data(), key.length()) == entry_name; }();
if (!ok)
return -1;
return static_cast<int>(entry - &GetEntries()[1]);
}
/// Entry 0 is the root entry.
ResDicEntry* GetEntries() { return entries; }
/// Entry 0 is the root entry.
const ResDicEntry* GetEntries() const { return entries; }
u32 magic;
int num_entries;
ResDicEntry entries[1];
// Followed by ResDicEntry[num_entries].
};
void SwapEndian(ResEndian* endian, ResDic* dic);
} // namespace ore

View File

@ -0,0 +1,75 @@
#pragma once
#ifdef _MSC_VER
#include <stdlib.h>
#endif
#include <cstring>
#include <ore/Types.h>
namespace ore {
[[nodiscard]] inline u8 SwapEndian(u8 x) {
return x;
}
[[nodiscard]] inline u16 SwapEndian(u16 x) {
#ifdef _MSC_VER
return _byteswap_ushort(x);
#else
return __builtin_bswap16(x);
#endif
}
[[nodiscard]] inline u32 SwapEndian(u32 x) {
#ifdef _MSC_VER
return _byteswap_ulong(x);
#else
return __builtin_bswap32(x);
#endif
}
[[nodiscard]] inline u64 SwapEndian(u64 x) {
#ifdef _MSC_VER
return _byteswap_uint64(x);
#else
return __builtin_bswap64(x);
#endif
}
[[nodiscard]] inline s8 SwapEndian(s8 x) {
return SwapEndian(u8(x));
}
[[nodiscard]] inline s16 SwapEndian(s16 x) {
return SwapEndian(u16(x));
}
[[nodiscard]] inline s32 SwapEndian(s32 x) {
return SwapEndian(u32(x));
}
[[nodiscard]] inline s64 SwapEndian(s64 x) {
return SwapEndian(u64(x));
}
[[nodiscard]] inline f32 SwapEndian(f32 x) {
static_assert(sizeof(u32) == sizeof(f32));
u32 i;
std::memcpy(&i, &x, sizeof(i));
i = SwapEndian(i);
std::memcpy(&x, &i, sizeof(i));
return x;
}
template <typename T>
inline void SwapEndian(T* value) {
*value = SwapEndian(*value);
}
struct ResEndian {
char* base;
bool is_serializing;
};
} // namespace ore

View File

@ -0,0 +1,58 @@
#pragma once
#include <ore/BinaryFile.h>
#include <ore/EnumUtil.h>
#include <ore/ResDic.h>
#include <ore/StringView.h>
#include <ore/Types.h>
namespace ore {
struct ResDic;
struct ResEndian;
struct ResMetaData {
struct ActorIdentifier {
BinTPtr<BinString> name;
BinTPtr<BinString> sub_name;
};
union Value {
BinTPtr<ResMetaData> container;
// Also used for booleans. Anything that is != 0 is treated as true.
int i;
float f;
BinTPtr<BinString> str;
BinTPtr<BinWString> wstr;
ActorIdentifier actor;
};
ORE_ENUM(DataType, kArgument, kContainer, kInt, kBool, kFloat, kString, kWString, kIntArray, kBoolArray, kFloatArray, kStringArray, kWStringArray, kActorIdentifier)
/// @warning Only usable if type == kContainer.
const ResMetaData* Get(const StringView& key, DataType::Type expected_type) const {
const int idx = dictionary.Get()->FindIndex(key);
if (idx == -1)
return nullptr;
const auto* meta = (&value.container + idx)->Get();
if (meta->type != expected_type)
return nullptr;
return meta;
}
SizedEnum<DataType::Type, u8> type;
u16 num_items;
BinTPtr<ResDic> dictionary;
Value value;
};
// XXX: is this unused?
struct ResUserData {
ORE_ENUM(DataType, kInt, kFloat, kString, kWString, kStream)
};
void SwapEndian(ResEndian* endian, ResMetaData* res);
} // namespace ore

View File

@ -0,0 +1,20 @@
#pragma once
#include <ore/BinaryFile.h>
#include <ore/Types.h>
namespace ore {
struct StringPool : BinaryBlockHeader {
int GetLength() const;
void SetLength(int len);
BinString* GetFirstString() { return dummy_string.NextString(); }
u32 reserved_8;
u32 reserved_c;
int length;
BinString dummy_string;
};
} // namespace ore

View File

@ -0,0 +1,79 @@
#pragma once
#include <algorithm>
#include <ore/Types.h>
#include <string>
namespace ore {
template <typename T>
constexpr size_t StringLength(const T* str) {
if (str == nullptr || str[0] == 0)
return 0;
size_t len = 0;
while (*str++ != 0)
++len;
#ifdef MATCHING_HACK_NX_CLANG
__builtin_assume(len <= 0xffffffff);
#endif
return len;
}
template <typename T>
class TStringView {
public:
// Annoyingly enough, this cannot be defaulted (otherwise Clang will not dynamically
// initialize static StringView variables).
TStringView() {}
constexpr TStringView(const T* data, size_t len) : m_data(data), m_len(len) {}
/// @param data A null-terminated string. Must not be nullptr.
// NOLINTNEXTLINE(google-explicit-constructor)
TStringView(const T* data) : m_data(data), m_len(StringLength(data)) {}
constexpr const T* data() const { return m_data; }
constexpr int size() const { return m_len; }
constexpr int length() const { return m_len; }
constexpr bool empty() const { return size() == 0; }
constexpr auto begin() const { return m_data; }
constexpr auto cbegin() const { return m_data; }
constexpr auto end() const { return m_data + m_len; }
constexpr auto cend() const { return m_data + m_len; }
const T& operator[](size_t idx) const { return m_data[idx]; }
static int Compare(TStringView lhs, TStringView rhs) {
const T* s1 = lhs.data();
const T* s2 = rhs.data();
int len = std::min(lhs.size(), rhs.size());
if (len < 1)
return lhs.size() - rhs.size();
while (len-- > 0) {
if (*s1 == 0 || *s1 != *s2)
return *s1 - *s2;
++s1, ++s2;
}
return lhs.size() - rhs.size();
}
int Compare(TStringView rhs) const { return Compare(*this, rhs); }
friend bool operator==(TStringView lhs, TStringView rhs) {
return lhs.size() == rhs.size() && Compare(lhs, rhs) == 0;
}
friend bool operator!=(TStringView lhs, TStringView rhs) { return !operator==(lhs, rhs); }
private:
const T* m_data{};
u32 m_len{};
};
using StringView = TStringView<char>;
using WStringView = TStringView<wchar_t>;
} // namespace ore

View File

@ -0,0 +1,20 @@
#pragma once
#include <cstddef>
#include <cstdint>
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using s8 = std::int8_t;
using s16 = std::int16_t;
using s32 = std::int32_t;
using s64 = std::int64_t;
using f32 = float;
using f64 = double;
using char16 = char16_t;
using size_t = std::size_t;

View File

@ -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

View File

@ -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

View File

@ -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 = (&params->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 = (&params->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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

@ -1 +0,0 @@
Subproject commit dea2e24df14973b3c80901c34766b87f89f708b0

View File

@ -0,0 +1,74 @@
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 100
CommentPragmas: '^ (IWYU pragma:|NOLINT)'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros: []
IncludeCategories:
- Regex: '^<[Ww]indows\.h>$'
Priority: 1
- Regex: '^<'
Priority: 2
- Regex: '^"'
Priority: 3
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++17
TabWidth: 4
UseTab: Never
...

12
lib/NintendoSDK/.gitrepo Normal file
View File

@ -0,0 +1,12 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = https://github.com/open-ead/nnheaders
branch = master
commit = 9ee21399ff05176c98b0651be3851fba3f70ceda
parent = ffcc7f659ebc9bc9d149e52bec553b906bb47369
method = merge
cmdver = 0.4.3

View File

@ -0,0 +1,129 @@
project(NintendoSDK CXX ASM)
add_library(NintendoSDK OBJECT
include/nvn/nvn_types.h
include/nvn/nvn.h
include/nvn/nvn_api.h
include/nn/types.h
include/nn/os.h
include/nn/nifm.h
include/nn/prepo.h
include/nn/vfx/Config.h
include/nn/vfx/System.h
include/nn/vfx/Heap.h
include/nn/socket.h
include/nn/aoc.h
include/nn/fs.h
include/nn/ro.h
include/nn/oe.h
include/nn/util.h
include/nn/image.h
include/nn/util/Float2.h
include/nn/ui2d/detail/TexCoordArray.h
include/nn/ui2d/Layout.h
include/nn/ui2d/Parts.h
include/nn/ui2d/Pane.h
include/nn/ui2d/Material.h
include/nn/g3d/ResMaterialAnim.h
include/nn/g3d/ResShapeAnim.h
include/nn/g3d/ResLightAnim.h
include/nn/g3d/ResSkeletalAnim.h
include/nn/g3d/ResSceneAnim.h
include/nn/g3d/BindFuncTable.h
include/nn/g3d/ResFile.h
include/nn/g3d/ResMaterial.h
include/nn/g3d/ResFogAnim.h
include/nn/g3d/ResModel.h
include/nn/nn.h
include/nn/settings.h
include/nn/hid.h
include/nn/atk/detail/StreamSoundRuntime.h
include/nn/atk/detail/BasicSound.h
include/nn/atk/detail/WaveSoundRuntime.h
include/nn/atk/detail/SequenceSoundRuntime.h
include/nn/atk/detail/SoundArchiveManager.h
include/nn/atk/detail/AdvancedWaveSoundRuntime.h
include/nn/atk/SoundArchivePlayer.h
include/nn/atk/SoundPlayer.h
include/nn/atk/SoundDataManager.h
include/nn/nex/client.h
include/nn/nex/RootObject.h
include/nn/nex/socket.h
include/nn/nex/key.h
include/nn/nex/checksum.h
include/nn/nex/reference.h
include/nn/nex/pseudo.h
include/nn/nex/string.h
include/nn/nex/instance.h
include/nn/nex/ddl.h
include/nn/nex/encryption.h
include/nn/nex/cache.h
include/nn/nex/time.h
include/nn/nex/plugin.h
include/nn/nex/dynamic.h
include/nn/nex/data.h
include/nn/nex/auth.h
include/nn/nex/buffer.h
include/nn/nex/system.h
include/nn/nex/hash.h
include/nn/time.h
include/nn/diag.h
include/nn/init.h
include/nn/crypto.h
include/nn/ssl.h
include/nn/gfx/detail/pool.h
include/nn/gfx/detail/deviceimpl.h
include/nn/gfx/detail/bufferimpl.h
include/nn/gfx/device.h
include/nn/gfx/memory.h
include/nn/gfx/buffer.h
include/nn/gfx/api.h
include/nn/vi.h
include/nn/account.h
include/nn/audio.h
include/nn/friends.h
include/nn/mem.h
include/nv.h
include/vapours/results.hpp
include/vapours/results/sf_results.hpp
include/vapours/results/capsrv_results.hpp
include/vapours/results/pgl_results.hpp
include/vapours/results/lr_results.hpp
include/vapours/results/spl_results.hpp
include/vapours/results/pm_results.hpp
include/vapours/results/settings_results.hpp
include/vapours/results/debug_results.hpp
include/vapours/results/cal_results.hpp
include/vapours/results/i2c_results.hpp
include/vapours/results/results_common.hpp
include/vapours/results/time_results.hpp
include/vapours/results/vi_results.hpp
include/vapours/results/ns_results.hpp
include/vapours/results/fs_results.hpp
include/vapours/results/hipc_results.hpp
include/vapours/results/os_results.hpp
include/vapours/results/ro_results.hpp
include/vapours/results/loader_results.hpp
include/vapours/results/err_results.hpp
include/vapours/results/ncm_results.hpp
include/vapours/results/svc_results.hpp
include/vapours/results/nim_results.hpp
include/vapours/results/exosphere_results.hpp
include/vapours/results/creport_results.hpp
include/vapours/results/erpt_results.hpp
include/vapours/results/kvdb_results.hpp
include/vapours/results/dmnt_results.hpp
include/vapours/results/updater_results.hpp
include/vapours/results/fatal_results.hpp
include/vapours/results/sm_results.hpp
include/vapours/results/psc_results.hpp
modules/nvn/nvnInit.cpp
)
target_include_directories(NintendoSDK PUBLIC include/)
target_compile_options(NintendoSDK PRIVATE -fno-strict-aliasing)
target_compile_options(NintendoSDK PRIVATE -Wall -Wextra)
target_compile_options(NintendoSDK PRIVATE -Wno-invalid-offsetof)
target_link_libraries(NintendoSDK PUBLIC)

10
lib/NintendoSDK/README.md Normal file
View File

@ -0,0 +1,10 @@
# nnheaders
Repository of user created nnsdk headers.
The header files contained herewithin are entirely user created via Reverse Engineering or publicly available sources (non stripped binaries containing symbols).
Do not ask for or PR any copyrighted material to this repo. You will be ignored.
# Credits
- [Shadow](https://github.com/shadowninja108/) - For [Skyline](https://github.com/shadowninja108/Skyline), which was the primary inspiration and use case for this repo.
- [Shibbo](https://github.com/shibbo) - For [OdysseyReversed](https://github.com/shibbo/OdysseyReversed/), which most of these headers came from.

View File

@ -0,0 +1,57 @@
/**
* @file account.h
* @brief Account service implementation.
*/
#pragma once
#include <nn/os.h>
#include <nn/types.h>
namespace nn {
namespace account {
typedef char Nickname[0x21];
typedef u64 NetworkServiceAccountId;
class AsyncContext;
class Uid {
public:
bool IsValid() const { return m_Storage[0] != 0 || m_Storage[1] != 0; }
u64 m_Storage[2];
};
class UserHandle {
public:
Uid m_Uid;
void* m_Handle;
};
void Initialize();
Result ListAllUsers(s32*, Uid*, s32 numUsers);
Result OpenUser(UserHandle*, Uid const&);
Result IsNetworkServiceAccountAvailable(bool* out, UserHandle const&);
void CloseUser(UserHandle const&);
Result EnsureNetworkServiceAccountAvailable(UserHandle const& userHandle);
Result EnsureNetworkServiceAccountIdTokenCacheAsync(AsyncContext*, UserHandle const&);
Result LoadNetworkServiceAccountIdTokenCache(u64*, char*, u64, UserHandle const&);
Result GetLastOpenedUser(Uid*);
Result GetNickname(Nickname* nickname, Uid const& userID);
Result GetUserId(Uid* uid, const UserHandle& handle);
Result OpenPreselectedUser(UserHandle* handle);
class AsyncContext {
public:
AsyncContext();
Result HasDone(bool*);
Result GetResult();
Result Cancel();
Result GetSystemEvent(nn::os::SystemEvent*);
};
}; // namespace account
}; // namespace nn

View File

@ -0,0 +1,9 @@
#pragma once
#include <nn/types.h>
namespace nn::aoc {
int ListAddOnContent(int*, int, int);
} // namespace nn::aoc

View File

@ -0,0 +1,37 @@
/**
* @file SoundArchivePlayer.h
* @brief Basic sound player from a sound archive.
*/
#pragma once
#include <nn/atk/detail/AdvancedWaveSoundRuntime.h>
#include <nn/atk/detail/SequenceSoundRuntime.h>
#include <nn/atk/detail/SoundArchiveManager.h>
#include <nn/atk/detail/StreamSoundRuntime.h>
#include <nn/atk/detail/WaveSoundRuntime.h>
namespace nn {
namespace atk {
class SoundArchivePlayer {
public:
SoundArchivePlayer();
virtual ~SoundArchivePlayer();
bool IsAvailable() const;
void Finalize();
void StopAllSound(s32, bool);
void DisposeInstances();
nn::atk::detail::SoundArchiveManager mArchiveManager; // _8
nn::atk::detail::SequenceSoundRuntime mSeqSoundRuntime; // _50
nn::atk::detail::WaveSoundRuntime mWaveSoundRuntime; // _130
nn::atk::detail::AdvancedWaveSoundRuntime mAdvancedWaveSound; // _1B0
nn::atk::detail::StreamSoundRuntime mStreamSoundRuntime; // _1E0
u64 _290;
u32 _298;
u8 _29C[0x2E8 - 0x29C];
};
}; // namespace atk
}; // namespace nn

View File

@ -0,0 +1,25 @@
/**
* @file SoundDataManager.h
* @brief Sound data management implementation.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace atk {
class SoundDataManager {
public:
SoundDataManager();
virtual ~SoundDataManager();
virtual void InvalidateData(void const*, void const*);
virtual void SetFileAddressToTable(u32, void const*);
virtual u64 GetFileAddressFromTable(u32) const;
virtual u32 GetFileAddressImpl(u32) const;
u8 _0[0x240];
};
}; // namespace atk
}; // namespace nn

View File

@ -0,0 +1,56 @@
/**
* @file SoundPlayer.h
* @brief Sound player.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace atk {
enum PauseMode {
};
class SoundPlayer {
public:
SoundPlayer();
~SoundPlayer();
void StopAllSound(s32);
void Update();
void DoFreePlayerHeap();
void detail_SortPriorityList(bool);
void PauseAllSound(s32, bool);
void PauseAllSound(bool, s32, nn::atk::PauseMode);
void SetVolume(f32 vol);
void SetLowPassFilterFrequency(f32 filterFreq);
void SetBiquadFilter(s32 filterType, f32 baseFreq);
void SetDefaultOutputLine(u32 line);
void detail_SetPlayableSoundLimit(s32 limit);
bool CanPlaySound(s32);
u64 _0;
u64 _8;
u64 _10;
u64 _18;
u64 _20;
u64 _28;
u64 _30;
u64 _38;
s32 _40;
s32 mPlayableSoundCount; // _44
s32 _48;
f32 mVolume; // _4C
f32 mLowPassFreq; // _50
s32 mFilterType; // _54
f32 mBaseFreq; // _58
u32 mDefaultOutputLine; // _5C
f32 mOutputVolume; // _60
u64 _64;
u64 _6C;
};
}; // namespace atk
}; // namespace nn

View File

@ -0,0 +1,28 @@
/**
* @file AdvancedWaveSoundRuntime.h
* @brief Runtime wave sound api.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace atk {
namespace detail {
class AdvancedWaveSoundRuntime {
public:
AdvancedWaveSoundRuntime();
~AdvancedWaveSoundRuntime();
void Initialize(s32, void**, void const*);
void Finalize();
s32 GetActiveCount() const;
void SetupUserParam(void**, u64);
void Update();
u8 _0[0x30];
};
}; // namespace detail
}; // namespace atk
}; // namespace nn

View File

@ -0,0 +1,137 @@
/**
* @file BasicSound.h
* @brief A basic sound.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace atk {
class SoundActor;
enum MixMode {
};
namespace detail {
class PlayerHeap;
class ExternalSoundPlayer;
class BasicSound {
public:
BasicSound();
virtual ~BasicSound();
virtual void Initialize();
virtual void Finalize();
virtual bool IsPrepared() const = 0;
virtual bool IsAttachedTempSpecialHandle() = 0;
virtual void DetachTempSpecialHandle() = 0;
virtual void OnUpdatePlayerPriority();
virtual void UpdateMoveValue();
virtual void OnUpdateParam();
void SetPriority(s32, s32);
void GetPriority(s32*, s32*) const;
void ClearIsFinalizedForCannotAllocatedResourceFlag();
void SetId(u32 newID);
bool IsAttachedGeneralHandle();
void DetachGeneralHandle();
bool IsAttachedTempGeneralHandle();
void DetachTempGeneralHandle();
void StartPrepared();
void Stop(s32);
void SetPlayerPriority(s32);
void ForceStop();
void Pause(bool, s32);
void Mute(bool, s32);
void SetAutoStopCounter(s32);
void FadeIn(s32);
bool IsPause() const;
bool IsMute() const;
void Update();
void UpdateParam();
void UpdateMoveValue();
void CalculateVolume() const;
f32 CalculatePitch() const;
f32 CalculateLpfFrequency() const;
u32 CalculateOutLineFlag() const;
void CalculateBiquadFilter(s32*, f32*) const;
void AttachPlayerHeap(nn::atk::detail::PlayerHeap*);
void DetachPlayerHeap(nn::atk::detail::PlayerHeap*);
void AttachSoundPlayer(nn::atk::SoundPlayer*);
void DetachSoundPlayer(nn::atk::SoundPlayer*);
void AttachSoundActor(nn::atk::SoundActor*);
void DetachSoundActor(nn::atk::SoundActor*);
void AttachExternalSoundPlayer(nn::atk::detail::ExternalSoundPlayer*);
void DetachExternalSoundPlayer(nn::atk::detail::ExternalSoundPlayer*);
u32 GetRemainingFadeFrames() const;
u32 GetRemainingPauseFadeFrames() const;
u32 GetRemainingMuteFadeFrames() const;
void SetInitialVolume(f32 vol);
f32 GetInitialVolume() const;
void SetVolume(f32, s32);
s32 GetVolume() const;
void SetPitch(f32);
f32 GetPitch() const;
void SetLpfFreq(f32);
f32 GetLpfFreq() const;
void SetBiquadFilter(s32, f32);
void GetBiquadFilter(s32*, f32*) const;
void SetOutputLine(u32);
u32 GetOutputLine() const;
void ResetOutputLine();
void SetMixMode(nn::atk::MixMode);
nn::atk::MixMode GetMixMode();
void SetPan(f32);
f32 GetPan() const;
void SetSurroundPan(f32);
f32 GetSurroundPan() const;
void SetMainSend(f32);
f32 GetMainSend() const;
u64* _8; // nn::atk::detail::PlayerHeap*
u64* _10; // nn::atk::SoundHandle*
u64* _18; // nn::atk::SoundHandle*
nn::atk::SoundPlayer* mSoundPlayer; // _20
u64* _28; // nn::atk::SoundActor*
u64* _30; // nn::atk::detail::ExternalSoundPlayer*
u64* _38; // nn::atk::SoundArchive*
u8 _40[0xF0 - 0x40];
s32 mPriority; // _F0
u32 _F4;
u32 _F8;
s32 mAutoStopCounter; // _FC
u64 _100;
u32 mID; // _108
u32 _10C;
u32 _110;
u32 _114;
f32 mInitialVolume; // _118
f32 mPitch; // _11C
f32 mLpfFreq; // _120
f32 _124;
u32 mOutputLine; // _128
f32 _12C;
f32 mVolume; // _130
u32 _134;
u32 _138;
nn::atk::MixMode mMixMode; // _13C
f32 mPan; // _140
f32 mSurroundPan; // _144
f32 mMainSend; // _148
u8 _14C[0x158 - 0x14C];
f32 mOutputVol; // _158
u8 _15C[0x190 - 0x15C];
f32 mOutputPan; // _190
f32 mOutputSurroundPan; // _194
f32 mOutputMainSend; // _198
f32 mOutputFxSend; // _19C
static u64 g_LastInstanceId;
};
}; // namespace detail
}; // namespace atk
}; // namespace nn

View File

@ -0,0 +1,37 @@
/**
* @file SequenceSoundRuntime.h
* @brief Sequenced Sound Runtime Info
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace atk {
namespace detail {
class SoundArchiveManager;
class SequenceSoundRuntime {
public:
SequenceSoundRuntime();
~SequenceSoundRuntime();
void Initialize(s32, void**, void const*);
void Finalize();
void SetupSequenceTrack(s32, void**, void const*);
void SetupUserParam(void**, u64);
bool IsSoundArchiveAvailable() const;
s32 GetActiveCount() const;
s32 GetFreeCount() const;
void SetSequenceSkipIntervalTick(s32 tick);
s32 GetSequenceSkipIntervalTick();
void Update();
u8 _0[0xD0];
nn::atk::detail::SoundArchiveManager* mArchiveManager; // _D0
u64 _D8;
};
}; // namespace detail
}; // namespace atk
}; // namespace nn

View File

@ -0,0 +1,42 @@
/**
* @file SoundArchiveManager.h
* @brief Sound archive manager implementation.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace atk {
class SoundHandle;
class SoundArchive;
class SoundDataManager;
namespace detail {
class AddonSoundArchiveContainer;
class SoundArchiveManager {
public:
SoundArchiveManager();
virtual ~SoundArchiveManager();
void Initialize(nn::atk::SoundArchive const*, nn::atk::SoundDataManager const*);
void ChangeTargetArchive(char const*);
void Finalize();
bool IsAvailable() const;
nn::atk::detail::AddonSoundArchiveContainer* GetAddonSoundArchive(char const*) const;
u64 _8;
u64* _10;
nn::atk::detail::AddonSoundArchiveContainer* _18;
u64* _20;
nn::atk::SoundArchive* mSoundArchive; // _28
u64 _30;
u64 _38;
u64 _40;
};
}; // namespace detail
}; // namespace atk
}; // namespace nn

View File

@ -0,0 +1,22 @@
/**
* @file StreamSoundRuntime.h
* @brief Stream sound runtime information.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace atk {
namespace detail {
class StreamSoundRuntime {
public:
StreamSoundRuntime();
~StreamSoundRuntime();
u8 _0[0xB0];
};
}; // namespace detail
}; // namespace atk
}; // namespace nn

View File

@ -0,0 +1,29 @@
/**
* @file WaveSoundRuntime.h
* @brief Wave sound runtime info.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace atk {
namespace detail {
class WaveSoundRuntime {
public:
WaveSoundRuntime();
~WaveSoundRuntime();
void Initialize(s32, void**, void const*);
void Finalize();
s32 GetActiveCount() const;
s32 GetFreeWaveSoundCount() const;
void SetupUserParam(void**, u64);
void Update();
u8 _0[0x80];
};
}; // namespace detail
}; // namespace atk
}; // namespace nn

View File

@ -0,0 +1,344 @@
/**
* @file audio.h
* @brief Audio implementation.
*/
#pragma once
#include <nn/os.h>
#include <nn/types.h>
namespace nn {
namespace audio {
// Common audio
struct AudioDeviceName {
char raw_name[0x100];
};
static_assert(sizeof(AudioDeviceName) == 0x100);
void AcquireAudioDeviceSwitchNotification(nn::os::SystemEvent* event);
s32 ListAudioDeviceName(nn::audio::AudioDeviceName* buffer, s32 bufferCount);
Result SetAudioDeviceOutputVolume(nn::audio::AudioDeviceName const* device, float volume);
u32 GetActiveChannelCount();
struct AudioRendererConfig {
u64* _0;
u64* _8;
u64* _10;
u64* _18;
u64* _20;
u64* _28;
u64* _30;
u64* _38;
u64* _40;
u64* _48;
u64* _50;
};
enum AudioRendererRenderingDevice : u32 {
AudioRendererRenderingDevice_Cpu,
AudioRendererRenderingDevice_Dsp
};
enum AudioRendererExecutionMode : u32 {
AudioRendererExecutionMode_Manual,
AudioRendererExecutionMode_Auto,
};
enum SampleFormat : u32 {
SampleFormat_Invalid,
SampleFormat_PcmInt8,
SampleFormat_PcmInt16,
SampleFormat_PcmInt24,
SampleFormat_PcmInt32,
SampleFormat_PcmFloat,
SampleFormat_Adpcm,
};
enum MemoryPoolState : u32 {
MemoryPoolState_Invalid,
MemoryPoolState_New,
MemoryPoolState_RequestDetach,
MemoryPoolState_Detached,
MemoryPoolState_RequestAttach,
MemoryPoolState_Attached,
MemoryPoolState_Released,
};
struct AudioRendererParameter {
u32 sampleRate;
u32 sampleCount;
u32 mixBufferCount;
u32 subMixCount;
u32 voiceCount;
u32 sinkCount;
u32 effectCount;
u32 performanceFrameCount;
bool isVoiceDropEnabled;
u32 splitterCount;
u32 splitterSendChannelCount;
AudioRendererRenderingDevice renderingDevice;
AudioRendererExecutionMode executionMode;
u32 _34;
u32 revision;
};
static_assert(sizeof(AudioRendererParameter) == 0x3C);
struct BiquadFilterParameter {
bool enabled;
s16 numerator[3];
s16 denominator[2];
};
static_assert(sizeof(BiquadFilterParameter) == 0xC);
struct WaveBuffer {
void* buffer;
size_t bufferSize;
s32 startSampleOffset;
s32 endSampleOffset;
bool shouldLoop;
bool isEndOfStream;
void* context;
size_t contextSize;
};
static_assert(sizeof(WaveBuffer) == 0x30);
struct AudioRendererHandle {
u64* _0;
u64* _8;
};
struct MemoryPoolType {
u64* _0;
};
struct CircularBufferSinkType {
u64* _0;
};
struct AuxType {
u64* _0;
};
struct DelayType {
u64* _0;
};
struct FinalMixType {
u64* _0;
};
struct SubMixType {
u64* _0;
};
struct VoiceType {
u64* _0;
enum PlayState : u32 {
PlayState_Start,
PlayState_Stop,
PlayState_Pause,
};
};
struct DeviceSinkType {
u64* _0;
};
// Audio Renderer base APIs
void InitializeAudioRendererParameter(nn::audio::AudioRendererParameter* inParameter);
bool IsValidAudioRendererParameter(nn::audio::AudioRendererParameter const& inParameter);
size_t GetAudioRendererWorkBufferSize(nn::audio::AudioRendererParameter const& inParameter);
size_t GetAudioRendererConfigWorkBufferSize(nn::audio::AudioRendererParameter const& inParameter);
Result InitializeAudioRendererConfig(nn::audio::AudioRendererConfig* outConfig,
nn::audio::AudioRendererParameter const& inParameter,
void* buffer, size_t bufferSize);
Result OpenAudioRenderer(nn::audio::AudioRendererHandle* outHandle,
nn::audio::AudioRendererParameter const& inParameter, void* workBuffer,
size_t workBufferSize);
void CloseAudioRenderer(nn::audio::AudioRendererHandle handle);
Result StartAudioRenderer(nn::audio::AudioRendererHandle handle);
Result StopAudioRenderer(nn::audio::AudioRendererHandle handle);
Result RequestUpdateAudioRenderer(nn::audio::AudioRendererHandle handle,
nn::audio::AudioRendererConfig const* config);
// Audio Renderer MemoryPool APIs
bool IsMemoryPoolAttached(nn::audio::MemoryPoolType const* pool);
bool RequestAttachMemoryPool(nn::audio::MemoryPoolType* pool);
bool RequestDetachMemoryPool(nn::audio::MemoryPoolType* pool);
bool AcquireMemoryPool(nn::audio::AudioRendererConfig* config, nn::audio::MemoryPoolType* outPool,
void* address, size_t size);
void ReleaseMemoryPool(nn::audio::AudioRendererConfig* config, nn::audio::MemoryPoolType* pool);
void* GetMemoryPoolAddress(nn::audio::MemoryPoolType const* pool);
size_t GetMemoryPoolSize(nn::audio::MemoryPoolType const* pool);
MemoryPoolState GetMemoryPoolState(nn::audio::MemoryPoolType const* pool);
// Audio Renderer Effect APIs
void SetDelayInputOutput(nn::audio::DelayType* delay, s8 const* input, s8 const* output, s32 count);
void* RemoveDelay(nn::audio::AudioRendererConfig* config, nn::audio::DelayType* delay,
nn::audio::FinalMixType* mix);
void* RemoveDelay(nn::audio::AudioRendererConfig* config, nn::audio::DelayType* delay,
nn::audio::SubMixType* mix);
bool IsDelayRemovable(nn::audio::DelayType* delay);
size_t GetRequiredBufferSizeForAuxSendReturnBuffer(nn::audio::AudioRendererParameter const* config,
s32 mixBufferFrameCount, s32 channelCount);
Result AddAux(nn::audio::AudioRendererConfig* config, nn::audio::AuxType* aux,
nn::audio::FinalMixType* mix, void* sendBuffer, void* returnBuffer,
size_t bufferSize);
Result AddAux(nn::audio::AudioRendererConfig* config, nn::audio::AuxType* aux,
nn::audio::SubMixType* mix, void* sendBuffer, void* returnBuffer, size_t bufferSize);
void RemoveAux(nn::audio::AudioRendererConfig* config, nn::audio::AuxType* aux,
nn::audio::FinalMixType* mix);
void RemoveAux(nn::audio::AudioRendererConfig* config, nn::audio::AuxType* aux,
nn::audio::SubMixType* mix);
void SetAuxEnabled(nn::audio::AuxType* aux, bool enable);
void SetAuxInputOutput(nn::audio::AuxType* aux, s8 const* input, s8 const* output, s32 count);
bool IsAuxRemovable(nn::audio::AuxType* aux);
s32 GetAuxSampleCount(nn::audio::AuxType const* aux);
s32 GetAuxSampleRate(nn::audio::AuxType const* aux);
s32 ReadAuxSendBuffer(nn::audio::AuxType* aux, s32* buffer, s32 count);
s32 WriteAuxReturnBuffer(nn::audio::AuxType* aux, s32 const* buffer, s32 count);
// Audio Renderer Performance APIs
size_t
GetRequiredBufferSizeForPerformanceFrames(nn::audio::AudioRendererParameter const& inParameter);
void* SetPerformanceFrameBuffer(nn::audio::AudioRendererConfig* config, void* buffer,
size_t bufferSize);
enum PerformanceEntryType : u8 {
PerformanceEntryType_Invalid,
PerformanceEntryType_Voice,
PerformanceEntryType_SubMix,
PerformanceEntryType_FinalMix,
PerformanceEntryType_Sink
};
struct PerformanceEntry {
s32 nodeId;
s32 startTime;
s32 processingTime;
PerformanceEntryType entryType;
};
static_assert(sizeof(PerformanceEntry) == 0x10);
enum PerformanceDetailType : u8 {
PerformanceDetailType_Unknown,
PerformanceDetailType_PcmInt16,
PerformanceDetailType_Adpcm,
PerformanceDetailType_VolumeRamp,
PerformanceDetailType_BiquadFilter,
PerformanceDetailType_Mix,
PerformanceDetailType_Delay,
PerformanceDetailType_Aux,
PerformanceDetailType_Reverb,
PerformanceDetailType_Reverb3d,
PerformanceDetailType_PcmFloat
};
struct PerformanceDetail {
s32 nodeId;
s32 startTime;
s32 processingTime;
PerformanceDetailType detailType;
PerformanceEntryType entryType;
};
static_assert(sizeof(PerformanceDetail) == 0x10);
class PerformanceInfo {
public:
PerformanceInfo();
~PerformanceInfo();
bool SetBuffer(void const* buffer, size_t bufferSize);
bool MoveToNextFrame();
s32 GetTotalProcessingTime();
PerformanceEntry GetEntries(s32* count);
PerformanceDetail GetDetails(s32* count);
private:
void* buffer;
size_t bufferSize;
void* header;
PerformanceEntry* entries;
PerformanceDetail* details;
};
static_assert(sizeof(PerformanceInfo) == 0x28);
// Audio Renderer Sink APIs
Result AddDeviceSink(nn::audio::AudioRendererConfig* config, nn::audio::DeviceSinkType* sink,
nn::audio::FinalMixType* mix, s8 const* input, s32 inputCount,
char const* deviceName);
void RemoveDeviceSink(nn::audio::AudioRendererConfig* config, nn::audio::DeviceSinkType* sink,
nn::audio::FinalMixType* mix);
u32 GetSinkNodeId(nn::audio::DeviceSinkType const* sink);
Result AddCircularBufferSink(nn::audio::AudioRendererConfig* config,
nn::audio::CircularBufferSinkType* sink, nn::audio::FinalMixType* mix,
s8 const* input, s32 inputCount, void* buffer, size_t bufferSize,
nn::audio::SampleFormat sampleFormat);
void RemoveCircularBufferSink(nn::audio::AudioRendererConfig* config,
nn::audio::CircularBufferSinkType* sink,
nn::audio::FinalMixType* mix);
size_t ReadCircularBufferSink(nn::audio::CircularBufferSinkType* sink, void* buffer,
size_t buffer_size);
u32 GetSinkNodeId(nn::audio::CircularBufferSinkType const* sink);
size_t
GetRequiredBufferSizeForCircularBufferSink(nn::audio::AudioRendererParameter const* inParameter,
s32 inputCount, s32 frameCount,
nn::audio::SampleFormat sampleFormat);
// Audio Renderer Mix APIs
bool AcquireFinalMix(nn::audio::AudioRendererConfig* config, nn::audio::FinalMixType* mix,
s32 bufferCount);
bool AcquireSubMix(nn::audio::AudioRendererConfig* config, nn::audio::SubMixType* mix,
s32 sampleRate, s32 bufferCount);
void ReleaseSubMix(nn::audio::AudioRendererConfig* config, nn::audio::SubMixType* mix);
void SetSubMixDestination(nn::audio::AudioRendererConfig* config, nn::audio::SubMixType* source,
nn::audio::FinalMixType* destination);
void SetSubMixDestination(nn::audio::AudioRendererConfig* config, nn::audio::SubMixType* source,
nn::audio::SubMixType* destination);
void SetSubMixMixVolume(nn::audio::SubMixType* source, nn::audio::FinalMixType* destination,
float volume, s32 sourceIndex, s32 destinationIndex);
void SetSubMixMixVolume(nn::audio::SubMixType* source, nn::audio::SubMixType* destination,
float volume, s32 sourceIndex, s32 destinationIndex);
u32 GetSubMixNodeId(nn::audio::SubMixType const* mix);
// Audio Renderer Voice APIs
Result AcquireVoiceSlot(nn::audio::AudioRendererConfig* config, nn::audio::VoiceType* voice,
s32 sampleRate, s32 channelCount, nn::audio::SampleFormat sampleFormat,
s32 priority, void const* buffer, size_t bufferSize);
void ReleaseVoiceSlot(nn::audio::AudioRendererConfig* config, nn::audio::VoiceType* voice);
bool IsVoiceValid(nn::audio::VoiceType const* voice);
bool IsVoiceDroppedFlagOn(nn::audio::VoiceType const* voice);
void ResetVoiceDroppedFlag(nn::audio::VoiceType* voice);
void SetVoiceDestination(nn::audio::AudioRendererConfig* config, nn::audio::VoiceType* voice,
nn::audio::FinalMixType* mix);
void SetVoiceDestination(nn::audio::AudioRendererConfig* config, nn::audio::VoiceType* voice,
nn::audio::SubMixType* mix);
void SetVoiceVolume(nn::audio::VoiceType* config, float volume);
void SetVoicePitch(nn::audio::VoiceType* config, float pitch);
void SetVoicePlayState(nn::audio::VoiceType* voice, nn::audio::VoiceType::PlayState state);
void SetVoicePriority(nn::audio::VoiceType* voice, s32 priority);
void SetVoiceBiquadFilterParameter(nn::audio::VoiceType* voice, s32 index,
nn::audio::BiquadFilterParameter const& biquadFilterParameter);
void SetVoiceMixVolume(nn::audio::VoiceType* voice, nn::audio::FinalMixType* mix, float volume,
int sourceIndex, int destinationIndex);
void SetVoiceMixVolume(nn::audio::VoiceType* voice, nn::audio::SubMixType* mix, float volume,
int sourceIndex, int destinationIndex);
nn::audio::VoiceType::PlayState GetVoicePlayState(nn::audio::VoiceType const* voice);
s32 GetVoicePriority(nn::audio::VoiceType const* voice);
u64 GetVoicePlayedSampleCount(nn::audio::VoiceType const* voice);
u32 GetVoiceNodeId(nn::audio::VoiceType const* voice);
bool AppendWaveBuffer(nn::audio::VoiceType* voice, nn::audio::WaveBuffer const* waveBuffer);
nn::audio::WaveBuffer* GetReleasedWaveBuffer(nn::audio::VoiceType* voice);
}; // namespace audio
}; // namespace nn

View File

@ -0,0 +1,112 @@
/**
* @file bcat.h
* @brief BCAT service implementation.
*/
#pragma once
#include <nn/os.h>
#include <nn/types.h>
namespace nn {
namespace bcat {
struct DirectoryName {
void isValid();
};
struct FileName {
void isValid();
};
class DeliveryCacheDirectory {
public:
DeliveryCacheDirectory();
~DeliveryCacheDirectory();
Result Open(nn::bcat::DirectoryName const&);
Result GetCount();
Result Close();
};
class DeliveryCacheFile {
public:
DeliveryCacheFile();
~DeliveryCacheFile();
Result Open(nn::bcat::DirectoryName const&, nn::bcat::FileName const&);
Result Read(size_t file1, s64, void*, size_t file2);
Result GetSize();
Result GetDigest();
Result Close();
};
class DeliveryCacheProgress {
public:
DeliveryCacheProgress();
~DeliveryCacheProgress();
Result Detach();
Result Update();
Result GetStatus();
Result GetCurrentDirectoryName();
Result GetCurrentFileName();
Result GetCurrentDownloaded();
Result GetCurrentTotale();
Result GetWholeDownloaded();
Result GetWholeTotal();
Result GetResult();
// void Attach(nn::bcat::detail::ipc::IDeliveryCacheProgressService*);
};
namespace detail {
namespace DeliveryCacheProgressImpl {
Result Clear();
Result NotifyStartConnect();
Result NotifyStartProcessList();
Result SetWholeDownloadSize(s64);
Result SetDownloadProgress(s64, nn::bcat::DirectoryName const&, nn::bcat::FileName const&, s64);
Result NotifyStartDownloadFile(nn::bcat::DirectoryName const&, nn::bcat::FileName const&, s64);
Result UpdateDownloadFileProgress(s64);
Result NotifyStartCommitDirectory(nn::bcat::DirectoryName const&);
Result NotifyDone(nn::Result);
} // namespace DeliveryCacheProgressImpl
class ShimLibraryGlobal {
public:
ShimLibraryGlobal();
~ShimLibraryGlobal();
Result Initialize();
Result GetSession();
Result MountDeliveryCacheStorage();
Result IsDeliveryCacheStorageMounted();
Result UnmountDeliveryCacheStorage();
// void CreateFileService(nn::bcat::detail::ipc::IDeliveryCacheFileService**);
// void CreateDirectoryService(nn::bcat::detail::ipc::IDeliveryCacheDirectoryService**)
Result IncrementDeliveryCacheFileCount();
Result DecrementDeliveryCacheFileCount();
Result IncrementDeliveryCacheDirectoryCount();
Result DecrementDeliveryCacheFileCount();
Result EnumerateDeliveryCacheDirectory(int*, nn::bcat::DirectoryName*, int);
};
namespace ipc {
Result Initialize();
// void CreateBcatService(nn::bcat::detail::ipc::IBcatService**);
Result Finalize();
// void CreateBcatService(nn::bcat::detail::ipc::IBcatService**);
// void CreateDeliveryCacheStorageService(nn::bcat::detail::ipc::IDeliveryCacheStorageService**);
// void CreateDeliveryCacheStorageService(nn::bcat::detail::ipc::IDeliveryCacheStorageService**,
// nn::ApplicationId);
} // namespace ipc
} // namespace detail
Result Initialize();
Result MountDeliveryCacheStorage();
Result UnmountDeliveryCacheStorage();
Result EnumerateDeliveryCacheDirectory(int*, nn::bcat::DirectoryName*, int);
Result RequestSyncDeliveryCache(nn::bcat::DeliveryCacheProgress*);
Result EnumerateDeliveryCacheDirectory(int*, nn::bcat::DirectoryName*, int);
} // namespace bcat
} // namespace nn

View File

@ -0,0 +1,86 @@
/**
* @file crypto.h
* @brief Crypto service implementation.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace crypto {
void GenerateSha256Hash(void*, ulong, void const*, ulong);
class Sha256Context;
void DecryptAes128Cbc(void*, u64, void const*, u64, void const*, u64, void const*, u64);
void EncryptAes128Cbc(void*, u64, void const*, u64, void const*, u64, void const*, u64);
void DecryptAes128Ccm(void*, u64, void*, u64, void const*, u64, void const*, u64, void const*, u64,
void const*, u64, u64);
namespace detail {
class Md5Impl {
public:
void Initialize();
void Update(void const*, u64 dataSize);
void ProcessBlock();
void GetHash(void*, u64 hashSize);
void ProcessLastBlock();
u32 _x0;
u32 _x4;
u32 _x8;
u32 _xC;
u8 _x10[0x50 - 0x10];
u64 _x50;
u32 _x58;
};
class Sha1Impl {
public:
void Initialize();
void Update(void const*, u64);
void ProcessBlock(void const*);
void GetHash(void* destHash, u64);
void ProcessLastBlock();
u64 _x0;
u64 _x8;
u32 _x10;
u128 _x14;
u128 _x24;
u128 _x34;
u32 _x44;
u64 _x48;
u64 _x50;
u64 _x58;
u64 _x60;
};
class Sha256Impl {
public:
void Initialize();
void Update(void const*, u64);
void ProcessBlocks(u8 const*, u64);
void GetHash(void* destHash, u64);
void ProcessLastBlock();
void InitializeWithContext(nn::crypto::Sha256Context const*);
void GetContext(nn::crypto::Sha256Context*) const;
u64 _x0;
u64 _x8;
u32 _x10;
u128 _x14;
u128 _x24;
u128 _x34;
u32 _x44;
u64 _x48;
u64 _x50;
u64 _x58;
u64 _x60;
u64 _x68;
u32 _x70;
};
}; // namespace detail
}; // namespace crypto
}; // namespace nn

View File

@ -0,0 +1,33 @@
/**
* @file diag.h
* @brief Module, logging, and symbol operations.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace diag {
struct LogMetaData;
struct ModuleInfo {
char* mPath;
u64 mBaseAddr;
u64 mSize;
};
namespace detail {
// LOG
void LogImpl(nn::diag::LogMetaData const&, char const*, ...);
void AbortImpl(char const*, char const*, char const*, s32);
void AbortImpl(char const*, char const*, char const*, int, Result);
}; // namespace detail
// MODULE / SYMBOL
u32* GetSymbolName(char* name, u64 nameSize, u64 addr);
u64 GetRequiredBufferSizeForGetAllModuleInfo();
s32 GetAllModuleInfo(nn::diag::ModuleInfo** out, void* buffer, u64 bufferSize);
u64 GetSymbolSize(u64 addr);
}; // namespace diag
}; // namespace nn

View File

@ -0,0 +1,31 @@
#pragma once
#include <nn/settings.h>
namespace nn {
namespace err {
enum ErrorCodeCategoryType : u32 {
unk1,
unk2,
};
class ApplicationErrorArg {
public:
ApplicationErrorArg();
ApplicationErrorArg(u32 error_code, const char* dialog_message, const char* fullscreen_message,
const nn::settings::LanguageCode& languageCode);
void SetApplicationErrorCodeNumber(u32 error_code);
void SetDialogMessage(const char* message);
void SetFullScreenMessage(const char* message);
u64 unk;
u32 error_code;
nn::settings::LanguageCode language_code;
char dialog_message[2048];
char fullscreen_message[2048];
};
u32 MakeErrorCode(ErrorCodeCategoryType err_category_type, u32 errorCodeNumber);
void ShowApplicationError(const ApplicationErrorArg& arg);
} // namespace err
} // namespace nn

View File

@ -0,0 +1,42 @@
/**
* @file friends.h
* @brief Friend implementation.
*/
#pragma once
#include <nn/account.h>
#include <nn/os.h>
namespace nn {
namespace friends {
typedef char Url[0xA0];
class AsyncContext;
class Profile;
void Initialize();
Result GetProfileList(nn::friends::AsyncContext* context, nn::friends::Profile* profiles,
nn::account::Uid const& userID,
nn::account::NetworkServiceAccountId const* accountIDs, s32 numAccounts);
class Profile {
public:
Profile();
nn::account::NetworkServiceAccountId GetAccountId() const;
nn::account::Nickname& GetNickname() const;
bool IsValid() const;
Result GetProfileImageUrl(nn::friends::Url*, s32);
};
class AsyncContext {
public:
AsyncContext();
~AsyncContext();
Result GetSystemEvent(nn::os::SystemEvent*);
Result GetResult() const;
};
}; // namespace friends
}; // namespace nn

View File

@ -0,0 +1,132 @@
/**
* @file fs.h
* @brief Filesystem implementation.
*/
#pragma once
#include <nn/account.h>
#include <nn/types.h>
namespace nn {
typedef u64 ApplicationId;
namespace fs {
using namespace ams::fs;
typedef u64 UserId;
struct DirectoryEntry {
char name[0x300 + 1];
char _x302[3];
u8 type;
char _x304;
s64 fileSize;
};
struct FileHandle {
void* handle;
};
struct DirectoryHandle {
void* handle;
};
enum DirectoryEntryType { DirectoryEntryType_Directory, DirectoryEntryType_File };
enum OpenMode {
OpenMode_Read = 1 << 0,
OpenMode_Write = 1 << 1,
OpenMode_Append = 1 << 2,
OpenMode_ReadWrite = OpenMode_Read | OpenMode_Write
};
enum OpenDirectoryMode {
OpenDirectoryMode_Directory = 1 << 0,
OpenDirectoryMode_File = 1 << 1,
OpenDirectoryMode_All = OpenDirectoryMode_Directory | OpenDirectoryMode_File,
};
struct ReadOption {
u32 value;
static const ReadOption None;
};
inline constexpr const ReadOption ReadOption::None = {0};
enum WriteOptionFlag {
WriteOptionFlag_Flush = 1 << 0,
};
struct WriteOption {
int flags;
static WriteOption CreateOption(int flags) {
WriteOption op;
op.flags = flags;
return op;
}
};
// ROM
Result QueryMountRomCacheSize(size_t* size);
Result QueryMountRomCacheSize(size_t* size, nn::ApplicationId);
Result QueryMountAddOnContentCacheSize(size_t* size, int id);
Result MountRom(char const* name, void* cache, size_t cache_size);
Result MountAddOnContent(char const* name, int id, void* cache, size_t cache_size);
bool CanMountRomForDebug();
Result CanMountRom(nn::ApplicationId);
Result QueryMountRomOnFileCacheSize(u64*, nn::fs::FileHandle);
Result MountRomOnFile(char const*, nn::fs::FileHandle, void*, u64);
// SAVE
Result EnsureSaveData(nn::account::Uid const&);
Result MountSaveData(char const*, nn::fs::UserId);
Result MountSaveDataForDebug(char const*);
Result CommitSaveData(const char* path);
// FILE
Result GetEntryType(nn::fs::DirectoryEntryType* type, char const* path);
Result CreateFile(char const* filepath, s64 size);
Result OpenFile(nn::fs::FileHandle*, char const* path, s32);
Result SetFileSize(FileHandle fileHandle, s64 filesize);
void CloseFile(FileHandle fileHandle);
Result FlushFile(FileHandle fileHandle);
Result DeleteFile(char const* filepath);
Result ReadFile(u64* outSize, nn::fs::FileHandle handle, s64 offset, void* buffer, u64 bufferSize,
const ReadOption& option);
Result ReadFile(u64* outSize, nn::fs::FileHandle handle, s64 offset, void* buffer, u64 bufferSize);
Result ReadFile(nn::fs::FileHandle handle, s64 offset, void* buffer, u64 bufferSize);
Result WriteFile(FileHandle handle, s64 fileOffset, void const* buff, u64 size,
WriteOption const& option);
Result GetFileSize(s64* size, FileHandle fileHandle);
// DIRECTORY
// there are three open modes; dir, file, all
Result OpenDirectory(DirectoryHandle* handle, char const* path, s32 openMode);
void CloseDirectory(DirectoryHandle directoryHandle);
Result ReadDirectory(s64*, DirectoryEntry*, DirectoryHandle directoryHandle, s64);
Result CreateDirectory(char const* directorypath);
Result GetDirectoryEntryCount(s64*, DirectoryHandle);
// SD
Result MountSdCard(char const*);
Result MountSdCardForDebug(char const*);
bool IsSdCardInserted();
Result FormatSdCard();
Result FormatSdCardDryRun();
bool IsExFatSupported();
Result MountHost(char const* mount, char const* path);
Result MountHostRoot();
Result UnmountHostRoot();
Result Unmount(char const* mount);
// BCAT
Result MountBcatSaveData(char const*, ApplicationId);
Result CreateBcatSaveData(ApplicationId, s64);
}; // namespace fs
}; // namespace nn

View File

@ -0,0 +1,31 @@
#pragma once
#include <nn/types.h>
namespace nn {
namespace g3d {
struct DDLDeclarations {
u64 _0;
u32 _8;
u32 DDLDeclarations_xC;
u64 _10;
u64 _18;
};
class BindFuncTable {
struct StringLength {
size_t length;
const char* content;
};
struct EntryPointer {
void* something_0;
nn::g3d::BindFuncTable::StringLength* string;
};
private:
int lengths[4];
nn::g3d::BindFuncTable::EntryPointer strings[4];
};
}; // namespace g3d
}; // namespace nn

View File

@ -0,0 +1,75 @@
/**
* @file ResFile.h
* @brief Resource file for models.
*/
#pragma once
#include <nn/gfx/api.h>
#include <nn/gfx/memory.h>
#include <nn/types.h>
#include <nn/util.h>
namespace nn {
namespace gfx {
template <typename T>
class TDevice;
}
namespace g3d {
class ResModel;
class ResMaterialAnim;
class ResShapeAnim;
class ResSceneAnim;
typedef void* TextureRef;
class ResFile : public nn::util::BinaryFileHeader {
public:
static bool IsValid(void const* modelSrc);
void Relocate();
void Unrelocate();
static nn::g3d::ResFile* ResCast(void*);
s32 BindTexture(nn::g3d::TextureRef (*ref)(char const*, void*), void*);
void ReleaseTexture();
void
Setup(nn::gfx::TDevice<nn::gfx::ApiVariation<nn::gfx::ApiType<4>, nn::gfx::ApiVersion<8>>>*);
void
Setup(nn::gfx::TDevice<nn::gfx::ApiVariation<nn::gfx::ApiType<4>, nn::gfx::ApiVersion<8>>>*,
nn::gfx::TMemoryPool<nn::gfx::ApiVariation<nn::gfx::ApiType<4>, nn::gfx::ApiVersion<8>>>*,
s64, u64);
void
Cleanup(nn::gfx::TDevice<nn::gfx::ApiVariation<nn::gfx::ApiType<4>, nn::gfx::ApiVersion<8>>>*);
void Reset();
u64 mFileNameLength; // _20
nn::g3d::ResModel* mModels; // _28
u64 mModelDictOffset; // _30
u64 mSkeleAnimOffset; // _38
u64 mSkeleAnimDictOffset; // _40
nn::g3d::ResMaterialAnim* mMatAnims; // _48
u64 mMatAnimsDictOffset; // _50
u64 mBoneVisiOffset; // _58
u64 mBoneVisiDictOffset; // _60
nn::g3d::ResShapeAnim* mShapeAnims; // _68
u64 mShapeAnimDictOffset; // _70
nn::g3d::ResSceneAnim* mSceneAnims; // _78
u64 mSceneAnimDictOffset; // _80
u64 mMemoryPool; // _88
u64 mBufferSection; // _90
u64 mEmbeddedFilesOffset; // _98
u64 mEmbeddedFilesDictOffset; // _A0
u64 mPadding; // _A8
u64 mStrTableOffset; // _B0
u32 mStrTableSize; // _B8
u16 mModelCount; // _BC
u16 mSkeleAnimCount; // _BE
u16 mMatAnimCount; // _C0
u16 mBoneAnimCount; // _C2
u16 mShapeAnimCount; // _C4
u16 mSceneAnimCount; // _C6
u16 mExternalFileCount; // _C8
u8 mPad[0x6]; // _CA
};
}; // namespace g3d
}; // namespace nn

View File

@ -0,0 +1,21 @@
#pragma once
#include <nn/types.h>
namespace nn {
namespace g3d {
class ResFogAnim {
public:
char mMagic[4]; // _0
u16 mFlags; // _4
u16 mPad; // _6
s32 mNumFrames; // _8
u8 mNumCurves; // _C
u8 mIdxDistanceAttnFunc; // _D
u16 mNumUserData; // _E
u32 mSizeBaked; // _10
u64 mNameOffset; // _14
u64 mFuncNameOffset; // _1C
};
}; // namespace g3d
}; // namespace nn

View File

@ -0,0 +1,12 @@
#pragma once
#include <nn/types.h>
namespace nn {
namespace g3d {
class ResLightAnim {
public:
s32 Bind(nn::g3d::BindFuncTable const&);
};
}; // namespace g3d
}; // namespace nn

View File

@ -0,0 +1,29 @@
/**
* @file ResMaterial.h
* @brief Resource material for models.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace g3d {
typedef void* TextureRef;
class ResMaterial {
public:
u64 BindTexture(nn::g3d::TextureRef (*)(char const*, void*), void*);
void ForceBindTexture(nn::g3d::TextureRef const&, char const*);
void ReleaseTexture();
void
Setup(nn::gfx::TDevice<nn::gfx::ApiVariation<nn::gfx::ApiType<4>, nn::gfx::ApiVersion<4>>>*);
void
Cleanup(nn::gfx::TDevice<nn::gfx::ApiVariation<nn::gfx::ApiType<8>, nn::gfx::ApiVersion<8>>>*);
void Reset();
void Reset(u32);
u8 _0[0xB4];
};
}; // namespace g3d
}; // namespace nn

View File

@ -0,0 +1,19 @@
/**
* @file ResMaterialAnim.h
* @brief Resource file for material animations.
*/
#pragma once
namespace nn {
namespace g3d {
typedef void* TextureRef;
class ResMaterialAnim {
public:
void ReleaseTexture();
s32 BindTexture(nn::g3d::TextureRef (*)(char const*, void*), void*);
void Reset();
};
}; // namespace g3d
}; // namespace nn

View File

@ -0,0 +1,32 @@
/**
* @file ResModel.h
* @brief Resource model.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace g3d {
class ResMaterial;
typedef void* TextureRef;
class ResModel {
public:
u64 BindTexture(nn::g3d::TextureRef (*)(char const*, void*), void*);
void ForceBindTexture(nn::g3d::TextureRef const&, char const*);
void ReleaseTexture();
void
Setup(nn::gfx::TDevice<nn::gfx::ApiVariation<nn::gfx::ApiType<4>, nn::gfx::ApiVersion<8>>>*);
void
Cleanup(nn::gfx::TDevice<nn::gfx::ApiVariation<nn::gfx::ApiType<4>, nn::gfx::ApiVersion<8>>>*);
void Reset();
void Reset(u32);
nn::g3d::ResMaterial* FindMaterial(char const* materialName) const;
u8 _0[0x70];
};
}; // namespace g3d
}; // namespace nn

View File

@ -0,0 +1,41 @@
/**
* @file ResSceneAnim.h
* @brief Resource file for scene animations.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace g3d {
class ResLightAnim;
class ResFogAnim;
struct BindFuncTable;
class ResSceneAnim {
public:
s32 Bind(nn::g3d::BindFuncTable const&);
void Release();
void Reset();
char mMagic[4]; // _0
s32 mBlockOffset; // _4
u64 mBlockSize; // _8
u64 mNameOffset; // _10
u64 mPathOffset; // _18
u64 mCameraAnimOffset; // _20
u64 mCameraAnimDictOffset; // _28
nn::g3d::ResLightAnim* mLightAnims; // _30
u64 mLightAnimDictOffset; // _38
nn::g3d::ResFogAnim* mFogAnims; // _40
u64 mFogAnimDictOffset; // _48
u64 mUserDataOffset; // _50
u64 mUserDataDictOffset; // _58
u16 mUserDataCount; // _60
u16 mCameraAnimCount; // _62
u16 mLightAnimCount; // _64
u16 mFogAnimCount; // _66
};
}; // namespace g3d
}; // namespace nn

View File

@ -0,0 +1,15 @@
/**
* @file ResShapeAnim.h
* @brief Resource file for shape animations.
*/
#pragma once
namespace nn {
namespace g3d {
class ResShapeAnim {
public:
void Reset();
};
}; // namespace g3d
}; // namespace nn

View File

@ -0,0 +1,15 @@
/**
* @file ResSkeletalAnim.h
* @brief Resource file for skeletal animations.
*/
#pragma once
namespace nn {
namespace g3d {
class ResSkeletalAnim {
public:
void Reset();
};
}; // namespace g3d
}; // namespace nn

View File

@ -0,0 +1,22 @@
/**
* @file api.h
* @brief GFX API version and typing.
*/
#pragma once
namespace nn {
namespace gfx {
// passes both ApiType<4> and ApiVersion<8>
template <typename T, typename T2>
class ApiVariation {};
// usually passed as just a 4
template <int T>
class ApiType {};
// usually passed as just a 8
template <int T>
class ApiVersion {};
}; // namespace gfx
}; // namespace nn

View File

@ -0,0 +1,28 @@
/**
* @file buffer.h
* @brief GFX Buffers.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace gfx {
class BufferInfo {
public:
void SetDefault();
u64 mInfo; // _0
};
class BufferTextureViewInfo {
public:
void SetDefault();
u64 _0;
u64 _8;
u64 _10;
};
}; // namespace gfx
}; // namespace nn

View File

@ -0,0 +1,55 @@
/**
* @file bufferimpl.h
* @brief Buffer implementation for GFX.
*/
#pragma once
namespace nn {
namespace gfx {
class GpuAddress;
class BufferInfo;
namespace detail {
template <typename T>
class MemoryPoolImpl;
template <typename T>
class DeviceImpl;
template <typename T>
class BufferImpl {
public:
BufferImpl();
~BufferImpl();
void Initialize(nn::gfx::detail::DeviceImpl<
nn::gfx::ApiVariation<nn::gfx::ApiType<4>, nn::gfx::ApiVersion<8>>>*,
nn::gfx::BufferInfo const&,
nn::gfx::detail::MemoryPoolImpl<
nn::gfx::ApiVariation<nn::gfx::ApiType<4>, nn::gfx::ApiVersion<8>>>*,
s64, u64);
void Finalize(nn::gfx::detail::DeviceImpl<
nn::gfx::ApiVariation<nn::gfx::ApiType<4>, nn::gfx::ApiVersion<8>>>*);
void* Map() const;
void Unmap() const;
void FlushMappedRange(s64, u64) const;
void InvalidateMappedRange(s64, u64) const;
void GetGpuAddress(nn::gfx::GpuAddress*) const;
T* mBuff; // _0
};
template <typename T>
class CommandBufferImpl {
public:
CommandBufferImpl();
~CommandBufferImpl();
void Reset();
void Begin();
void End();
void Dispatch(s32, s32, s32);
};
}; // namespace detail
}; // namespace gfx
}; // namespace nn

View File

@ -0,0 +1,24 @@
/**
* @file deviceimpl.h
* @brief Device implementation for GFX.
*/
#pragma once
namespace nn {
namespace gfx {
class DeviceInfo;
namespace detail {
template <typename T>
class DeviceImpl {
public:
DeviceImpl();
~DeviceImpl();
void Initialize(nn::gfx::DeviceInfo const& deviceInfo);
void Finalize();
};
}; // namespace detail
}; // namespace gfx
}; // namespace nn

View File

@ -0,0 +1,41 @@
#pragma once
#include <nn/types.h>
namespace nn {
namespace gfx {
struct MemoryPoolInfo;
template <typename T>
struct DeviceImpl;
namespace detail {
class MemoryPoolData {
public:
void SetDefault();
s32 _0; // set to 0x88
s32 _4;
u64 _8;
};
template <typename T>
class MemoryPoolImpl {
public:
MemoryPoolImpl();
~MemoryPoolImpl();
void Initialize(nn::gfx::detail::DeviceImpl<
nn::gfx::ApiVariation<nn::gfx::ApiType<4>, nn::gfx::ApiVersion<8>>>*,
nn::gfx::MemoryPoolInfo const&);
void Finalize(nn::gfx::detail::DeviceImpl<
nn::gfx::ApiVariation<nn::gfx::ApiType<4>, nn::gfx::ApiVersion<8>>>*);
void* Map() const;
void Unmap() const;
void FlushMappedRange(s64, u64) const;
void InvalidateMappedRange(s64, u64) const;
u8 _0[0x120]; // pool data
};
}; // namespace detail
}; // namespace gfx
}; // namespace nn

View File

@ -0,0 +1,20 @@
/**
* @file device.h
* @brief Device implementation for GFX.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace gfx {
class DeviceInfo {
public:
u64 mInfo; // _0
};
template <typename T>
class TDevice {};
}; // namespace gfx
}; // namespace nn

View File

@ -0,0 +1,14 @@
/**
* @file memory.h
* @brief GFX Memory Pool.
*/
#pragma once
namespace nn {
namespace gfx {
// todo: finish me!
template <typename T>
class TMemoryPool {};
}; // namespace gfx
}; // namespace nn

View File

@ -0,0 +1,43 @@
/**
* @file hid.h
* @brief Functions that help process gamepad inputs.
*/
#pragma once
#include <nn/types.h>
#include <nn/util.h>
namespace nn {
namespace hid {
struct NpadHandheldState;
struct NpadStyleTag;
struct ControllerSupportArg {
u8 mMinPlayerCount;
u8 mMaxPlayerCount;
u8 mTakeOverConnection;
bool mLeftJustify;
bool mPermitJoyconDual;
bool mSingleMode;
bool mUseColors;
sead::Color4u8 mColors[4];
u8 mUsingControllerNames;
char mControllerNames[4][0x81];
};
struct ControllerSupportResultInfo {
int mPlayerCount;
int mSelectedId;
};
void InitializeNpad();
void SetSupportedNpadIdType(u32 const*, u64);
void SetSupportedNpadStyleSet(nn::util::BitFlagSet<32, nn::hid::NpadStyleTag>);
void GetNpadStyleSet(u32 const&);
void GetNpadStates(nn::hid::NpadHandheldState*, s32, u32 const&);
static int ShowControllerSupport(nn::hid::ControllerSupportResultInfo*,
ControllerSupportArg const&);
}; // namespace hid
}; // namespace nn

View File

@ -0,0 +1,50 @@
/**
* @file image.h
* @brief JPEG decoding library.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace image {
// there's probably more
enum JpegStatus {
OK = 0,
INVALID_FORMAT = -32,
UNSUPPORTED_FORMAT = -33,
OUT_OF_MEMORY = -64,
};
enum PixelFormat { RGBA32, RGB24 };
enum ProcessStage { UNREGISTERED = 0, REGISTERED = 1, ANALYZED = 2 };
struct Dimension {
f32 width;
f32 height;
};
class JpegDecoder {
public:
JpegDecoder();
virtual ~JpegDecoder();
void SetImageData(void const* source, u64 size);
nn::image::JpegStatus Analyze();
nn::image::Dimension GetAnalyzedDimension() const;
s64 GetAnalyzedWorkBufferSize() const;
JpegStatus Decode(void* out, s64, s32 alignment, void*, s64);
nn::image::ProcessStage mProcessStage; // _8
void* mData; // _C
s64 mSize; // _14
s32 _18;
nn::image::PixelFormat mFormat; // _1C
Dimension mImgDimensions; // _20
s64 _28;
// rest is related to EXIF processing
};
}; // namespace image
}; // namespace nn

View File

@ -0,0 +1,21 @@
/**
* @file init.h
* @brief Initialization functions for OS related functions.
*/
#pragma once
#include <nn/mem.h>
#include <nn/types.h>
namespace nn {
namespace init {
void InitializeAllocator(void* addr, u64 size);
nn::mem::StandardAllocator* GetAllocator();
namespace detail {
void* DefaultAllocatorForThreadLocal(u64, u64);
void* DefaultDeallocatorForThreadLocal(void*, u64);
}; // namespace detail
} // namespace init
}; // namespace nn

View File

@ -0,0 +1,30 @@
/**
* @file mem.h
* @brief Memory allocation functions.
*/
#pragma once
#include <nn/os.h>
#include <nn/types.h>
namespace nn {
namespace mem {
class StandardAllocator {
public:
StandardAllocator();
void Initialize(void* address, u64 size);
void Finalize();
void* Reallocate(void* address, u64 newSize);
void* Allocate(u64 size);
void Free(void* address);
void Dump();
bool mIsInitialized; // _0
bool mIsEnabledThreadCache; // _1
u16 _2;
u64* mAllocAddr; // _4
};
}; // namespace mem
}; // namespace nn

View File

@ -0,0 +1,30 @@
/**
* @file RootObject.h
* @brief RootObject for NEX.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace nex {
class RootObject {
public:
enum TargetPool {};
virtual ~RootObject();
void* operator new(u64);
void operator delete(void*);
void* operator new(u64, char const*, u32);
void* operator new[](u64);
void* operator new[](u64, char const*, u32);
void operator delete[](void*);
void operator delete(void*, char const*, u32);
void operator delete[](void*, char const*, u32);
void* operator new(u64, nn::nex::RootObject::TargetPool);
void* operator new(u64, nn::nex::RootObject::TargetPool, char const*, u32);
};
}; // namespace nex
}; // namespace nn

View File

@ -0,0 +1,20 @@
/**
* @file auth.h
* @brief Authorization for DDL.
*/
#pragma once
#include <nn/nex/ddl.h>
namespace nn {
namespace nex {
class NintendoAuthenticationDDLDeclarations : public nn::nex::DDLDeclarations {
public:
virtual ~NintendoAuthenticationDDLDeclarations();
virtual void Init();
void Register();
};
}; // namespace nex
}; // namespace nn

View File

@ -0,0 +1,25 @@
/**
* @file buffer.h
* @brief NEX buffer implementation.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace nex {
class String;
// todo
class Buffer {
public:
Buffer(nn::nex::Buffer const&);
Buffer(nn::nex::Buffer&&);
Buffer(nn::nex::String const&);
void FreeDataBuffer(u8*, u64);
virtual ~Buffer();
};
}; // namespace nex
}; // namespace nn

View File

@ -0,0 +1,33 @@
/**
* @file cache.h
* @brief NEX Cache Mangement.
*/
#pragma once
#include <nn/types.h>
namespace nn {
namespace nex {
class String;
class BasicCache;
class CacheManager {
public:
CacheManager();
~CacheManager();
nn::nex::BasicCache* GetCache(nn::nex::String const&);
};
class BasicCache {
public:
BasicCache(nn::nex::String const&);
virtual ~BasicCache();
u64 _8;
u8 _10;
};
}; // namespace nex
}; // namespace nn

View File

@ -0,0 +1,61 @@
/**
* @file checksum.h
* @brief NEX Checksum Implementation.
*/
#pragma once
#include <nn/nex/RootObject.h>
namespace nn {
namespace nex {
class Buffer;
class ChecksumAlgorithm : public nn::nex::RootObject {
public:
ChecksumAlgorithm();
virtual ~ChecksumAlgorithm();
virtual bool ComputeChecksum(nn::nex::Buffer const&, nn::nex::Buffer*) = 0;
// virtual bool ComputeChecksum(u8 const **, u64 const *, u64, nn::nex::SignatureBytes &) = 0;
virtual bool IsReady() const;
virtual void ComputeChecksumForTransport(u8 const*, u64);
virtual u32 ComputeChecksumForTransportArray(u8 const**, u64 const*, u64) = 0;
virtual u32 GetChecksumLength() = 0;
u64 _8;
u8 _10;
};
class MD5Checksum : public nn::nex::ChecksumAlgorithm {
public:
MD5Checksum();
virtual ~MD5Checksum();
virtual bool ComputeChecksum(nn::nex::Buffer const&, nn::nex::Buffer*);
virtual u32 ComputeChecksumForTransportArray(u8 const**, u64 const*, u64);
virtual u32 GetChecksumLength();
};
class CRC16Checksum : public nn::nex::ChecksumAlgorithm {
public:
CRC16Checksum();
virtual ~CRC16Checksum();
virtual bool ComputeChecksum(nn::nex::Buffer const&, nn::nex::Buffer*);
virtual u32 ComputeChecksumForTransportArray(u8 const**, u64 const*, u64);
virtual u32 GetChecksumLength();
};
/*
this actually inherits some sort of KeyedChecksum thing. whatever
class HMACChecksum : public nn::nex::ChecksumAlgorithm
{
};
*/
}; // namespace nex
}; // namespace nn

View File

@ -0,0 +1,111 @@
/**
* @file client.h
* @brief Client implementations for NEX.
*/
#pragma once
#include <nn/nex/system.h>
namespace nn {
namespace nex {
class Credentials;
class EndPoint;
class Message;
class ProtocolCallContext;
class ProtocolRequestBrokerInterface;
class Protocol : public nn::nex::SystemComponent {
public:
enum _Command { Response, Request };
enum _Type {
Client, // implemented in nn::nex::ClientProtocol
Server // implemented in nn::nex::ServerProtocol
};
Protocol(u32);
virtual ~Protocol();
virtual char* GetType() const;
virtual bool IsAKindOf(char const*) const;
virtual void EnforceDeclareSysComponentMacro();
virtual bool BeginInitialization();
virtual bool BeginTermination();
virtual nn::nex::Protocol::_Type GetProtocolType() const = 0;
virtual void EndPointDisconnected(nn::nex::EndPoint*);
virtual void FaultDetected(nn::nex::EndPoint*, u32);
virtual nn::nex::Protocol* Clone() const;
virtual bool Reload();
nn::nex::EndPoint* GetOutgoingConnection() const;
void SetIncomingConnection(nn::nex::EndPoint*);
void SetProtocolID(u16);
void AddMethodID(nn::nex::Message*, u32);
void CopyMembers(nn::nex::Protocol const*);
void AssociateProtocolRequestBroker(nn::nex::ProtocolRequestBrokerInterface*);
void ClearFlag(u32 newFlag);
static void ExtractProtocolKey(nn::nex::Message*, nn::nex::Protocol::_Command&, u16&);
static bool IsOldRVDDLVersion(nn::nex::EndPoint*);
u16 mProtocolID; // _48
u16 _4A;
u32 _4C;
nn::nex::EndPoint* mOutgoingConnection; // _50
nn::nex::ProtocolRequestBrokerInterface* mBrokerInterface; // _58
u32 mFlags; // _60
u32 _64;
nn::nex::EndPoint* mIncomingConnection; // _68
u32 mUseLoopback; // _70 (boolean)
u32 _74;
u64 _78;
u32 _80;
u32 _84;
};
class ClientProtocol : public nn::nex::Protocol {
public:
ClientProtocol(u32);
virtual ~ClientProtocol();
virtual char* GetType() const;
virtual bool IsAKindOf(char const*) const;
virtual void EnforceDeclareSysComponentMacro();
virtual nn::nex::Protocol::_Type GetProtocolType() const = 0;
virtual void ExtractCallSpecificResults(nn::nex::Message*, nn::nex::ProtocolCallContext*) = 0;
virtual nn::nex::ClientProtocol* CreateResponder() const = 0;
virtual void SetDefaultCredentials(nn::nex::Credentials*);
bool SendOverLocalLoopback(nn::nex::ProtocolCallContext*, nn::nex::Message*);
bool SendRMCMessage(nn::nex::ProtocolCallContext*, nn::nex::Message*);
void ProcessResponse(nn::nex::Message*, nn::nex::EndPoint*);
nn::nex::Credentials* mCredentials; // _88
};
class ServerProtocol : public nn::nex::Protocol {
public:
ServerProtocol(u32);
virtual ~ServerProtocol();
virtual char* GetType() const;
virtual bool IsAKindOf(char const*) const;
virtual void EnforceDeclareSysComponentMacro();
virtual nn::nex::Protocol::_Type GetProtocolType() const = 0;
virtual void DispatchProtocolMessage(nn::nex::Message*, nn::nex::Message*, bool*,
nn::nex::EndPoint*) = 0;
virtual void DispatchProtocolMessageWithAttemptCount(u64, nn::nex::Message*, nn::nex::Message*,
bool*, int*, nn::nex::EndPoint*);
virtual bool UseAttemptCountMethod();
};
}; // namespace nex
}; // namespace nn

View File

@ -0,0 +1,21 @@
/**
* @file data.h
* @brief NEX Data.
*/
#pragma once
#include <nn/nex/RootObject.h>
namespace nn {
namespace nex {
class Data : public nn::nex::RootObject {
public:
Data();
virtual ~Data();
u8 _8;
};
}; // namespace nex
}; // namespace nn

View File

@ -0,0 +1,40 @@
/**
* @file ddl.h
* @brief DDL Declaration Implementation.
*/
#pragma once
#include <nn/nex/RootObject.h>
namespace nn {
namespace nex {
class DDLDeclarations : public nn::nex::RootObject {
public:
DDLDeclarations(bool);
virtual ~DDLDeclarations();
virtual void Init() = 0;
void RegisterIfRequired();
void Unregister();
static void UnregisterAll();
void LoadAll();
void Load();
void UnloadAll();
void Unload();
void ResetDOClassIDs();
u32 mNumDecsLoaded; // _8
u8 DDLDeclarations_xC;
u8 _D; // padding
u8 _E; // ^^
u8 _F; // ^^
u64 _10;
bool _18;
static nn::nex::DDLDeclarations* s_pFirstDDLDecl;
};
}; // namespace nex
}; // namespace nn

Some files were not shown because too many files have changed in this diff Show More