mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 05:04:15 +01:00
Change texture handling.
This commit is contained in:
parent
9fb8090781
commit
4026dd5867
3 changed files with 73 additions and 63 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 066637abe05c659b79d84f52e6db33487498f433
|
||||
Subproject commit 19923f8d5649f11edcfae710c26d6273cf2e9d62
|
||||
|
|
@ -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!;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue