From 5737a6ea7c4a7d18f6290e3b30d3b1d18e64400e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 20 Mar 2021 15:49:25 +0100 Subject: [PATCH] ksys/res: Implement BoneControl --- data/uking_functions.csv | 50 ++-- ...s11BoneControl6parse_EPhmPN4sead4HeapE.bin | Bin 0 -> 7664 bytes .../Resource/resResourceBoneControl.cpp | 243 ++++++++++++++++++ .../Resource/resResourceBoneControl.h | 154 +++++++++++ 4 files changed, 422 insertions(+), 25 deletions(-) create mode 100644 expected/_ZN4ksys3res11BoneControl6parse_EPhmPN4sead4HeapE.bin diff --git a/data/uking_functions.csv b/data/uking_functions.csv index e94176cb..f8499c8a 100644 --- a/data/uking_functions.csv +++ b/data/uking_functions.csv @@ -96693,31 +96693,31 @@ 0x00000071012f7028,sub_71012F7028,340,_ZThn664_N4ksys3res9AwarenessD1Ev 0x00000071012f717c,sub_71012F717C,328,_ZThn664_N4ksys3res9AwarenessD0Ev 0x00000071012f72c4,sub_71012F72C4,372,_ZN4ksys3res9Awareness33checkDerivedRuntimeTypeInfoStaticEPKN4sead15RuntimeTypeInfo9InterfaceE -0x00000071012f7438,sub_71012F7438,520, -0x00000071012f7640,sub_71012F7640,940, -0x00000071012f79ec,sub_71012F79EC,8, -0x00000071012f79f4,sub_71012F79F4,8, -0x00000071012f79fc,sub_71012F79FC,36, -0x00000071012f7a20,sub_71012F7A20,40, -0x00000071012f7a48,sub_71012F7A48,40, -0x00000071012f7a70,nullsub_4835,4, -0x00000071012f7a74,nullsub_4836,4, -0x00000071012f7a78,sub_71012F7A78,7664, -0x00000071012f9868,sub_71012F9868,312, -0x00000071012f99a0,sub_71012F99A0,8, -0x00000071012f99a8,sub_71012F99A8,452, -0x00000071012f9b6c,sub_71012F9B6C,8, -0x00000071012f9b74,sub_71012F9B74,8, -0x00000071012f9b7c,sub_71012F9B7C,92, -0x00000071012f9bd8,sub_71012F9BD8,8, -0x00000071012f9be0,sub_71012F9BE0,8, -0x00000071012f9be8,sub_71012F9BE8,92, -0x00000071012f9c44,sub_71012F9C44,8, -0x00000071012f9c4c,sub_71012F9C4C,252, -0x00000071012f9d48,sub_71012F9D48,296, -0x00000071012f9e70,sub_71012F9E70,372, -0x00000071012f9fe4,sub_71012F9FE4,384, -0x00000071012fa164,sub_71012FA164,368, +0x00000071012f7438,sub_71012F7438,520,_ZN4ksys3res11BoneControlC1Ev +0x00000071012f7640,sub_71012F7640,940,_ZN4ksys3res11BoneControlD1Ev +0x00000071012f79ec,sub_71012F79EC,8,_ZThn632_N4ksys3res11BoneControlD1Ev +0x00000071012f79f4,sub_71012F79F4,8,_ZThn664_N4ksys3res11BoneControlD1Ev +0x00000071012f79fc,sub_71012F79FC,36,_ZN4ksys3res11BoneControlD0Ev +0x00000071012f7a20,sub_71012F7A20,40,_ZThn632_N4ksys3res11BoneControlD0Ev +0x00000071012f7a48,sub_71012F7A48,40,_ZThn664_N4ksys3res11BoneControlD0Ev +0x00000071012f7a70,nullsub_4835,4,_ZN4ksys3res11BoneControl9doCreate_EPhjPN4sead4HeapE +0x00000071012f7a74,nullsub_4836,4,_ZThn632_N4ksys3res11BoneControl9doCreate_EPhjPN4sead4HeapE +0x00000071012f7a78,sub_71012F7A78,7664,_ZN4ksys3res11BoneControl6parse_EPhmPN4sead4HeapE? +0x00000071012f9868,sub_71012F9868,312,_ZN4ksys3res15allocBoneGroupsERN4sead6BufferINS0_11BoneControl9BoneGroupEEEiPNS1_4HeapEi +0x00000071012f99a0,sub_71012F99A0,8,_ZThn632_N4ksys3res11BoneControl6parse_EPhmPN4sead4HeapE +0x00000071012f99a8,sub_71012F99A8,452,_ZNK4ksys3res11BoneControl12getBoneGroupERKN4sead14SafeStringBaseIcEE +0x00000071012f9b6c,sub_71012F9B6C,8,_ZN4ksys3res11BoneControl10ParamIO_m0Ev +0x00000071012f9b74,sub_71012F9B74,8,_ZNK4ksys3res11BoneControl27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE +0x00000071012f9b7c,sub_71012F9B7C,92,_ZNK4ksys3res11BoneControl18getRuntimeTypeInfoEv +0x00000071012f9bd8,sub_71012F9BD8,8,_ZNK4ksys3res11BoneControl10needsParseEv +0x00000071012f9be0,sub_71012F9BE0,8,_ZThn632_NK4ksys3res11BoneControl27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE +0x00000071012f9be8,sub_71012F9BE8,92,_ZThn632_NK4ksys3res11BoneControl18getRuntimeTypeInfoEv +0x00000071012f9c44,sub_71012F9C44,8,_ZThn632_NK4ksys3res11BoneControl10needsParseEv +0x00000071012f9c4c,sub_71012F9C4C,252,_ZN4ksys3res11BoneControl16FootIkControllerC2Ev +0x00000071012f9d48,sub_71012F9D48,296,_ZN4ksys3res11BoneControl5SpineC2Ev +0x00000071012f9e70,sub_71012F9E70,372,_ZN4ksys3res11BoneControl33checkDerivedRuntimeTypeInfoStaticEPKN4sead15RuntimeTypeInfo9InterfaceE +0x00000071012f9fe4,sub_71012F9FE4,384,_ZN4ksys3res11BoneControl6EyeSetC2Ev +0x00000071012fa164,sub_71012FA164,368,_ZN4ksys3res11BoneControl9SpineNodeC2Ev 0x00000071012fa2d4,sub_71012FA2D4,272, 0x00000071012fa3e4,nullsub_4837,4, 0x00000071012fa3e8,nullsub_4838,4, diff --git a/expected/_ZN4ksys3res11BoneControl6parse_EPhmPN4sead4HeapE.bin b/expected/_ZN4ksys3res11BoneControl6parse_EPhmPN4sead4HeapE.bin new file mode 100644 index 0000000000000000000000000000000000000000..96650dc9ea1445c58c5401111751bbb819637be7 GIT binary patch literal 7664 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 buffer, int size, sead::Heap* heap, + int align = sizeof(void*)) { + return buffer.tryAllocBuffer(size, heap, align); +} + +// NON_MATCHING: mFootIkController.isInvalidFt (???) +bool BoneControl::parse_(u8* data, size_t size, sead::Heap* heap) { + if (!data) + return true; + + agl::utl::ResParameterArchive archive{data}; + const auto root = archive.getRootList(); + + mWhole.neckAndEyeRatio.init(0.0, "neckAndEyeRatio", "首向けと眼球制御の比率", &mWhole.obj); + mWhole.isFaceCtrlInvalid.init(true, "isFaceCtrlInvalid", "顔全体無効", &mWhole.obj); + addObj(&mWhole.obj, "Whole"); + + mEyeBall.isEyeBallCtrlInvalid.init(false, "isEyeBallCtrlInvalid", "無効にする", &mEyeBall.obj); + mEyeBall.isEyeBallRotWorldAxis.init(false, "isEyeBallRotWorldAxis", "ワールド軸で回転する", + &mEyeBall.obj); + mEyeBall.eyeBallSRTName.init("", "eyeBallSRTName", "眼球SRT名", &mEyeBall.obj); + mEyeBall.eyeRotRateLR.init(0.0, "eyeRotRateLR", "左右回転比率", &mEyeBall.obj); + mEyeBall.eyeRotRateUD.init(0.0, "eyeRotRateUD", "上下回転比率", &mEyeBall.obj); + mEyeBall.eyeMinRotPerFrame.init(0.5, "eyeMinRotPerFrame", "フレーム毎の最小回転量", + &mEyeBall.obj); + mEyeBall.eyeMaxRotPerFrame.init(6.0, "eyeMaxRotPerFrame", "フレーム毎の最大回転量", + &mEyeBall.obj); + mEyeBall.eyeSetNum.init(0, "eyeSetNum", "眼球セット数", &mEyeBall.obj); + addObj(&mEyeBall.obj, "EyeBall"); + + applyResParameterArchive(agl::utl::ResParameterArchive{data}); + + const int eye_set_num = mEyeBall.eyeSetNum.ref(); + if (eye_set_num > 0) { + if (!mEyeSets.tryAllocBuffer(eye_set_num, heap)) + return false; + + for (int i = 0; i < eye_set_num; ++i) { + sead::FormatFixedSafeString<64> name(""); + name.format("EyeSet_%02d", i); + + mEyeSets[i].isControlTexture.init(false, "isControlTexture", "テクスチャ制御する", + &mEyeSets[i].obj); + mEyeSets[i].materialName.init("", "materialName", "マテリアル名", &mEyeSets[i].obj); + mEyeSets[i].boneName.init("", "boneName", "ボーン名", &mEyeSets[i].obj); + mEyeSets[i].forwardBoneName.init("", "forwardBoneName", "前方方向ボーン名", + &mEyeSets[i].obj); + mEyeSets[i].forwardAxis.init(1, "forwardAxis", "前方方向とする軸", &mEyeSets[i].obj); + mEyeSets[i].axisLR.init(0, "axisLR", "左右回転軸", &mEyeSets[i].obj); + mEyeSets[i].axisUD.init(0, "axisUD", "上下回転軸", &mEyeSets[i].obj); + mEyeSets[i].lTransLimit.init(0.0, "lTransLimit", "左移動量上限", &mEyeSets[i].obj); + mEyeSets[i].rTransLimit.init(0.0, "rTransLimit", "右移動量上限", &mEyeSets[i].obj); + mEyeSets[i].dTransLimit.init(0.0, "dTransLimit", "下移動量上限", &mEyeSets[i].obj); + mEyeSets[i].uTransLimit.init(0.0, "uTransLimit", "上移動量上限", &mEyeSets[i].obj); + mEyeSets[i].isCorrectForward.init(false, "isCorrectForward", "前方方向を補正する", + &mEyeSets[i].obj); + mEyeSets[i].axisCorrect.init(0, "axisCorrect", "補正軸", &mEyeSets[i].obj); + mEyeSets[i].correctRot.init(0.0, "correctRot", "補正量", &mEyeSets[i].obj); + mEyeSets[i].lRotLimit.init(0.0, "lRotLimit", "左向き角上限", &mEyeSets[i].obj); + mEyeSets[i].rRotLimit.init(0.0, "rRotLimit", "右向き角上限", &mEyeSets[i].obj); + mEyeSets[i].dRotLimit.init(0.0, "dRotLimit", "下向き角上限", &mEyeSets[i].obj); + mEyeSets[i].uRotLimit.init(0.0, "uRotLimit", "上向き角上限", &mEyeSets[i].obj); + mEyeSets[i].offset.init({0.0, 0.0, 0.0}, "offset", "オフセット", &mEyeSets[i].obj); + + addObj(&mEyeSets[i].obj, name); + } + } + + mSpine.isInvalid.init(false, "isInvalid", "無効にする", &mSpine.obj); + mSpine.isBasisSelfPosNeckLR.init(false, "isBasisSelfPosNeckLR", + "左右計算を自分の位置基準にする", &mSpine.obj); + mSpine.isBasisSelfPosNeckUD.init(false, "isBasisSelfPosNeckUD", + "上下計算を自分の位置基準にする", &mSpine.obj); + mSpine.isBattleNeckRecalcUD.init(false, "isBattleNeckRecalcUD", + "戦闘時の首向け上下角を再計算する", &mSpine.obj); + mSpine.spineDisableBaseDirAlongXZ.init(false, "spineDisableBaseDirAlongXZ", + "基準方向をXZ平面に沿わせない", &mSpine.obj); + mSpine.spineRotRate.init(0.0, "spineRotRate", "回転比率", &mSpine.obj); + mSpine.spineRetRotRate.init(0.0, "spineRetRotRate", "戻り回転比率", &mSpine.obj); + mSpine.spineNeckBaseBone.init("", "spineNeckBaseBone", "首向け基準位置ボーン名", &mSpine.obj); + mSpine.neckPosOffset.init(sead::Vector3f::zero, "neckPosOffset", "オフセット", &mSpine.obj); + mSpine.spineNodeNum.init(0, "spineNodeNum", "背骨ノード数", &mSpine.obj); + mSpine.spineNeckNodeNum.init(0, "spineNeckNodeNum", "首とみなすノード数", &mSpine.obj); + addObj(&mSpine.obj, "Spine"); + + applyResParameterArchive(agl::utl::ResParameterArchive{data}); + + const auto spine_node_num = mSpine.spineNodeNum.ref(); + if (spine_node_num > 0) { + if (!mSpine.spineNodes.tryAllocBuffer(spine_node_num, heap)) + return false; + + for (int i = 0; i < spine_node_num; ++i) { + sead::FormatFixedSafeString<64> name(""); + name.format("SpineNode_%02d", i); + + mSpine.spineNodes[i].boneName.init("", "boneName", "ボーン名", + &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].isRotWorldAxis.init( + false, "isRotWorldAxis", "ワールド軸で回転する", &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].axisLR.init(0, "axisLR", "左右回転軸", &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].axisUD.init(0, "axisUD", "上下回転軸", &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].lLimit.init(0.0, "lLimit", "左向き角上限", + &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].rLimit.init(0.0, "rLimit", "右向き角上限", + &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].dLimit.init(0.0, "dLimit", "下向き角上限", + &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].uLimit.init(0.0, "uLimit", "上向き角上限", + &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].lBattleLimit.init(0.0, "lBattleLimit", "左向き角上限(戦闘時)", + &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].rBattleLimit.init(0.0, "rBattleLimit", "右向き角上限(戦闘時)", + &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].dBattleLimit.init(0.0, "dBattleLimit", "下向き角上限(戦闘時)", + &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].uBattleLimit.init(0.0, "uBattleLimit", "上向き角上限(戦闘時)", + &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].minRotPerFrame.init( + 0.5, "minRotPerFrame", "フレーム毎の最小回転量", &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].maxRotPerFrame.init( + 6.0, "maxRotPerFrame", "フレーム毎の最大回転量", &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].isEnableCorrect.init( + false, "isEnableCorrect", "左右回転時に補正する", &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].axisCorrect.init(0, "axisCorrect", "補正回転軸", + &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].lCorrect.init(0.0, "lCorrect", "左向き補正回転量", + &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].rCorrect.init(0.0, "rCorrect", "右向き補正回転量", + &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].lBattleCorrect.init( + 0.0, "lBattleCorrect", "左向き補正回転量(戦闘時)", &mSpine.spineNodes[i].obj); + mSpine.spineNodes[i].rBattleCorrect.init( + 0.0, "rBattleCorrect", "右向き補正回転量(戦闘時)", &mSpine.spineNodes[i].obj); + + addObj(&mSpine.spineNodes[i].obj, name); + } + } + + mFootIkController.isInvalidFt.init(true, "isInvalidFt", "無効", &mFootIkController.obj); + mFootIkController.calculateTypeFt.init(1, "calculateTypeFt", "計算タイプ", + &mFootIkController.obj); + mFootIkController.ankleOffsetYFt.init(0.125, "ankleOffsetYFt", "地面から足首(Ankle)までの高さ", + &mFootIkController.obj); + mFootIkController.ankleOffsetAngleDegFt.init(-27.0, "ankleOffsetAngleDegFt", + "足首オフセット角度(Deg)", &mFootIkController.obj); + mFootIkController.ankleAngleLimitUpDegFt.init( + 90.0, "ankleAngleLimitUpDegFt", "上方向への足首回転最大角度(Deg)", &mFootIkController.obj); + mFootIkController.ankleAngleLimitDownDegFt.init(-90.0, "ankleAngleLimitDownDegFt", + "下方向への足首回転最大角度(Deg)", + &mFootIkController.obj); + mFootIkController.ankleHeightLimitRateFt.init( + 0.8, "ankleHeightLimitRateFt", "地面に対して足位置の制限比率", &mFootIkController.obj); + mFootIkController.waistDownRateFt.init(0.7, "waistDownRateFt", "腰を落とす最長比率", + &mFootIkController.obj); + mFootIkController.kneeRotateAgnleMinDegFt.init( + 0.0, "kneeRotateAgnleMinDegFt", "ヒザの最小回転角度(Deg)", &mFootIkController.obj); + mFootIkController.kneeRotateAgnleMaxDegFt.init( + 180.0, "kneeRotateAgnleMaxDegFt", "ヒザの最大回転角度(Deg)", &mFootIkController.obj); + mFootIkController.enableLimitThighAngleFt.init( + false, "enableLimitThighAngleFt", "モモの角度制限を行なうか?", &mFootIkController.obj); + mFootIkController.thighRotateAngleMinDegFt.init( + -180.0, "thighRotateAngleMinDegFt", "モモの最小回転角度(Deg)", &mFootIkController.obj); + mFootIkController.thighRotateAngleMaxDegFt.init( + 180.0, "thighRotateAngleMaxDegFt", "モモの最大回転角度(Deg)", &mFootIkController.obj); + addObj(&mFootIkController.obj, "FootIkController"); + + const auto bone_groups = agl::utl::getResParameterList(root, "BoneGroups"); + if (bone_groups.ptr() && bone_groups.getResParameterListNum() != 0) { + if (!allocBoneGroups(mBoneGroups, bone_groups.getResParameterListNum(), heap)) + return false; + + sead::FixedSafeString<32> bone_group_name{"BoneGroup_"}; + const auto bone_group_name_base_len = bone_group_name.calcLength(); + + sead::FixedSafeString<32> bone_name{"Bone_"}; + const auto bone_name_base_len = bone_name.calcLength(); + + for (auto it = mBoneGroups.begin(), end = mBoneGroups.end(); it != end; ++it) { + const auto list = bone_groups.getResParameterList(it.getIndex()); + if (!list.ptr()) + continue; + + const auto bones = agl::utl::getResParameterObj(list, "Bones"); + if (!bones.ptr()) + continue; + + it->groupName.init("", "GroupName", "グループ名", &it->paramObj); + + const auto num_bones = bones.getNum(); + if (num_bones != 0 && !it->bones.tryAllocBuffer(num_bones, heap)) + return false; + + auto& bones_obj = it->bonesObj; + for (auto b = it->bones.begin(), bone_end = it->bones.end(); b != bone_end; ++b) { + bone_name.trim(bone_name_base_len); + bone_name.appendWithFormat("%d", b.getIndex()); + b->name.init("", bone_name, "ボーン名", &bones_obj); + } + + it->list.addObj(&bones_obj, "Bones"); + it->list.addObj(&it->paramObj, "Param"); + + bone_group_name.trim(bone_group_name_base_len); + bone_group_name.appendWithFormat("%d", it.getIndex()); + mBoneGroupsList.addList(&it->list, bone_group_name); + } + + addList(&mBoneGroupsList, "BoneGroups"); + } + + applyResParameterArchive(agl::utl::ResParameterArchive{data}); + return true; +} + +const BoneControl::BoneGroup* BoneControl::getBoneGroup(const sead::SafeString& name) const { + const auto idx = mBoneGroups.binarySearch( + name, +[](const BoneGroup& group, const sead::SafeString& key) { + return group.groupName.ref().compare(key); + }); + if (idx == -1) + return nullptr; + return &mBoneGroups[idx]; +} + +} // namespace ksys::res diff --git a/src/KingSystem/Resource/resResourceBoneControl.h b/src/KingSystem/Resource/resResourceBoneControl.h index 9adedd57..2bf131d6 100644 --- a/src/KingSystem/Resource/resResourceBoneControl.h +++ b/src/KingSystem/Resource/resResourceBoneControl.h @@ -1,13 +1,167 @@ #pragma once +#include +#include +#include +#include +#include +#include #include "KingSystem/Resource/resResource.h" #include "KingSystem/Utils/ParamIO.h" +#include "KingSystem/Utils/Types.h" namespace ksys::res { // TODO class BoneControl : public ParamIO, public Resource { SEAD_RTTI_OVERRIDE(BoneControl, Resource) +public: + struct Whole { + agl::utl::ParameterObj obj; + agl::utl::Parameter isFaceCtrlInvalid; + agl::utl::Parameter neckAndEyeRatio; + }; + KSYS_CHECK_SIZE_NX150(Whole, 0x70); + + struct EyeBall { + agl::utl::ParameterObj obj; + agl::utl::Parameter isEyeBallCtrlInvalid; + agl::utl::Parameter isEyeBallRotWorldAxis; + agl::utl::Parameter eyeBallSRTName; + agl::utl::Parameter eyeRotRateLR; + agl::utl::Parameter eyeRotRateUD; + agl::utl::Parameter eyeMinRotPerFrame; + agl::utl::Parameter eyeMaxRotPerFrame; + agl::utl::Parameter eyeSetNum; + }; + KSYS_CHECK_SIZE_NX150(EyeBall, 0x138); + + struct EyeSet { + agl::utl::ParameterObj obj; + agl::utl::Parameter boneName; + agl::utl::Parameter isControlTexture; + agl::utl::Parameter materialName; + agl::utl::Parameter forwardBoneName; + agl::utl::Parameter forwardAxis; + agl::utl::Parameter axisLR; + agl::utl::Parameter axisUD; + agl::utl::Parameter lTransLimit; + agl::utl::Parameter rTransLimit; + agl::utl::Parameter dTransLimit; + agl::utl::Parameter uTransLimit; + agl::utl::Parameter isCorrectForward; + agl::utl::Parameter axisCorrect; + agl::utl::Parameter correctRot; + agl::utl::Parameter lRotLimit; + agl::utl::Parameter rRotLimit; + agl::utl::Parameter dRotLimit; + agl::utl::Parameter uRotLimit; + agl::utl::Parameter offset; + }; + KSYS_CHECK_SIZE_NX150(EyeSet, 0x2b0); + + struct Bone { + agl::utl::Parameter name; + }; + KSYS_CHECK_SIZE_NX150(Bone, 0x28); + + struct BoneGroup { + agl::utl::ParameterList list; + agl::utl::ParameterObj paramObj; + agl::utl::ParameterObj bonesObj; + agl::utl::Parameter groupName; + sead::Buffer bones; + }; + KSYS_CHECK_SIZE_NX150(BoneGroup, 0xe0); + + struct SpineNode { + agl::utl::ParameterObj obj; + agl::utl::Parameter boneName; + agl::utl::Parameter isRotWorldAxis; + agl::utl::Parameter axisLR; + agl::utl::Parameter axisUD; + agl::utl::Parameter lLimit; + agl::utl::Parameter rLimit; + agl::utl::Parameter dLimit; + agl::utl::Parameter uLimit; + agl::utl::Parameter lBattleLimit; + agl::utl::Parameter rBattleLimit; + agl::utl::Parameter dBattleLimit; + agl::utl::Parameter uBattleLimit; + agl::utl::Parameter isEnableCorrect; + agl::utl::Parameter axisCorrect; + agl::utl::Parameter lCorrect; + agl::utl::Parameter rCorrect; + agl::utl::Parameter lBattleCorrect; + agl::utl::Parameter rBattleCorrect; + agl::utl::Parameter minRotPerFrame; + agl::utl::Parameter maxRotPerFrame; + }; + KSYS_CHECK_SIZE_NX150(SpineNode, 0x2b8); + + struct Spine { + agl::utl::ParameterObj obj; + agl::utl::ParameterList list; + agl::utl::Parameter isInvalid; + agl::utl::Parameter isBasisSelfPosNeckLR; + agl::utl::Parameter isBasisSelfPosNeckUD; + agl::utl::Parameter isBattleNeckRecalcUD; + agl::utl::Parameter spineDisableBaseDirAlongXZ; + agl::utl::Parameter spineRotRate; + agl::utl::Parameter spineRetRotRate; + agl::utl::Parameter spineNeckBaseBone; + agl::utl::Parameter neckPosOffset; + agl::utl::Parameter spineNeckNodeNum; + agl::utl::Parameter spineNodeNum; + sead::Buffer spineNodes; + }; + KSYS_CHECK_SIZE_NX150(Spine, 0x1f8); + + struct FootIkController { + agl::utl::ParameterObj obj; + agl::utl::Parameter isInvalidFt; + agl::utl::Parameter calculateTypeFt; + agl::utl::Parameter ankleOffsetYFt; + agl::utl::Parameter ankleOffsetAngleDegFt; + agl::utl::Parameter ankleAngleLimitUpDegFt; + agl::utl::Parameter ankleAngleLimitDownDegFt; + agl::utl::Parameter ankleHeightLimitRateFt; + agl::utl::Parameter waistDownRateFt; + agl::utl::Parameter kneeRotateAgnleMinDegFt; + agl::utl::Parameter kneeRotateAgnleMaxDegFt; + agl::utl::Parameter enableLimitThighAngleFt; + agl::utl::Parameter thighRotateAngleMinDegFt; + agl::utl::Parameter thighRotateAngleMaxDegFt; + }; + KSYS_CHECK_SIZE_NX150(FootIkController, 0x1d0); + + BoneControl(); + ~BoneControl() override; + + void doCreate_(u8* buffer, u32 buffer_size, sead::Heap* heap) override; + bool parse_(u8* data, size_t size, sead::Heap* heap) override; + bool ParamIO_m0() override { return true; } + bool needsParse() const override { return true; } + + const Whole& getWhole() const { return mWhole; } + const Spine& getSpine() const { return mSpine; } + const EyeBall& getEyeBall() const { return mEyeBall; } + const sead::Buffer& getEyeSets() const { return mEyeSets; } + const FootIkController& getFootIkController() const { return mFootIkController; } + const sead::Buffer& getBoneGroups() const { return mBoneGroups; } + + const BoneGroup* getBoneGroup(const sead::SafeString& name) const; + +private: + Whole mWhole; + Spine mSpine; + EyeBall mEyeBall; + agl::utl::ParameterList _650; + sead::Buffer mEyeSets; + FootIkController mFootIkController; + agl::utl::ParameterList mBoneGroupsList; + sead::Buffer mBoneGroups; }; +KSYS_CHECK_SIZE_NX150(BoneControl, 0x8d0); } // namespace ksys::res