From d3b065bfa2c36257775a0e92a2ede6003a303ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 5 Dec 2020 12:39:29 +0100 Subject: [PATCH] ksys/act: Implement the rest of AIClassDef --- data/uking_functions.csv | 8 +- src/KingSystem/ActorSystem/actAiClassDef.cpp | 248 +++++++++++++++++- src/KingSystem/ActorSystem/actAiClassDef.h | 59 ++++- .../Resource/resResourceAIProgram.cpp | 2 +- 4 files changed, 291 insertions(+), 26 deletions(-) diff --git a/data/uking_functions.csv b/data/uking_functions.csv index 0db3e879..f6229949 100644 --- a/data/uking_functions.csv +++ b/data/uking_functions.csv @@ -92291,10 +92291,10 @@ 0x00000071011df8b0,sub_71011DF8B0,136,_ZN4ksys10AIClassDefD1Ev 0x00000071011df938,AIClassDef::init,608,_ZN4ksys10AIClassDef4initERKN4sead14SafeStringBaseIcEEPNS1_4HeapE 0x00000071011dfb98,AIClassDef::Data::load,1124,_ZN4ksys10AIClassDef4Data4loadEPN4sead4HeapE! -0x00000071011dfffc,AIClassDef::getInfo,540, -0x00000071011e0218,ai::doGetClassDef,4284, -0x00000071011e12d4,AIClassDef::getClassDef,320, -0x00000071011e1414,AIClassDef::isSystemQuery,264,_ZNK4ksys10AIClassDef13isSystemQueryERKN4sead14SafeStringBaseIcEE! +0x00000071011dfffc,AIClassDef::getInfo,540,_ZNK4ksys10AIClassDef6getDefERKN4sead14SafeStringBaseIcEEPNS_8AIDefSetENS_9AIDefTypeE +0x00000071011e0218,ai::doGetClassDef,4284,_ZNK4ksys10AIClassDef8doGetDefEPNS_5AIDefERKN2al9ByamlIterENS_18AIDefInstParamKindENS_9AIDefTypeEi! +0x00000071011e12d4,AIClassDef::getClassDef,320,_ZNK4ksys10AIClassDef6getDefEPNS_5AIDefERKN4sead14SafeStringBaseIcEENS_18AIDefInstParamKindENS_9AIDefTypeE +0x00000071011e1414,AIClassDef::isSystemQuery,264,_ZNK4ksys10AIClassDef13isSystemQueryERKN4sead14SafeStringBaseIcEE 0x00000071011e151c,MassRenderer::ctor,376, 0x00000071011e1694,sub_71011E1694,244, 0x00000071011e1788,sub_71011E1788,380, diff --git a/src/KingSystem/ActorSystem/actAiClassDef.cpp b/src/KingSystem/ActorSystem/actAiClassDef.cpp index b9b144c9..6ff69a14 100644 --- a/src/KingSystem/ActorSystem/actAiClassDef.cpp +++ b/src/KingSystem/ActorSystem/actAiClassDef.cpp @@ -4,10 +4,11 @@ #include #include #include "KingSystem/Resource/resLoadRequest.h" +#include "KingSystem/Utils/Byaml/ByamlData.h" +#include "KingSystem/Utils/Byaml/ByamlHashIter.h" namespace ksys { -namespace { const char* str_AIAfter = "AIAfter"; const char* str_ModelAfter = "ModelAfter"; const char* str_ChangeDeleteState = "ChangeDeleteState"; @@ -19,7 +20,6 @@ const char* str_AIs = "AIs"; const char* str_Actions = "Actions"; const char* str_Behaviors = "Behaviors"; const char* str_Querys = "Querys"; -} // namespace SEAD_SINGLETON_DISPOSER_IMPL(AIClassDef) @@ -48,10 +48,10 @@ bool AIClassDef::Data::load(sead::Heap* heap) { root_iter.tryGetIterByKey(&iters[s32(AIDefType::Behavior)], str_Behaviors); root_iter.tryGetIterByKey(&iters[s32(AIDefType::Query)], str_Querys); - idx_StaticInstParams = root_iter.getKeyIndex(str_StaticInstParams); - idx_DynamicInstParams = root_iter.getKeyIndex(str_DynamicInstParams); - idx_MapUnitInstParams = root_iter.getKeyIndex(str_MapUnitInstParams); - idx_AITreeVariables = root_iter.getKeyIndex(str_AITreeVariables); + inst_params_key_idx[0] = root_iter.getKeyIndex(str_StaticInstParams); + inst_params_key_idx[1] = root_iter.getKeyIndex(str_DynamicInstParams); + inst_params_key_idx[2] = root_iter.getKeyIndex(str_MapUnitInstParams); + inst_params_key_idx[3] = root_iter.getKeyIndex(str_AITreeVariables); idx_Childs = root_iter.getKeyIndex("childs"); static_cast(heap->getFreeSize()); @@ -82,21 +82,241 @@ bool AIClassDef::Data::load(sead::Heap* heap) { return true; } -// NON_MATCHING: binary search might be a handwritten loop? -bool AIClassDef::isSystemQuery(const sead::SafeString& query) const { - bool ret = false; - const u32 hash = sead::HashCRC32::calcStringHash(query); +s32 AIClassDef::getRawDefIdx(const sead::SafeString& def_name, AIDefType type) const { + const auto hash = sead::HashCRC32::calcStringHash(def_name); if (!mData) - return false; + return -1; - const s32 idx = mData->defs[s32(AIDefType::Query)].binarySearchC( - [hash](const Data::Def& def) -> s32 { return hash - def.name_hash; }); + auto& buffer = mData->defs[s32(type)]; + if (buffer.size() == 0) + return -1; + s32 a = 0; + s32 b = buffer.size() - 1; + while (a < b) { + const s32 m = (a + b) / 2; + auto* def = &buffer(m); + +#ifdef MATCHING_HACK_NX_CLANG + { + // The original code has a bunch of useless comparisons that look like this: + // if (def->name_hash < hash == def->name_hash > hash) + // Unfortunately it doesn't match when written that way or with a more obvious + // equality check. Inline assembly to the rescue. + int lt, gt; + asm("cmp %w[def_hash], %w[hash]\n" + "cset %w[lt], cc\n" + "cset %w[gt], hi\n" + : [lt] "=r"(lt), [gt] "=r"(gt) + : [def_hash] "r"(def->name_hash), [hash] "r"(hash) + : "cc"); + if (gt == lt) + return m; + } +#else + if (def->name_hash == hash) + return m; +#endif + + if (def->name_hash >= hash) + b = m; + else + a = m + 1; + } + + if (buffer(a).name_hash != hash) + return -1; + + return a; +} + +void AIClassDef::getDef(const sead::SafeString& class_name, AIDefSet* set, + AIDefType class_type) const { + set->num_children = 0; + set->dynamic_params.num_params = 0; + set->map_unit_params.num_params = 0; + set->ai_tree_params.num_params = 0; + + const s32 idx = getRawDefIdx(class_name, class_type); + if (idx < 0) + return; + + const auto* data = mData; + const auto& iter = getRawDefs(class_type)[idx].iter; + + if (class_type == AIDefType::AI) { + al::ByamlHashIter hash_iter{iter.getRootNode()}; + al::ByamlData byaml_data; + if (hash_iter.getDataByKey(&byaml_data, data->idx_Childs)) { + al::ByamlIter it{iter.getData(), iter.getData() + byaml_data.getValue()}; + if (it.isValid()) { + const s32 num_children = it.getSize(); + for (s32 i = 0; i < num_children; ++i) { + if (!it.tryGetStringByIndex(&set->children[set->num_children], i)) + set->children[set->num_children] = nullptr; + ++set->num_children; + } + } + } + } + + doGetDef(&set->dynamic_params, iter, AIDefInstParamKind::Dynamic, class_type, + data->inst_params_key_idx[s32(AIDefInstParamKind::Dynamic)]); + doGetDef(&set->map_unit_params, iter, AIDefInstParamKind::MapUnit, class_type, + data->inst_params_key_idx[s32(AIDefInstParamKind::MapUnit)]); + doGetDef(&set->ai_tree_params, iter, AIDefInstParamKind::AITree, class_type, + data->inst_params_key_idx[s32(AIDefInstParamKind::AITree)]); +} + +// NON_MATCHING: CalcTiming ifs are reordered +void AIClassDef::doGetDef(AIDef* def, const al::ByamlIter& iter, AIDefInstParamKind param_kind, + AIDefType class_type, s32 key_idx) const { + def->no_stop = false; + def->trigger_action = false; + def->dynamic_param_child = false; + def->_24b = 0; + def->num_params = 0; + def->calc_timing = CalcTiming::AIAfter; + + if (param_kind == AIDefInstParamKind::Static) { + switch (class_type) { + case AIDefType::Action: + iter.tryGetBoolByKey(&def->trigger_action, "TriggerAction"); + break; + case AIDefType::AI: + iter.tryGetBoolByKey(&def->trigger_action, "TriggerAction"); + iter.tryGetBoolByKey(&def->dynamic_param_child, "DynamicParamChild"); + break; + case AIDefType::Behavior: { + const char* timing_c; + if (iter.tryGetStringByKey(&timing_c, "CalcTiming")) { + const sead::SafeString timing = timing_c; + if (timing == str_AIAfter) + def->calc_timing = CalcTiming::AIAfter; + else if (timing == str_ModelAfter) + def->calc_timing = CalcTiming::ModelAfter; + else if (timing == str_ChangeDeleteState) + def->calc_timing = CalcTiming::ChangeDeleteState; + } + iter.tryGetBoolByKey(&def->no_stop, "NoStop"); + break; + } + case AIDefType::Query: + break; + } + } + + if (!iter.getRootNode()) + return; + + al::ByamlHashIter hash_iter{iter.getRootNode()}; + al::ByamlData data; + if (!hash_iter.getDataByKey(&data, key_idx)) + return; + + al::ByamlIter params_iter{iter.getData(), iter.getData() + data.getValue()}; + if (!params_iter.isValid()) + return; + + const s32 num_params = params_iter.getSize(); + for (s32 i = 0; i < num_params; ++i) { + al::ByamlIter it; + if (!params_iter.tryGetIterByIndex(&it, i)) + continue; + + if (it.tryGetStringByKey(&def->param_names[def->num_params], "Name")) { + const char* type_cstr = nullptr; + if (it.tryGetStringByKey(&type_cstr, "Type")) { + const sead::SafeString type = type_cstr; + + if (type == "Int") { + def->param_types[def->num_params] = AIDefParamType::Int; + def->param_values[def->num_params].i = 0; + if (paramKindHasValue(param_kind)) + it.tryGetIntByKey(&def->param_values[def->num_params].i, "Value"); + + } else if (type == "Float" || type == "Angle") { + def->param_types[def->num_params] = AIDefParamType::Float; + def->param_values[def->num_params].f = 0; + if (paramKindHasValue(param_kind)) + it.tryGetFloatByKey(&def->param_values[def->num_params].f, "Value"); + + } else if (type == "Bool") { + def->param_types[def->num_params] = AIDefParamType::Bool; + def->param_values[def->num_params].b = false; + if (paramKindHasValue(param_kind)) + it.tryGetBoolByKey(&def->param_values[def->num_params].b, "Value"); + + } else if (type == "Vec3" || type == "Angle3") { + def->param_types[def->num_params] = AIDefParamType::Vec3; + al::ByamlIter vec_iter; + def->param_values[def->num_params].vec3.x = 0; + def->param_values[def->num_params].vec3.y = 0; + def->param_values[def->num_params].vec3.z = 0; + if (paramKindHasValue(param_kind) && it.tryGetIterByKey(&vec_iter, "Value")) { + vec_iter.tryGetFloatByIndex(&def->param_values[def->num_params].vec3.x, 0); + vec_iter.tryGetFloatByIndex(&def->param_values[def->num_params].vec3.y, 1); + vec_iter.tryGetFloatByIndex(&def->param_values[def->num_params].vec3.z, 2); + } + + } else if (type == "String" || type == "AS") { + def->param_types[def->num_params] = AIDefParamType::String; + def->param_values[def->num_params].str = ""; + if (paramKindHasValue(param_kind)) + it.tryGetStringByKey(&def->param_values[def->num_params].str, "Value"); + + } else if (type == "Tree") { + def->param_types[def->num_params] = AIDefParamType::Tree; + } else if (type == "Actor") { + def->param_types[def->num_params] = AIDefParamType::BaseProcLink; + } else if (type == "MesTransceiverId") { + def->param_types[def->num_params] = AIDefParamType::MesTransceiverId; + } else if (type == "BaseProcHandle") { + def->param_types[def->num_params] = AIDefParamType::BaseProcHandle; + } else if (type == "AITreeVariablePointer") { + def->param_types[def->num_params] = AIDefParamType::AITreeVariablePointer; + } else if (type == "Rail") { + def->param_types[def->num_params] = AIDefParamType::Rail; + } else { + def->param_types[def->num_params] = AIDefParamType::Other; + } + } else { + def->param_types[def->num_params] = AIDefParamType::Other; + } + } else { + def->param_names[def->num_params] = nullptr; + def->param_types[def->num_params] = AIDefParamType::Other; + } + ++def->num_params; + } +} + +void AIClassDef::getDef(AIDef* def, const sead::SafeString& class_name, + AIDefInstParamKind param_kind, AIDefType class_type) const { + def->num_params = 0; + def->calc_timing = CalcTiming::AIAfter; + def->no_stop = false; + def->trigger_action = false; + def->dynamic_param_child = false; + def->_24b = 0; + + const s32 idx = getRawDefIdx(class_name, class_type); + if (idx < 0) + return; + + doGetDef(def, getRawDefs(class_type)[idx].iter, param_kind, class_type, + mData->inst_params_key_idx[s32(param_kind)]); +} + +bool AIClassDef::isSystemQuery(const sead::SafeString& query) const { + bool ret = false; + + const s32 idx = getRawDefIdx(query, AIDefType::Query); if (idx < 0) return false; - mData->defs[s32(AIDefType::Query)][idx].iter.tryGetBoolByKey(&ret, "SystemQuery"); + getRawDefs(AIDefType::Query)[idx].iter.tryGetBoolByKey(&ret, "SystemQuery"); return ret; } diff --git a/src/KingSystem/ActorSystem/actAiClassDef.h b/src/KingSystem/ActorSystem/actAiClassDef.h index 9208ab7c..127da9a6 100644 --- a/src/KingSystem/ActorSystem/actAiClassDef.h +++ b/src/KingSystem/ActorSystem/actAiClassDef.h @@ -1,8 +1,10 @@ #pragma once +#include #include #include #include +#include #include #include #include "KingSystem/Resource/resHandle.h" @@ -12,6 +14,10 @@ namespace ksys { +namespace act { +class BaseProcLink; +} + enum class AIDefType { AI = 0, Action = 1, @@ -19,7 +25,7 @@ enum class AIDefType { Query = 3, }; -static constexpr s32 NumAIDefTypes = 4; +constexpr s32 NumAIDefTypes = 4; enum class AIDefInstParamKind { Static = 0, @@ -28,6 +34,12 @@ enum class AIDefInstParamKind { AITree = 3, }; +constexpr bool paramKindHasValue(AIDefInstParamKind kind) { + return kind == AIDefInstParamKind::MapUnit || kind == AIDefInstParamKind::AITree; +} + +constexpr s32 NumAIDefInstParamKinds = 4; + enum class AIDefParamType { String = 0, Int = 1, @@ -44,9 +56,25 @@ enum class AIDefParamType { Other = 12, }; +enum class CalcTiming { + AIAfter = 0, + ModelAfter = 1, + ChangeDeleteState = 2, +}; + struct AIDef { union Value { + Value() {} u8 raw[16]; + const char* str; + s32 i; + f32 f; + sead::Vector3f vec3; + bool b; + void* tree; + void* variable_ptr; + u32 u; + act::BaseProcLink* proc_link; }; static constexpr size_t NumParametersMax = 64; @@ -54,7 +82,7 @@ struct AIDef { const char* param_names[NumParametersMax]; sead::SizedEnum param_types[NumParametersMax]; s32 num_params; - u32 calc_timing; + CalcTiming calc_timing; bool no_stop; bool trigger_action; bool dynamic_param_child; @@ -63,6 +91,17 @@ struct AIDef { }; KSYS_CHECK_SIZE_NX150(AIDef, 0x650); +struct AIDefSet { + static constexpr size_t NumChildrenMax = 256; + + const char* children[NumChildrenMax]; + s32 num_children; + AIDef dynamic_params; + AIDef map_unit_params; + AIDef ai_tree_params; +}; +KSYS_CHECK_SIZE_NX150(AIDefSet, 0x1af8); + class AIClassDef { SEAD_SINGLETON_DISPOSER(AIClassDef) AIClassDef() = default; @@ -71,8 +110,9 @@ class AIClassDef { public: void init(const sead::SafeString& aidef_file_name, sead::Heap* heap); + void getDef(const sead::SafeString& class_name, AIDefSet* set, AIDefType class_type) const; void getDef(AIDef* def, const sead::SafeString& class_name, AIDefInstParamKind param_kind, - AIDefType class_type); + AIDefType class_type) const; bool isSystemQuery(const sead::SafeString& query) const; @@ -88,10 +128,7 @@ private: sead::SafeArray iters; sead::SafeArray, NumAIDefTypes> defs; - s32 idx_StaticInstParams; - s32 idx_DynamicInstParams; - s32 idx_MapUnitInstParams; - s32 idx_AITreeVariables; + sead::SafeArray inst_params_key_idx; s32 idx_Childs = -1; al::ByamlIter root_iter; }; @@ -107,6 +144,14 @@ private: util::safeDelete(mData); } + s32 getRawDefIdx(const sead::SafeString& def_name, AIDefType type) const; + const sead::Buffer& getRawDefs(AIDefType type) const { + return mData->defs[s32(type)]; + } + + void doGetDef(AIDef* def, const al::ByamlIter& iter, AIDefInstParamKind param_kind, + AIDefType class_type, s32 key_idx) const; + Data* mData = nullptr; res::Handle mResHandle; }; diff --git a/src/KingSystem/Resource/resResourceAIProgram.cpp b/src/KingSystem/Resource/resResourceAIProgram.cpp index fad0ce6f..f7271975 100644 --- a/src/KingSystem/Resource/resResourceAIProgram.cpp +++ b/src/KingSystem/Resource/resResourceAIProgram.cpp @@ -375,7 +375,7 @@ bool AIProgram::parseDefParams(AIProgram::Definition* def, void* buffer, sead::H } else if (&mBehaviors == buffer) { AIClassDef::instance()->getDef(&aidef, def->mClassName, AIDefInstParamKind::Static, AIDefType::Behavior); - *param1 = aidef.calc_timing; + *param1 = u16(aidef.calc_timing); *param2 = aidef.no_stop; } else { AIClassDef::instance()->getDef(&aidef, def->mClassName, AIDefInstParamKind::Static,