mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Add Kdb files to ResourceTree
This commit is contained in:
parent
c8b6325a87
commit
1fca78fa71
7 changed files with 73 additions and 9 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit 15e7c8eb41867e6bbd3fe6a8885404df087bc7e7
|
Subproject commit 73010350338ecd7b98ad85d127bed08d7d8718d4
|
||||||
|
|
@ -40,7 +40,7 @@ public static unsafe class SkinMtrlPathEarlyProcessing
|
||||||
|
|
||||||
if (character->TempSlotData is not null)
|
if (character->TempSlotData is not null)
|
||||||
{
|
{
|
||||||
// TODO ClientStructs-ify
|
// TODO ClientStructs-ify (aers/FFXIVClientStructs#1564)
|
||||||
var handle = *(ModelResourceHandle**)((nint)character->TempSlotData + 0xE0 * slotIndex + 0x8);
|
var handle = *(ModelResourceHandle**)((nint)character->TempSlotData + 0xE0 * slotIndex + 0x8);
|
||||||
if (handle != null)
|
if (handle != null)
|
||||||
return handle;
|
return handle;
|
||||||
|
|
|
||||||
|
|
@ -338,6 +338,34 @@ internal partial record ResolveContext
|
||||||
return Utf8GamePath.FromByteString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
return Utf8GamePath.FromByteString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Utf8GamePath ResolveKineDriverModulePath(uint partialSkeletonIndex)
|
||||||
|
{
|
||||||
|
// Correctness and Safety:
|
||||||
|
// Resolving a KineDriver module path through the game's code can use EST metadata for human skeletons.
|
||||||
|
// Additionally, it can dereference null pointers for human equipment skeletons.
|
||||||
|
return ModelType switch
|
||||||
|
{
|
||||||
|
ModelType.Human => ResolveHumanKineDriverModulePath(partialSkeletonIndex),
|
||||||
|
_ => ResolveKineDriverModulePathNative(partialSkeletonIndex),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Utf8GamePath ResolveHumanKineDriverModulePath(uint partialSkeletonIndex)
|
||||||
|
{
|
||||||
|
var (raceCode, slot, set) = ResolveHumanSkeletonData(partialSkeletonIndex);
|
||||||
|
if (set.Id is 0)
|
||||||
|
return Utf8GamePath.Empty;
|
||||||
|
|
||||||
|
var path = GamePaths.Kdb.Customization(raceCode, slot, set);
|
||||||
|
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe Utf8GamePath ResolveKineDriverModulePathNative(uint partialSkeletonIndex)
|
||||||
|
{
|
||||||
|
var path = CharacterBase->ResolveKdbPathAsByteString(partialSkeletonIndex);
|
||||||
|
return Utf8GamePath.FromByteString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
private unsafe Utf8GamePath ResolveMaterialAnimationPath(ResourceHandle* imc)
|
private unsafe Utf8GamePath ResolveMaterialAnimationPath(ResourceHandle* imc)
|
||||||
{
|
{
|
||||||
var animation = ResolveImcData(imc).MaterialAnimationId;
|
var animation = ResolveImcData(imc).MaterialAnimationId;
|
||||||
|
|
|
||||||
|
|
@ -371,7 +371,8 @@ internal unsafe partial record ResolveContext(
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceNode? CreateNodeFromPartialSkeleton(PartialSkeleton* sklb, ResourceHandle* phybHandle, uint partialSkeletonIndex)
|
public ResourceNode? CreateNodeFromPartialSkeleton(PartialSkeleton* sklb, ResourceHandle* phybHandle, ResourceHandle* kdbHandle,
|
||||||
|
uint partialSkeletonIndex)
|
||||||
{
|
{
|
||||||
if (sklb is null || sklb->SkeletonResourceHandle is null)
|
if (sklb is null || sklb->SkeletonResourceHandle is null)
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -386,6 +387,8 @@ internal unsafe partial record ResolveContext(
|
||||||
node.Children.Add(skpNode);
|
node.Children.Add(skpNode);
|
||||||
if (CreateNodeFromPhyb(phybHandle, partialSkeletonIndex) is { } phybNode)
|
if (CreateNodeFromPhyb(phybHandle, partialSkeletonIndex) is { } phybNode)
|
||||||
node.Children.Add(phybNode);
|
node.Children.Add(phybNode);
|
||||||
|
if (CreateNodeFromKdb(kdbHandle, partialSkeletonIndex) is { } kdbNode)
|
||||||
|
node.Children.Add(kdbNode);
|
||||||
Global.Nodes.Add((path, (nint)sklb->SkeletonResourceHandle), node);
|
Global.Nodes.Add((path, (nint)sklb->SkeletonResourceHandle), node);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
|
|
@ -427,6 +430,24 @@ internal unsafe partial record ResolveContext(
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResourceNode? CreateNodeFromKdb(ResourceHandle* kdbHandle, uint partialSkeletonIndex)
|
||||||
|
{
|
||||||
|
if (kdbHandle is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var path = ResolveKineDriverModulePath(partialSkeletonIndex);
|
||||||
|
|
||||||
|
if (Global.Nodes.TryGetValue((path, (nint)kdbHandle), out var cached))
|
||||||
|
return cached;
|
||||||
|
|
||||||
|
var node = CreateNode(ResourceType.Phyb, 0, kdbHandle, path, false);
|
||||||
|
if (Global.WithUiData)
|
||||||
|
node.FallbackName = "KineDriver Module";
|
||||||
|
Global.Nodes.Add((path, (nint)kdbHandle), node);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
internal ResourceNode.UiData GuessModelUiData(Utf8GamePath gamePath)
|
internal ResourceNode.UiData GuessModelUiData(Utf8GamePath gamePath)
|
||||||
{
|
{
|
||||||
var path = gamePath.Path.Split((byte)'/');
|
var path = gamePath.Path.Split((byte)'/');
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,9 @@ public class ResourceNode : ICloneable
|
||||||
|
|
||||||
/// <summary> Whether to treat the file as protected (require holding the Mod Deletion Modifier to make a quick import). </summary>
|
/// <summary> Whether to treat the file as protected (require holding the Mod Deletion Modifier to make a quick import). </summary>
|
||||||
public bool Protected
|
public bool Protected
|
||||||
=> ForceProtected || Internal || Type is ResourceType.Shpk or ResourceType.Sklb or ResourceType.Pbd;
|
=> ForceProtected
|
||||||
|
|| Internal
|
||||||
|
|| Type is ResourceType.Shpk or ResourceType.Sklb or ResourceType.Skp or ResourceType.Phyb or ResourceType.Kdb or ResourceType.Pbd;
|
||||||
|
|
||||||
internal ResourceNode(ResourceType type, nint objectAddress, nint resourceHandle, ulong length, ResolveContext? resolveContext)
|
internal ResourceNode(ResourceType type, nint objectAddress, nint resourceHandle, ulong length, ResolveContext? resolveContext)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ public class ResourceTree(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddSkeleton(Nodes, genericContext, model->EID, model->Skeleton, model->BonePhysicsModule);
|
AddSkeleton(Nodes, genericContext, model);
|
||||||
AddMaterialAnimationSkeleton(Nodes, genericContext, model->MaterialAnimationSkeleton);
|
AddMaterialAnimationSkeleton(Nodes, genericContext, model->MaterialAnimationSkeleton);
|
||||||
|
|
||||||
AddWeapons(globalContext, model);
|
AddWeapons(globalContext, model);
|
||||||
|
|
@ -178,8 +178,7 @@ public class ResourceTree(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddSkeleton(weaponNodes, genericContext, subObject->EID, subObject->Skeleton, subObject->BonePhysicsModule,
|
AddSkeleton(weaponNodes, genericContext, subObject, $"Weapon #{weaponIndex}, ");
|
||||||
$"Weapon #{weaponIndex}, ");
|
|
||||||
AddMaterialAnimationSkeleton(weaponNodes, genericContext, subObject->MaterialAnimationSkeleton,
|
AddMaterialAnimationSkeleton(weaponNodes, genericContext, subObject->MaterialAnimationSkeleton,
|
||||||
$"Weapon #{weaponIndex}, ");
|
$"Weapon #{weaponIndex}, ");
|
||||||
|
|
||||||
|
|
@ -242,8 +241,11 @@ public class ResourceTree(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private unsafe void AddSkeleton(List<ResourceNode> nodes, ResolveContext context, CharacterBase* model, string prefix = "")
|
||||||
|
=> AddSkeleton(nodes, context, model->EID, model->Skeleton, model->BonePhysicsModule, *(void**)((nint)model + 0x160), prefix);
|
||||||
|
|
||||||
private unsafe void AddSkeleton(List<ResourceNode> nodes, ResolveContext context, void* eid, Skeleton* skeleton, BonePhysicsModule* physics,
|
private unsafe void AddSkeleton(List<ResourceNode> nodes, ResolveContext context, void* eid, Skeleton* skeleton, BonePhysicsModule* physics,
|
||||||
string prefix = "")
|
void* kineDriver, string prefix = "")
|
||||||
{
|
{
|
||||||
var eidNode = context.CreateNodeFromEid((ResourceHandle*)eid);
|
var eidNode = context.CreateNodeFromEid((ResourceHandle*)eid);
|
||||||
if (eidNode != null)
|
if (eidNode != null)
|
||||||
|
|
@ -259,7 +261,9 @@ public class ResourceTree(
|
||||||
for (var i = 0; i < skeleton->PartialSkeletonCount; ++i)
|
for (var i = 0; i < skeleton->PartialSkeletonCount; ++i)
|
||||||
{
|
{
|
||||||
var phybHandle = physics != null ? physics->BonePhysicsResourceHandles[i] : null;
|
var phybHandle = physics != null ? physics->BonePhysicsResourceHandles[i] : null;
|
||||||
if (context.CreateNodeFromPartialSkeleton(&skeleton->PartialSkeletons[i], phybHandle, (uint)i) is { } sklbNode)
|
// TODO ClientStructs-ify (aers/FFXIVClientStructs#1562)
|
||||||
|
var kdbHandle = kineDriver != null ? *(ResourceHandle**)((nint)kineDriver + 0x20 + 0x18 * i) : null;
|
||||||
|
if (context.CreateNodeFromPartialSkeleton(&skeleton->PartialSkeletons[i], phybHandle, kdbHandle, (uint)i) is { } sklbNode)
|
||||||
{
|
{
|
||||||
if (context.Global.WithUiData)
|
if (context.Global.WithUiData)
|
||||||
sklbNode.FallbackName = $"{prefix}Skeleton #{i}";
|
sklbNode.FallbackName = $"{prefix}Skeleton #{i}";
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,15 @@ internal static class StructExtensions
|
||||||
return ToOwnedByteString(character.ResolvePhybPath(pathBuffer, partialSkeletonIndex));
|
return ToOwnedByteString(character.ResolvePhybPath(pathBuffer, partialSkeletonIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static unsafe CiByteString ResolveKdbPathAsByteString(ref this CharacterBase character, uint partialSkeletonIndex)
|
||||||
|
{
|
||||||
|
// TODO ClientStructs-ify (aers/FFXIVClientStructs#1561)
|
||||||
|
var vf80 = (delegate* unmanaged<CharacterBase*, byte*, nuint, uint, byte*>)((nint*)character.VirtualTable)[80];
|
||||||
|
var pathBuffer = stackalloc byte[CharacterBase.PathBufferSize];
|
||||||
|
return ToOwnedByteString(vf80((CharacterBase*)Unsafe.AsPointer(ref character), pathBuffer, CharacterBase.PathBufferSize,
|
||||||
|
partialSkeletonIndex));
|
||||||
|
}
|
||||||
|
|
||||||
private static unsafe CiByteString ToOwnedByteString(CStringPointer str)
|
private static unsafe CiByteString ToOwnedByteString(CStringPointer str)
|
||||||
=> str.HasValue ? new CiByteString(str.Value).Clone() : CiByteString.Empty;
|
=> str.HasValue ? new CiByteString(str.Value).Clone() : CiByteString.Empty;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue