Change texture handling.

This commit is contained in:
Ottermandias 2024-07-05 12:14:31 +02:00
parent 9fb8090781
commit 4026dd5867
3 changed files with 73 additions and 63 deletions

@ -1 +1 @@
Subproject commit 066637abe05c659b79d84f52e6db33487498f433
Subproject commit 19923f8d5649f11edcfae710c26d6273cf2e9d62

View file

@ -1,93 +1,110 @@
using Dalamud.Hooking;
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using Lumina.Excel.GeneratedSheets2;
using OtterGui.Services;
using Penumbra.Api.Enums;
using Penumbra.GameData;
using Penumbra.Interop.Structs;
using Penumbra.String.Classes;
using FileMode = Penumbra.Interop.Structs.FileMode;
using ResourceHandle = FFXIVClientStructs.FFXIV.Client.System.Resource.Handle.ResourceHandle;
namespace Penumbra.Interop.Hooks.ResourceLoading;
public unsafe class TexMdlService : IDisposable, IRequiredService
{
/// <summary>
/// We need to be able to obtain the requested LoD level.
/// This replicates the LoD behavior of a textures OnLoad function.
/// </summary>
private readonly struct LodService
{
public LodService(IGameInteropProvider interop)
=> interop.InitializeFromAttributes(this);
[Signature(Sigs.LodConfig)]
private readonly nint _lodConfig = nint.Zero;
public byte GetLod(TextureResourceHandle* handle)
{
if (handle->ChangeLod)
{
var config = *(byte*)_lodConfig + 0xE;
if (config == byte.MaxValue)
return 2;
}
return 0;
}
}
/// <summary> Custom ulong flag to signal our files as opposed to SE files. </summary>
public static readonly nint CustomFileFlag = new(0xDEADBEEF);
/// <summary>
/// We need to keep a list of all CRC64 hash values of our replaced Mdl and Tex files,
/// i.e. CRC32 of filename in the lower bytes, CRC32 of parent path in the upper bytes.
/// </summary>
public IReadOnlySet<ulong> CustomFileCrc
=> _customFileCrc;
private readonly LodService _lodService;
public TexMdlService(IGameInteropProvider interop)
{
interop.InitializeFromAttributes(this);
_lodService = new LodService(interop);
if (HookSettings.ReplacementHooks)
{
_checkFileStateHook.Enable();
_loadMdlFileExternHook.Enable();
_textureSomethingHook.Enable();
_vf32Hook.Enable();
//_loadTexFileExternHook.Enable();
_textureOnLoadHook.Enable();
}
}
/// <summary> Add CRC64 if the given file is a model or texture file and has an associated path. </summary>
public void AddCrc(ResourceType type, FullPath? path)
{
if (path.HasValue && type is ResourceType.Mdl)
_customFileCrc.Add(path.Value.Crc64);
_ = type switch
{
ResourceType.Mdl when path.HasValue => _customMdlCrc.Add(path.Value.Crc64),
ResourceType.Tex when path.HasValue => _customTexCrc.Add(path.Value.Crc64),
_ => false,
};
}
/// <summary> Add a fixed CRC64 value. </summary>
public void AddCrc(ulong crc64)
=> _customFileCrc.Add(crc64);
public void Dispose()
{
_checkFileStateHook.Dispose();
//_loadTexFileExternHook.Dispose();
_textureSomethingHook.Dispose();
_loadMdlFileExternHook.Dispose();
_vf32Hook.Dispose();
_textureOnLoadHook.Dispose();
}
private readonly HashSet<ulong> _customFileCrc = [];
/// <summary>
/// We need to keep a list of all CRC64 hash values of our replaced Mdl and Tex files,
/// i.e. CRC32 of filename in the lower bytes, CRC32 of parent path in the upper bytes.
/// </summary>
private readonly HashSet<ulong> _customMdlCrc = [];
private readonly HashSet<ulong> _customTexCrc = [];
private delegate nint CheckFileStatePrototype(nint unk1, ulong crc64);
private delegate nint TextureSomethingDelegate(TextureResourceHandle* handle, int lod, SeFileDescriptor* descriptor);
[Signature(Sigs.CheckFileState, DetourName = nameof(CheckFileStateDetour))]
private readonly Hook<CheckFileStatePrototype> _checkFileStateHook = null!;
[Signature("E8 ?? ?? ?? ?? 0F B6 C8 EB ?? 4C 8B 83", DetourName = nameof(TextureSomethingDetour))]
private readonly Hook<TextureSomethingDelegate> _textureSomethingHook = null!;
private nint TextureSomethingDetour(TextureResourceHandle* handle, int lod, SeFileDescriptor* descriptor)
{
//Penumbra.Log.Information($"SomethingDetour {handle->Handle.FileName()}");
//if (!handle->Handle.GamePath(out var path) || !path.IsRooted())
return _textureSomethingHook.Original(handle, lod, descriptor);
descriptor->FileMode = FileMode.LoadUnpackedResource;
return _loadTexFileLocal.Invoke((ResourceHandle*)handle, lod, (nint)descriptor, true);
}
private readonly ThreadLocal<bool> _texReturnData = new(() => default);
/// <summary>
/// The function that checks a files CRC64 to determine whether it is 'protected'.
/// We use it to check against our stored CRC64s and if it corresponds, we return the custom flag.
/// We use it to check against our stored CRC64s and if it corresponds, we return the custom flag for models.
/// Since Dawntrail inlined the RSF function for textures, we can not use the flag method here.
/// Instead, we signal the caller that this will fail and let it call the local function after intentionally failing.
/// </summary>
private nint CheckFileStateDetour(nint ptr, ulong crc64)
=> _customFileCrc.Contains(crc64) ? CustomFileFlag : _checkFileStateHook.Original(ptr, crc64);
{
if (_customMdlCrc.Contains(crc64))
return CustomFileFlag;
if (_customTexCrc.Contains(crc64))
_texReturnData.Value = true;
private delegate byte LoadTexFileLocalDelegate(ResourceHandle* handle, int unk1, nint unk2, bool unk3);
return _checkFileStateHook.Original(ptr, crc64);
}
private delegate byte LoadTexFileLocalDelegate(TextureResourceHandle* handle, int unk1, SeFileDescriptor* unk2, bool unk3);
/// <summary> We use the local functions for our own files in the extern hook. </summary>
[Signature(Sigs.LoadTexFileLocal)]
@ -99,36 +116,23 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
[Signature(Sigs.LoadMdlFileLocal)]
private readonly LoadMdlFileLocalPrototype _loadMdlFileLocal = null!;
private delegate byte TexResourceHandleOnLoadPrototype(TextureResourceHandle* handle, SeFileDescriptor* descriptor, byte unk2);
private delegate byte LoadTexFileExternPrototype(ResourceHandle* handle, int unk1, nint unk2, bool unk3, nint unk4);
[Signature(Sigs.TexResourceHandleOnLoad, DetourName = nameof(OnLoadDetour))]
private readonly Hook<TexResourceHandleOnLoadPrototype> _textureOnLoadHook = null!;
private delegate byte TexResourceHandleVf32Prototype(TextureResourceHandle* handle, SeFileDescriptor* descriptor, byte unk2);
[Signature("40 53 55 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 48 8B D9", DetourName = nameof(Vf32Detour))]
private readonly Hook<TexResourceHandleVf32Prototype> _vf32Hook = null!;
private byte Vf32Detour(TextureResourceHandle* handle, SeFileDescriptor* descriptor, byte unk2)
private byte OnLoadDetour(TextureResourceHandle* handle, SeFileDescriptor* descriptor, byte unk2)
{
//if (handle->Handle.GamePath(out var path) && path.IsRooted())
//{
// Penumbra.Log.Information($"Replacing {descriptor->FileMode} with {FileMode.LoadSqPackResource} in VF32 for {path}.");
// descriptor->FileMode = FileMode.LoadSqPackResource;
//}
var ret = _textureOnLoadHook.Original(handle, descriptor, unk2);
if (!_texReturnData.Value)
return ret;
var ret = _vf32Hook.Original(handle, descriptor, unk2);
return ret;
// Function failed on a replaced texture, call local.
_texReturnData.Value = false;
return _loadTexFileLocal(handle, _lodService.GetLod(handle), descriptor, unk2 != 0);
}
//[Signature(Sigs.LoadTexFileExtern, DetourName = nameof(LoadTexFileExternDetour))]
//private readonly Hook<LoadTexFileExternPrototype> _loadTexFileExternHook = null!;
/// <summary> We hook the extern functions to just return the local one if given the custom flag as last argument. </summary>
//private byte LoadTexFileExternDetour(ResourceHandle* resourceHandle, int unk1, nint unk2, bool unk3, nint ptr)
// => ptr.Equals(CustomFileFlag)
// ? _loadTexFileLocal.Invoke(resourceHandle, unk1, unk2, unk3)
// : _loadTexFileExternHook.Original(resourceHandle, unk1, unk2, unk3, ptr);
public delegate byte LoadMdlFileExternPrototype(ResourceHandle* handle, nint unk1, bool unk2, nint unk3);
private delegate byte LoadMdlFileExternPrototype(ResourceHandle* handle, nint unk1, bool unk2, nint unk3);
[Signature(Sigs.LoadMdlFileExtern, DetourName = nameof(LoadMdlFileExternDetour))]
private readonly Hook<LoadMdlFileExternPrototype> _loadMdlFileExternHook = null!;

View file

@ -14,6 +14,12 @@ public unsafe struct TextureResourceHandle
[FieldOffset(0x0)]
public CsHandle.TextureResourceHandle CsHandle;
[FieldOffset(0x104)]
public byte SomeLodFlag;
public bool ChangeLod
=> (SomeLodFlag & 1) != 0;
}
public enum LoadState : byte