mirror of https://github.com/zeldaret/botw.git
Initial commit
This commit is contained in:
commit
2de366be0f
|
@ -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
|
||||
...
|
|
@ -0,0 +1,28 @@
|
|||
__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
|
|
@ -0,0 +1,9 @@
|
|||
[submodule "sead"]
|
||||
path = lib/sead
|
||||
url = git@github.com:open-ead/sead
|
||||
[submodule "NintendoSDK"]
|
||||
path = lib/NintendoSDK
|
||||
url = git@github.com:open-ead/nnheaders
|
||||
[submodule "agl"]
|
||||
path = lib/agl
|
||||
url = git@github.com:open-ead/agl
|
|
@ -0,0 +1,67 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
project(uking CXX)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
add_compile_options(-fdiagnostics-color=always)
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
add_compile_options(-fcolor-diagnostics)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
add_executable(uking
|
||||
src/Game/Action/actionSetLinkTagBasic.cpp
|
||||
src/Game/Action/actionSetLinkTagBasic.h
|
||||
|
||||
src/KingSystem/ActorSystem/actActor.h
|
||||
src/KingSystem/ActorSystem/actActorCreator.cpp
|
||||
src/KingSystem/ActorSystem/actActorCreator.h
|
||||
src/KingSystem/ActorSystem/actActorFactory.cpp
|
||||
src/KingSystem/ActorSystem/actActorFactory.h
|
||||
src/KingSystem/ActorSystem/actActorLinkConstDataAccess.cpp
|
||||
src/KingSystem/ActorSystem/actActorLinkConstDataAccess.h
|
||||
src/KingSystem/ActorSystem/actAiAction.cpp
|
||||
src/KingSystem/ActorSystem/actAiAction.h
|
||||
src/KingSystem/ActorSystem/actAiClass.cpp
|
||||
src/KingSystem/ActorSystem/actAiClass.h
|
||||
src/KingSystem/ActorSystem/actAiParam.cpp
|
||||
src/KingSystem/ActorSystem/actAiParam.h
|
||||
src/KingSystem/ActorSystem/actBaseProc.cpp
|
||||
src/KingSystem/ActorSystem/actBaseProc.h
|
||||
src/KingSystem/ActorSystem/actBaseProcJob.cpp
|
||||
src/KingSystem/ActorSystem/actBaseProcJob.h
|
||||
src/KingSystem/ActorSystem/actBaseProcJobHandler.cpp
|
||||
src/KingSystem/ActorSystem/actBaseProcJobHandler.h
|
||||
src/KingSystem/ActorSystem/actBaseProcLinkDataMgr.cpp
|
||||
src/KingSystem/ActorSystem/actBaseProcLinkDataMgr.h
|
||||
src/KingSystem/ActorSystem/actBaseProcMap.cpp
|
||||
src/KingSystem/ActorSystem/actBaseProcMap.h
|
||||
src/KingSystem/ActorSystem/actBaseProcMgr.cpp
|
||||
src/KingSystem/ActorSystem/actBaseProcMgr.h
|
||||
src/KingSystem/ActorSystem/actBaseProcUnit.cpp
|
||||
src/KingSystem/ActorSystem/actBaseProcUnit.h
|
||||
|
||||
src/KingSystem/MessageSystem/mesTransceiver.h
|
||||
|
||||
src/KingSystem/Terrain/teraSystem.h
|
||||
|
||||
src/KingSystem/Utils/StrTreeMap.h
|
||||
src/KingSystem/Utils/Types.h
|
||||
)
|
||||
|
||||
target_include_directories(uking PRIVATE src/)
|
||||
target_compile_options(uking PRIVATE -fno-rtti -fno-exceptions)
|
||||
target_compile_options(uking PRIVATE -Wall -Wextra)
|
||||
target_compile_options(uking PRIVATE -fno-strict-aliasing)
|
||||
target_compile_options(uking PRIVATE -Wno-invalid-offsetof)
|
||||
|
||||
include_directories(lib/NintendoSDK/include)
|
||||
|
||||
add_subdirectory(lib/sead)
|
||||
target_link_libraries(uking PUBLIC sead)
|
||||
|
||||
add_subdirectory(lib/agl)
|
||||
target_link_libraries(uking PUBLIC agl)
|
|
@ -0,0 +1,82 @@
|
|||
# uking
|
||||
|
||||
This is a decompilation of U-King v1.5.0 (Switch).
|
||||
|
||||
File names, class or function names and the file organization come from leftover strings. Unlike some other first-party games such as *Super Mario Odyssey*, all known public versions of U-King are completely stripped, so most names are just more or less educated guesses.
|
||||
|
||||
Currently, the focus is on decompiling AI classes and other small, mostly self-contained components (e.g. LevelSensor).
|
||||
|
||||
## Building
|
||||
|
||||
Building this project requires:
|
||||
|
||||
- A C++17 capable compiler (or >= Clang 4.0)
|
||||
- CMake 3.10+
|
||||
|
||||
### Building a matching version for Switch
|
||||
|
||||
1. Download [Clang 4.0.1](https://releases.llvm.org/download.html#4.0.1) and extract the archive.
|
||||
2. Set the UKING_CLANG environment variable to point to the extracted archive, such that `$UKING_CLANG/bin/clang` exists.
|
||||
3. You'll also need devkitA64. Set the DEVKITA64 environment variable. For Linux, $DEVKITA64 is typically `/opt/devkitpro/devkitA64`.
|
||||
4. In the root of this repository, run: `mkdir build`
|
||||
5. `cd build`
|
||||
6. `cmake -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_TOOLCHAIN_FILE=../ToolchainNX64.cmake ..`
|
||||
7. `ninja` to start the build
|
||||
|
||||
On subsequent builds, just run `ninja -C build` from the project root.
|
||||
|
||||
## Contributing
|
||||
|
||||
Using a decompiler such as Hex-Rays or Ghidra is strongly recommended.
|
||||
|
||||
### Resources
|
||||
|
||||
* The [ZeldaMods](https://zeldamods.org/wiki/Main_Page) wiki
|
||||
* [MrCheeze's botw-tools](https://github.com/MrCheeze/botw-tools)
|
||||
* [botw-re-notes](https://github.com/leoetlino/botw-re-notes)
|
||||
|
||||
### Project tools
|
||||
|
||||
* To compare assembly: `./diff.py <mangled function name>`
|
||||
* The function must be listed in data/uking_functions.csv first.
|
||||
* [asm-differ](https://github.com/simonlindholm/asm-differ) must be in your $PATH, such that running `asm-differ` works.
|
||||
* Pass the `--source` flag to show source code interleaved with assembly code.
|
||||
* Add the `--inlines` flag to show inline function calls. This is not enabled by default because it usually produces too much output to be useful.
|
||||
* For more options, see [asm-differ](https://github.com/simonlindholm/asm-differ).
|
||||
* To print progress: `tools/progress.py`
|
||||
* Note that progress is only approximate because of inline functions, templating and compiler-generated functions.
|
||||
* To dump symbols: `tools/print_decomp_symbols.py`
|
||||
* `-u` for undefined symbols (default)
|
||||
* `-a` for all symbols
|
||||
* Useful for getting the mangled name of a function. For example:
|
||||
|
||||
```
|
||||
$ tools/print_decomp_symbols.py -a | grep BaseProcMgr::createInstance
|
||||
UNLISTED ksys::act::BaseProcMgr::createInstance(sead::Heap*) (_ZN4ksys3act11BaseProcMgr14createInstanceEPN4sead4HeapE)
|
||||
```
|
||||
|
||||
### Non-inlined functions
|
||||
When **implementing non-inlined functions**, please compare the assembly output against the original function and make it match the original code. At this scale, that is pretty much the only reliable way to ensure accuracy and functional equivalency.
|
||||
|
||||
However, given the large number of functions, certain kinds of small differences can be ignored when a function would otherwise be equivalent:
|
||||
|
||||
* Regalloc differences.
|
||||
|
||||
* Instruction reorderings when it is obvious the function is still semantically equivalent (e.g. two add/mov instructions that operate on entirely different registers being reordered)
|
||||
|
||||
When ignoring minor differences, add a `// NOT_MATCHING: explanation` comment and explain what does not match.
|
||||
|
||||
Finally, add the mangled name of each function you have decompiled to data/uking_functions.csv to make it easier to track progress.
|
||||
|
||||
### Header utilities or inlined functions
|
||||
For **header-only utilities** (like container classes), use pilot/debug builds, assertion messages and common sense to try to undo function inlining. For example, if you see the same assertion appear in many functions and the file name is a header file, or if you see identical snippets of code in many different places, chances are that you are dealing with an inlined function. In that case, you should refactor the inlined code into its own function.
|
||||
|
||||
Also note that introducing inlined functions is sometimes necessary to get the desired codegen.
|
||||
|
||||
If a function is inlined, you should try as hard as possible to make it match perfectly. For inlined functions, it is better to use weird code or small hacks to force a match as differences would otherwise appear in every single function that inlines the non-matching code, which drastically complicates matching other functions. If a hack is used, wrap it inside a `#ifdef MATCHING_HACK_{PLATFORM}` (see below for a list of defines).
|
||||
|
||||
### Matching hacks
|
||||
|
||||
This project sometimes uses small hacks to force particular code to be generated by the compiler. Those have no semantic effects but can help with matching assembly code especially when the hacks are used for functions that are inlined.
|
||||
|
||||
* `MATCHING_HACK_NX_CLANG`: Hacks for Switch, when compiling with Clang.
|
|
@ -0,0 +1,42 @@
|
|||
if (NOT DEFINED ENV{UKING_CLANG})
|
||||
message(FATAL_ERROR "Please define the UKING_CLANG env variable. It should point to a path such that $UKING_CLANG/bin/clang exists")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED ENV{DEVKITA64})
|
||||
message(FATAL_ERROR "Please define the DEVKITA64 env variable.")
|
||||
endif()
|
||||
|
||||
set(UKING_CLANG "$ENV{UKING_CLANG}")
|
||||
set(DEVKITA64 "$ENV{DEVKITA64}")
|
||||
set(NX64_OPT_FLAGS "-O3 -g")
|
||||
set(triple aarch64-none-elf)
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
|
||||
set(CMAKE_SYSROOT ${UKING_CLANG})
|
||||
set(CMAKE_C_COMPILER "${UKING_CLANG}/bin/clang")
|
||||
set(CMAKE_C_COMPILER_TARGET ${triple})
|
||||
set(CMAKE_CXX_COMPILER "${UKING_CLANG}/bin/clang++")
|
||||
set(CMAKE_CXX_COMPILER_TARGET ${triple})
|
||||
|
||||
set(CMAKE_C_FLAGS_RELEASE ${NX64_OPT_FLAGS})
|
||||
set(CMAKE_CXX_FLAGS_RELEASE ${NX64_OPT_FLAGS})
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO ${NX64_OPT_FLAGS})
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO ${NX64_OPT_FLAGS})
|
||||
|
||||
set(ARCH "-mcpu=cortex-a57+fp+simd+crypto+crc")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ARCH} -isystem ${DEVKITA64}/aarch64-none-elf/include")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${UKING_CLANG}/include/c++/v1 -D _LIBCPP_HAS_THREAD_API_PTHREAD ${CMAKE_C_FLAGS}")
|
||||
set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -x assembler-with-cpp ${ARCH}")
|
||||
|
||||
add_compile_options(-fPIC -fno-vectorize -fno-slp-vectorize -stdlib=libc++ -mno-implicit-float)
|
||||
add_link_options(-fPIC -Wl,-Bsymbolic-functions -shared -nodefaultlibs)
|
||||
add_definitions(-D SWITCH -D __DEVKITA64__ -D __ELF__)
|
||||
add_definitions(-D NNSDK)
|
||||
add_definitions(-D MATCHING_HACK_NX_CLANG)
|
||||
|
||||
# Helps with matching as this causes Clang to emit debug type info even for dynamic classes
|
||||
# with undefined vtables.
|
||||
add_compile_options(-fstandalone-debug)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
|||
tools/diff_settings.py
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 583c350e47ed5f1bf3d257a7158859e3f1fc0845
|
|
@ -0,0 +1 @@
|
|||
Subproject commit d4a06627f75334555dec5a91637919535a27342c
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 8f9642d8d08a75d2065551173d3817e1afef0a4c
|
|
@ -0,0 +1,24 @@
|
|||
#include "Game/Action/actionSetLinkTagBasic.h"
|
||||
#include "KingSystem/ActorSystem/actActor.h"
|
||||
|
||||
namespace uking::action {
|
||||
|
||||
SetLinkTagBasicAction::SetLinkTagBasicAction(const ksys::act::ai::ClassArg& arg)
|
||||
: ksys::act::ai::Action(arg) {}
|
||||
|
||||
SetLinkTagBasicAction::~SetLinkTagBasicAction() = default;
|
||||
|
||||
void SetLinkTagBasicAction::enter() {
|
||||
if (IsOn.value())
|
||||
mActor->emitBasicSigOn();
|
||||
else
|
||||
mActor->emitBasicSigOff();
|
||||
|
||||
setFinished();
|
||||
}
|
||||
|
||||
void SetLinkTagBasicAction::loadParams() {
|
||||
getParamStatic(&IsOn, "IsOn");
|
||||
}
|
||||
|
||||
} // namespace uking::action
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "KingSystem/ActorSystem/actAiAction.h"
|
||||
#include "KingSystem/ActorSystem/actAiParam.h"
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace uking::action {
|
||||
|
||||
class SetLinkTagBasicAction : public ksys::act::ai::Action {
|
||||
public:
|
||||
SetLinkTagBasicAction(const ksys::act::ai::ClassArg& arg);
|
||||
~SetLinkTagBasicAction() override;
|
||||
|
||||
void enter() override;
|
||||
void loadParams() override;
|
||||
|
||||
private:
|
||||
ksys::act::ai::ParamRef<bool> IsOn;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(SetLinkTagBasicAction, 0x28);
|
||||
|
||||
} // namespace uking::action
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "KingSystem/ActorSystem/actBaseProc.h"
|
||||
|
||||
namespace ksys {
|
||||
|
||||
namespace act {
|
||||
|
||||
class Actor : public BaseProc {
|
||||
public:
|
||||
Actor(); // FIXME
|
||||
~Actor() override;
|
||||
|
||||
void emitBasicSigOn();
|
||||
void emitBasicSigOff();
|
||||
};
|
||||
|
||||
} // namespace act
|
||||
|
||||
} // namespace ksys
|
|
@ -0,0 +1,7 @@
|
|||
#include "KingSystem/ActorSystem/actActorCreator.h"
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
SEAD_SINGLETON_DISPOSER_IMPL(ActorCreator)
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include <container/seadOffsetList.h>
|
||||
#include <heap/seadDisposer.h>
|
||||
#include <prim/seadDelegate.h>
|
||||
#include <thread/seadCriticalSection.h>
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
class Actor;
|
||||
struct ActorCreateArg;
|
||||
class ActorFactory;
|
||||
|
||||
class ActorCreator {
|
||||
SEAD_SINGLETON_DISPOSER(ActorCreator)
|
||||
ActorCreator();
|
||||
virtual ~ActorCreator();
|
||||
|
||||
public:
|
||||
void setActorFactory(ActorFactory* factory) { mActorFactory = factory; }
|
||||
|
||||
private:
|
||||
sead::Heap* forBaseProcDualHeap;
|
||||
sead::Heap* placementMgrHeap;
|
||||
sead::OffsetList<Actor> mActorList;
|
||||
void* _50;
|
||||
bool mEnableDistanceChecks;
|
||||
bool _59;
|
||||
bool _5a;
|
||||
void* _60;
|
||||
sead::Delegate1<ActorCreator, ActorCreateArg&> mCreateActorDelegate;
|
||||
// FIXME: argument type
|
||||
sead::Delegate1<ActorCreator, void*> mCleanUpDelegate;
|
||||
sead::CriticalSection mActorListCS;
|
||||
ActorFactory* mActorFactory;
|
||||
};
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,21 @@
|
|||
#include "KingSystem/ActorSystem/actActorFactory.h"
|
||||
#include "KingSystem/ActorSystem/actActorCreator.h"
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
SEAD_SINGLETON_DISPOSER_IMPL(ActorFactoryHolder)
|
||||
|
||||
void ActorFactory::dummy() {}
|
||||
|
||||
void ActorFactory::dummy2() {}
|
||||
|
||||
ActorFactoryHolder::~ActorFactoryHolder() {
|
||||
ActorCreator::deleteInstance();
|
||||
}
|
||||
|
||||
void ActorFactoryHolder::init(sead::Heap* heap) {
|
||||
ActorCreator::createInstance(heap);
|
||||
ActorCreator::instance()->setActorFactory(&mFactory);
|
||||
}
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <heap/seadDisposer.h>
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
struct ActorCreateArg;
|
||||
class BaseProc;
|
||||
|
||||
class ActorFactory {
|
||||
public:
|
||||
virtual void dummy();
|
||||
virtual BaseProc* createActor(const ActorCreateArg& arg);
|
||||
virtual void dummy2();
|
||||
};
|
||||
|
||||
class ActorFactoryHolder {
|
||||
SEAD_SINGLETON_DISPOSER(ActorFactoryHolder)
|
||||
virtual ~ActorFactoryHolder();
|
||||
|
||||
public:
|
||||
void init(sead::Heap* heap);
|
||||
|
||||
private:
|
||||
ActorFactory mFactory;
|
||||
};
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
namespace ksys {
|
||||
|
||||
namespace act {
|
||||
|
||||
class BaseProc;
|
||||
|
||||
/// Provides read-only access to actor data for safe, multi-threaded access.
|
||||
class ActorLinkConstDataAccess {
|
||||
public:
|
||||
~ActorLinkConstDataAccess();
|
||||
|
||||
private:
|
||||
bool mAcquired = false;
|
||||
BaseProc* mProc = nullptr;
|
||||
};
|
||||
|
||||
} // namespace act
|
||||
|
||||
} // namespace ksys
|
|
@ -0,0 +1,7 @@
|
|||
#include "KingSystem/ActorSystem/actAiAction.h"
|
||||
|
||||
namespace ksys::act::ai {
|
||||
|
||||
Action::Action(const ClassArg& arg) : ActionBase(arg) {}
|
||||
|
||||
} // namespace ksys::act::ai
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "KingSystem/ActorSystem/actAiClass.h"
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace ksys::act::ai {
|
||||
|
||||
class Action : public ActionBase {
|
||||
public:
|
||||
Action(const ClassArg& arg);
|
||||
|
||||
virtual void enter() {}
|
||||
virtual void loadParams() {}
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(Action, 0x20);
|
||||
|
||||
} // namespace ksys::act::ai
|
|
@ -0,0 +1,8 @@
|
|||
#include "KingSystem/ActorSystem/actAiClass.h"
|
||||
|
||||
namespace ksys::act::ai {
|
||||
|
||||
ActionBase::ActionBase(const ClassArg& arg)
|
||||
: mActor{arg.actor}, mDefinitionIdx{u16(arg.definitionIdx)}, mRootIdx{u8(arg.rootIdx)} {}
|
||||
|
||||
} // namespace ksys::act::ai
|
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include <basis/seadTypes.h>
|
||||
|
||||
#include "KingSystem/ActorSystem/actAiParam.h"
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
class Actor;
|
||||
|
||||
namespace ai {
|
||||
|
||||
struct ClassArg {
|
||||
Actor* actor;
|
||||
u32 definitionIdx;
|
||||
u32 rootIdx;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(ClassArg, 0x10);
|
||||
|
||||
class ActionBase {
|
||||
public:
|
||||
ActionBase(const ClassArg& arg);
|
||||
virtual ~ActionBase() = default;
|
||||
|
||||
protected:
|
||||
void setFinished();
|
||||
|
||||
template <typename T>
|
||||
void getParamStatic(ParamRef<T>* value, const sead::SafeString& key);
|
||||
|
||||
Actor* mActor;
|
||||
ParamPack mParams;
|
||||
u16 mDefinitionIdx;
|
||||
u8 mRootIdx;
|
||||
u8 mStatus = 0;
|
||||
u16 mClassIdx;
|
||||
u16 mPrevClassIdx;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(ActionBase, 0x20);
|
||||
|
||||
} // namespace ai
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,110 @@
|
|||
#include "KingSystem/ActorSystem/actAiParam.h"
|
||||
|
||||
namespace ksys::act::ai {
|
||||
|
||||
ParamPack::ParamPack() = default;
|
||||
|
||||
ParamPack::~ParamPack() {
|
||||
auto* param = mParams;
|
||||
while (param) {
|
||||
if (param->data) {
|
||||
switch (Param::Type(param->type)) {
|
||||
case Param::Type::String:
|
||||
delete static_cast<sead::SafeString*>(param->data);
|
||||
break;
|
||||
case Param::Type::Int:
|
||||
delete static_cast<int*>(param->data);
|
||||
break;
|
||||
case Param::Type::Float:
|
||||
delete static_cast<float*>(param->data);
|
||||
break;
|
||||
case Param::Type::Vec3:
|
||||
delete static_cast<sead::Vector3f*>(param->data);
|
||||
break;
|
||||
case Param::Type::Bool:
|
||||
delete static_cast<bool*>(param->data);
|
||||
break;
|
||||
case Param::Type::AITreeVariablePointer:
|
||||
delete static_cast<void**>(param->data);
|
||||
break;
|
||||
case Param::Type::UInt:
|
||||
delete static_cast<u32*>(param->data);
|
||||
break;
|
||||
case Param::Type::MesTransceiverId:
|
||||
delete static_cast<mes::TransceiverId*>(param->data);
|
||||
break;
|
||||
case Param::Type::BaseProcHandle:
|
||||
delete static_cast<BaseProcHandle**>(param->data);
|
||||
break;
|
||||
case Param::Type::Rail:
|
||||
delete static_cast<Rail**>(param->data);
|
||||
break;
|
||||
case Param::Type::BaseProcLink:
|
||||
delete static_cast<BaseProcLink*>(param->data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
param = mParams->next;
|
||||
if (mParams)
|
||||
delete mParams;
|
||||
mParams = param;
|
||||
}
|
||||
}
|
||||
|
||||
void InlineParamPack::copyToParamPack(ParamPack& pack) const {
|
||||
const u32 n = count >= 0x1F ? 0x1F : count;
|
||||
const InlineParam* param = params;
|
||||
for (u32 i = 0; i < n; ++i, ++param) {
|
||||
switch (param->type) {
|
||||
case Param::Type::String: {
|
||||
const sead::SafeString src = param->cstr;
|
||||
const sead::SafeString key = param->key;
|
||||
auto* dst = pack.getVariable<sead::BufferedSafeString>(key, Param::Type::String);
|
||||
if (dst)
|
||||
dst->copy(src);
|
||||
break;
|
||||
}
|
||||
case Param::Type::Int:
|
||||
pack.setVariable(param->key, Param::Type::Int, param->i);
|
||||
break;
|
||||
case Param::Type::Float:
|
||||
pack.setVariable(param->key, Param::Type::Float, param->f);
|
||||
break;
|
||||
case Param::Type::Vec3:
|
||||
if (auto* dst = pack.getVariable<sead::Vector3f>(param->key, Param::Type::Vec3)) {
|
||||
dst->x = param->vec3.x;
|
||||
dst->y = param->vec3.y;
|
||||
dst->z = param->vec3.z;
|
||||
}
|
||||
break;
|
||||
case Param::Type::Bool:
|
||||
pack.setVariable(param->key, Param::Type::Bool, param->b);
|
||||
break;
|
||||
case Param::Type::UInt:
|
||||
pack.setVariable(param->key, Param::Type::UInt, param->u);
|
||||
break;
|
||||
case Param::Type::BaseProcLink:
|
||||
pack.setVariable(param->key, Param::Type::BaseProcLink, param->baseProcLink);
|
||||
break;
|
||||
case Param::Type::MesTransceiverId:
|
||||
pack.setVariable(param->key, Param::Type::MesTransceiverId, param->mesTransceiverId);
|
||||
break;
|
||||
case Param::Type::BaseProcHandle: {
|
||||
auto* baseProcHandle = static_cast<BaseProcHandle*>(param->ptr);
|
||||
pack.setVariable(param->key, Param::Type::BaseProcHandle, baseProcHandle);
|
||||
break;
|
||||
}
|
||||
case Param::Type::Rail: {
|
||||
auto* rail = static_cast<Rail*>(param->ptr);
|
||||
pack.setVariable(param->key, Param::Type::Rail, rail);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ksys::act::ai
|
|
@ -0,0 +1,113 @@
|
|||
#pragma once
|
||||
|
||||
#include <agl/Utils/aglParameter.h>
|
||||
#include <basis/seadTypes.h>
|
||||
#include <math/seadVector.h>
|
||||
#include <prim/seadSafeString.h>
|
||||
|
||||
#include "KingSystem/ActorSystem/actBaseProc.h"
|
||||
#include "KingSystem/MessageSystem/mesTransceiver.h"
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace ksys {
|
||||
|
||||
class Rail;
|
||||
|
||||
namespace act::ai {
|
||||
|
||||
struct Param {
|
||||
enum class Type {
|
||||
String = 0,
|
||||
Int = 1,
|
||||
Float = 2,
|
||||
Vec3 = 3,
|
||||
Bool = 4,
|
||||
Tree = 5,
|
||||
AITreeVariablePointer = 6,
|
||||
UInt = 7,
|
||||
BaseProcLink = 8,
|
||||
MesTransceiverId = 9,
|
||||
BaseProcHandle = 10,
|
||||
Rail = 11,
|
||||
Other = 12,
|
||||
};
|
||||
|
||||
Param* next;
|
||||
u32 hash;
|
||||
const char* name;
|
||||
void* data;
|
||||
u16 type;
|
||||
bool used;
|
||||
u8 _23;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(Param, 0x28);
|
||||
|
||||
class ParamPack {
|
||||
public:
|
||||
ParamPack();
|
||||
~ParamPack();
|
||||
|
||||
template <typename T>
|
||||
T* getVariable(const sead::SafeString& key, Param::Type type, bool a4 = true) const {
|
||||
const u32 hash = agl::utl::ParameterBase::calcHash(key);
|
||||
auto* param = mParams;
|
||||
if (!param)
|
||||
return nullptr;
|
||||
while (param->hash != hash || Param::Type(param->type) != type) {
|
||||
param = param->next;
|
||||
if (!param)
|
||||
return nullptr;
|
||||
}
|
||||
if (a4)
|
||||
param->used = true;
|
||||
return static_cast<T*>(param->data);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void setVariable(const sead::SafeString& key, Param::Type type, const T& val) const {
|
||||
T* variable = getVariable<T>(key, type, true);
|
||||
if (variable)
|
||||
*variable = val;
|
||||
}
|
||||
|
||||
private:
|
||||
Param* mParams = nullptr;
|
||||
};
|
||||
|
||||
struct InlineParam {
|
||||
union {
|
||||
bool b;
|
||||
int i;
|
||||
u32 u;
|
||||
float f;
|
||||
const char* cstr;
|
||||
void* ptr;
|
||||
};
|
||||
BaseProcLink baseProcLink;
|
||||
sead::Vector3f vec3;
|
||||
mes::TransceiverId mesTransceiverId;
|
||||
Param::Type type;
|
||||
const char* key;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(InlineParam, 0x50);
|
||||
|
||||
struct InlineParamPack {
|
||||
void copyToParamPack(ParamPack& pack) const;
|
||||
|
||||
InlineParam params[32];
|
||||
int count;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(InlineParamPack, 0xA08);
|
||||
|
||||
template <typename T>
|
||||
class ParamRef {
|
||||
public:
|
||||
const T& value() const { return *mValue; }
|
||||
void setValuePtr(const T* ptr) { mValue = ptr; }
|
||||
|
||||
private:
|
||||
const T* mValue = nullptr;
|
||||
};
|
||||
} // namespace act::ai
|
||||
|
||||
} // namespace ksys
|
|
@ -0,0 +1,409 @@
|
|||
#include "KingSystem/ActorSystem/actBaseProc.h"
|
||||
#include "KingSystem/ActorSystem/actBaseProcJobHandler.h"
|
||||
#include "KingSystem/ActorSystem/actBaseProcLinkDataMgr.h"
|
||||
#include "KingSystem/ActorSystem/actBaseProcMgr.h"
|
||||
#include "KingSystem/ActorSystem/actBaseProcUnit.h"
|
||||
#include "KingSystem/Terrain/teraSystem.h"
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
BaseProcLink::BaseProcLink() = default;
|
||||
|
||||
BaseProc::BaseProc(const CreateArg& arg)
|
||||
: mName(arg.actor_name), mPriority(arg.class_info->priority) {
|
||||
BaseProcMgr* mgr = BaseProcMgr::instance();
|
||||
mgr->generateProcId(&mId);
|
||||
mgr->registerProc(*this);
|
||||
|
||||
mJobHandlers.fill(nullptr);
|
||||
|
||||
BaseProcLinkDataMgr::instance()->acquireLink(this);
|
||||
}
|
||||
|
||||
BaseProcMapNode::~BaseProcMapNode() = default;
|
||||
|
||||
BaseProc::~BaseProc() {
|
||||
unlinkCalcChild_();
|
||||
unlinkCalcParent_();
|
||||
|
||||
if (mDeleteListNode.isLinked())
|
||||
BaseProcMgr::instance()->eraseFromUpdateStateList(*this);
|
||||
}
|
||||
|
||||
bool BaseProc::init(sead::Heap* heap, bool sleep_after_init) {
|
||||
InitContext context;
|
||||
context.sleep_after_init = sleep_after_init;
|
||||
context.result = InitResult::Skipped;
|
||||
|
||||
if (shouldInit_()) {
|
||||
PrepareArg arg;
|
||||
if (prepareInit_(heap, arg)) {
|
||||
if (context.result != InitResult::Failed)
|
||||
context.result = init_();
|
||||
} else {
|
||||
context.result = InitResult::Failed;
|
||||
}
|
||||
}
|
||||
|
||||
finalizeInit_(&context);
|
||||
return context.result == InitResult::Ok && mStateFlags.isOff(StateFlags::RequestDelete);
|
||||
}
|
||||
|
||||
bool BaseProc::deleteLater(DeleteReason reason) {
|
||||
if (isDeletedOrDeleting())
|
||||
return false;
|
||||
|
||||
// Debug leftovers?
|
||||
mName.cstr();
|
||||
mName.cstr();
|
||||
|
||||
BaseProcMgr* mgr = BaseProcMgr::instance();
|
||||
const bool is_high_prio = mgr->isHighPriorityThread();
|
||||
|
||||
if (!is_high_prio) {
|
||||
mgr->getProcUpdateStateListCS().lock();
|
||||
if (isDeletedOrDeleting()) {
|
||||
mgr->getProcUpdateStateListCS().unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!BaseProcMgr::instance()->addToUpdateStateList(*this))
|
||||
onDeleteRequested_(reason);
|
||||
|
||||
if (!is_high_prio)
|
||||
mgr->getProcUpdateStateListCS().unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BaseProc::InitResult BaseProc::init_() {
|
||||
return InitResult::Ok;
|
||||
}
|
||||
|
||||
bool BaseProc::shouldInit_() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseProc::finalizeInit_(InitContext* context) {
|
||||
if (mState == State::Delete)
|
||||
return;
|
||||
|
||||
if (context->result != InitResult::Ok) {
|
||||
deleteLater(DeleteReason::_1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mProcUnit) {
|
||||
if (context->sleep_after_init)
|
||||
sleep(SleepWakeReason::_0);
|
||||
else
|
||||
wakeUp(SleepWakeReason::_0);
|
||||
} else {
|
||||
sleep(SleepWakeReason::_0);
|
||||
if (!mProcUnit->setProc(this))
|
||||
deleteLater(DeleteReason::_2);
|
||||
}
|
||||
}
|
||||
|
||||
BaseProc::PreDeletePrepareResult BaseProc::prepareForPreDelete_() {
|
||||
return PreDeletePrepareResult::Done;
|
||||
}
|
||||
|
||||
// NON_MATCHING: branching
|
||||
bool BaseProc::startPreparingForPreDelete_() {
|
||||
if (mDeleteListNode.isLinked())
|
||||
return false;
|
||||
|
||||
return !mBaseProcLinkData || mBaseProcLinkData->refCount() <= 0 ||
|
||||
BaseProcMgr::instance()->getUnk3() || tera::checkTeraSystemStatus();
|
||||
}
|
||||
|
||||
void BaseProc::destruct_(int should_destruct) {
|
||||
if (should_destruct == 1) {
|
||||
BaseProcMgr::instance()->eraseFromPreDeleteList(*this);
|
||||
BaseProcMgr::instance()->unregisterProc(*this);
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wundefined-bool-conversion"
|
||||
// Yes, this check looks strange.
|
||||
if (this)
|
||||
delete this;
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
void BaseProc::onEnterCalc_() {}
|
||||
|
||||
void BaseProc::onDeleteRequested_(BaseProc::DeleteReason) {}
|
||||
|
||||
void BaseProc::onSleepRequested_(BaseProc::SleepWakeReason) {}
|
||||
|
||||
void BaseProc::onWakeUpRequested_(BaseProc::SleepWakeReason) {}
|
||||
|
||||
void BaseProc::onEnterDelete_() {}
|
||||
|
||||
void BaseProc::onEnterSleep_() {}
|
||||
|
||||
void BaseProc::preDelete3_(bool*) {}
|
||||
|
||||
bool BaseProc::prepareInit_(sead::Heap*, BaseProc::PrepareArg&) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseProc::onPreDeleteStart_(PrepareArg&) {}
|
||||
|
||||
void BaseProc::preDelete2_(bool*) {}
|
||||
|
||||
void BaseProc::preDelete1_() {}
|
||||
|
||||
// NON_MATCHING: branching
|
||||
bool BaseProc::isSpecialJobType_(JobType type) {
|
||||
return BaseProcMgr::instance()->isSpecialJobType(type) || isSpecialJobTypeForThisActor_(type);
|
||||
}
|
||||
|
||||
bool BaseProc::canWakeUp_() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseProc::queueExtraJobPush_(JobType type) {
|
||||
if (!isDeletedOrDeleting())
|
||||
BaseProcMgr::instance()->queueExtraJobPush(&mJobHandlers[int(type)]->getLink());
|
||||
}
|
||||
|
||||
bool BaseProc::hasJobType_(JobType type) {
|
||||
return mJobHandlers[int(type)] != nullptr;
|
||||
}
|
||||
|
||||
void BaseProc::afterUpdateState_() {
|
||||
mFlags.reset(Flags::_80);
|
||||
mFlags.reset(Flags::_100);
|
||||
}
|
||||
|
||||
bool BaseProc::shouldSkipJobPush_(JobType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void BaseProc::onJobPush1_(JobType) {}
|
||||
|
||||
void BaseProc::onJobPush2_(JobType) {}
|
||||
|
||||
// NON_MATCHING: branching
|
||||
bool BaseProc::processStateUpdate(u8 counter) {
|
||||
const bool delete_requested = mStateFlags.isOn(StateFlags::RequestDelete);
|
||||
const bool initialized = mFlags.isOn(Flags::Initialized);
|
||||
|
||||
if (delete_requested) {
|
||||
// Deletion cannot start until the reference count is 0.
|
||||
if (initialized && mRefCount.compareExchange(0, -1)) {
|
||||
if (mState != State::Delete) {
|
||||
BaseProcMgr::instance()->eraseJobs(*this);
|
||||
BaseProcMgr::instance()->addToPreDeleteList(*this);
|
||||
startDelete_();
|
||||
}
|
||||
mStateFlags.makeAllZero();
|
||||
return true;
|
||||
}
|
||||
// Try the deletion again later.
|
||||
mStateFlags.setDirect(StateFlags::RequestDelete);
|
||||
return false;
|
||||
}
|
||||
|
||||
sead::TypedBitFlag<StateFlags> new_flags{mStateFlags};
|
||||
|
||||
if (initialized) {
|
||||
if (new_flags.isOn(StateFlags::RequestDeleteProcUnit))
|
||||
unlinkProcUnit_();
|
||||
|
||||
if (mStateFlags.isOn(StateFlags::_4000)) {
|
||||
if (shouldClearStateFlag4000_())
|
||||
new_flags.makeAllZero();
|
||||
else
|
||||
new_flags.setDirect(StateFlags::_4000);
|
||||
} else {
|
||||
new_flags.makeAllZero();
|
||||
}
|
||||
|
||||
const auto check_calc_sleep = [&] {
|
||||
const bool sleep_requested = mStateFlags.isOn(StateFlags::RequestSleep);
|
||||
const State state = mState;
|
||||
if (sleep_requested) {
|
||||
handleSleepRequest_();
|
||||
if (mStateFlags.isAnyOn({StateFlags::RequestWakeUp, StateFlags::_2})) {
|
||||
new_flags.setDirect(mStateFlags.getDirect() &
|
||||
(u32(StateFlags::RequestWakeUp) | u32(StateFlags::_2)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const sead::TypedBitFlag<StateFlags> flags{mStateFlags};
|
||||
|
||||
if (state == State::Calc) {
|
||||
if (flags.isOn(StateFlags::RequestChangeCalcJobPriority))
|
||||
handleJobPriorityChangeRequest_();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!flags.isAnyOn({StateFlags::RequestWakeUp, StateFlags::_2}))
|
||||
return true;
|
||||
|
||||
if (canWakeUpOrFlagsSet_()) {
|
||||
handleWakeUpRequest_();
|
||||
return true;
|
||||
}
|
||||
|
||||
return mStateFlags.isOn(StateFlags::RequestSleep);
|
||||
};
|
||||
|
||||
if (!check_calc_sleep()) {
|
||||
new_flags.setDirect(mStateFlags.getDirect() &
|
||||
(u32(StateFlags::RequestWakeUp) | u32(StateFlags::_2)));
|
||||
}
|
||||
|
||||
if (mStateFlags.isOn(StateFlags::RequestDelete)) {
|
||||
new_flags.set(StateFlags::RequestDelete);
|
||||
mStateFlags.setDirect(new_flags.getDirect());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mStateFlags.isOn(StateFlags::RequestSleep))
|
||||
handleSleepRequest_();
|
||||
|
||||
if (mStateFlags.isOn(StateFlags::RequestResetChild))
|
||||
unlinkCalcChild_();
|
||||
|
||||
if (mStateFlags.isOn(StateFlags::RequestResetParent))
|
||||
unlinkCalcParent_();
|
||||
|
||||
if (mStateFlags.isOn(StateFlags::RequestSetChild))
|
||||
loadNewCalcChild_(counter);
|
||||
|
||||
if (mStateFlags.isOn(StateFlags::RequestSetParent))
|
||||
loadNewCalcParent_(counter);
|
||||
}
|
||||
|
||||
const bool ret = new_flags.isZero();
|
||||
mStateFlags = new_flags;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BaseProc::processPreDelete() {
|
||||
if (!mFlags.isOn(Flags::Initialized))
|
||||
return;
|
||||
|
||||
if (mFlags.isOn(Flags::PreDeleteFailed)) {
|
||||
// Try again.
|
||||
if (BaseProcMgr::instance()->requestPreDelete(*this)) {
|
||||
mFlags.set(Flags::PreDeleteStarted);
|
||||
mFlags.reset(Flags::PreDeleteFailed);
|
||||
}
|
||||
|
||||
} else if (!mFlags.isOn(Flags::PreDeleteStarted) && startPreparingForPreDelete_()) {
|
||||
mFlags.set(Flags::PreDeleting);
|
||||
if (prepareForPreDelete_() == PreDeletePrepareResult::Done) {
|
||||
BaseProcLinkDataMgr::instance()->releaseLink(this);
|
||||
PrepareArg arg;
|
||||
onPreDeleteStart_(arg);
|
||||
|
||||
if (BaseProcMgr::instance()->requestPreDelete(*this))
|
||||
mFlags.set(Flags::PreDeleteStarted);
|
||||
else
|
||||
mFlags.set(Flags::PreDeleteFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BaseProc::doPreDelete(bool* do_not_destruct_immediately) {
|
||||
preDelete1_();
|
||||
preDelete2_(do_not_destruct_immediately);
|
||||
preDelete3_(do_not_destruct_immediately);
|
||||
|
||||
if (*do_not_destruct_immediately)
|
||||
return;
|
||||
|
||||
mFlags.set(Flags::Destructed);
|
||||
destruct_(1);
|
||||
BaseProcMgr::instance()->decrementPendingDeletions();
|
||||
}
|
||||
|
||||
void BaseProc::unlinkProcUnit_() {
|
||||
if (mProcUnit) {
|
||||
mProcUnit->unlinkProc(this);
|
||||
mProcUnit = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseProc::unlinkCalcChild_() {
|
||||
if (mConnectedCalcChild) {
|
||||
mConnectedCalcChild->mConnectedCalcParent = nullptr;
|
||||
mConnectedCalcChild = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseProc::unlinkCalcParent_() {
|
||||
auto parent = mConnectedCalcParent;
|
||||
if (parent && parent->mConnectedCalcChild) {
|
||||
parent->mConnectedCalcChild->mConnectedCalcParent = nullptr;
|
||||
parent->mConnectedCalcChild = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseProc::loadNewCalcChild_(u8 counter) {
|
||||
auto child = mConnectedCalcChildNew;
|
||||
if (child && !child->isDeletedOrDeleting() && !mConnectedCalcChild &&
|
||||
!child->mConnectedCalcParent && !isDeletedOrDeleting() &&
|
||||
child->mStateFlags.isOff(StateFlags::RequestDelete)) {
|
||||
mConnectedCalcChild = child;
|
||||
child->mConnectedCalcParent = this;
|
||||
mCounter = counter;
|
||||
}
|
||||
mConnectedCalcChildNew = nullptr;
|
||||
}
|
||||
|
||||
void BaseProc::loadNewCalcParent_(u8 counter) {
|
||||
auto parent = mConnectedCalcParentNew;
|
||||
if (parent && !parent->isDeletedOrDeleting() && !parent->mConnectedCalcChild &&
|
||||
!mConnectedCalcParent && parent->mStateFlags.isOff(StateFlags::RequestDelete) &&
|
||||
!isDeletedOrDeleting()) {
|
||||
parent->mConnectedCalcChild = this;
|
||||
mConnectedCalcParent = parent;
|
||||
parent->mCounter = counter;
|
||||
}
|
||||
mConnectedCalcParentNew = nullptr;
|
||||
}
|
||||
|
||||
void BaseProc::handleSleepRequest_() {
|
||||
if (isDeletedOrDeleting() || mState == State::Sleep)
|
||||
return;
|
||||
|
||||
mState = State::Sleep;
|
||||
onEnterSleep_();
|
||||
BaseProcMgr::instance()->eraseJobs(*this);
|
||||
}
|
||||
|
||||
void BaseProc::handleWakeUpRequest_() {
|
||||
if (isDeletedOrDeleting())
|
||||
return;
|
||||
|
||||
onEnterCalc_();
|
||||
|
||||
if (!mStateFlags.isOff(StateFlags::RequestDelete))
|
||||
return;
|
||||
|
||||
mState = State::Calc;
|
||||
BaseProcMgr::instance()->pushJobs(*this);
|
||||
unlinkProcUnit_();
|
||||
}
|
||||
|
||||
void BaseProc::handleJobPriorityChangeRequest_() {
|
||||
for (u32 i = 0; i < BaseProcMgr::instance()->getNumJobTypes(); ++i) {
|
||||
if (!mJobHandlers[i] || !mJobHandlers[i]->getLink().hasPriorityChange())
|
||||
continue;
|
||||
|
||||
BaseProcMgr::instance()->eraseJob(*this, JobType(i));
|
||||
mJobHandlers[i]->getLink().loadNewPriority();
|
||||
mJobHandlers[i]->getLink().loadNewPriority2();
|
||||
BaseProcMgr::instance()->pushJob(*this, JobType(i));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,286 @@
|
|||
#pragma once
|
||||
|
||||
#include <basis/seadTypes.h>
|
||||
#include <container/seadListImpl.h>
|
||||
#include <container/seadSafeArray.h>
|
||||
#include <container/seadTreeMap.h>
|
||||
#include <prim/seadBitFlag.h>
|
||||
#include <prim/seadRuntimeTypeInfo.h>
|
||||
#include <prim/seadSafeString.h>
|
||||
#include <prim/seadTypedBitFlag.h>
|
||||
#include <thread/seadAtomic.h>
|
||||
#include "KingSystem/ActorSystem/actBaseProcJob.h"
|
||||
#include "KingSystem/ActorSystem/actBaseProcMap.h"
|
||||
#include "KingSystem/Utils/StrTreeMap.h"
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace ksys {
|
||||
|
||||
namespace act {
|
||||
|
||||
class ActorLinkConstDataAccess;
|
||||
class BaseProc;
|
||||
class BaseProcLinkData;
|
||||
class BaseProcJobHandler;
|
||||
|
||||
class BaseProcLink {
|
||||
public:
|
||||
BaseProcLink();
|
||||
~BaseProcLink() { reset(); }
|
||||
BaseProcLink& operator=(const BaseProcLink&);
|
||||
bool operator==(const BaseProcLink&) const;
|
||||
bool operator!=(const BaseProcLink& rhs) const { return !operator==(rhs); }
|
||||
BaseProc* fromAccessorAndActor(ActorLinkConstDataAccess&, BaseProc*);
|
||||
BaseProc* fromAccessor(ActorLinkConstDataAccess&);
|
||||
void reset();
|
||||
|
||||
private:
|
||||
BaseProcLinkData* mData = nullptr;
|
||||
u32 mId = -1;
|
||||
bool mAcquired = false;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(BaseProcLink, 0x10);
|
||||
|
||||
class BaseProcUnit;
|
||||
|
||||
class BaseProcHandle {
|
||||
public:
|
||||
BaseProcHandle();
|
||||
~BaseProcHandle();
|
||||
|
||||
private:
|
||||
BaseProcUnit* mUnit;
|
||||
u8 mFlag;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(BaseProcHandle, 0x10);
|
||||
|
||||
/// Actor base class that encapsulates all the low-level actor lifetime logic.
|
||||
class BaseProc {
|
||||
public:
|
||||
enum class State : u8 {
|
||||
/// The actor is constructed, but not fully initialized yet.
|
||||
Init = 0,
|
||||
/// The actor is active.
|
||||
Calc = 1,
|
||||
/// The actor is sleeping: its update jobs won't be run.
|
||||
Sleep = 2,
|
||||
/// The actor is scheduled for deletion.
|
||||
Delete = 3,
|
||||
};
|
||||
|
||||
enum class DeleteReason : u32 {
|
||||
_0 = 0,
|
||||
_1 = 1,
|
||||
_2 = 2,
|
||||
};
|
||||
|
||||
enum class SleepWakeReason : u32 {
|
||||
_0 = 0,
|
||||
};
|
||||
|
||||
struct ClassInfo {
|
||||
sead::SafeString name;
|
||||
u8 priority;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(ClassInfo, 0x18);
|
||||
|
||||
struct CreateArg {
|
||||
const ClassInfo* class_info;
|
||||
void* _10;
|
||||
u32 _14;
|
||||
u32 _18;
|
||||
sead::SafeString actor_name;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(CreateArg, 0x28);
|
||||
|
||||
explicit BaseProc(const CreateArg& arg);
|
||||
virtual ~BaseProc();
|
||||
|
||||
SEAD_RTTI_BASE(BaseProc)
|
||||
|
||||
/// @return true iff state is Calc and the actor is not being deleted.
|
||||
bool init(sead::Heap* heap, bool sleep_after_init);
|
||||
/// Put this actor to sleep. The request is queued.
|
||||
void sleep(SleepWakeReason reason);
|
||||
/// Wake up this actor. The request is queued.
|
||||
void wakeUp(SleepWakeReason reason);
|
||||
/// Delete this actor. The request is queued.
|
||||
bool deleteLater(DeleteReason reason);
|
||||
|
||||
u8 getPriority() const { return mPriority; }
|
||||
void setPriority(u8 priority) { mPriority = priority; }
|
||||
|
||||
bool isDeletedOrDeleting() const {
|
||||
return mState == State::Delete || mStateFlags.isOn(StateFlags::RequestDelete);
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class BaseProcLinkDataMgr;
|
||||
friend class BaseProcMgr;
|
||||
|
||||
enum class Flags : u32 {
|
||||
Initialized = 1,
|
||||
PreDeleteStarted = 2,
|
||||
PreDeleteFailed = 4,
|
||||
Destructed = 8,
|
||||
DoNotDelete = 0x10,
|
||||
DeleteChildOnDelete = 0x20,
|
||||
DeleteParentOnDelete = 0x40,
|
||||
_80 = 0x80,
|
||||
_100 = 0x100,
|
||||
PreDeleting = 0x200,
|
||||
SleepWakeReason0 = 0x400,
|
||||
};
|
||||
|
||||
enum class StateFlags : u32 {
|
||||
RequestDelete = 1,
|
||||
_2 = 2,
|
||||
RequestSleep = 4,
|
||||
RequestWakeUp = 8,
|
||||
RequestChangeCalcJobPriority = 0x10,
|
||||
_20 = 0x20,
|
||||
_40 = 0x40,
|
||||
_80 = 0x80,
|
||||
RequestSetParent = 0x100,
|
||||
RequestSetChild = 0x200,
|
||||
RequestResetParent = 0x400,
|
||||
RequestResetChild = 0x800,
|
||||
_1000 = 0x1000,
|
||||
_2000 = 0x2000,
|
||||
_4000 = 0x4000,
|
||||
RequestDeleteProcUnit = 0x8000,
|
||||
};
|
||||
|
||||
enum class InitResult : u32 {
|
||||
Ok = 1,
|
||||
Failed = 2,
|
||||
Skipped = 3,
|
||||
};
|
||||
|
||||
enum class PreDeletePrepareResult : u32 {
|
||||
NotDone = 0,
|
||||
Done = 1,
|
||||
};
|
||||
|
||||
struct InitContext {
|
||||
InitResult result;
|
||||
bool sleep_after_init;
|
||||
};
|
||||
|
||||
struct PrepareArg {
|
||||
bool _0 = false;
|
||||
int _4;
|
||||
};
|
||||
|
||||
/// Initialize the actor.
|
||||
/// @return Ok to keep the actor alive, anything else to kill it?
|
||||
virtual InitResult init_();
|
||||
/// @return whether prepareInit_ and init_ should be called.
|
||||
virtual bool shouldInit_();
|
||||
/// Finalize the initialization; the actor is deleted if the result is not Ok.
|
||||
virtual void finalizeInit_(InitContext* context);
|
||||
|
||||
/// Called every tick to prepare for pre-delete (after startPreparingForPreDelete_).
|
||||
virtual PreDeletePrepareResult prepareForPreDelete_();
|
||||
/// Called to start preparing for pre-delete. Return true to allow pre-delete to go ahead.
|
||||
virtual bool startPreparingForPreDelete_();
|
||||
|
||||
/// Destructs this actor if should_destruct is 1.
|
||||
/// @warning The actor must NOT be used after calling this function.
|
||||
virtual void destruct_(int should_destruct);
|
||||
|
||||
/// Called when entering the Calc state.
|
||||
virtual void onEnterCalc_();
|
||||
|
||||
/// Called when a new delete operation is queued.
|
||||
virtual void onDeleteRequested_(DeleteReason reason);
|
||||
/// Called when a new sleep operation is queued.
|
||||
virtual void onSleepRequested_(SleepWakeReason reason);
|
||||
/// Called when a new wakeup operation is queued.
|
||||
virtual void onWakeUpRequested_(SleepWakeReason reason);
|
||||
|
||||
virtual bool shouldClearStateFlag4000_() { return true; }
|
||||
|
||||
/// Called when entering the Delete state.
|
||||
virtual void onEnterDelete_();
|
||||
/// Called when entering the Sleep state.
|
||||
virtual void onEnterSleep_();
|
||||
|
||||
/// Called to actually pre-delete (third and final callback).
|
||||
virtual void preDelete3_(bool* do_not_destruct_immediately);
|
||||
|
||||
virtual bool prepareInit_(sead::Heap* heap, PrepareArg& arg);
|
||||
|
||||
/// Called when pre-delete actually starts (after preparation, before requesting it).
|
||||
virtual void onPreDeleteStart_(PrepareArg&);
|
||||
/// Called to actually pre-delete (second callback).
|
||||
virtual void preDelete2_(bool* do_not_destruct_immediately);
|
||||
/// Called to actually pre-delete (first callback).
|
||||
virtual void preDelete1_();
|
||||
|
||||
virtual bool isSpecialJobType_(JobType type);
|
||||
virtual bool canWakeUp_();
|
||||
virtual void queueExtraJobPush_(JobType type);
|
||||
virtual bool hasJobType_(JobType type);
|
||||
/// Called after processStateUpdate() is called for all actors in the update state list.
|
||||
virtual void afterUpdateState_();
|
||||
|
||||
virtual bool shouldSkipJobPush_(JobType type);
|
||||
/// Called before pushing a job with the specified job type (first callback).
|
||||
virtual void onJobPush1_(JobType type);
|
||||
/// Called before pushing a job with the specified job type (second and final callback).
|
||||
virtual void onJobPush2_(JobType type);
|
||||
|
||||
bool processStateUpdate(u8 counter);
|
||||
void processPreDelete();
|
||||
/// Actually pre-delete the actor. Called from BaseProcDeleter.
|
||||
void doPreDelete(bool* do_not_destruct_immediately);
|
||||
void startDelete_();
|
||||
|
||||
bool isSpecialJobTypeForThisActor_(JobType type) const {
|
||||
return mSpecialJobTypesMask.isOnBit(int(type));
|
||||
}
|
||||
|
||||
sead::FixedSafeString<64> mName;
|
||||
u32 mId = -1;
|
||||
State mState = State::Init;
|
||||
u8 mPriority = 0;
|
||||
u8 mCreatePriorityState = 0;
|
||||
u8 mCounter = 0;
|
||||
BaseProcLinkData* mBaseProcLinkData = nullptr;
|
||||
sead::BitFlag16 mSkippedJobTypesMask;
|
||||
sead::BitFlag16 mSpecialJobTypesMask;
|
||||
sead::TypedBitFlag<Flags, sead::Atomic<u32>> mFlags;
|
||||
sead::TypedBitFlag<StateFlags, sead::Atomic<u32>> mStateFlags;
|
||||
BaseProc* mConnectedCalcParent = nullptr;
|
||||
BaseProc* mConnectedCalcChild = nullptr;
|
||||
BaseProc* mConnectedCalcParentNew = nullptr;
|
||||
BaseProc* mConnectedCalcChildNew = nullptr;
|
||||
sead::SafeArray<BaseProcJobHandler*, 7> mJobHandlers{};
|
||||
sead::Delegate<BaseProc> mInvoker; // TODO: is this correct?
|
||||
sead::ListNode mPostDeleteListNode;
|
||||
sead::ListNode mDeleteListNode;
|
||||
BaseProcMapNode mMapNode{this};
|
||||
BaseProcUnit* mProcUnit = nullptr;
|
||||
sead::Atomic<u32> mRefCount = 0;
|
||||
|
||||
private:
|
||||
void unlinkProcUnit_();
|
||||
void unlinkCalcChild_();
|
||||
void unlinkCalcParent_();
|
||||
void loadNewCalcChild_(u8 counter);
|
||||
void loadNewCalcParent_(u8 counter);
|
||||
|
||||
void handleSleepRequest_();
|
||||
void handleWakeUpRequest_();
|
||||
void handleJobPriorityChangeRequest_();
|
||||
|
||||
bool canWakeUpOrFlagsSet_() {
|
||||
return mFlags.isOn(Flags::_80) ? mFlags.isOn(Flags::_100) : canWakeUp_();
|
||||
}
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(BaseProc, 0x180);
|
||||
|
||||
} // namespace act
|
||||
|
||||
} // namespace ksys
|
|
@ -0,0 +1,9 @@
|
|||
#include "KingSystem/ActorSystem/actBaseProcJob.h"
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
BaseProcJobLink::BaseProcJobLink(BaseProc* proc, u8 priority)
|
||||
: TListNode(proc), mPriority(priority), mNewPriority(priority), mPriority2(3),
|
||||
mNewPriority2(3) {}
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include <container/seadTList.h>
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
class BaseProc;
|
||||
|
||||
enum class Priority : u8 {
|
||||
PlayerBefore = 0,
|
||||
Player = 2,
|
||||
PlayerAfter = 2,
|
||||
AllAfter = 3,
|
||||
};
|
||||
|
||||
enum class JobType {
|
||||
PreCalc = 0,
|
||||
Calc1 = 1,
|
||||
Calc2 = 2,
|
||||
Calc3 = 3,
|
||||
Calc4 = 4,
|
||||
AfterMessageDispatch = 5,
|
||||
Calc3Alt = 6,
|
||||
Invalid = 7,
|
||||
};
|
||||
|
||||
class BaseProcJobLink : public sead::TListNode<BaseProc*> {
|
||||
public:
|
||||
BaseProcJobLink(BaseProc* proc, u8 priority);
|
||||
|
||||
u8 getPriority() const { return mPriority; }
|
||||
u8 getPriority2() const { return mPriority2; }
|
||||
|
||||
void setNewPriority(u8 priority) { mNewPriority = priority; }
|
||||
void setNewPriority2(u8 priority) { mNewPriority2 = priority; }
|
||||
|
||||
void loadNewPriority() { mPriority = mNewPriority; }
|
||||
void loadNewPriority2() { mPriority2 = mNewPriority2; }
|
||||
|
||||
bool hasPriorityChange() const {
|
||||
return mPriority != mNewPriority || mPriority2 != mNewPriority2;
|
||||
}
|
||||
|
||||
private:
|
||||
u8 mPriority;
|
||||
u8 mNewPriority;
|
||||
u8 mPriority2;
|
||||
u8 mNewPriority2;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(BaseProcJobLink, 0x28);
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,8 @@
|
|||
#include "KingSystem/ActorSystem/actBaseProcJobHandler.h"
|
||||
#include "KingSystem/ActorSystem/actBaseProc.h"
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
BaseProcJobHandler::BaseProcJobHandler(BaseProc* proc) : mLink(proc, proc->getPriority()) {}
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "KingSystem/ActorSystem/actBaseProcJob.h"
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
class BaseProcJobHandler {
|
||||
public:
|
||||
BaseProcJobHandler(BaseProc* proc);
|
||||
virtual ~BaseProcJobHandler() = default;
|
||||
virtual void invoke() = 0;
|
||||
virtual void invokeSpecial() {}
|
||||
|
||||
BaseProcJobLink& getLink() { return mLink; }
|
||||
const BaseProcJobLink& getLink() const { return mLink; }
|
||||
|
||||
protected:
|
||||
BaseProcJobLink mLink;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(BaseProcJobHandler, 0x30);
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,52 @@
|
|||
#include "KingSystem/ActorSystem/actBaseProcLinkDataMgr.h"
|
||||
#include <prim/seadScopedLock.h>
|
||||
#include <thread/seadThread.h>
|
||||
#include "KingSystem/ActorSystem/actBaseProc.h"
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
SEAD_SINGLETON_DISPOSER_IMPL(BaseProcLinkDataMgr)
|
||||
|
||||
bool BaseProcLinkDataMgr::acquireLink(BaseProc* proc) {
|
||||
auto lock = sead::makeScopedLock(mCS);
|
||||
s32 index = mIndex;
|
||||
for (s32 i = 0; i < mData.size(); ++i) {
|
||||
const s32 j = index == 0x800 ? 0 : index;
|
||||
auto& data = mData[j];
|
||||
if (data.mId == u32(-1)) {
|
||||
{
|
||||
auto data_lock = sead::makeScopedLock(data.mCS);
|
||||
data.mProc = proc;
|
||||
data.mId = proc->mId;
|
||||
proc->mBaseProcLinkData = &data;
|
||||
}
|
||||
mIndex = j + 1;
|
||||
return true;
|
||||
}
|
||||
index = j + 1;
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < mData.size(); ++i) {
|
||||
auto data_lock = sead::makeScopedLock(mData[i].mCS);
|
||||
// Debug code was probably here?
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BaseProcLinkDataMgr::releaseLink(BaseProc* proc) {
|
||||
auto* data = proc->mBaseProcLinkData;
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
const auto thread = sead::ThreadMgr::instance()->getCurrentThread();
|
||||
thread->getPriority();
|
||||
|
||||
auto lock = sead::makeScopedLock(data->mCS);
|
||||
data->mProc = nullptr;
|
||||
data->mId = -1;
|
||||
proc->mBaseProcLinkData = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include <container/seadSafeArray.h>
|
||||
#include <heap/seadDisposer.h>
|
||||
#include <prim/seadScopedLock.h>
|
||||
#include <thread/seadCriticalSection.h>
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
class BaseProc;
|
||||
|
||||
class BaseProcLinkData {
|
||||
public:
|
||||
u32 id() const { return mId; }
|
||||
BaseProc* proc() const { return mProc; }
|
||||
s32 refCount() const { return mRefCount; }
|
||||
|
||||
private:
|
||||
friend class BaseProcLinkDataMgr;
|
||||
|
||||
sead::CriticalSection mCS;
|
||||
u32 mId = -1;
|
||||
BaseProc* mProc = nullptr;
|
||||
s32 mRefCount = 0;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(BaseProcLinkData, 0x58);
|
||||
|
||||
class BaseProcLinkDataMgr {
|
||||
SEAD_SINGLETON_DISPOSER(BaseProcLinkDataMgr)
|
||||
|
||||
public:
|
||||
bool acquireLink(BaseProc* proc);
|
||||
bool releaseLink(BaseProc* proc);
|
||||
|
||||
private:
|
||||
sead::CriticalSection mCS;
|
||||
s32 mIndex = 0;
|
||||
sead::SafeArray<BaseProcLinkData, 2048> mData{};
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(BaseProcLinkDataMgr, 0x2C068);
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,22 @@
|
|||
#include "KingSystem/ActorSystem/actBaseProcMap.h"
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
void BaseProcMapNode::add(BaseProcMapNode* node) {
|
||||
auto next = mNext;
|
||||
node->mPrev = this;
|
||||
node->mNext = next;
|
||||
mNext = node;
|
||||
if (next)
|
||||
next->mPrev = node;
|
||||
}
|
||||
|
||||
void BaseProcMap::insert(BaseProcMapNode* node) {
|
||||
if (auto existing_node = find(node->key()))
|
||||
existing_node->add(node);
|
||||
else
|
||||
StrTreeMap::insert(node);
|
||||
node->setInserted();
|
||||
}
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include "KingSystem/Utils/StrTreeMap.h"
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
class BaseProc;
|
||||
|
||||
class BaseProcMapNode final : public util::StrTreeMapNode {
|
||||
public:
|
||||
explicit BaseProcMapNode(BaseProc* proc) : mProc(proc) {}
|
||||
~BaseProcMapNode() override;
|
||||
|
||||
// NON_MATCHING: addressing mode for mPrev
|
||||
void erase_() override {
|
||||
if (mPrev)
|
||||
mPrev->mNext = mNext;
|
||||
|
||||
if (mNext)
|
||||
mNext->mPrev = mPrev;
|
||||
|
||||
StrTreeMapNode::erase_();
|
||||
mInserted = false;
|
||||
mPrev = mNext = nullptr;
|
||||
}
|
||||
|
||||
util::StrTreeMapKey& key() { return mKey; }
|
||||
const util::StrTreeMapKey& key() const { return mKey; }
|
||||
BaseProc* proc() const { return mProc; }
|
||||
BaseProcMapNode* prev() const { return mPrev; }
|
||||
BaseProcMapNode* next() const { return mNext; }
|
||||
|
||||
void add(BaseProcMapNode* node);
|
||||
|
||||
bool isInserted() const { return mInserted; }
|
||||
void setInserted() { mInserted = true; }
|
||||
|
||||
private:
|
||||
BaseProc* mProc = nullptr;
|
||||
BaseProcMapNode* mPrev = nullptr;
|
||||
BaseProcMapNode* mNext = nullptr;
|
||||
bool mInserted = false;
|
||||
};
|
||||
|
||||
class BaseProcMap : public util::StrTreeMap<BaseProcMapNode> {
|
||||
public:
|
||||
void insert(BaseProcMapNode* node);
|
||||
|
||||
void erase(BaseProcMapNode* node) {
|
||||
auto prev = node->prev();
|
||||
auto next = node->next();
|
||||
if (prev)
|
||||
node->erase_();
|
||||
else if (next)
|
||||
StrTreeMap::insert(next);
|
||||
else
|
||||
StrTreeMap::erase(node->key());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,133 @@
|
|||
#include "KingSystem/ActorSystem/actBaseProcMgr.h"
|
||||
#include <prim/seadScopedLock.h>
|
||||
#include <thread/seadThread.h>
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
SEAD_SINGLETON_DISPOSER_IMPL(BaseProcMgr)
|
||||
|
||||
void BaseProcMgr::generateProcId(u32* id) {
|
||||
*id = mCreatedProcCounter.increment();
|
||||
}
|
||||
|
||||
void BaseProcMgr::registerProc(BaseProc& proc) {
|
||||
auto lock = sead::makeScopedLock(mProcMapCS);
|
||||
proc.mMapNode.key().setKey(proc.mName);
|
||||
mProcMap.insert(&proc.mMapNode);
|
||||
}
|
||||
|
||||
void BaseProcMgr::unregisterProc(BaseProc& proc) {
|
||||
if (!proc.mMapNode.isInserted())
|
||||
return;
|
||||
|
||||
auto lock = sead::makeScopedLock(mProcMapCS);
|
||||
mProcMap.erase(&proc.mMapNode);
|
||||
}
|
||||
|
||||
void BaseProcMgr::addToPreDeleteList(BaseProc& proc) {
|
||||
auto lock = sead::makeScopedLock(mProcPreDeleteListCS);
|
||||
mProcPreDeleteList.pushBack(&proc);
|
||||
}
|
||||
|
||||
void BaseProcMgr::doAddToUpdateStateList_(BaseProc& proc) {
|
||||
if (mProcUpdateStateList.isNodeLinked(&proc))
|
||||
return;
|
||||
|
||||
if (proc.mCreatePriorityState == 1) {
|
||||
proc.mCreatePriorityState = 0;
|
||||
mProcUpdateStateList.pushFront(&proc);
|
||||
} else {
|
||||
mProcUpdateStateList.pushBack(&proc);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseProcMgr::eraseFromPreDeleteList(BaseProc& proc) {
|
||||
auto lock = sead::makeScopedLock(mProcPreDeleteListCS);
|
||||
if (mProcPreDeleteList.isNodeLinked(&proc))
|
||||
mProcPreDeleteList.erase(&proc);
|
||||
}
|
||||
|
||||
void BaseProcMgr::eraseFromUpdateStateList(BaseProc& proc) {
|
||||
auto lock = sead::makeScopedLock(mProcUpdateStateListCS);
|
||||
if (mProcUpdateStateList.isNodeLinked(&proc))
|
||||
mProcUpdateStateList.erase(&proc);
|
||||
}
|
||||
|
||||
void BaseProcMgr::processPreDeleteList() {
|
||||
mStatus = Status::ProcessingPreDeleteList;
|
||||
|
||||
auto lock = sead::makeScopedLock(mProcPreDeleteListCS);
|
||||
for (auto& proc : mProcPreDeleteList.robustRange())
|
||||
proc.processPreDelete();
|
||||
|
||||
mStatus = Status::Idle;
|
||||
}
|
||||
|
||||
bool BaseProcMgr::hasExtraJobLink(BaseProcJobLink* job_link, s32 idx) {
|
||||
for (auto& ptr : mExtraJobLinkArrays[idx]) {
|
||||
if (&ptr == job_link)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BaseProcMgr::clearExtraJobArrays() {
|
||||
mExtraJobLinkArrays[0].clear();
|
||||
mExtraJobLinkArrays[1].clear();
|
||||
}
|
||||
|
||||
void BaseProcMgr::setActorJobTypeAndPrio(JobType type, s32 prio, bool x) {
|
||||
mStatus = Status::ProcessingActorJobs;
|
||||
mJobType = type;
|
||||
mCurrentlyProcessingPrio = prio;
|
||||
mUnk2 = x;
|
||||
}
|
||||
|
||||
void BaseProcMgr::goIdle() {
|
||||
mStatus = Status::Idle;
|
||||
mJobType = JobType::Invalid;
|
||||
mIsPushingJobs = false;
|
||||
mUnk2 = false;
|
||||
mEnableExtraJobPush = false;
|
||||
mCurrentlyProcessingPrio = 8;
|
||||
}
|
||||
|
||||
void BaseProcMgr::clearMode() {
|
||||
mMode = Mode::_0;
|
||||
}
|
||||
|
||||
bool BaseProcMgr::isHighPriorityThread() const {
|
||||
const auto current_thread = sead::ThreadMgr::instance()->getCurrentThread();
|
||||
|
||||
if (!current_thread)
|
||||
return false;
|
||||
|
||||
const auto id = current_thread->getId();
|
||||
|
||||
if (!current_thread->isDefaultPriority())
|
||||
return false;
|
||||
|
||||
return id == mMainThreadId || id == mHavokThreadId1 || id == mHavokThreadId2;
|
||||
}
|
||||
|
||||
void BaseProcMgr::incrementUnk3() {
|
||||
if (mUnk3 != 0xFF)
|
||||
++mUnk3;
|
||||
}
|
||||
|
||||
void BaseProcMgr::decrementUnk3() {
|
||||
if (mUnk3 != 0)
|
||||
--mUnk3;
|
||||
}
|
||||
|
||||
sead::CriticalSection* BaseProcMgr::lockProcMap() {
|
||||
mProcMapCS.lock();
|
||||
return &mProcMapCS;
|
||||
}
|
||||
|
||||
void BaseProcMgr::unlockProcMap() {
|
||||
mLastProcMapNode = nullptr;
|
||||
mProcMapCS.unlock();
|
||||
}
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,219 @@
|
|||
#pragma once
|
||||
|
||||
#include <agl/Utils/aglAtomicPtrArray.h>
|
||||
#include <container/seadOffsetList.h>
|
||||
#include <container/seadPtrArray.h>
|
||||
#include <container/seadSafeArray.h>
|
||||
#include <heap/seadDisposer.h>
|
||||
#include <prim/seadBitFlag.h>
|
||||
#include <prim/seadScopedLock.h>
|
||||
#include <thread/seadAtomic.h>
|
||||
#include <thread/seadCriticalSection.h>
|
||||
#include "KingSystem/ActorSystem/actBaseProc.h"
|
||||
#include "KingSystem/ActorSystem/actBaseProcJob.h"
|
||||
#include "KingSystem/ActorSystem/actBaseProcMap.h"
|
||||
#include "KingSystem/Utils/StrTreeMap.h"
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace sead {
|
||||
class FixedSizeJQ;
|
||||
template <typename F>
|
||||
class IFunction;
|
||||
class WorkerMgr;
|
||||
} // namespace sead
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
class BaseProcDeleter;
|
||||
class BaseProcInitializer;
|
||||
class BaseProcInitializerArgs;
|
||||
class BaseProcJobLists;
|
||||
class BaseProcJobQue;
|
||||
|
||||
class BaseProcMgr {
|
||||
SEAD_SINGLETON_DISPOSER(BaseProcMgr)
|
||||
BaseProcMgr();
|
||||
|
||||
public:
|
||||
class ProcCreateRequest;
|
||||
|
||||
enum class Status : u8 {
|
||||
Idle = 0,
|
||||
_1 = 1,
|
||||
ProcessingActorJobs = 2,
|
||||
ProcessingPreDeleteList = 3,
|
||||
ProcessingUpdateStateList = 4,
|
||||
};
|
||||
|
||||
enum class Mode : u8 {
|
||||
_0 = 0,
|
||||
};
|
||||
|
||||
using ExtraJobLinkArray = agl::utl::FixedPtrArray<BaseProcJobLink, 512>;
|
||||
|
||||
static void createInstanceAndInit(sead::Heap* heap);
|
||||
static u32 getConstant0() { return sConstant0; }
|
||||
static u32 getConstant1() { return sConstant1; }
|
||||
static u32 getConstant2() { return sConstant2; }
|
||||
static u32 getConstant4() { return sConstant4; }
|
||||
|
||||
virtual ~BaseProcMgr();
|
||||
|
||||
void init(sead::Heap* heap, s32 num_job_types, u32 main_thread_id, u32 havok_thread_id1,
|
||||
u32 havok_thread_id2, BaseProcInitializerArgs* initializer_args);
|
||||
|
||||
// region BaseProc management
|
||||
|
||||
void generateProcId(u32* id);
|
||||
void registerProc(BaseProc& proc);
|
||||
void unregisterProc(BaseProc& proc);
|
||||
|
||||
void addToPreDeleteList(BaseProc& proc);
|
||||
bool addToUpdateStateList(BaseProc& proc);
|
||||
void eraseFromPreDeleteList(BaseProc& proc);
|
||||
void eraseFromUpdateStateList(BaseProc& proc);
|
||||
void processPreDeleteList();
|
||||
|
||||
void forEachProc(const sead::IDelegate1<BaseProc*>& callback, u32 flags);
|
||||
void deleteAllProcs();
|
||||
bool hasFinishedDeletingAllProcs();
|
||||
|
||||
// endregion
|
||||
|
||||
bool requestPreDelete(BaseProc& proc);
|
||||
void requestUnloadActorParam(void*);
|
||||
|
||||
// region Job processing
|
||||
|
||||
void pushJob(BaseProc& proc, JobType type);
|
||||
void pushJobs(BaseProc& proc);
|
||||
void eraseJob(BaseProc& proc, JobType type);
|
||||
void eraseJobs(BaseProc& proc);
|
||||
|
||||
void processExtraJobsDirectly(JobType type, s32 prio, bool);
|
||||
ExtraJobLinkArray& getExtraJobs();
|
||||
void swapExtraJobArray();
|
||||
|
||||
void queueExtraJobPush(BaseProcJobLink* job_link);
|
||||
void moveExtraJobsToOtherBuffer();
|
||||
bool hasExtraJobLink(BaseProcJobLink* job_link, s32 idx);
|
||||
void clearExtraJobArrays();
|
||||
|
||||
void pushJobQueues(sead::WorkerMgr* mgr, JobType type, bool);
|
||||
bool pushExtraJobsEx(sead::FixedSizeJQ* jq, JobType type, bool, bool);
|
||||
bool pushExtraJobsForCurrentTypeAndPrio(sead::FixedSizeJQ* jq, ExtraJobLinkArray& array);
|
||||
bool pushPreCalcJobs(sead::FixedSizeJQ* jq, JobType type, s32 prio, bool, bool);
|
||||
|
||||
void setActorJobType(JobType type);
|
||||
void setActorJobTypeAndPrio(JobType type, s32 prio, bool);
|
||||
void goIdle();
|
||||
void clearMode();
|
||||
void calc();
|
||||
void jobInvoked(BaseProcJobLink* link, s32 required_calc_rounds);
|
||||
|
||||
// endregion
|
||||
|
||||
// region Special job types
|
||||
|
||||
bool isSpecialJobType(JobType type) const;
|
||||
void addSpecialJobTypes(u16 mask);
|
||||
void removeSpecialJobTypes(u16 mask);
|
||||
|
||||
// endregion
|
||||
|
||||
/// Returns true if and only if the calling thread is the game thread or a Havok thread.
|
||||
bool isHighPriorityThread() const;
|
||||
/// Returns true if and only if it is safe to access the specified BaseProc.
|
||||
bool isAccessingProcSafe(BaseProc* proc, BaseProc* other);
|
||||
|
||||
// region BaseProc creation
|
||||
|
||||
bool requestCreateProc(ProcCreateRequest& req);
|
||||
BaseProc* createProc(ProcCreateRequest& req);
|
||||
|
||||
// endregion
|
||||
|
||||
// region Actor initializer control
|
||||
|
||||
void resumeThreadMaybe();
|
||||
void stopThreads();
|
||||
void clearMessageQueueMaybe();
|
||||
void startActorCreateThread();
|
||||
|
||||
void clearInitializerMessageQueuesMaybe();
|
||||
s32 getActorCreateInitializerQueueSize();
|
||||
s32 getActorCreateInitializerQueueSizeEx(s32 x);
|
||||
void invokeOnActorCreateInitializerThreadMaybe(void* delegate);
|
||||
void setActorGenerationEnabled(bool enabled);
|
||||
|
||||
// endregion
|
||||
|
||||
auto getUnk3() const { return mUnk3; }
|
||||
void incrementUnk3();
|
||||
void decrementUnk3();
|
||||
|
||||
void writeResidentActorsCsv(const sead::SafeString& file_path);
|
||||
|
||||
auto& getProcUpdateStateListCS() { return mProcUpdateStateListCS; }
|
||||
|
||||
void incrementPendingDeletions() { mNumPendingDeletions.increment(); }
|
||||
void decrementPendingDeletions() { mNumPendingDeletions.decrement(); }
|
||||
|
||||
u32 getNumJobTypes() const { return mNumJobTypes; }
|
||||
|
||||
private:
|
||||
void doAddToUpdateStateList_(BaseProc& proc);
|
||||
|
||||
sead::CriticalSection* lockProcMap();
|
||||
void unlockProcMap();
|
||||
void getNextActor(sead::CriticalSection* cs, BaseProc* proc, u32 flags);
|
||||
|
||||
static sead::BufferedSafeString* sResidentActorListStr;
|
||||
static u32 sConstant0;
|
||||
static u32 sConstant1;
|
||||
static u32 sConstant2;
|
||||
static u32 sConstant4;
|
||||
|
||||
Status mStatus = Status::Idle;
|
||||
util::SizedEnum<JobType, u8> mJobType = JobType::Invalid;
|
||||
u8 mCurrentlyProcessingPrio = 8;
|
||||
u8 mCounter = 0;
|
||||
sead::CriticalSection mProcMapCS;
|
||||
sead::OffsetList<BaseProc> mProcPreDeleteList;
|
||||
u32 mNumJobTypes = 0;
|
||||
BaseProcJobLists* mJobLists = nullptr;
|
||||
BaseProcMap mProcMap;
|
||||
sead::OffsetList<BaseProc> mProcUpdateStateList;
|
||||
sead::CriticalSection mProcUpdateStateListCS;
|
||||
sead::CriticalSection mProcPreDeleteListCS;
|
||||
BaseProcMapNode* mLastProcMapNode = nullptr;
|
||||
BaseProcJobQue* mProcJobQue = nullptr;
|
||||
sead::Atomic<u32> mCreatedProcCounter = 0;
|
||||
sead::Atomic<u32> mNumPendingDeletions = 0;
|
||||
BaseProcInitializer* mProcInitializer = nullptr;
|
||||
BaseProcDeleter* mProcDeleter = nullptr;
|
||||
bool mIsPushingJobs = false;
|
||||
bool mPushActorJobType3InsteadOf6 = false;
|
||||
bool mEnableExtraJobPush = false;
|
||||
Mode mMode = Mode::_0;
|
||||
bool mUnk2 = false;
|
||||
bool mIsInitialisingQuestMgrMaybe = false;
|
||||
u8 mCurrentExtraJobArrayIdx = 0;
|
||||
u8 mUnk3 = 0;
|
||||
sead::BitFlag16 mSpecialJobTypesMask = 0;
|
||||
u32 mMainThreadId = 0;
|
||||
u32 mHavokThreadId1 = 0;
|
||||
u32 mHavokThreadId2 = 0;
|
||||
u32 mUnk4 = 0;
|
||||
sead::SafeArray<ExtraJobLinkArray, 2> mExtraJobLinkArrays{};
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(BaseProcMgr, 0x21a0);
|
||||
|
||||
inline bool BaseProcMgr::addToUpdateStateList(BaseProc& proc) {
|
||||
auto lock = sead::makeScopedLock(mProcUpdateStateListCS);
|
||||
doAddToUpdateStateList_(proc);
|
||||
return (proc.mStateFlags.set(BaseProc::StateFlags::RequestDelete) &
|
||||
u32(BaseProc::StateFlags::RequestDelete)) != 0;
|
||||
}
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <basis/seadTypes.h>
|
||||
#include <thread/seadCriticalSection.h>
|
||||
|
||||
namespace ksys::act {
|
||||
|
||||
class BaseProc;
|
||||
class BaseProcHandle;
|
||||
|
||||
class BaseProcUnit {
|
||||
public:
|
||||
bool deleteProc(void*, BaseProcHandle* handle);
|
||||
bool setProc(BaseProc* proc);
|
||||
void unlinkProc(BaseProc* proc);
|
||||
void cleanUp(BaseProc* proc, bool set_flag_5);
|
||||
bool isParentHandleDefault() const;
|
||||
|
||||
private:
|
||||
u32 mFlags;
|
||||
BaseProcHandle* mHandle;
|
||||
BaseProc* mProc;
|
||||
// FIXME:
|
||||
// BaseProcRequest mRequest;
|
||||
sead::CriticalSection mCS;
|
||||
};
|
||||
|
||||
} // namespace ksys::act
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <basis/seadTypes.h>
|
||||
|
||||
#include "KingSystem/Utils/Types.h"
|
||||
|
||||
namespace ksys {
|
||||
|
||||
namespace mes {
|
||||
|
||||
struct TransceiverId {
|
||||
TransceiverId();
|
||||
|
||||
TransceiverId& operator=(const TransceiverId& other) {
|
||||
_0 = other._0;
|
||||
_4 = other._4;
|
||||
_8 = other._8;
|
||||
_10 = other._10;
|
||||
return *this;
|
||||
}
|
||||
|
||||
u32 _0;
|
||||
u32 _4;
|
||||
void* _8;
|
||||
void* _10;
|
||||
};
|
||||
KSYS_CHECK_SIZE_NX150(TransceiverId, 0x18);
|
||||
|
||||
} // namespace mes
|
||||
|
||||
} // namespace ksys
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
namespace ksys::tera {
|
||||
|
||||
// TODO:
|
||||
class ApertureMaps {
|
||||
void updateMap();
|
||||
};
|
||||
class ApertureMapsCollector {
|
||||
void allocateImage();
|
||||
void releaseImage();
|
||||
};
|
||||
class Core {
|
||||
class Grass;
|
||||
class Model;
|
||||
class Tree;
|
||||
};
|
||||
class ImageResourceMgr {
|
||||
void procUnloadResidualRequest();
|
||||
};
|
||||
class ResourceHolder;
|
||||
class Scene {
|
||||
void exportFileBinary();
|
||||
};
|
||||
class System {
|
||||
void loadScene();
|
||||
};
|
||||
class Water {
|
||||
void setUpAttributeTable();
|
||||
};
|
||||
|
||||
bool checkTeraSystemStatus();
|
||||
|
||||
} // namespace ksys::tera
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include <basis/seadTypes.h>
|
||||
#include <codec/seadHashCRC32.h>
|
||||
#include <container/seadTreeMap.h>
|
||||
|
||||
namespace ksys::util {
|
||||
|
||||
class StrTreeMapKey {
|
||||
public:
|
||||
const sead::SafeString& key() const { return mKey; }
|
||||
|
||||
// NON_MATCHING: stack
|
||||
void setKey(u32 hash, sead::SafeString key) {
|
||||
mKeyHash = hash;
|
||||
mKey = key;
|
||||
}
|
||||
|
||||
void setKey(const sead::SafeString& key) {
|
||||
setKey(sead::HashCRC32::calcStringHash(key.cstr()), key);
|
||||
}
|
||||
|
||||
s32 compare(const StrTreeMapKey& rhs) const {
|
||||
if (mKeyHash < rhs.mKeyHash)
|
||||
return -1;
|
||||
if (mKeyHash > rhs.mKeyHash)
|
||||
return 1;
|
||||
return mKey.compare(rhs.mKey);
|
||||
}
|
||||
|
||||
private:
|
||||
u32 mKeyHash = 0;
|
||||
sead::SafeString mKey;
|
||||
};
|
||||
|
||||
class StrTreeMapNode : public sead::TreeMapNode<StrTreeMapKey> {
|
||||
public:
|
||||
~StrTreeMapNode() override { ; }
|
||||
void erase_() override { mLeft = mRight = nullptr; }
|
||||
};
|
||||
|
||||
template <typename Node>
|
||||
class StrTreeMap : public sead::IntrusiveTreeMap<StrTreeMapKey, Node> {};
|
||||
|
||||
} // namespace ksys::util
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef SEAD_DEBUG
|
||||
#define KSYS_CHECK_SIZE_NX150(CLASS, SIZE)
|
||||
#else
|
||||
#define KSYS_CHECK_SIZE_NX150(CLASS, SIZE) static_assert(sizeof(CLASS) == SIZE)
|
||||
#endif
|
||||
|
||||
namespace ksys::util {
|
||||
|
||||
/// 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 ksys::util
|
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
from colorama import Fore, Style
|
||||
import subprocess
|
||||
import sys
|
||||
import utils
|
||||
|
||||
parser = argparse.ArgumentParser(description="Diff assembly")
|
||||
parser.add_argument("function", help="Name of the function to diff. Pass | to get a WIP function", nargs="?", default="|")
|
||||
args, unknown = parser.parse_known_args()
|
||||
|
||||
find_wip = args.function == "|"
|
||||
|
||||
for info in utils.get_functions():
|
||||
addr_end = info.addr + info.size
|
||||
|
||||
if info.name == args.function or info.decomp_name == args.function or (find_wip and info.status == utils.FunctionStatus.Wip):
|
||||
if not info.decomp_name:
|
||||
utils.fail(f"{args.function} has not been decompiled")
|
||||
|
||||
subprocess.call(["asm-differ", "-e", info.decomp_name, "0x%016x" %
|
||||
info.addr, "0x%016x" % addr_end] + unknown)
|
||||
|
||||
if info.status == utils.FunctionStatus.NonMatching:
|
||||
utils.warn(
|
||||
f"{args.function} is marked as non-matching and possibly NOT functionally equivalent")
|
||||
elif info.status == utils.FunctionStatus.Equivalent:
|
||||
utils.warn(f"{args.function} is marked as functionally equivalent but non-matching")
|
||||
|
||||
if find_wip:
|
||||
print(
|
||||
f"WIP function name: {Style.BRIGHT}{Fore.BLUE}{info.decomp_name}{Style.RESET_ALL}")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
if find_wip:
|
||||
utils.fail("no WIP function")
|
||||
|
||||
utils.fail(
|
||||
f"unknown function '{args.function}'\nfor constructors and destructors, list the complete object constructor (C1) or destructor (D1)")
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
def apply(config, args):
|
||||
config['arch'] = 'aarch64'
|
||||
config['baseimg'] = 'data/main.elf'
|
||||
config['myimg'] = 'build/uking.elf'
|
||||
config['source_directories'] = ['src']
|
||||
config['objdump_executable'] = 'aarch64-linux-gnu-objdump'
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
from colorama import Fore, Style
|
||||
import diff_settings
|
||||
import subprocess
|
||||
import utils
|
||||
|
||||
parser = argparse.ArgumentParser(description="Prints build/uking.elf symbols")
|
||||
parser.add_argument("--print-undefined", "-u",
|
||||
help="Print symbols that are undefined", action="store_true")
|
||||
parser.add_argument("--print-c2-d2", "-c",
|
||||
help="Print C2/D2 (base object constructor/destructor) symbols", action="store_true")
|
||||
parser.add_argument("--hide-unknown", "-H",
|
||||
help="Hide symbols that are not present in the original game", action="store_true")
|
||||
parser.add_argument("--all", "-a", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
listed_decomp_symbols = {info.decomp_name for info in utils.get_functions()}
|
||||
original_symbols = {info.name for info in utils.get_functions()}
|
||||
|
||||
config: dict = dict()
|
||||
diff_settings.apply(config, {})
|
||||
myimg: str = config["myimg"]
|
||||
|
||||
entries = [x.strip().split() for x in subprocess.check_output(["nm", myimg], text=True).split("\n")]
|
||||
|
||||
for entry in entries:
|
||||
if len(entry) == 3:
|
||||
addr = int(entry[0], 16)
|
||||
symbol_type: str = entry[1]
|
||||
name = entry[2]
|
||||
|
||||
if (symbol_type == "T" or symbol_type == "W") and (args.all or name not in listed_decomp_symbols):
|
||||
c1_name = name.replace("C2", "C1")
|
||||
is_c2_ctor = "C2" in name and c1_name in listed_decomp_symbols and utils.are_demangled_names_equal(
|
||||
c1_name, name)
|
||||
|
||||
d1_name = name.replace("D2", "D1")
|
||||
is_d2_dtor = "D2" in name and d1_name in listed_decomp_symbols and utils.are_demangled_names_equal(
|
||||
d1_name, name)
|
||||
|
||||
if args.print_c2_d2 or not (is_c2_ctor or is_d2_dtor):
|
||||
color = Fore.YELLOW
|
||||
if name in original_symbols:
|
||||
color = Fore.RED
|
||||
elif args.hide_unknown:
|
||||
continue
|
||||
if is_c2_ctor or is_d2_dtor:
|
||||
color += Style.DIM
|
||||
print(f"{color}UNLISTED {Fore.RESET} {utils.format_symbol_name(name)}")
|
||||
|
||||
elif len(entry) == 2:
|
||||
symbol_type = entry[0]
|
||||
name = entry[1]
|
||||
|
||||
if symbol_type.upper() == "U" and args.print_undefined:
|
||||
print(f"{Fore.CYAN}UNDEFINED{Style.RESET_ALL} {utils.format_symbol_name(name)}")
|
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
from colorama import Back, Fore, Style
|
||||
import utils
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--print-nm", "-n", action="store_true",
|
||||
help="Print non-matching functions")
|
||||
parser.add_argument("--print-eq", "-e", action="store_true",
|
||||
help="Print non-matching, semantically equivalent functions")
|
||||
parser.add_argument("--print-ok", "-m", action="store_true",
|
||||
help="Print matching functions")
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
num_total = 0
|
||||
num_matching = 0
|
||||
num_equivalent = 0
|
||||
num_nonmatching = 0
|
||||
num_wip = 0
|
||||
|
||||
num_ai_action_done = 0
|
||||
num_ai_ai_done = 0
|
||||
num_ai_behavior_done = 0
|
||||
num_ai_query_done = 0
|
||||
|
||||
num_ai_action = 0
|
||||
num_ai_ai = 0
|
||||
num_ai_behavior = 0
|
||||
num_ai_query = 0
|
||||
|
||||
for info in utils.get_functions():
|
||||
num_total += 1
|
||||
|
||||
if info.name.startswith("AI_F_Action_"):
|
||||
num_ai_action += 1
|
||||
num_ai_action_done += bool(info.decomp_name)
|
||||
if info.name.startswith("AI_F_AI_"):
|
||||
num_ai_ai += 1
|
||||
num_ai_ai_done += bool(info.decomp_name)
|
||||
if info.name.startswith("AI_F_Behavior_"):
|
||||
num_ai_behavior += 1
|
||||
num_ai_behavior_done += bool(info.decomp_name)
|
||||
if info.name.startswith("AI_F_Query_"):
|
||||
num_ai_query += 1
|
||||
num_ai_query_done += bool(info.decomp_name)
|
||||
|
||||
if not info.decomp_name:
|
||||
continue
|
||||
|
||||
if info.status == utils.FunctionStatus.NonMatching:
|
||||
num_nonmatching += 1
|
||||
if args.print_nm:
|
||||
print(f"{Fore.RED}NM{Fore.RESET} {utils.format_symbol_name(info.decomp_name)}")
|
||||
elif info.status == utils.FunctionStatus.Equivalent:
|
||||
num_equivalent += 1
|
||||
if args.print_eq:
|
||||
print(f"{Fore.YELLOW}EQ{Fore.RESET} {utils.format_symbol_name(info.decomp_name)}")
|
||||
elif info.status == utils.FunctionStatus.Matching:
|
||||
num_matching += 1
|
||||
if args.print_ok:
|
||||
print(f"{Fore.GREEN}OK{Fore.RESET} {utils.format_symbol_name(info.decomp_name)}")
|
||||
elif info.status == utils.FunctionStatus.Wip:
|
||||
num_wip += 1
|
||||
print(f"{Back.RED}{Style.BRIGHT}{Fore.WHITE} WIP {Style.RESET_ALL} {utils.format_symbol_name(info.decomp_name)}{Style.RESET_ALL}")
|
||||
|
||||
print()
|
||||
print(f"{num_total} functions")
|
||||
print(f"{num_matching + num_equivalent} {Fore.CYAN}matching or equivalent{Fore.RESET} ({round(100 * (num_matching + num_equivalent) / num_total, 3)}%)")
|
||||
print(f"{num_matching} {Fore.GREEN}matching{Fore.RESET} ({round(100 * num_matching / num_total, 3)}%)")
|
||||
print(f"{num_equivalent} {Fore.YELLOW}equivalent{Fore.RESET} ({round(100 * num_equivalent / num_total, 3)}%)")
|
||||
print(f"{num_nonmatching} {Fore.RED}non-matching{Fore.RESET} ({round(100 * num_nonmatching / num_total, 3)}%)")
|
||||
|
||||
print()
|
||||
print(f"{num_ai_action_done}/{num_ai_action} actions ({round(100 * num_ai_action_done / num_ai_action, 3)}%)")
|
||||
print(f"{num_ai_ai_done}/{num_ai_ai} AIs ({round(100 * num_ai_ai_done / num_ai_ai, 3)}%)")
|
||||
print(f"{num_ai_behavior_done}/{num_ai_behavior} behaviors ({round(100 * num_ai_behavior_done / num_ai_behavior, 3)}%)")
|
||||
print(f"{num_ai_query_done}/{num_ai_query} queries ({round(100 * num_ai_query_done / num_ai_query, 3)}%)")
|
|
@ -0,0 +1,73 @@
|
|||
from colorama import Fore, Style
|
||||
import csv
|
||||
import cxxfilt
|
||||
import enum
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import typing as tp
|
||||
|
||||
|
||||
class FunctionStatus(enum.Enum):
|
||||
Matching = 0
|
||||
Equivalent = 1 # semantically equivalent but not perfectly matching
|
||||
NonMatching = 2
|
||||
Wip = 3
|
||||
NotDecompiled = 4
|
||||
|
||||
|
||||
class FunctionInfo(tp.NamedTuple):
|
||||
addr: int # without the 0x7100000000 base
|
||||
name: str
|
||||
size: int
|
||||
decomp_name: str
|
||||
status: FunctionStatus
|
||||
|
||||
|
||||
_markers = {
|
||||
"?": FunctionStatus.Equivalent,
|
||||
"!": FunctionStatus.NonMatching,
|
||||
"|": FunctionStatus.Wip,
|
||||
}
|
||||
|
||||
|
||||
def parse_function_csv_entry(row) -> FunctionInfo:
|
||||
ea, name, size, decomp_name = row
|
||||
if decomp_name:
|
||||
status = FunctionStatus.Matching
|
||||
|
||||
for marker, new_status in _markers.items():
|
||||
if decomp_name[-1] == marker:
|
||||
status = new_status
|
||||
decomp_name = decomp_name[:-1]
|
||||
break
|
||||
else:
|
||||
status = FunctionStatus.NotDecompiled
|
||||
|
||||
addr = int(ea, 16) - 0x7100000000
|
||||
return FunctionInfo(addr, name, int(size, 0), decomp_name, status)
|
||||
|
||||
|
||||
def get_functions() -> tp.Iterable[FunctionInfo]:
|
||||
with (Path(__file__).parent.parent / "data" / "uking_functions.csv").open() as f:
|
||||
for row in csv.reader(f):
|
||||
yield parse_function_csv_entry(row)
|
||||
|
||||
|
||||
def format_symbol_name(name: str) -> str:
|
||||
try:
|
||||
return f"{cxxfilt.demangle(name)} {Style.DIM}({name}){Style.RESET_ALL}"
|
||||
except:
|
||||
return name
|
||||
|
||||
|
||||
def are_demangled_names_equal(name1: str, name2: str):
|
||||
return cxxfilt.demangle(name1) == cxxfilt.demangle(name2)
|
||||
|
||||
|
||||
def warn(msg: str):
|
||||
sys.stderr.write(f"{Fore.MAGENTA}{Style.BRIGHT}warning:{Fore.RESET} {msg}{Style.RESET_ALL}\n")
|
||||
|
||||
|
||||
def fail(msg: str):
|
||||
sys.stderr.write(f"{Fore.RED}{Style.BRIGHT}error:{Fore.RESET} {msg}{Style.RESET_ALL}\n")
|
||||
sys.exit(1)
|
Loading…
Reference in New Issue