ClientStructs-ify a few things

This commit is contained in:
Exter-N 2023-10-27 01:52:16 +02:00
parent 6375faa758
commit 00dc5f48b1
23 changed files with 102 additions and 314 deletions

@ -1 +1 @@
Subproject commit a4f9b285c82f84ff0841695c0787dbba93afc59b
Subproject commit 6f17ef70c41f3b31a401fdc9d6e37087e64f2035

View file

@ -31,7 +31,7 @@ public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase
if (mtrlHandle == null)
throw new InvalidOperationException("Material doesn't have a resource handle");
var colorSetTextures = ((Structs.CharacterBaseExt*)DrawObject)->ColorTableTextures;
var colorSetTextures = DrawObject->ColorTableTextures;
if (colorSetTextures == null)
throw new InvalidOperationException("Draw object doesn't have color table textures");
@ -79,7 +79,7 @@ public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase
textureSize[1] = TextureHeight;
using var texture =
new SafeTextureHandle(Structs.TextureUtility.Create2D(Device.Instance(), textureSize, 1, 0x2460, 0x80000804, 7), false);
new SafeTextureHandle(Device.Instance()->CreateTexture2D(textureSize, 1, 0x2460, 0x80000804, 7), false);
if (texture.IsInvalid)
return;
@ -88,7 +88,7 @@ public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase
{
fixed (Half* colorTable = _colorTable)
{
success = Structs.TextureUtility.InitializeContents(texture.Texture, colorTable);
success = texture.Texture->InitializeContents(colorTable);
}
}
@ -101,7 +101,7 @@ public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase
if (!base.IsStillValid())
return false;
var colorSetTextures = ((Structs.CharacterBaseExt*)DrawObject)->ColorTableTextures;
var colorSetTextures = DrawObject->ColorTableTextures;
if (colorSetTextures == null)
return false;

View file

@ -18,7 +18,7 @@ public sealed unsafe class LiveMaterialPreviewer : LiveMaterialPreviewerBase
if (mtrlHandle == null)
throw new InvalidOperationException("Material doesn't have a resource handle");
var shpkHandle = ((Structs.MtrlResource*)mtrlHandle)->ShpkResourceHandle;
var shpkHandle = mtrlHandle->ShaderPackageResourceHandle;
if (shpkHandle == null)
throw new InvalidOperationException("Material doesn't have a ShPk resource handle");
@ -61,7 +61,7 @@ public sealed unsafe class LiveMaterialPreviewer : LiveMaterialPreviewerBase
if (!CheckValidity())
return;
((Structs.Material*)Material)->ShaderPackageFlags = shPkFlags;
Material->ShaderFlags = shPkFlags;
}
public void SetMaterialParameter(uint parameterCrc, Index offset, Span<float> value)

View file

@ -93,7 +93,7 @@ public readonly record struct MaterialInfo(ObjectIndex ObjectIndex, DrawObjectTy
continue;
var mtrlHandle = material->MaterialResourceHandle;
var path = ResolveContext.GetResourceHandlePath((Structs.ResourceHandle*)mtrlHandle);
var path = ResolveContext.GetResourceHandlePath(&mtrlHandle->ResourceHandle);
if (path == needle)
result.Add(new MaterialInfo(index, type, i, j));
}

View file

