mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Move more hooks in own classes.
This commit is contained in:
parent
be588e2fa3
commit
91cea50f02
26 changed files with 274 additions and 262 deletions
|
|
@ -72,7 +72,24 @@ public class GameState : IService
|
|||
|
||||
#endregion
|
||||
|
||||
/// <summary> Return the correct resolve data from the stored data. </summary>
|
||||
#region Subfiles
|
||||
|
||||
public readonly ThreadLocal<ResolveData> MtrlData = new(() => ResolveData.Invalid);
|
||||
public readonly ThreadLocal<ResolveData> AvfxData = new(() => ResolveData.Invalid);
|
||||
|
||||
public readonly ConcurrentDictionary<nint, ResolveData> SubFileCollection = new();
|
||||
|
||||
public ResolveData LoadSubFileHelper(nint resourceHandle)
|
||||
{
|
||||
if (resourceHandle == nint.Zero)
|
||||
return ResolveData.Invalid;
|
||||
|
||||
return SubFileCollection.TryGetValue(resourceHandle, out var c) ? c : ResolveData.Invalid;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary> Return the correct resolve data from the stored data. </summary>
|
||||
public unsafe bool HandleFiles(CollectionResolver resolver, ResourceType type, Utf8GamePath _, out ResolveData resolveData)
|
||||
{
|
||||
switch (type)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ using OtterGui.Classes;
|
|||
using OtterGui.Services;
|
||||
using Penumbra.UI.AdvancedWindow;
|
||||
|
||||
namespace Penumbra.Interop.Hooks;
|
||||
namespace Penumbra.Interop.Hooks.Objects;
|
||||
|
||||
public sealed unsafe class CharacterBaseDestructor : EventWrapperPtr<CharacterBase, CharacterBaseDestructor.Priority>, IHookService
|
||||
{
|
||||
|
|
@ -4,7 +4,7 @@ using OtterGui.Classes;
|
|||
using OtterGui.Services;
|
||||
using Penumbra.GameData;
|
||||
|
||||
namespace Penumbra.Interop.Hooks;
|
||||
namespace Penumbra.Interop.Hooks.Objects;
|
||||
|
||||
public sealed unsafe class CharacterDestructor : EventWrapperPtr<Character, CharacterDestructor.Priority>, IHookService
|
||||
{
|
||||
|
|
@ -3,7 +3,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
|||
using OtterGui.Classes;
|
||||
using OtterGui.Services;
|
||||
|
||||
namespace Penumbra.Interop.Hooks;
|
||||
namespace Penumbra.Interop.Hooks.Objects;
|
||||
|
||||
public sealed unsafe class CopyCharacter : EventWrapperPtr<Character, Character, CopyCharacter.Priority>, IHookService
|
||||
{
|
||||
|
|
@ -4,7 +4,7 @@ using OtterGui.Classes;
|
|||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Penumbra.Interop.Hooks;
|
||||
namespace Penumbra.Interop.Hooks.Objects;
|
||||
|
||||
public sealed unsafe class CreateCharacterBase : EventWrapperPtr<ModelCharaId, CustomizeArray, CharacterArmor, CreateCharacterBase.Priority>, IHookService
|
||||
{
|
||||
|
|
@ -39,7 +39,7 @@ public sealed unsafe class CreateCharacterBase : EventWrapperPtr<ModelCharaId, C
|
|||
|
||||
private CharacterBase* Detour(ModelCharaId model, CustomizeArray* customize, CharacterArmor* equipment, byte unk)
|
||||
{
|
||||
Penumbra.Log.Verbose($"[{Name}] Triggered with model: {model.Id}, customize: 0x{(nint) customize:X}, equipment: 0x{(nint)equipment:X}, unk: {unk}.");
|
||||
Penumbra.Log.Verbose($"[{Name}] Triggered with model: {model.Id}, customize: 0x{(nint)customize:X}, equipment: 0x{(nint)equipment:X}, unk: {unk}.");
|
||||
Invoke(&model, customize, equipment);
|
||||
var ret = _task.Result.Original(model, customize, equipment, unk);
|
||||
_postEvent.Invoke(model, customize, equipment, ret);
|
||||
|
|
@ -3,7 +3,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
|||
using OtterGui.Services;
|
||||
using Penumbra.GameData;
|
||||
|
||||
namespace Penumbra.Interop.Hooks;
|
||||
namespace Penumbra.Interop.Hooks.Objects;
|
||||
|
||||
/// <summary>
|
||||
/// EnableDraw is what creates DrawObjects for gameObjects,
|
||||
|
|
@ -12,12 +12,12 @@ namespace Penumbra.Interop.Hooks;
|
|||
public sealed unsafe class EnableDraw : IHookService
|
||||
{
|
||||
private readonly Task<Hook<Delegate>> _task;
|
||||
private readonly GameState _state;
|
||||
private readonly GameState _state;
|
||||
|
||||
public EnableDraw(HookManager hooks, GameState state)
|
||||
{
|
||||
_state = state;
|
||||
_task = hooks.CreateHook<Delegate>("Enable Draw", Sigs.EnableDraw, Detour, true);
|
||||
_task = hooks.CreateHook<Delegate>("Enable Draw", Sigs.EnableDraw, Detour, true);
|
||||
}
|
||||
|
||||
private delegate void Delegate(GameObject* gameObject);
|
||||
|
|
@ -26,7 +26,7 @@ public sealed unsafe class EnableDraw : IHookService
|
|||
private void Detour(GameObject* gameObject)
|
||||
{
|
||||
_state.QueueGameObject(gameObject);
|
||||
Penumbra.Log.Excessive($"[Enable Draw] Invoked on 0x{(nint) gameObject:X}.");
|
||||
Penumbra.Log.Excessive($"[Enable Draw] Invoked on 0x{(nint)gameObject:X}.");
|
||||
_task.Result.Original.Invoke(gameObject);
|
||||
_state.DequeueGameObject();
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ using OtterGui.Classes;
|
|||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Penumbra.Interop.Hooks;
|
||||
namespace Penumbra.Interop.Hooks.Objects;
|
||||
|
||||
public sealed unsafe class WeaponReload : EventWrapperPtr<DrawDataContainer, Character, CharacterWeapon, WeaponReload.Priority>, IHookService
|
||||
{
|
||||
28
Penumbra/Interop/Hooks/Resources/ApricotResourceLoad.cs
Normal file
28
Penumbra/Interop/Hooks/Resources/ApricotResourceLoad.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData;
|
||||
|
||||
namespace Penumbra.Interop.Hooks.Resources;
|
||||
|
||||
public sealed unsafe class ApricotResourceLoad : FastHook<ApricotResourceLoad.Delegate>
|
||||
{
|
||||
private readonly GameState _gameState;
|
||||
|
||||
public ApricotResourceLoad(HookManager hooks, GameState gameState)
|
||||
{
|
||||
_gameState = gameState;
|
||||
Task = hooks.CreateHook<Delegate>("Load Apricot Resource", Sigs.ApricotResourceLoad, Detour, true);
|
||||
}
|
||||
|
||||
public delegate byte Delegate(ResourceHandle* handle, nint unk1, byte unk2);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private byte Detour(ResourceHandle* handle, nint unk1, byte unk2)
|
||||
{
|
||||
var last = _gameState.AvfxData.Value;
|
||||
_gameState.AvfxData.Value = _gameState.LoadSubFileHelper((nint)handle);
|
||||
var ret = Task.Result.Original(handle, unk1, unk2);
|
||||
_gameState.AvfxData.Value = last;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
32
Penumbra/Interop/Hooks/Resources/LoadMtrlShpk.cs
Normal file
32
Penumbra/Interop/Hooks/Resources/LoadMtrlShpk.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.Interop.Hooks.Resources;
|
||||
|
||||
public sealed unsafe class LoadMtrlShpk : FastHook<LoadMtrlShpk.Delegate>
|
||||
{
|
||||
private readonly GameState _gameState;
|
||||
private readonly CommunicatorService _communicator;
|
||||
|
||||
public LoadMtrlShpk(HookManager hooks, GameState gameState, CommunicatorService communicator)
|
||||
{
|
||||
_gameState = gameState;
|
||||
_communicator = communicator;
|
||||
Task = hooks.CreateHook<Delegate>("Load Material Shaders", Sigs.LoadMtrlShpk, Detour, true);
|
||||
}
|
||||
|
||||
public delegate byte Delegate(MaterialResourceHandle* mtrlResourceHandle);
|
||||
|
||||
private byte Detour(MaterialResourceHandle* handle)
|
||||
{
|
||||
var last = _gameState.MtrlData.Value;
|
||||
var mtrlData = _gameState.LoadSubFileHelper((nint)handle);
|
||||
_gameState.MtrlData.Value = mtrlData;
|
||||
var ret = Task.Result.Original(handle);
|
||||
_gameState.MtrlData.Value = last;
|
||||
_communicator.MtrlShpkLoaded.Invoke((nint)handle, mtrlData.AssociatedGameObject);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
28
Penumbra/Interop/Hooks/Resources/LoadMtrlTex.cs
Normal file
28
Penumbra/Interop/Hooks/Resources/LoadMtrlTex.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData;
|
||||
|
||||
namespace Penumbra.Interop.Hooks.Resources;
|
||||
|
||||
public sealed unsafe class LoadMtrlTex : FastHook<LoadMtrlTex.Delegate>
|
||||
{
|
||||
private readonly GameState _gameState;
|
||||
|
||||
public LoadMtrlTex(HookManager hooks, GameState gameState)
|
||||
{
|
||||
_gameState = gameState;
|
||||
Task = hooks.CreateHook<Delegate>("Load Material Textures", Sigs.LoadMtrlTex, Detour, true);
|
||||
}
|
||||
|
||||
public delegate byte Delegate(MaterialResourceHandle* mtrlResourceHandle);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private byte Detour(MaterialResourceHandle* handle)
|
||||
{
|
||||
var last = _gameState.MtrlData.Value;
|
||||
_gameState.MtrlData.Value = _gameState.LoadSubFileHelper((nint)handle);
|
||||
var ret = Task.Result.Original(handle);
|
||||
_gameState.MtrlData.Value = last;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
38
Penumbra/Interop/Hooks/Resources/ResolvePathHooks.cs
Normal file
38
Penumbra/Interop/Hooks/Resources/ResolvePathHooks.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
using OtterGui.Services;
|
||||
using Penumbra.Interop.PathResolving;
|
||||
|
||||
namespace Penumbra.Interop.Hooks.Resources;
|
||||
|
||||
public sealed unsafe class ResolvePathHooks(HookManager hooks, CharacterBaseVTables vTables, PathState pathState) : IDisposable, IRequiredService
|
||||
{
|
||||
// @formatter:off
|
||||
private readonly ResolvePathHooksBase _human = new("Human", hooks, pathState, vTables.HumanVTable, ResolvePathHooksBase.Type.Human);
|
||||
private readonly ResolvePathHooksBase _weapon = new("Weapon", hooks, pathState, vTables.WeaponVTable, ResolvePathHooksBase.Type.Other);
|
||||
private readonly ResolvePathHooksBase _demiHuman = new("DemiHuman", hooks, pathState, vTables.DemiHumanVTable, ResolvePathHooksBase.Type.Other);
|
||||
private readonly ResolvePathHooksBase _monster = new("Monster", hooks, pathState, vTables.MonsterVTable, ResolvePathHooksBase.Type.Other);
|
||||
// @formatter:on
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
_human.Enable();
|
||||
_weapon.Enable();
|
||||
_demiHuman.Enable();
|
||||
_monster.Enable();
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
_human.Disable();
|
||||
_weapon.Disable();
|
||||
_demiHuman.Disable();
|
||||
_monster.Disable();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_human.Dispose();
|
||||
_weapon.Dispose();
|
||||
_demiHuman.Dispose();
|
||||
_monster.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
using Dalamud.Hooking;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Interop.PathResolving;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Interop.PathResolving;
|
||||
namespace Penumbra.Interop.Hooks.Resources;
|
||||
|
||||
public unsafe class ResolvePathHooks : IDisposable
|
||||
public sealed unsafe class ResolvePathHooksBase : IDisposable
|
||||
{
|
||||
public enum Type
|
||||
{
|
||||
|
|
@ -19,7 +20,9 @@ public unsafe class ResolvePathHooks : IDisposable
|
|||
private delegate nint NamedResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex, nint name);
|
||||
private delegate nint PerSlotResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex);
|
||||
private delegate nint SingleResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize);
|
||||
|
||||
private delegate nint TmbResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize, nint timelineName);
|
||||
|
||||
// Kept separate from NamedResolveDelegate because the 5th parameter has out semantics here, instead of in.
|
||||
private delegate nint VfxResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex, nint unkOutParam);
|
||||
|
||||
|
|
@ -38,21 +41,24 @@ public unsafe class ResolvePathHooks : IDisposable
|
|||
|
||||
private readonly PathState _parent;
|
||||
|
||||
public ResolvePathHooks(IGameInteropProvider interop, PathState parent, nint* vTable, Type type)
|
||||
public ResolvePathHooksBase(string name, HookManager hooks, PathState parent, nint* vTable, Type type)
|
||||
{
|
||||
_parent = parent;
|
||||
_resolveDecalPathHook = Create<PerSlotResolveDelegate>(interop, vTable[83], ResolveDecal);
|
||||
_resolveEidPathHook = Create<SingleResolveDelegate>(interop, vTable[85], ResolveEid);
|
||||
_resolveImcPathHook = Create<PerSlotResolveDelegate>(interop, vTable[81], ResolveImc);
|
||||
_resolveMPapPathHook = Create<MPapResolveDelegate>(interop, vTable[79], ResolveMPap);
|
||||
_resolveMdlPathHook = Create<PerSlotResolveDelegate>(interop, vTable[73], type, ResolveMdl, ResolveMdlHuman);
|
||||
_resolveMtrlPathHook = Create<NamedResolveDelegate>(interop, vTable[82], ResolveMtrl);
|
||||
_resolvePapPathHook = Create<NamedResolveDelegate>(interop, vTable[76], type, ResolvePap, ResolvePapHuman);
|
||||
_resolvePhybPathHook = Create<PerSlotResolveDelegate>(interop, vTable[75], type, ResolvePhyb, ResolvePhybHuman);
|
||||
_resolveSklbPathHook = Create<PerSlotResolveDelegate>(interop, vTable[72], type, ResolveSklb, ResolveSklbHuman);
|
||||
_resolveSkpPathHook = Create<PerSlotResolveDelegate>(interop, vTable[74], type, ResolveSkp, ResolveSkpHuman);
|
||||
_resolveTmbPathHook = Create<TmbResolveDelegate>(interop, vTable[77], ResolveTmb);
|
||||
_resolveVfxPathHook = Create<VfxResolveDelegate>(interop, vTable[84], ResolveVfx);
|
||||
_parent = parent;
|
||||
// @formatter:off
|
||||
_resolveDecalPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveDecal)}", hooks, vTable[83], ResolveDecal);
|
||||
_resolveEidPathHook = Create<SingleResolveDelegate>( $"{name}.{nameof(ResolveEid)}", hooks, vTable[85], ResolveEid);
|
||||
_resolveImcPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveImc)}", hooks, vTable[81], ResolveImc);
|
||||
_resolveMPapPathHook = Create<MPapResolveDelegate>( $"{name}.{nameof(ResolveMPap)}", hooks, vTable[79], ResolveMPap);
|
||||
_resolveMdlPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveMdl)}", hooks, vTable[73], type, ResolveMdl, ResolveMdlHuman);
|
||||
_resolveMtrlPathHook = Create<NamedResolveDelegate>( $"{name}.{nameof(ResolveMtrl)}", hooks, vTable[82], ResolveMtrl);
|
||||
_resolvePapPathHook = Create<NamedResolveDelegate>( $"{name}.{nameof(ResolvePap)}", hooks, vTable[76], type, ResolvePap, ResolvePapHuman);
|
||||
_resolvePhybPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolvePhyb)}", hooks, vTable[75], type, ResolvePhyb, ResolvePhybHuman);
|
||||
_resolveSklbPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveSklb)}", hooks, vTable[72], type, ResolveSklb, ResolveSklbHuman);
|
||||
_resolveSkpPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveSkp)}", hooks, vTable[74], type, ResolveSkp, ResolveSkpHuman);
|
||||
_resolveTmbPathHook = Create<TmbResolveDelegate>( $"{name}.{nameof(ResolveTmb)}", hooks, vTable[77], ResolveTmb);
|
||||
_resolveVfxPathHook = Create<VfxResolveDelegate>( $"{name}.{nameof(ResolveVfx)}", hooks, vTable[84], ResolveVfx);
|
||||
// @formatter:on
|
||||
Enable();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
|
|
@ -177,9 +183,8 @@ public unsafe class ResolvePathHooks : IDisposable
|
|||
{
|
||||
data = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||
if (_parent.InInternalResolve)
|
||||
{
|
||||
return DisposableContainer.Empty;
|
||||
}
|
||||
|
||||
return new DisposableContainer(data.ModCollection.TemporarilySetEstFile(_parent.CharacterUtility, EstManipulation.EstType.Face),
|
||||
data.ModCollection.TemporarilySetEstFile(_parent.CharacterUtility, EstManipulation.EstType.Body),
|
||||
data.ModCollection.TemporarilySetEstFile(_parent.CharacterUtility, EstManipulation.EstType.Hair),
|
||||
|
|
@ -188,19 +193,19 @@ public unsafe class ResolvePathHooks : IDisposable
|
|||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static Hook<T> Create<T>(IGameInteropProvider interop, nint address, Type type, T other, T human) where T : Delegate
|
||||
private static Hook<T> Create<T>(string name, HookManager hooks, nint address, Type type, T other, T human) where T : Delegate
|
||||
{
|
||||
var del = type switch
|
||||
{
|
||||
Type.Human => human,
|
||||
_ => other,
|
||||
Type.Human => human,
|
||||
_ => other,
|
||||
};
|
||||
return interop.HookFromAddress(address, del);
|
||||
return hooks.CreateHook(name, address, del).Result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static Hook<T> Create<T>(IGameInteropProvider interop, nint address, T del) where T : Delegate
|
||||
=> interop.HookFromAddress(address, del);
|
||||
private static Hook<T> Create<T>(string name, HookManager hooks, nint address, T del) where T : Delegate
|
||||
=> hooks.CreateHook(name, address, del).Result;
|
||||
|
||||
|
||||
// Implementation
|
||||
|
|
@ -2,10 +2,9 @@ using Dalamud.Hooking;
|
|||
using OtterGui.Classes;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.Interop.Services;
|
||||
using Penumbra.Interop.Structs;
|
||||
|
||||
namespace Penumbra.Interop.Hooks;
|
||||
namespace Penumbra.Interop.Hooks.Resources;
|
||||
|
||||
public sealed unsafe class ResourceHandleDestructor : EventWrapperPtr<ResourceHandle, ResourceHandleDestructor.Priority>, IHookService
|
||||
{
|
||||
|
|
@ -16,11 +16,9 @@ public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase
|
|||
private readonly Texture** _colorTableTexture;
|
||||
private readonly SafeTextureHandle _originalColorTableTexture;
|
||||
|
||||
private Half[] _colorTable;
|
||||
private bool _updatePending;
|
||||
private bool _updatePending;
|
||||
|
||||
public Half[] ColorTable
|
||||
=> _colorTable;
|
||||
public Half[] ColorTable { get; }
|
||||
|
||||
public LiveColorTablePreviewer(IObjectTable objects, IFramework framework, MaterialInfo materialInfo)
|
||||
: base(objects, materialInfo)
|
||||
|
|
@ -41,7 +39,7 @@ public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase
|
|||
if (_originalColorTableTexture == null)
|
||||
throw new InvalidOperationException("Material doesn't have a color table");
|
||||
|
||||
_colorTable = new Half[TextureLength];
|
||||
ColorTable = new Half[TextureLength];
|
||||
_updatePending = true;
|
||||
|
||||
framework.Update += OnFrameworkUpdate;
|
||||
|
|
@ -84,9 +82,9 @@ public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase
|
|||
return;
|
||||
|
||||
bool success;
|
||||
lock (_colorTable)
|
||||
lock (ColorTable)
|
||||
{
|
||||
fixed (Half* colorTable = _colorTable)
|
||||
fixed (Half* colorTable = ColorTable)
|
||||
{
|
||||
success = texture.Texture->InitializeContents(colorTable);
|
||||
}
|
||||
|
|
@ -105,9 +103,6 @@ public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase
|
|||
if (colorSetTextures == null)
|
||||
return false;
|
||||
|
||||
if (_colorTableTexture != colorSetTextures + (MaterialInfo.ModelSlot * 4 + MaterialInfo.MaterialSlot))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return _colorTableTexture == colorSetTextures + (MaterialInfo.ModelSlot * 4 + MaterialInfo.MaterialSlot);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,34 +26,29 @@ public sealed unsafe class LiveMaterialPreviewer : LiveMaterialPreviewerBase
|
|||
if (_shaderPackage == null)
|
||||
throw new InvalidOperationException("Material doesn't have a shader package");
|
||||
|
||||
var material = Material;
|
||||
_originalShPkFlags = Material->ShaderFlags;
|
||||
|
||||
_originalShPkFlags = material->ShaderFlags;
|
||||
_originalMaterialParameter = Material->MaterialParameterCBuffer->TryGetBuffer().ToArray();
|
||||
|
||||
_originalMaterialParameter = material->MaterialParameterCBuffer->TryGetBuffer().ToArray();
|
||||
|
||||
_originalSamplerFlags = new uint[material->TextureCount];
|
||||
_originalSamplerFlags = new uint[Material->TextureCount];
|
||||
for (var i = 0; i < _originalSamplerFlags.Length; ++i)
|
||||
_originalSamplerFlags[i] = material->Textures[i].SamplerFlags;
|
||||
_originalSamplerFlags[i] = Material->Textures[i].SamplerFlags;
|
||||
}
|
||||
|
||||
protected override void Clear(bool disposing, bool reset)
|
||||
{
|
||||
base.Clear(disposing, reset);
|
||||
|
||||
if (reset)
|
||||
{
|
||||
var material = Material;
|
||||
if (!reset)
|
||||
return;
|
||||
|
||||
material->ShaderFlags = _originalShPkFlags;
|
||||
Material->ShaderFlags = _originalShPkFlags;
|
||||
var materialParameter = Material->MaterialParameterCBuffer->TryGetBuffer();
|
||||
if (!materialParameter.IsEmpty)
|
||||
_originalMaterialParameter.AsSpan().CopyTo(materialParameter);
|
||||
|
||||
var materialParameter = material->MaterialParameterCBuffer->TryGetBuffer();
|
||||
if (!materialParameter.IsEmpty)
|
||||
_originalMaterialParameter.AsSpan().CopyTo(materialParameter);
|
||||
|
||||
for (var i = 0; i < _originalSamplerFlags.Length; ++i)
|
||||
material->Textures[i].SamplerFlags = _originalSamplerFlags[i];
|
||||
}
|
||||
for (var i = 0; i < _originalSamplerFlags.Length; ++i)
|
||||
Material->Textures[i].SamplerFlags = _originalSamplerFlags[i];
|
||||
}
|
||||
|
||||
public void SetShaderPackageFlags(uint shPkFlags)
|
||||
|
|
@ -80,16 +75,16 @@ public sealed unsafe class LiveMaterialPreviewer : LiveMaterialPreviewerBase
|
|||
for (var i = 0; i < _shaderPackage->MaterialElementCount; ++i)
|
||||
{
|
||||
ref var parameter = ref _shaderPackage->MaterialElementsSpan[i];
|
||||
if (parameter.CRC == parameterCrc)
|
||||
{
|
||||
if ((parameter.Offset & 0x3) != 0
|
||||
|| (parameter.Size & 0x3) != 0
|
||||
|| (parameter.Offset + parameter.Size) >> 2 > buffer.Length)
|
||||
return;
|
||||
if (parameter.CRC != parameterCrc)
|
||||
continue;
|
||||
|
||||
value.TryCopyTo(buffer.Slice(parameter.Offset >> 2, parameter.Size >> 2)[offset..]);
|
||||
if ((parameter.Offset & 0x3) != 0
|
||||
|| (parameter.Size & 0x3) != 0
|
||||
|| (parameter.Offset + parameter.Size) >> 2 > buffer.Length)
|
||||
return;
|
||||
}
|
||||
|
||||
value.TryCopyTo(buffer.Slice(parameter.Offset >> 2, parameter.Size >> 2)[offset..]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,25 +99,24 @@ public sealed unsafe class LiveMaterialPreviewer : LiveMaterialPreviewerBase
|
|||
var samplers = _shaderPackage->Samplers;
|
||||
for (var i = 0; i < _shaderPackage->SamplerCount; ++i)
|
||||
{
|
||||
if (samplers[i].CRC == samplerCrc)
|
||||
{
|
||||
id = samplers[i].Id;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (samplers[i].CRC != samplerCrc)
|
||||
continue;
|
||||
|
||||
id = samplers[i].Id;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return;
|
||||
|
||||
var material = Material;
|
||||
for (var i = 0; i < material->TextureCount; ++i)
|
||||
for (var i = 0; i < Material->TextureCount; ++i)
|
||||
{
|
||||
if (material->Textures[i].Id == id)
|
||||
{
|
||||
material->Textures[i].SamplerFlags = (samplerFlags & 0xFFFFFDFF) | 0x000001C0;
|
||||
break;
|
||||
}
|
||||
if (Material->Textures[i].Id != id)
|
||||
continue;
|
||||
|
||||
Material->Textures[i].SamplerFlags = (samplerFlags & 0xFFFFFDFF) | 0x000001C0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,9 +133,6 @@ public sealed unsafe class LiveMaterialPreviewer : LiveMaterialPreviewerBase
|
|||
if (shpkHandle == null)
|
||||
return false;
|
||||
|
||||
if (_shaderPackage != shpkHandle->ShaderPackage)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return _shaderPackage == shpkHandle->ShaderPackage;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,9 +61,6 @@ public abstract unsafe class LiveMaterialPreviewerBase : IDisposable
|
|||
if ((nint)DrawObject != MaterialInfo.GetDrawObject(gameObject))
|
||||
return false;
|
||||
|
||||
if (Material != MaterialInfo.GetDrawObjectMaterial(DrawObject))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return Material == MaterialInfo.GetDrawObjectMaterial(DrawObject);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,22 +24,6 @@ public readonly record struct MaterialInfo(ObjectIndex ObjectIndex, DrawObjectTy
|
|||
public nint GetDrawObject(nint address)
|
||||
=> GetDrawObject(Type, address);
|
||||
|
||||
public static unsafe nint GetDrawObject(DrawObjectType type, nint address)
|
||||
{
|
||||
var gameObject = (Character*)address;
|
||||
if (gameObject == null)
|
||||
return nint.Zero;
|
||||
|
||||
return type switch
|
||||
{
|
||||
DrawObjectType.Character => (nint)gameObject->GameObject.GetDrawObject(),
|
||||
DrawObjectType.Mainhand => (nint)gameObject->DrawData.Weapon(DrawDataContainer.WeaponSlot.MainHand).DrawObject,
|
||||
DrawObjectType.Offhand => (nint)gameObject->DrawData.Weapon(DrawDataContainer.WeaponSlot.OffHand).DrawObject,
|
||||
DrawObjectType.Vfx => (nint)gameObject->DrawData.Weapon(DrawDataContainer.WeaponSlot.Unk).DrawObject,
|
||||
_ => nint.Zero,
|
||||
};
|
||||
}
|
||||
|
||||
public unsafe Material* GetDrawObjectMaterial(IObjectTable objects)
|
||||
=> GetDrawObjectMaterial((CharacterBase*)GetDrawObject(GetCharacter(objects)));
|
||||
|
||||
|
|
@ -103,4 +87,20 @@ public readonly record struct MaterialInfo(ObjectIndex ObjectIndex, DrawObjectTy
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static unsafe nint GetDrawObject(DrawObjectType type, nint address)
|
||||
{
|
||||
var gameObject = (Character*)address;
|
||||
if (gameObject == null)
|
||||
return nint.Zero;
|
||||
|
||||
return type switch
|
||||
{
|
||||
DrawObjectType.Character => (nint)gameObject->GameObject.GetDrawObject(),
|
||||
DrawObjectType.Mainhand => (nint)gameObject->DrawData.Weapon(DrawDataContainer.WeaponSlot.MainHand).DrawObject,
|
||||
DrawObjectType.Offhand => (nint)gameObject->DrawData.Weapon(DrawDataContainer.WeaponSlot.OffHand).DrawObject,
|
||||
DrawObjectType.Vfx => (nint)gameObject->DrawData.Weapon(DrawDataContainer.WeaponSlot.Unk).DrawObject,
|
||||
_ => nint.Zero,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ using Dalamud.Plugin.Services;
|
|||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Interop.Hooks;
|
||||
using Penumbra.Interop.Hooks.Objects;
|
||||
|
||||
namespace Penumbra.Interop.PathResolving;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using OtterGui.Services;
|
|||
using Penumbra.Interop.Hooks;
|
||||
using Object = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.Object;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Hooks.Objects;
|
||||
|
||||
namespace Penumbra.Interop.PathResolving;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using Penumbra.Collections;
|
|||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.Communication;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.Interop.Hooks;
|
||||
using Penumbra.Interop.Hooks.Objects;
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.Interop.PathResolving;
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ using Penumbra.Collections;
|
|||
using Penumbra.Api.Enums;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Hooks;
|
||||
using Penumbra.Interop.ResourceLoading;
|
||||
using Penumbra.Interop.Services;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.String.Classes;
|
||||
using ObjectType = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.ObjectType;
|
||||
using CharacterUtility = Penumbra.Interop.Services.CharacterUtility;
|
||||
using Penumbra.Interop.Hooks.Objects;
|
||||
|
||||
namespace Penumbra.Interop.PathResolving;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,34 +1,15 @@
|
|||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.Interop.Services;
|
||||
using Penumbra.String;
|
||||
|
||||
namespace Penumbra.Interop.PathResolving;
|
||||
|
||||
public unsafe class PathState : IDisposable
|
||||
public sealed class PathState(CollectionResolver collectionResolver, MetaState metaState, CharacterUtility characterUtility)
|
||||
: IDisposable
|
||||
{
|
||||
[Signature(Sigs.HumanVTable, ScanType = ScanType.StaticAddress)]
|
||||
private readonly nint* _humanVTable = null!;
|
||||
|
||||
[Signature(Sigs.WeaponVTable, ScanType = ScanType.StaticAddress)]
|
||||
private readonly nint* _weaponVTable = null!;
|
||||
|
||||
[Signature(Sigs.DemiHumanVTable, ScanType = ScanType.StaticAddress)]
|
||||
private readonly nint* _demiHumanVTable = null!;
|
||||
|
||||
[Signature(Sigs.MonsterVTable, ScanType = ScanType.StaticAddress)]
|
||||
private readonly nint* _monsterVTable = null!;
|
||||
|
||||
public readonly CollectionResolver CollectionResolver;
|
||||
public readonly MetaState MetaState;
|
||||
public readonly CharacterUtility CharacterUtility;
|
||||
|
||||
private readonly ResolvePathHooks _human;
|
||||
private readonly ResolvePathHooks _weapon;
|
||||
private readonly ResolvePathHooks _demiHuman;
|
||||
private readonly ResolvePathHooks _monster;
|
||||
public readonly CollectionResolver CollectionResolver = collectionResolver;
|
||||
public readonly MetaState MetaState = metaState;
|
||||
public readonly CharacterUtility CharacterUtility = characterUtility;
|
||||
|
||||
private readonly ThreadLocal<ResolveData> _resolveData = new(() => ResolveData.Invalid, true);
|
||||
private readonly ThreadLocal<uint> _internalResolve = new(() => 0, false);
|
||||
|
|
@ -39,31 +20,11 @@ public unsafe class PathState : IDisposable
|
|||
public bool InInternalResolve
|
||||
=> _internalResolve.Value != 0u;
|
||||
|
||||
public PathState(CollectionResolver collectionResolver, MetaState metaState, CharacterUtility characterUtility, IGameInteropProvider interop)
|
||||
{
|
||||
interop.InitializeFromAttributes(this);
|
||||
CollectionResolver = collectionResolver;
|
||||
MetaState = metaState;
|
||||
CharacterUtility = characterUtility;
|
||||
_human = new ResolvePathHooks(interop, this, _humanVTable, ResolvePathHooks.Type.Human);
|
||||
_weapon = new ResolvePathHooks(interop, this, _weaponVTable, ResolvePathHooks.Type.Other);
|
||||
_demiHuman = new ResolvePathHooks(interop, this, _demiHumanVTable, ResolvePathHooks.Type.Other);
|
||||
_monster = new ResolvePathHooks(interop, this, _monsterVTable, ResolvePathHooks.Type.Other);
|
||||
_human.Enable();
|
||||
_weapon.Enable();
|
||||
_demiHuman.Enable();
|
||||
_monster.Enable();
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_resolveData.Dispose();
|
||||
_internalResolve.Dispose();
|
||||
_human.Dispose();
|
||||
_weapon.Dispose();
|
||||
_demiHuman.Dispose();
|
||||
_monster.Dispose();
|
||||
}
|
||||
|
||||
public bool Consume(ByteString _, out ResolveData collection)
|
||||
|
|
@ -86,9 +47,7 @@ public unsafe class PathState : IDisposable
|
|||
return path;
|
||||
|
||||
if (!InInternalResolve)
|
||||
{
|
||||
_resolveData.Value = collection.ToResolveData(gameObject);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
|
|
@ -99,9 +58,7 @@ public unsafe class PathState : IDisposable
|
|||
return path;
|
||||
|
||||
if (!InInternalResolve)
|
||||
{
|
||||
_resolveData.Value = data;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +83,7 @@ public unsafe class PathState : IDisposable
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public readonly void Dispose()
|
||||
public void Dispose()
|
||||
{
|
||||
--_internalResolve.Value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,10 @@
|
|||
using Dalamud.Hooking;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.Interop.Hooks;
|
||||
using Penumbra.Interop.Hooks.Resources;
|
||||
using Penumbra.Interop.ResourceLoading;
|
||||
using Penumbra.Interop.Services;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.String;
|
||||
using Penumbra.String.Classes;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Interop.PathResolving;
|
||||
|
||||
|
|
@ -20,49 +13,37 @@ namespace Penumbra.Interop.PathResolving;
|
|||
/// Those are loaded synchronously.
|
||||
/// Thus, we need to ensure the correct files are loaded when a material is loaded.
|
||||
/// </summary>
|
||||
public unsafe class SubfileHelper : IDisposable, IReadOnlyCollection<KeyValuePair<nint, ResolveData>>
|
||||
public sealed unsafe class SubfileHelper : IDisposable, IReadOnlyCollection<KeyValuePair<nint, ResolveData>>
|
||||
{
|
||||
private readonly PerformanceTracker _performance;
|
||||
private readonly GameState _gameState;
|
||||
private readonly ResourceLoader _loader;
|
||||
private readonly ResourceHandleDestructor _resourceHandleDestructor;
|
||||
private readonly CommunicatorService _communicator;
|
||||
|
||||
private readonly ThreadLocal<ResolveData> _mtrlData = new(() => ResolveData.Invalid);
|
||||
private readonly ThreadLocal<ResolveData> _avfxData = new(() => ResolveData.Invalid);
|
||||
|
||||
private readonly ConcurrentDictionary<nint, ResolveData> _subFileCollection = new();
|
||||
|
||||
public SubfileHelper(PerformanceTracker performance, ResourceLoader loader, CommunicatorService communicator, IGameInteropProvider interop, ResourceHandleDestructor resourceHandleDestructor)
|
||||
public SubfileHelper(GameState gameState, ResourceLoader loader, ResourceHandleDestructor resourceHandleDestructor)
|
||||
{
|
||||
interop.InitializeFromAttributes(this);
|
||||
|
||||
_performance = performance;
|
||||
_loader = loader;
|
||||
_communicator = communicator;
|
||||
_gameState = gameState;
|
||||
_loader = loader;
|
||||
_resourceHandleDestructor = resourceHandleDestructor;
|
||||
|
||||
_loadMtrlShpkHook.Enable();
|
||||
_loadMtrlTexHook.Enable();
|
||||
_apricotResourceLoadHook.Enable();
|
||||
_loader.ResourceLoaded += SubfileContainerRequested;
|
||||
_loader.ResourceLoaded += SubfileContainerRequested;
|
||||
_resourceHandleDestructor.Subscribe(ResourceDestroyed, ResourceHandleDestructor.Priority.SubfileHelper);
|
||||
}
|
||||
|
||||
|
||||
public IEnumerator<KeyValuePair<nint, ResolveData>> GetEnumerator()
|
||||
=> _subFileCollection.GetEnumerator();
|
||||
=> _gameState.SubFileCollection.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public int Count
|
||||
=> _subFileCollection.Count;
|
||||
=> _gameState.SubFileCollection.Count;
|
||||
|
||||
public ResolveData MtrlData
|
||||
=> _mtrlData.IsValueCreated ? _mtrlData.Value : ResolveData.Invalid;
|
||||
=> _gameState.MtrlData.IsValueCreated ? _gameState.MtrlData.Value : ResolveData.Invalid;
|
||||
|
||||
public ResolveData AvfxData
|
||||
=> _avfxData.IsValueCreated ? _avfxData.Value : ResolveData.Invalid;
|
||||
=> _gameState.AvfxData.IsValueCreated ? _gameState.AvfxData.Value : ResolveData.Invalid;
|
||||
|
||||
/// <summary>
|
||||
/// Check specifically for shpk and tex files whether we are currently in a material load,
|
||||
|
|
@ -71,13 +52,13 @@ public unsafe class SubfileHelper : IDisposable, IReadOnlyCollection<KeyValuePai
|
|||
{
|
||||
switch (type)
|
||||
{
|
||||
case ResourceType.Tex when _mtrlData.Value.Valid:
|
||||
case ResourceType.Shpk when _mtrlData.Value.Valid:
|
||||
collection = _mtrlData.Value;
|
||||
case ResourceType.Tex when _gameState.MtrlData.Value.Valid:
|
||||
case ResourceType.Shpk when _gameState.MtrlData.Value.Valid:
|
||||
collection = _gameState.MtrlData.Value;
|
||||
return true;
|
||||
case ResourceType.Scd when _avfxData.Value.Valid:
|
||||
case ResourceType.Atex when _avfxData.Value.Valid:
|
||||
collection = _avfxData.Value;
|
||||
case ResourceType.Scd when _gameState.AvfxData.Value.Valid:
|
||||
case ResourceType.Atex when _gameState.AvfxData.Value.Valid:
|
||||
collection = _gameState.AvfxData.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -105,11 +86,8 @@ public unsafe class SubfileHelper : IDisposable, IReadOnlyCollection<KeyValuePai
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
_loader.ResourceLoaded -= SubfileContainerRequested;
|
||||
_loader.ResourceLoaded -= SubfileContainerRequested;
|
||||
_resourceHandleDestructor.Unsubscribe(ResourceDestroyed);
|
||||
_loadMtrlShpkHook.Dispose();
|
||||
_loadMtrlTexHook.Dispose();
|
||||
_apricotResourceLoadHook.Dispose();
|
||||
}
|
||||
|
||||
private void SubfileContainerRequested(ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
|
||||
|
|
@ -120,66 +98,12 @@ public unsafe class SubfileHelper : IDisposable, IReadOnlyCollection<KeyValuePai
|
|||
case ResourceType.Mtrl:
|
||||
case ResourceType.Avfx:
|
||||
if (handle->FileSize == 0)
|
||||
_subFileCollection[(nint)handle] = resolveData;
|
||||
_gameState.SubFileCollection[(nint)handle] = resolveData;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResourceDestroyed(ResourceHandle* handle)
|
||||
=> _subFileCollection.TryRemove((nint)handle, out _);
|
||||
|
||||
private delegate byte LoadMtrlFilesDelegate(nint mtrlResourceHandle);
|
||||
|
||||
[Signature(Sigs.LoadMtrlTex, DetourName = nameof(LoadMtrlTexDetour))]
|
||||
private readonly Hook<LoadMtrlFilesDelegate> _loadMtrlTexHook = null!;
|
||||
|
||||
private byte LoadMtrlTexDetour(nint mtrlResourceHandle)
|
||||
{
|
||||
using var performance = _performance.Measure(PerformanceType.LoadTextures);
|
||||
var last = _mtrlData.Value;
|
||||
_mtrlData.Value = LoadFileHelper(mtrlResourceHandle);
|
||||
var ret = _loadMtrlTexHook.Original(mtrlResourceHandle);
|
||||
_mtrlData.Value = last;
|
||||
return ret;
|
||||
}
|
||||
|
||||
[Signature(Sigs.LoadMtrlShpk, DetourName = nameof(LoadMtrlShpkDetour))]
|
||||
private readonly Hook<LoadMtrlFilesDelegate> _loadMtrlShpkHook = null!;
|
||||
|
||||
private byte LoadMtrlShpkDetour(nint mtrlResourceHandle)
|
||||
{
|
||||
using var performance = _performance.Measure(PerformanceType.LoadShaders);
|
||||
var last = _mtrlData.Value;
|
||||
var mtrlData = LoadFileHelper(mtrlResourceHandle);
|
||||
_mtrlData.Value = mtrlData;
|
||||
var ret = _loadMtrlShpkHook.Original(mtrlResourceHandle);
|
||||
_mtrlData.Value = last;
|
||||
_communicator.MtrlShpkLoaded.Invoke(mtrlResourceHandle, mtrlData.AssociatedGameObject);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private ResolveData LoadFileHelper(nint resourceHandle)
|
||||
{
|
||||
if (resourceHandle == nint.Zero)
|
||||
return ResolveData.Invalid;
|
||||
|
||||
return _subFileCollection.TryGetValue(resourceHandle, out var c) ? c : ResolveData.Invalid;
|
||||
}
|
||||
|
||||
|
||||
private delegate byte ApricotResourceLoadDelegate(nint handle, nint unk1, byte unk2);
|
||||
|
||||
[Signature(Sigs.ApricotResourceLoad, DetourName = nameof(ApricotResourceLoadDetour))]
|
||||
private readonly Hook<ApricotResourceLoadDelegate> _apricotResourceLoadHook = null!;
|
||||
|
||||
private byte ApricotResourceLoadDetour(nint handle, nint unk1, byte unk2)
|
||||
{
|
||||
using var performance = _performance.Measure(PerformanceType.LoadApricotResources);
|
||||
var last = _avfxData.Value;
|
||||
_avfxData.Value = LoadFileHelper(handle);
|
||||
var ret = _apricotResourceLoadHook.Original(handle, unk1, unk2);
|
||||
_avfxData.Value = last;
|
||||
return ret;
|
||||
}
|
||||
=> _gameState.SubFileCollection.TryRemove((nint)handle, out _);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
|||
using OtterGui.Classes;
|
||||
using Penumbra.Communication;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.Interop.Hooks;
|
||||
using Penumbra.Interop.Hooks.Resources;
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.Interop.Services;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using OtterGui.Raii;
|
|||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Hooks;
|
||||
using Penumbra.Interop.Hooks.Objects;
|
||||
using Penumbra.Interop.MaterialPreview;
|
||||
using Penumbra.String;
|
||||
using Penumbra.String.Classes;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ using Penumbra.GameData.Enums;
|
|||
using Penumbra.GameData.Files;
|
||||
using Penumbra.Import.Models;
|
||||
using Penumbra.Import.Textures;
|
||||
using Penumbra.Interop.Hooks;
|
||||
using Penumbra.Interop.Hooks.Objects;
|
||||
using Penumbra.Interop.ResourceTree;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Mods;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue