Allow changing the skin mtrl suffix

This commit is contained in:
Exter-N 2025-08-17 03:10:35 +02:00
parent aa920b5e9b
commit 41edc23820
2 changed files with 69 additions and 1 deletions

View file

@ -6,6 +6,7 @@ using Penumbra.Collections;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Interop.PathResolving;
using Penumbra.Interop.Processing;
using static FFXIVClientStructs.FFXIV.Client.Game.Character.ActionEffectHandler;
namespace Penumbra.Interop.Hooks.Resources;
@ -159,7 +160,13 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
=> ResolvePath(drawObject, _resolveMtrlPathHook.Original(drawObject, pathBuffer, pathBufferSize, slotIndex, mtrlFileName));
private nint ResolveSkinMtrl(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex)
=> ResolvePath(drawObject, _resolveSkinMtrlPathHook.Original(drawObject, pathBuffer, pathBufferSize, slotIndex));
{
var finalPathBuffer = _resolveSkinMtrlPathHook.Original(drawObject, pathBuffer, pathBufferSize, slotIndex);
if (finalPathBuffer != 0 && finalPathBuffer == pathBuffer)
SkinMtrlPathEarlyProcessing.Process(new Span<byte>((void*)pathBuffer, (int)pathBufferSize), (CharacterBase*)drawObject, slotIndex);
return ResolvePath(drawObject, finalPathBuffer);
}
private nint ResolvePap(nint drawObject, nint pathBuffer, nint pathBufferSize, uint unkAnimationIndex, nint animationName)
=> ResolvePath(drawObject, _resolvePapPathHook.Original(drawObject, pathBuffer, pathBufferSize, unkAnimationIndex, animationName));

View file

@ -0,0 +1,61 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
namespace Penumbra.Interop.Processing;
public static unsafe class SkinMtrlPathEarlyProcessing
{
public static void Process(Span<byte> path, CharacterBase* character, uint slotIndex)
{
var end = path.IndexOf(".mtrl\0"u8);
if (end < 0)
return;
var suffixPos = path[..end].LastIndexOf((byte)'_');
if (suffixPos < 0)
return;
var handle = GetModelResourceHandle(character, slotIndex);
if (handle == null)
return;
var skinSuffix = GetSkinSuffix(handle);
if (skinSuffix.IsEmpty || skinSuffix.Length > path.Length - suffixPos - 7)
return;
skinSuffix.CopyTo(path[(suffixPos + 1)..]);
".mtrl\0"u8.CopyTo(path[(suffixPos + 1 + skinSuffix.Length)..]);
}
private static ModelResourceHandle* GetModelResourceHandle(CharacterBase* character, uint slotIndex)
{
if (character == null)
return null;
if (character->TempSlotData != null)
{
// TODO ClientStructs-ify
var handle = *(ModelResourceHandle**)((nint)character->TempSlotData + 0xE0 * slotIndex + 0x8);
if (handle != null)
return handle;
}
var model = character->Models[slotIndex];
if (model == null)
return null;
return model->ModelResourceHandle;
}
private static ReadOnlySpan<byte> GetSkinSuffix(ModelResourceHandle* handle)
{
foreach (var (attribute, _) in handle->Attributes)
{
var attributeSpan = attribute.AsSpan();
if (attributeSpan.Length > 12 && attributeSpan[..11].SequenceEqual("skin_suffix"u8) && attributeSpan[11] is (byte)'=' or (byte)'_')
return attributeSpan[12..];
}
return [];
}
}