@ -1,14 +1,15 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
using OtterGui;
using Penumbra.Api.Enums;
using Penumbra.GameData;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Interop.Structs;
using Penumbra.String;
using Penumbra.String.Classes;
using Penumbra.UI;
using static Penumbra.Interop.Structs.StructExtensions;
namespace Penumbra.Interop.ResourceTree;
@ -36,7 +37,7 @@ internal record ResolveContext(IObjectIdentifier Identifier, TreeBuildCache Tree
if (!Utf8GamePath.FromByteString(ByteString.Join((byte)'/', ShpkPrefix, gamePath), out var path, false))
return null;
return CreateNodeFromGamePath(ResourceType.Shpk, (nint)resourceHandle->ShaderPackage, &resourceHandle->Handle, path, @internal);
return CreateNodeFromGamePath(ResourceType.Shpk, (nint)resourceHandle->ShaderPackage, &resourceHandle->ResourceHandle, path, @internal);
}
private unsafe ResourceNode? CreateNodeFromTex(TextureResourceHandle* resourceHandle, ByteString gamePath, bool @internal, bool dx11)
@ -68,7 +69,7 @@ internal record ResolveContext(IObjectIdentifier Identifier, TreeBuildCache Tree
if (!Utf8GamePath.FromByteString(gamePath, out var path))
return null;
return CreateNodeFromGamePath(ResourceType.Tex, (nint)resourceHandle->KernelTexture, &resourceHandle->Handle, path, @internal);
return CreateNodeFromGamePath(ResourceType.Tex, (nint)resourceHandle->Texture, &resourceHandle->ResourceHandle, path, @internal);
}
private unsafe ResourceNode CreateNodeFromGamePath(ResourceType type, nint objectAddress, ResourceHandle* resourceHandle,
@ -118,22 +119,22 @@ internal record ResolveContext(IObjectIdentifier Identifier, TreeBuildCache Tree
if (Nodes.TryGetValue((nint)tex, out var cached))
return cached;
var node = CreateNodeFromResourceHandle(ResourceType.Tex, (nint)tex->KernelTexture, &tex->Handle, false);
var node = CreateNodeFromResourceHandle(ResourceType.Tex, (nint)tex->Texture, &tex->ResourceHandle, false);
if (node != null)
Nodes.Add((nint)tex, node);
return node;
}
public unsafe ResourceNode? CreateNodeFromRenderModel(RenderModel* mdl)
public unsafe ResourceNode? CreateNodeFromRenderModel(Model* mdl)
{
if (mdl == null || mdl->ResourceHandle == null || mdl->ResourceHandle->Category != ResourceCategory.Chara)
if (mdl == null || mdl->ModelResourceHandle == null || mdl->ModelResourceHandle->ResourceHandle.Type.Category != ResourceHandleType.HandleCategory.Chara)
return null;
if (Nodes.TryGetValue((nint)mdl->ResourceHandle, out var cached))
if (Nodes.TryGetValue((nint)mdl->ModelResourceHandle, out var cached))
return cached;
var node = CreateNodeFromResourceHandle(ResourceType.Mdl, (nint)mdl, mdl->ResourceHandle, false);
var node = CreateNodeFromResourceHandle(ResourceType.Mdl, (nint)mdl, &mdl->ModelResourceHandle->ResourceHandle, false);
if (node == null)
return null;
@ -149,7 +150,7 @@ internal record ResolveContext(IObjectIdentifier Identifier, TreeBuildCache Tree
}
}
Nodes.Add((nint)mdl->ResourceHandle, node);
Nodes.Add((nint)mdl->ModelResourceHandle, node);
return node;
}
@ -169,27 +170,27 @@ internal record ResolveContext(IObjectIdentifier Identifier, TreeBuildCache Tree
}
static uint? GetTextureSamplerId(Material* mtrl, TextureResourceHandle* handle, HashSet<uint> alreadyVisitedSamplerIds)
=> mtrl->TextureSpan.FindFirst(p => p.ResourceHandle == handle && !alreadyVisitedSamplerIds.Contains(p.Id), out var p)
=> mtrl->TexturesSpan.FindFirst(p => p.Texture == handle && !alreadyVisitedSamplerIds.Contains(p.Id), out var p)
? p.Id
: null;
static uint? GetSamplerCrcById(ShaderPackage* shpk, uint id)
=> new ReadOnlySpan<ShaderPackageUtility.Sampler>(shpk->Samplers, shpk->SamplerCount).FindFirst(s => s.Id == id, out var s)
? s.Crc
=> shpk->SamplersSpan.FindFirst(s => s.Id == id, out var s)
? s.CRC
: null;
if (mtrl == null)
return null;
var resource = mtrl->ResourceHandle;
var resource = mtrl->MaterialResourceHandle;
if (Nodes.TryGetValue((nint)resource, out var cached))
return cached;
var node = CreateNodeFromResourceHandle(ResourceType.Mtrl, (nint)mtrl, &resource->Handle, false);
var node = CreateNodeFromResourceHandle(ResourceType.Mtrl, (nint)mtrl, &resource->ResourceHandle, false);
if (node == null)
return null;
var shpkNode = CreateNodeFromShpk(resource->ShpkResourceHandle, new ByteString(resource->ShpkString), false);
var shpkNode = CreateNodeFromShpk(resource->ShaderPackageResourceHandle, new ByteString(resource->ShpkName), false);
if (shpkNode != null)
{
if (WithUiData)
@ -200,10 +201,10 @@ internal record ResolveContext(IObjectIdentifier Identifier, TreeBuildCache Tree
var shpk = WithUiData && shpkNode != null ? (ShaderPackage*)shpkNode.ObjectAddress : null;
var alreadyProcessedSamplerIds = new HashSet<uint>();
for (var i = 0; i < resource->NumTex; i++)
for (var i = 0; i < resource->TextureCount; i++)
{
var texNode = CreateNodeFromTex(resource->TexSpace[i].ResourceHandle, new ByteString(resource->TexString(i)), false,
resource->TexIsDX11(i));
var texNode = CreateNodeFromTex(resource->Textures[i].TextureResourceHandle, new ByteString(resource->TexturePath(i)), false,
resource->Textures[i].IsDX11);
if (texNode == null)
continue;
@ -212,12 +213,12 @@ internal record ResolveContext(IObjectIdentifier Identifier, TreeBuildCache Tree
string? name = null;
if (shpk != null)
{
var index = GetTextureIndex(mtrl, resource->TexSpace[i].Flags, alreadyProcessedSamplerIds);
var index = GetTextureIndex(mtrl, resource->Textures[i].Flags, alreadyProcessedSamplerIds);
uint? samplerId;
if (index != 0x001F)
samplerId = mtrl->Textures[index].Id;
else
samplerId = GetTextureSamplerId(mtrl, resource->TexSpace[i].ResourceHandle, alreadyProcessedSamplerIds);
samplerId = GetTextureSamplerId(mtrl, resource->Textures[i].TextureResourceHandle, alreadyProcessedSamplerIds);
if (samplerId.HasValue)
{
alreadyProcessedSamplerIds.Add(samplerId.Value);
@ -367,7 +368,7 @@ internal record ResolveContext(IObjectIdentifier Identifier, TreeBuildCache Tree
if (handle == null)
return ByteString.Empty;
var name = handle->FileName();
var name = handle->FileName.AsByteString();
if (name.IsEmpty)
return ByteString.Empty;
@ -388,6 +389,6 @@ internal record ResolveContext(IObjectIdentifier Identifier, TreeBuildCache Tree
if (handle == null)
return 0;
return ResourceHandle.GetLength(handle);
return handle->GetLength();
}
}

View file

@ -1,9 +1,9 @@
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Interop.Structs;
using Penumbra.UI;
using CustomizeData = FFXIVClientStructs.FFXIV.Client.Game.Character.CustomizeData;
@ -50,11 +50,12 @@ public class ResourceTree
{
var character = (Character*)GameObjectAddress;
var model = (CharacterBase*)DrawObjectAddress;
var human = model->GetModelType() == CharacterBase.ModelType.Human ? (Human*)model : null;
var equipment = new ReadOnlySpan<CharacterArmor>(&character->DrawData.Head, 10);
// var customize = new ReadOnlySpan<byte>( character->CustomizeData, 26 );
ModelId = character->CharacterData.ModelCharaId;
CustomizeData = character->DrawData.CustomizeData;
RaceCode = model->GetModelType() == CharacterBase.ModelType.Human ? (GenderRace)((Human*)model)->RaceSexId : GenderRace.Unknown;
RaceCode = human != null ? (GenderRace)human->RaceSexId : GenderRace.Unknown;
for (var i = 0; i < model->SlotCount; ++i)
{
@ -72,7 +73,7 @@ public class ResourceTree
Nodes.Add(imcNode);
}
var mdl = (RenderModel*)model->Models[i];
var mdl = model->Models[i];
var mdlNode = context.CreateNodeFromRenderModel(mdl);
if (mdlNode != null)
{
@ -84,13 +85,13 @@ public class ResourceTree
AddSkeleton(Nodes, globalContext.CreateContext(EquipSlot.Unknown, default), model->Skeleton);
if (model->GetModelType() == CharacterBase.ModelType.Human)
AddHumanResources(globalContext, (HumanExt*)model);
if (human != null)
AddHumanResources(globalContext, human);
}
private unsafe void AddHumanResources(GlobalResolveContext globalContext, HumanExt* human)
private unsafe void AddHumanResources(GlobalResolveContext globalContext, Human* human)
{
var firstSubObject = (CharacterBase*)human->Human.CharacterBase.DrawObject.Object.ChildObject;
var firstSubObject = (CharacterBase*)human->CharacterBase.DrawObject.Object.ChildObject;
if (firstSubObject != null)
{
var subObjectNodes = new List<ResourceNode>();
@ -116,7 +117,7 @@ public class ResourceTree
subObjectNodes.Add(imcNode);
}
var mdl = (RenderModel*)subObject->Models[i];
var mdl = subObject->Models[i];
var mdlNode = subObjectContext.CreateNodeFromRenderModel(mdl);
if (mdlNode != null)
{
@ -137,7 +138,7 @@ public class ResourceTree
var context = globalContext.CreateContext(EquipSlot.Unknown, default);
var decalNode = context.CreateNodeFromTex((TextureResourceHandle*)human->Decal);
var decalNode = context.CreateNodeFromTex(human->Decal);
if (decalNode != null)
{
if (globalContext.WithUiData)
@ -149,7 +150,7 @@ public class ResourceTree
Nodes.Add(decalNode);
}
var legacyDecalNode = context.CreateNodeFromTex((TextureResourceHandle*)human->LegacyBodyDecal);
var legacyDecalNode = context.CreateNodeFromTex(human->LegacyBodyDecal);
if (legacyDecalNode != null)
{
if (globalContext.WithUiData)

View file

@ -1,5 +1,4 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using Penumbra.Interop.Structs;
namespace Penumbra.Interop.SafeHandles;
@ -18,7 +17,7 @@ public unsafe class SafeTextureHandle : SafeHandle
throw new ArgumentException("Non-owning SafeTextureHandle with IncRef is unsupported");
if (incRef && handle != null)
TextureUtility.IncRef(handle);
handle->IncRef();
SetHandle((nint)handle);
}
@ -43,7 +42,7 @@ public unsafe class SafeTextureHandle : SafeHandle
}
if (handle != 0)
TextureUtility.DecRef((Texture*)handle);
((Texture*)handle)->DecRef();
return true;
}

View file

@ -2,6 +2,7 @@ using Dalamud.Hooking;
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
using OtterGui.Classes;
using Penumbra.Communication;
using Penumbra.GameData;
@ -73,19 +74,19 @@ public sealed unsafe class SkinFixer : IDisposable
public ulong GetAndResetSlowPathCallDelta()
=> Interlocked.Exchange(ref _slowPathCallDelta, 0);
private static bool IsSkinMaterial(Structs.MtrlResource* mtrlResource)
private static bool IsSkinMaterial(MaterialResourceHandle* mtrlResource)
{
if (mtrlResource == null)
return false;
var shpkName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(mtrlResource->ShpkString);
var shpkName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(mtrlResource->ShpkName);
return SkinShpkName.SequenceEqual(shpkName);
}
private void OnMtrlShpkLoaded(nint mtrlResourceHandle, nint gameObject)
{
var mtrl = (Structs.MtrlResource*)mtrlResourceHandle;
var shpk = mtrl->ShpkResourceHandle;
var mtrl = (MaterialResourceHandle*)mtrlResourceHandle;
var shpk = mtrl->ShaderPackageResourceHandle;
if (shpk == null)
return;
@ -109,7 +110,7 @@ public sealed unsafe class SkinFixer : IDisposable
return _onRenderMaterialHook.Original(human, param);
var material = param->Model->Materials[param->MaterialIndex];
var mtrlResource = (Structs.MtrlResource*)material->MaterialResourceHandle;
var mtrlResource = material->MaterialResourceHandle;
if (!IsSkinMaterial(mtrlResource))
return _onRenderMaterialHook.Original(human, param);
@ -124,7 +125,7 @@ public sealed unsafe class SkinFixer : IDisposable
{
try
{
_utility.Address->SkinShpkResource = (Structs.ResourceHandle*)mtrlResource->ShpkResourceHandle;
_utility.Address->SkinShpkResource = (Structs.ResourceHandle*)mtrlResource->ShaderPackageResourceHandle;
return _onRenderMaterialHook.Original(human, param);
}
finally

View file

@ -1,14 +0,0 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
namespace Penumbra.Interop.Structs;
[StructLayout(LayoutKind.Explicit)]
public unsafe struct CharacterBaseExt
{
[FieldOffset(0x0)]
public CharacterBase CharacterBase;
[FieldOffset(0x258)]
public Texture** ColorTableTextures;
}

View file

@ -1,28 +0,0 @@
namespace Penumbra.Interop.Structs;
[StructLayout(LayoutKind.Explicit, Size = 0x70)]
public unsafe struct ConstantBuffer
{
[FieldOffset(0x20)]
public int Size;
[FieldOffset(0x24)]
public int Flags;
[FieldOffset(0x28)]
private void* _maybeSourcePointer;
public bool TryGetBuffer(out Span<float> buffer)
{
if ((Flags & 0x4003) == 0 && _maybeSourcePointer != null)
{
buffer = new Span<float>(_maybeSourcePointer, Size >> 2);
return true;
}
else
{
buffer = null;
return false;
}
}
}

View file

@ -1,19 +0,0 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
namespace Penumbra.Interop.Structs;
[StructLayout(LayoutKind.Explicit)]
public unsafe struct HumanExt
{
[FieldOffset(0x0)]
public Human Human;
[FieldOffset(0x0)]
public CharacterBaseExt CharacterBase;
[FieldOffset(0x9E8)]
public ResourceHandle* Decal;
[FieldOffset(0x9F0)]
public ResourceHandle* LegacyBodyDecal;
}

View file

@ -1,47 +0,0 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
namespace Penumbra.Interop.Structs;
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
public unsafe struct Material
{
[FieldOffset(0x10)]
public MtrlResource* ResourceHandle;
[FieldOffset(0x18)]
public uint ShaderPackageFlags;
[FieldOffset(0x20)]
public uint* ShaderKeys;
public int ShaderKeyCount
=> (int)((uint*)Textures - ShaderKeys);
[FieldOffset(0x28)]
public ConstantBuffer* MaterialParameter;
[FieldOffset(0x30)]
public TextureEntry* Textures;
[FieldOffset(0x38)]
public ushort TextureCount;
public Texture* Texture(int index)
=> Textures[index].ResourceHandle->KernelTexture;
[StructLayout(LayoutKind.Explicit, Size = 0x18)]
public struct TextureEntry
{
[FieldOffset(0x00)]
public uint Id;
[FieldOffset(0x08)]
public TextureResourceHandle* ResourceHandle;
[FieldOffset(0x10)]
public uint SamplerFlags;
}
public ReadOnlySpan<TextureEntry> TextureSpan
=> new(Textures, TextureCount);
}

View file

@ -1,45 +0,0 @@
namespace Penumbra.Interop.Structs;
[StructLayout(LayoutKind.Explicit)]
public unsafe struct MtrlResource
{
[FieldOffset(0x00)]
public ResourceHandle Handle;
[FieldOffset(0xC8)]
public ShaderPackageResourceHandle* ShpkResourceHandle;
[FieldOffset(0xD0)]
public TextureEntry* TexSpace; // Contains the offsets for the tex files inside the string list.
[FieldOffset(0xE0)]
public byte* StringList;
[FieldOffset(0xF8)]
public ushort ShpkOffset;
[FieldOffset(0xFA)]
public byte NumTex;
public byte* ShpkString
=> StringList + ShpkOffset;
public byte* TexString(int idx)
=> StringList + TexSpace[idx].PathOffset;
public bool TexIsDX11(int idx)
=> TexSpace[idx].Flags >= 0x8000;
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
public struct TextureEntry
{
[FieldOffset(0x00)]
public TextureResourceHandle* ResourceHandle;
[FieldOffset(0x08)]
public ushort PathOffset;
[FieldOffset(0x0A)]
public ushort Flags;
}
}

View file

@ -5,6 +5,9 @@ namespace Penumbra.Interop.Structs;
[StructLayout(LayoutKind.Explicit)]
public unsafe struct RenderModel
{
[FieldOffset(0)]
public Model Model;
[FieldOffset(0x18)]
public RenderModel* PreviousModel;

View file

@ -1,10 +1,8 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
using Penumbra.Api.Enums;
using Penumbra.GameData;
using Penumbra.String;
using Penumbra.String.Classes;
using CsHandle = FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
namespace Penumbra.Interop.Structs;
@ -14,24 +12,8 @@ public unsafe struct TextureResourceHandle
[FieldOffset(0x0)]
public ResourceHandle Handle;
[FieldOffset(0x38)]
public IntPtr Unk;
[FieldOffset(0x118)]
public Texture* KernelTexture;
[FieldOffset(0x20)]
public IntPtr NewKernelTexture;
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct ShaderPackageResourceHandle
{
[FieldOffset(0x0)]
public ResourceHandle Handle;
[FieldOffset(0xB0)]
public ShaderPackage* ShaderPackage;
public CsHandle.TextureResourceHandle CsHandle;
}
public enum LoadState : byte
@ -59,27 +41,14 @@ public unsafe struct ResourceHandle
public ulong DataLength;
}
public const int SsoSize = 15;
public readonly ByteString FileName()
=> CsHandle.FileName.AsByteString();
public byte* FileNamePtr()
{
if (FileNameLength > SsoSize)
return FileNameData;
public readonly bool GamePath(out Utf8GamePath path)
=> Utf8GamePath.FromSpan(CsHandle.FileName.AsSpan(), out path);
fixed (byte** name = &FileNameData)
{
return (byte*)name;
}
}
public ByteString FileName()
=> ByteString.FromByteStringUnsafe(FileNamePtr(), FileNameLength, true);
public ReadOnlySpan<byte> FileNameAsSpan()
=> new(FileNamePtr(), FileNameLength);
public bool GamePath(out Utf8GamePath path)
=> Utf8GamePath.FromSpan(FileNameAsSpan(), out path);
[FieldOffset(0x00)]
public CsHandle.ResourceHandle CsHandle;
[FieldOffset(0x00)]
public void** VTable;
@ -90,18 +59,9 @@ public unsafe struct ResourceHandle
[FieldOffset(0x0C)]
public ResourceType FileType;
[FieldOffset(0x10)]
public uint Id;
[FieldOffset(0x28)]
public uint FileSize;
[FieldOffset(0x2C)]
public uint FileSize2;
[FieldOffset(0x34)]
public uint FileSize3;
[FieldOffset(0x48)]
public byte* FileNameData;
@ -114,13 +74,6 @@ public unsafe struct ResourceHandle
[FieldOffset(0xAC)]
public uint RefCount;
// May return null.
public static byte* GetData(ResourceHandle* handle)
=> ((delegate* unmanaged< ResourceHandle*, byte* >)handle->VTable[Offsets.ResourceHandleGetDataVfunc])(handle);
public static ulong GetLength(ResourceHandle* handle)
=> ((delegate* unmanaged< ResourceHandle*, ulong >)handle->VTable[Offsets.ResourceHandleGetLengthVfunc])(handle);
// Only use these if you know what you are doing.
// Those are actually only sure to be accessible for DefaultResourceHandles.

View file

@ -1,17 +0,0 @@
namespace Penumbra.Interop.Structs;
public static class ShaderPackageUtility
{
[StructLayout(LayoutKind.Explicit, Size = 0xC)]
public unsafe struct Sampler
{
[FieldOffset(0x0)]
public uint Crc;
[FieldOffset(0x4)]
public uint Id;
[FieldOffset(0xA)]
public ushort Slot;
}
}

View file

@ -0,0 +1,24 @@
using FFXIVClientStructs.STD;
using Penumbra.String;
namespace Penumbra.Interop.Structs;
internal static class StructExtensions
{
// TODO submit this to ClientStructs
public static unsafe ReadOnlySpan<byte> AsSpan(in this StdString str)
{
if (str.Length < 16)
{
fixed (StdString* pStr = &str)
{
return new(pStr->Buffer, (int)str.Length);
}
}
else
return new(str.BufferPtr, (int)str.Length);
}
public static unsafe ByteString AsByteString(in this StdString str)
=> ByteString.FromSpanUnsafe(str.AsSpan(), true);
}

View file

@ -1,31 +0,0 @@
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
namespace Penumbra.Interop.Structs;
public unsafe class TextureUtility
{
public TextureUtility(IGameInteropProvider interop)
=> interop.InitializeFromAttributes(this);
[Signature("E8 ?? ?? ?? ?? 8B 0F 48 8D 54 24")]
private static nint _textureCreate2D = nint.Zero;
[Signature("E9 ?? ?? ?? ?? 8B 02 25")]
private static nint _textureInitializeContents = nint.Zero;
public static Texture* Create2D(Device* device, int* size, byte mipLevel, uint textureFormat, uint flags, uint unk)
=> ((delegate* unmanaged<Device*, int*, byte, uint, uint, uint, Texture*>)_textureCreate2D)(device, size, mipLevel, textureFormat,
flags, unk);
public static bool InitializeContents(Texture* texture, void* contents)
=> ((delegate* unmanaged<Texture*, void*, bool>)_textureInitializeContents)(texture, contents);
public static void IncRef(Texture* texture)
=> ((delegate* unmanaged<Texture*, void>)(*(void***)texture)[2])(texture);
public static void DecRef(Texture* texture)
=> ((delegate* unmanaged<Texture*, void>)(*(void***)texture)[3])(texture);
}

View file

@ -75,7 +75,6 @@ public class Penumbra : IDalamudPlugin
_communicatorService = _services.GetRequiredService<CommunicatorService>();
_services.GetRequiredService<ResourceService>(); // Initialize because not required anywhere else.
_services.GetRequiredService<ModCacheManager>(); // Initialize because not required anywhere else.
_services.GetRequiredService<TextureUtility>(); // Initialize because not required anywhere else.
_collectionManager.Caches.CreateNecessaryCaches();
using (var t = _services.GetRequiredService<StartTracker>().Measure(StartTimeType.PathResolver))
{

View file

@ -90,8 +90,7 @@ public static class ServiceManager
.AddSingleton<CreateFileWHook>()
.AddSingleton<ResidentResourceManager>()
.AddSingleton<FontReloader>()
.AddSingleton<RedrawService>()
.AddSingleton<TextureUtility>();
.AddSingleton<RedrawService>();
private static IServiceCollection AddConfiguration(this IServiceCollection services)
=> services.AddTransient<ConfigMigrationService>()

View file

@ -669,8 +669,8 @@ public class DebugTab : Window, ITab
UiHelpers.Text(resource);
ImGui.TableNextColumn();
var data = (nint)ResourceHandle.GetData(resource);
var length = ResourceHandle.GetLength(resource);
var data = (nint)resource->CsHandle.GetData();
var length = resource->CsHandle.GetLength();
if (ImGui.Selectable($"0x{data:X}"))
if (data != nint.Zero && length > 0)
ImGui.SetClipboardText(string.Join("\n",

View file

@ -99,10 +99,10 @@ public class ResourceTab : ITab
UiHelpers.Text(resource);
if (ImGui.IsItemClicked())
{
var data = Interop.Structs.ResourceHandle.GetData(resource);
var data = resource->CsHandle.GetData();
if (data != null)
{
var length = (int)Interop.Structs.ResourceHandle.GetLength(resource);
var length = (int)resource->CsHandle.GetLength();
ImGui.SetClipboardText(string.Join(" ",
new ReadOnlySpan<byte>(data, length).ToArray().Select(b => b.ToString("X2"))));
}

View file

@ -19,9 +19,18 @@ public static class UiHelpers
public static unsafe void Text(byte* s, int length)
=> ImGuiNative.igTextUnformatted(s, s + length);
/// <summary> Draw text given by a byte span. </summary>
public static unsafe void Text(ReadOnlySpan<byte> s)
{
fixed (byte* pS = s)
{
Text(pS, s.Length);
}
}
/// <summary> Draw the name of a resource file. </summary>
public static unsafe void Text(ResourceHandle* resource)
=> Text(resource->FileName().Path, resource->FileNameLength);
=> Text(resource->CsHandle.FileName.AsSpan());
/// <summary> Draw a ByteString as a selectable. </summary>
public static unsafe bool Selectable(ByteString s, bool selected)