Some auto-formatting and ROS iteration for lookups.

This commit is contained in:
Ottermandias 2023-09-03 13:13:35 +02:00
parent cca626449d
commit 2a2fa3bf1d
4 changed files with 71 additions and 67 deletions

@ -1 +1 @@
Subproject commit 728dd8c33f8b43f7a2725ac7c8886fe7cb3f04a9 Subproject commit 8c7a309d039fdf008c85cf51923b4eac51b32428

View file

@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.System.Resource; using FFXIVClientStructs.FFXIV.Client.System.Resource;
using OtterGui;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
@ -15,19 +16,20 @@ using Penumbra.UI;
namespace Penumbra.Interop.ResourceTree; namespace Penumbra.Interop.ResourceTree;
internal record class GlobalResolveContext(Configuration Config, IObjectIdentifier Identifier, TreeBuildCache TreeBuildCache, ModCollection Collection, int Skeleton, bool WithUIData, internal record GlobalResolveContext(Configuration Config, IObjectIdentifier Identifier, TreeBuildCache TreeBuildCache,
bool RedactExternalPaths) ModCollection Collection, int Skeleton, bool WithUiData, bool RedactExternalPaths)
{ {
public readonly Dictionary<nint, ResourceNode> Nodes = new(128); public readonly Dictionary<nint, ResourceNode> Nodes = new(128);
public ResolveContext CreateContext(EquipSlot slot, CharacterArmor equipment) public ResolveContext CreateContext(EquipSlot slot, CharacterArmor equipment)
=> new(Config, Identifier, TreeBuildCache, Collection, Skeleton, WithUIData, RedactExternalPaths, Nodes, slot, equipment); => new(Config, Identifier, TreeBuildCache, Collection, Skeleton, WithUiData, RedactExternalPaths, Nodes, slot, equipment);
} }
internal record class ResolveContext(Configuration Config, IObjectIdentifier Identifier, TreeBuildCache TreeBuildCache, ModCollection Collection, int Skeleton, bool WithUIData, internal record ResolveContext(Configuration Config, IObjectIdentifier Identifier, TreeBuildCache TreeBuildCache, ModCollection Collection,
bool RedactExternalPaths, Dictionary<nint, ResourceNode> Nodes, EquipSlot Slot, CharacterArmor Equipment) int Skeleton, bool WithUiData, bool RedactExternalPaths, Dictionary<nint, ResourceNode> Nodes, EquipSlot Slot, CharacterArmor Equipment)
{ {
private static readonly ByteString ShpkPrefix = ByteString.FromSpanUnsafe("shader/sm5/shpk"u8, true, true, true); private static readonly ByteString ShpkPrefix = ByteString.FromSpanUnsafe("shader/sm5/shpk"u8, true, true, true);
private unsafe ResourceNode? CreateNodeFromShpk(ShaderPackageResourceHandle* resourceHandle, ByteString gamePath, bool @internal) private unsafe ResourceNode? CreateNodeFromShpk(ShaderPackageResourceHandle* resourceHandle, ByteString gamePath, bool @internal)
{ {
if (Nodes.TryGetValue((nint)resourceHandle, out var cached)) if (Nodes.TryGetValue((nint)resourceHandle, out var cached))
@ -73,26 +75,28 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
return CreateNodeFromGamePath(ResourceType.Tex, (nint)resourceHandle->KernelTexture, &resourceHandle->Handle, path, @internal); return CreateNodeFromGamePath(ResourceType.Tex, (nint)resourceHandle->KernelTexture, &resourceHandle->Handle, path, @internal);
} }
private unsafe ResourceNode CreateNodeFromGamePath(ResourceType type, nint objectAddress, ResourceHandle* resourceHandle, Utf8GamePath gamePath, bool @internal) private unsafe ResourceNode CreateNodeFromGamePath(ResourceType type, nint objectAddress, ResourceHandle* resourceHandle,
Utf8GamePath gamePath, bool @internal)
{ {
var fullPath = Utf8GamePath.FromByteString(GetResourceHandlePath(resourceHandle), out var p) ? new FullPath(p) : FullPath.Empty; var fullPath = Utf8GamePath.FromByteString(GetResourceHandlePath(resourceHandle), out var p) ? new FullPath(p) : FullPath.Empty;
if (fullPath.InternalName.IsEmpty) if (fullPath.InternalName.IsEmpty)
fullPath = Collection.ResolvePath(gamePath) ?? new FullPath(gamePath); fullPath = Collection.ResolvePath(gamePath) ?? new FullPath(gamePath);
var node = new ResourceNode(default, type, objectAddress, (nint)resourceHandle, gamePath, FilterFullPath(fullPath), GetResourceHandleLength(resourceHandle), @internal); var node = new ResourceNode(default, type, objectAddress, (nint)resourceHandle, gamePath, FilterFullPath(fullPath),
GetResourceHandleLength(resourceHandle), @internal);
if (resourceHandle != null) if (resourceHandle != null)
Nodes.Add((nint)resourceHandle, node); Nodes.Add((nint)resourceHandle, node);
return node; return node;
} }
private unsafe ResourceNode? CreateNodeFromResourceHandle(ResourceType type, nint objectAddress, ResourceHandle* handle, bool @internal, private unsafe ResourceNode? CreateNodeFromResourceHandle(ResourceType type, nint objectAddress, ResourceHandle* handle, bool @internal,
bool withName) bool withName)
{ {
var fullPath = Utf8GamePath.FromByteString(GetResourceHandlePath(handle), out var p) ? new FullPath(p) : FullPath.Empty; var fullPath = Utf8GamePath.FromByteString(GetResourceHandlePath(handle), out var p) ? new FullPath(p) : FullPath.Empty;
if (fullPath.InternalName.IsEmpty) if (fullPath.InternalName.IsEmpty)
return null; return null;
var gamePaths = Collection.ReverseResolvePath(fullPath).ToList(); var gamePaths = Collection.ReverseResolvePath(fullPath).ToList();
fullPath = FilterFullPath(fullPath); fullPath = FilterFullPath(fullPath);
@ -100,14 +104,16 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
gamePaths = Filter(gamePaths); gamePaths = Filter(gamePaths);
if (gamePaths.Count == 1) if (gamePaths.Count == 1)
return new ResourceNode(withName ? GuessUIDataFromPath(gamePaths[0]) : default, type, objectAddress, (nint)handle, gamePaths[0], fullPath, return new ResourceNode(withName ? GuessUIDataFromPath(gamePaths[0]) : default, type, objectAddress, (nint)handle, gamePaths[0],
fullPath,
GetResourceHandleLength(handle), @internal); GetResourceHandleLength(handle), @internal);
Penumbra.Log.Information($"Found {gamePaths.Count} game paths while reverse-resolving {fullPath} in {Collection.Name}:"); Penumbra.Log.Information($"Found {gamePaths.Count} game paths while reverse-resolving {fullPath} in {Collection.Name}:");
foreach (var gamePath in gamePaths) foreach (var gamePath in gamePaths)
Penumbra.Log.Information($"Game path: {gamePath}"); Penumbra.Log.Information($"Game path: {gamePath}");
return new ResourceNode(default, type, objectAddress, (nint)handle, gamePaths.ToArray(), fullPath, GetResourceHandleLength(handle), @internal); return new ResourceNode(default, type, objectAddress, (nint)handle, gamePaths.ToArray(), fullPath, GetResourceHandleLength(handle),
@internal);
} }
public unsafe ResourceNode? CreateHumanSkeletonNode(GenderRace gr) public unsafe ResourceNode? CreateHumanSkeletonNode(GenderRace gr)
@ -130,7 +136,7 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
if (node == null) if (node == null)
return null; return null;
if (WithUIData) if (WithUiData)
{ {
var uiData = GuessModelUIData(node.GamePath); var uiData = GuessModelUIData(node.GamePath);
node = node.WithUIData(uiData.PrependName("IMC: ")); node = node.WithUIData(uiData.PrependName("IMC: "));
@ -146,7 +152,7 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
if (Nodes.TryGetValue((nint)tex, out var cached)) if (Nodes.TryGetValue((nint)tex, out var cached))
return cached; return cached;
var node = CreateNodeFromResourceHandle(ResourceType.Tex, (nint)tex->KernelTexture, &tex->Handle, false, WithUIData); var node = CreateNodeFromResourceHandle(ResourceType.Tex, (nint)tex->KernelTexture, &tex->Handle, false, WithUiData);
if (node != null) if (node != null)
Nodes.Add((nint)tex, node); Nodes.Add((nint)tex, node);
@ -161,11 +167,11 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
if (Nodes.TryGetValue((nint)mdl->ResourceHandle, out var cached)) if (Nodes.TryGetValue((nint)mdl->ResourceHandle, out var cached))
return cached; return cached;
var node = CreateNodeFromResourceHandle(ResourceType.Mdl, (nint) mdl, mdl->ResourceHandle, false, false); var node = CreateNodeFromResourceHandle(ResourceType.Mdl, (nint)mdl, mdl->ResourceHandle, false, false);
if (node == null) if (node == null)
return null; return null;
if (WithUIData) if (WithUiData)
node = node.WithUIData(GuessModelUIData(node.GamePath)); node = node.WithUIData(GuessModelUIData(node.GamePath));
for (var i = 0; i < mdl->MaterialCount; i++) for (var i = 0; i < mdl->MaterialCount; i++)
@ -174,7 +180,7 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
var mtrlNode = CreateNodeFromMaterial(mtrl); var mtrlNode = CreateNodeFromMaterial(mtrl);
if (mtrlNode != null) if (mtrlNode != null)
// Don't keep the material's name if it's redundant with the model's name. // Don't keep the material's name if it's redundant with the model's name.
node.Children.Add(WithUIData node.Children.Add(WithUiData
? mtrlNode.WithUIData((string.Equals(mtrlNode.Name, node.Name, StringComparison.Ordinal) ? null : mtrlNode.Name) ? mtrlNode.WithUIData((string.Equals(mtrlNode.Name, node.Name, StringComparison.Ordinal) ? null : mtrlNode.Name)
?? $"Material #{i}", mtrlNode.Icon) ?? $"Material #{i}", mtrlNode.Icon)
: mtrlNode); : mtrlNode);
@ -191,33 +197,21 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
{ {
if ((texFlags & 0x001F) != 0x001F) if ((texFlags & 0x001F) != 0x001F)
return (ushort)(texFlags & 0x001F); return (ushort)(texFlags & 0x001F);
else if ((texFlags & 0x03E0) != 0x03E0) if ((texFlags & 0x03E0) != 0x03E0)
return (ushort)((texFlags >> 5) & 0x001F); return (ushort)((texFlags >> 5) & 0x001F);
else
return (ushort)((texFlags >> 10) & 0x001F); return (ushort)((texFlags >> 10) & 0x001F);
} }
static uint? GetTextureSamplerId(Material* mtrl, TextureResourceHandle* handle) static uint? GetTextureSamplerId(Material* mtrl, TextureResourceHandle* handle)
{ => mtrl->TextureSpan.FindFirst(p => p.ResourceHandle == handle, out var p)
var textures = mtrl->Textures; ? p.Id
for (var i = 0; i < mtrl->TextureCount; ++i) : null;
{
if (textures[i].ResourceHandle == handle)
return textures[i].Id;
}
return null;
}
static uint? GetSamplerCrcById(ShaderPackage* shpk, uint id) static uint? GetSamplerCrcById(ShaderPackage* shpk, uint id)
{ => new ReadOnlySpan<ShaderPackageUtility.Sampler>(shpk->Samplers, shpk->SamplerCount).FindFirst(s => s.Id == id, out var s)
var samplers = (ShaderPackageUtility.Sampler*)shpk->Samplers; ? s.Crc
for (var i = 0; i < shpk->SamplerCount; ++i) : null;
{
if (samplers[i].Id == id)
return samplers[i].Crc;
}
return null;
}
if (mtrl == null) if (mtrl == null)
return null; return null;
@ -225,29 +219,30 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
var resource = mtrl->ResourceHandle; var resource = mtrl->ResourceHandle;
if (Nodes.TryGetValue((nint)resource, out var cached)) if (Nodes.TryGetValue((nint)resource, out var cached))
return cached; return cached;
var node = CreateNodeFromResourceHandle(ResourceType.Mtrl, (nint) mtrl, &resource->Handle, false, WithUIData); var node = CreateNodeFromResourceHandle(ResourceType.Mtrl, (nint)mtrl, &resource->Handle, false, WithUiData);
if (node == null) if (node == null)
return null; return null;
var shpkNode = CreateNodeFromShpk(resource->ShpkResourceHandle, new ByteString(resource->ShpkString), false); var shpkNode = CreateNodeFromShpk(resource->ShpkResourceHandle, new ByteString(resource->ShpkString), false);
if (shpkNode != null) if (shpkNode != null)
node.Children.Add(WithUIData ? shpkNode.WithUIData("Shader Package", 0) : shpkNode); node.Children.Add(WithUiData ? shpkNode.WithUIData("Shader Package", 0) : shpkNode);
var shpkFile = WithUIData && shpkNode != null ? TreeBuildCache.ReadShaderPackage(shpkNode.FullPath) : null; var shpkFile = WithUiData && shpkNode != null ? TreeBuildCache.ReadShaderPackage(shpkNode.FullPath) : null;
var shpk = WithUIData && shpkNode != null ? (ShaderPackage*)shpkNode.ObjectAddress : null; var shpk = WithUiData && shpkNode != null ? (ShaderPackage*)shpkNode.ObjectAddress : null;
for (var i = 0; i < resource->NumTex; i++) for (var i = 0; i < resource->NumTex; i++)
{ {
var texNode = CreateNodeFromTex(resource->TexSpace[i].ResourceHandle, new ByteString(resource->TexString(i)), false, resource->TexIsDX11(i)); var texNode = CreateNodeFromTex(resource->TexSpace[i].ResourceHandle, new ByteString(resource->TexString(i)), false,
resource->TexIsDX11(i));
if (texNode == null) if (texNode == null)
continue; continue;
if (WithUIData) if (WithUiData)
{ {
string? name = null; string? name = null;
if (shpk != null) if (shpk != null)
{ {
var index = GetTextureIndex(resource->TexSpace[i].Flags); var index = GetTextureIndex(resource->TexSpace[i].Flags);
uint? samplerId; uint? samplerId;
if (index != 0x001F) if (index != 0x001F)
samplerId = mtrl->Textures[index].Id; samplerId = mtrl->Textures[index].Id;
@ -259,7 +254,8 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
if (samplerCrc.HasValue) if (samplerCrc.HasValue)
name = shpkFile?.GetSamplerById(samplerCrc.Value)?.Name ?? $"Texture 0x{samplerCrc.Value:X8}"; name = shpkFile?.GetSamplerById(samplerCrc.Value)?.Name ?? $"Texture 0x{samplerCrc.Value:X8}";
} }
} }
node.Children.Add(texNode.WithUIData(name ?? $"Texture #{i}", 0)); node.Children.Add(texNode.WithUIData(name ?? $"Texture #{i}", 0));
} }
else else
@ -281,7 +277,8 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
if (Nodes.TryGetValue((nint)sklb->SkeletonResourceHandle, out var cached)) if (Nodes.TryGetValue((nint)sklb->SkeletonResourceHandle, out var cached))
return cached; return cached;
var node = CreateNodeFromResourceHandle(ResourceType.Sklb, (nint)sklb, (ResourceHandle*)sklb->SkeletonResourceHandle, false, WithUIData); var node = CreateNodeFromResourceHandle(ResourceType.Sklb, (nint)sklb, (ResourceHandle*)sklb->SkeletonResourceHandle, false,
WithUiData);
if (node != null) if (node != null)
{ {
var skpNode = CreateParameterNodeFromPartialSkeleton(sklb); var skpNode = CreateParameterNodeFromPartialSkeleton(sklb);
@ -301,10 +298,11 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
if (Nodes.TryGetValue((nint)sklb->SkeletonParameterResourceHandle, out var cached)) if (Nodes.TryGetValue((nint)sklb->SkeletonParameterResourceHandle, out var cached))
return cached; return cached;
var node = CreateNodeFromResourceHandle(ResourceType.Skp, (nint)sklb, (ResourceHandle*)sklb->SkeletonParameterResourceHandle, true, WithUIData); var node = CreateNodeFromResourceHandle(ResourceType.Skp, (nint)sklb, (ResourceHandle*)sklb->SkeletonParameterResourceHandle, true,
WithUiData);
if (node != null) if (node != null)
{ {
if (WithUIData) if (WithUiData)
node = node.WithUIData("Skeleton Parameters", node.Icon); node = node.WithUIData("Skeleton Parameters", node.Icon);
Nodes.Add((nint)sklb->SkeletonParameterResourceHandle, node); Nodes.Add((nint)sklb->SkeletonParameterResourceHandle, node);
} }
@ -376,14 +374,16 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
_ => string.Empty, _ => string.Empty,
} }
+ item.Name.ToString(); + item.Name.ToString();
return new(name, ChangedItemDrawer.GetCategoryIcon(item.Name, item)); return new ResourceNode.UIData(name, ChangedItemDrawer.GetCategoryIcon(item.Name, item));
} }
var dataFromPath = GuessUIDataFromPath(gamePath); var dataFromPath = GuessUIDataFromPath(gamePath);
if (dataFromPath.Name != null) if (dataFromPath.Name != null)
return dataFromPath; return dataFromPath;
return isEquipment ? new(Slot.ToName(), ChangedItemDrawer.GetCategoryIcon(Slot.ToSlot())) : new(null, ChangedItemDrawer.ChangedItemIcon.Unknown); return isEquipment
? new ResourceNode.UIData(Slot.ToName(), ChangedItemDrawer.GetCategoryIcon(Slot.ToSlot()))
: new ResourceNode.UIData(null, ChangedItemDrawer.ChangedItemIcon.Unknown);
} }
private ResourceNode.UIData GuessUIDataFromPath(Utf8GamePath gamePath) private ResourceNode.UIData GuessUIDataFromPath(Utf8GamePath gamePath)
@ -394,10 +394,10 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
if (name.StartsWith("Customization:")) if (name.StartsWith("Customization:"))
name = name[14..].Trim(); name = name[14..].Trim();
if (name != "Unknown") if (name != "Unknown")
return new(name, ChangedItemDrawer.GetCategoryIcon(obj.Key, obj.Value)); return new ResourceNode.UIData(name, ChangedItemDrawer.GetCategoryIcon(obj.Key, obj.Value));
} }
return new(null, ChangedItemDrawer.ChangedItemIcon.Unknown); return new ResourceNode.UIData(null, ChangedItemDrawer.ChangedItemIcon.Unknown);
} }
private static string? SafeGet(ReadOnlySpan<string> array, Index index) private static string? SafeGet(ReadOnlySpan<string> array, Index index)

View file

@ -56,12 +56,12 @@ public class ResourceTree
var imc = (ResourceHandle*)model->IMCArray[i]; var imc = (ResourceHandle*)model->IMCArray[i];
var imcNode = context.CreateNodeFromImc(imc); var imcNode = context.CreateNodeFromImc(imc);
if (imcNode != null) if (imcNode != null)
Nodes.Add(globalContext.WithUIData ? imcNode.WithUIData(imcNode.Name ?? $"IMC #{i}", imcNode.Icon) : imcNode); Nodes.Add(globalContext.WithUiData ? imcNode.WithUIData(imcNode.Name ?? $"IMC #{i}", imcNode.Icon) : imcNode);
var mdl = (RenderModel*)model->Models[i]; var mdl = (RenderModel*)model->Models[i];
var mdlNode = context.CreateNodeFromRenderModel(mdl); var mdlNode = context.CreateNodeFromRenderModel(mdl);
if (mdlNode != null) if (mdlNode != null)
Nodes.Add(globalContext.WithUIData ? mdlNode.WithUIData(mdlNode.Name ?? $"Model #{i}", mdlNode.Icon) : mdlNode); Nodes.Add(globalContext.WithUiData ? mdlNode.WithUIData(mdlNode.Name ?? $"Model #{i}", mdlNode.Icon) : mdlNode);
} }
AddSkeleton(Nodes, globalContext.CreateContext(EquipSlot.Unknown, default), model->Skeleton); AddSkeleton(Nodes, globalContext.CreateContext(EquipSlot.Unknown, default), model->Skeleton);
@ -92,14 +92,14 @@ public class ResourceTree
var imc = (ResourceHandle*)subObject->IMCArray[i]; var imc = (ResourceHandle*)subObject->IMCArray[i];
var imcNode = subObjectContext.CreateNodeFromImc(imc); var imcNode = subObjectContext.CreateNodeFromImc(imc);
if (imcNode != null) if (imcNode != null)
subObjectNodes.Add(globalContext.WithUIData subObjectNodes.Add(globalContext.WithUiData
? imcNode.WithUIData(imcNode.Name ?? $"{subObjectNamePrefix} #{subObjectIndex}, IMC #{i}", imcNode.Icon) ? imcNode.WithUIData(imcNode.Name ?? $"{subObjectNamePrefix} #{subObjectIndex}, IMC #{i}", imcNode.Icon)
: imcNode); : imcNode);
var mdl = (RenderModel*)subObject->Models[i]; var mdl = (RenderModel*)subObject->Models[i];
var mdlNode = subObjectContext.CreateNodeFromRenderModel(mdl); var mdlNode = subObjectContext.CreateNodeFromRenderModel(mdl);
if (mdlNode != null) if (mdlNode != null)
subObjectNodes.Add(globalContext.WithUIData subObjectNodes.Add(globalContext.WithUiData
? mdlNode.WithUIData(mdlNode.Name ?? $"{subObjectNamePrefix} #{subObjectIndex}, Model #{i}", mdlNode.Icon) ? mdlNode.WithUIData(mdlNode.Name ?? $"{subObjectNamePrefix} #{subObjectIndex}, Model #{i}", mdlNode.Icon)
: mdlNode); : mdlNode);
} }
@ -117,11 +117,11 @@ public class ResourceTree
var decalNode = context.CreateNodeFromTex((TextureResourceHandle*)human->Decal); var decalNode = context.CreateNodeFromTex((TextureResourceHandle*)human->Decal);
if (decalNode != null) if (decalNode != null)
Nodes.Add(globalContext.WithUIData ? decalNode.WithUIData(decalNode.Name ?? "Face Decal", decalNode.Icon) : decalNode); Nodes.Add(globalContext.WithUiData ? decalNode.WithUIData(decalNode.Name ?? "Face Decal", decalNode.Icon) : decalNode);
var legacyDecalNode = context.CreateNodeFromTex((TextureResourceHandle*)human->LegacyBodyDecal); var legacyDecalNode = context.CreateNodeFromTex((TextureResourceHandle*)human->LegacyBodyDecal);
if (legacyDecalNode != null) if (legacyDecalNode != null)
Nodes.Add(globalContext.WithUIData ? legacyDecalNode.WithUIData(legacyDecalNode.Name ?? "Legacy Body Decal", legacyDecalNode.Icon) : legacyDecalNode); Nodes.Add(globalContext.WithUiData ? legacyDecalNode.WithUIData(legacyDecalNode.Name ?? "Legacy Body Decal", legacyDecalNode.Icon) : legacyDecalNode);
} }
private unsafe void AddSkeleton(List<ResourceNode> nodes, ResolveContext context, Skeleton* skeleton, string prefix = "") private unsafe void AddSkeleton(List<ResourceNode> nodes, ResolveContext context, Skeleton* skeleton, string prefix = "")
@ -133,7 +133,7 @@ public class ResourceTree
{ {
var sklbNode = context.CreateNodeFromPartialSkeleton(&skeleton->PartialSkeletons[i]); var sklbNode = context.CreateNodeFromPartialSkeleton(&skeleton->PartialSkeletons[i]);
if (sklbNode != null) if (sklbNode != null)
nodes.Add(context.WithUIData ? sklbNode.WithUIData($"{prefix}Skeleton #{i}", sklbNode.Icon) : sklbNode); nodes.Add(context.WithUiData ? sklbNode.WithUIData($"{prefix}Skeleton #{i}", sklbNode.Icon) : sklbNode);
} }
} }
} }

View file

@ -1,3 +1,4 @@
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using FFXIVClientStructs.FFXIV.Client.Graphics.Render; using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
@ -41,4 +42,7 @@ public unsafe struct Material
[FieldOffset( 0x10 )] [FieldOffset( 0x10 )]
public uint SamplerFlags; public uint SamplerFlags;
} }
public ReadOnlySpan<TextureEntry> TextureSpan
=> new(Textures, TextureCount);
} }