Treat AVFX similar to MTRL, and ATEX similar to TEX.

This commit is contained in:
Ottermandias 2022-12-25 18:22:52 +01:00
parent 3e26972a15
commit 707ae090bf
3 changed files with 78 additions and 77 deletions

View file

@ -1,8 +1,11 @@
using System; using System;
using System.Collections.Generic;
using Dalamud.Hooking; using Dalamud.Hooking;
using Dalamud.Utility.Signatures; using Dalamud.Utility.Signatures;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs;
using Penumbra.String;
using Penumbra.String.Classes; using Penumbra.String.Classes;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
@ -14,9 +17,8 @@ public unsafe partial class PathResolver
{ {
private readonly DrawObjectState _drawObjectState; private readonly DrawObjectState _drawObjectState;
private ResolveData _animationLoadData = ResolveData.Invalid; private ResolveData _animationLoadData = ResolveData.Invalid;
private ResolveData _lastAvfxData = ResolveData.Invalid; private ResolveData _characterSoundData = ResolveData.Invalid;
private ResolveData _characterSoundData = ResolveData.Invalid;
public AnimationState( DrawObjectState drawObjectState ) public AnimationState( DrawObjectState drawObjectState )
{ {
@ -24,15 +26,7 @@ public unsafe partial class PathResolver
SignatureHelper.Initialise( this ); SignatureHelper.Initialise( this );
} }
public void UpdateAvfx( ResourceType type, ResolveData data ) public bool HandleFiles( ResourceType type, Utf8GamePath path, out ResolveData resolveData )
{
if( type == ResourceType.Avfx )
{
_lastAvfxData = data;
}
}
public bool HandleFiles( ResourceType type, Utf8GamePath _, out ResolveData resolveData )
{ {
switch( type ) switch( type )
{ {
@ -60,9 +54,6 @@ public unsafe partial class PathResolver
break; break;
case ResourceType.Avfx: case ResourceType.Avfx:
_lastAvfxData = _animationLoadData.Valid
? _animationLoadData
: Penumbra.CollectionManager.Default.ToResolveData();
if( _animationLoadData.Valid ) if( _animationLoadData.Valid )
{ {
resolveData = _animationLoadData; resolveData = _animationLoadData;
@ -71,12 +62,6 @@ public unsafe partial class PathResolver
break; break;
case ResourceType.Atex: case ResourceType.Atex:
if( _lastAvfxData.Valid )
{
resolveData = _lastAvfxData;
return true;
}
if( _animationLoadData.Valid ) if( _animationLoadData.Valid )
{ {
resolveData = _animationLoadData; resolveData = _animationLoadData;
@ -99,7 +84,6 @@ public unsafe partial class PathResolver
_someActionLoadHook.Enable(); _someActionLoadHook.Enable();
_someOtherAvfxHook.Enable(); _someOtherAvfxHook.Enable();
_loadCharacterSoundHook.Enable(); _loadCharacterSoundHook.Enable();
//_apricotResourceLoadHook.Enable();
} }
public void Disable() public void Disable()
@ -111,7 +95,6 @@ public unsafe partial class PathResolver
_someActionLoadHook.Disable(); _someActionLoadHook.Disable();
_someOtherAvfxHook.Disable(); _someOtherAvfxHook.Disable();
_loadCharacterSoundHook.Disable(); _loadCharacterSoundHook.Disable();
//_apricotResourceLoadHook.Disable();
} }
public void Dispose() public void Dispose()
@ -123,7 +106,6 @@ public unsafe partial class PathResolver
_someActionLoadHook.Dispose(); _someActionLoadHook.Dispose();
_someOtherAvfxHook.Dispose(); _someOtherAvfxHook.Dispose();
_loadCharacterSoundHook.Dispose(); _loadCharacterSoundHook.Dispose();
//_apricotResourceLoadHook.Dispose();
} }
// Characters load some of their voice lines or whatever with this function. // Characters load some of their voice lines or whatever with this function.
@ -261,17 +243,5 @@ public unsafe partial class PathResolver
_someOtherAvfxHook.Original( unk ); _someOtherAvfxHook.Original( unk );
_animationLoadData = last; _animationLoadData = last;
} }
//private delegate byte ApricotResourceLoadDelegate( IntPtr handle, IntPtr unk1, byte unk2 );
//
//[Signature( "48 89 74 24 ?? 57 48 83 EC ?? 41 0F B6 F0 48 8B F9", DetourName = nameof( ApricotResourceLoadDetour ) )]
//private readonly Hook< ApricotResourceLoadDelegate > _apricotResourceLoadHook = null!;
//
//
//private byte ApricotResourceLoadDetour( IntPtr handle, IntPtr unk1, byte unk2 )
//{
// Penumbra.Log.Information( $"{handle:X} {new ByteString( ( ( ResourceHandle* )handle )->FileName() )} {unk1:X} {unk2} {_lastAvfxData.ModCollection.Name}" );
// return _apricotResourceLoadHook.Original( handle, unk1, unk2 );
//}
} }
} }

View file

@ -1,5 +1,4 @@
using System; using System;
using System.Linq;
using Dalamud.Hooking; using Dalamud.Hooking;
using Dalamud.Utility.Signatures; using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.System.Resource; using FFXIVClientStructs.FFXIV.Client.System.Resource;
@ -13,16 +12,17 @@ namespace Penumbra.Interop.Resolver;
public unsafe partial class PathResolver public unsafe partial class PathResolver
{ {
// Materials do contain their own paths to textures and shader packages. // Materials and avfx do contain their own paths to textures and shader packages or atex respectively.
// Those are loaded synchronously. // Those are loaded synchronously.
// Thus, we need to ensure the correct files are loaded when a material is loaded. // Thus, we need to ensure the correct files are loaded when a material is loaded.
public class MaterialState : IDisposable public class SubfileHelper : IDisposable
{ {
private readonly PathState _paths; private readonly PathState _paths;
private ResolveData _mtrlData = ResolveData.Invalid; private ResolveData _mtrlData = ResolveData.Invalid;
private ResolveData _avfxData = ResolveData.Invalid;
public MaterialState( PathState paths ) public SubfileHelper( PathState paths )
{ {
SignatureHelper.Initialise( this ); SignatureHelper.Initialise( this );
_paths = paths; _paths = paths;
@ -31,10 +31,20 @@ public unsafe partial class PathResolver
// Check specifically for shpk and tex files whether we are currently in a material load. // Check specifically for shpk and tex files whether we are currently in a material load.
public bool HandleSubFiles( ResourceType type, out ResolveData collection ) public bool HandleSubFiles( ResourceType type, out ResolveData collection )
{ {
if( _mtrlData.Valid && type is ResourceType.Tex or ResourceType.Shpk ) switch( type )
{ {
collection = _mtrlData; case ResourceType.Tex:
return true; case ResourceType.Shpk:
if( _mtrlData.Valid )
{
collection = _mtrlData;
return true;
}
break;
case ResourceType.Atex when _avfxData.Valid:
collection = _avfxData;
return true;
} }
collection = ResolveData.Invalid; collection = ResolveData.Invalid;
@ -45,29 +55,35 @@ public unsafe partial class PathResolver
public static void HandleCollection( ResolveData resolveData, string path, bool nonDefault, ResourceType type, FullPath? resolved, public static void HandleCollection( ResolveData resolveData, string path, bool nonDefault, ResourceType type, FullPath? resolved,
out (FullPath?, ResolveData) data ) out (FullPath?, ResolveData) data )
{ {
if( nonDefault && type == ResourceType.Mtrl ) if( nonDefault )
{ {
var fullPath = new FullPath( $"|{resolveData.ModCollection.Name}_{resolveData.ModCollection.ChangeCounter}|{path}" ); switch( type )
data = ( fullPath, resolveData ); {
} case ResourceType.Mtrl:
else case ResourceType.Avfx:
{ var fullPath = new FullPath( $"|{resolveData.ModCollection.Name}_{resolveData.ModCollection.ChangeCounter}|{path}" );
data = ( resolved, resolveData ); data = ( fullPath, resolveData );
return;
}
} }
data = ( resolved, resolveData );
} }
public void Enable() public void Enable()
{ {
_loadMtrlShpkHook.Enable(); _loadMtrlShpkHook.Enable();
_loadMtrlTexHook.Enable(); _loadMtrlTexHook.Enable();
Penumbra.ResourceLoader.ResourceLoadCustomization += MtrlLoadHandler; _apricotResourceLoadHook.Enable();
Penumbra.ResourceLoader.ResourceLoadCustomization += SubfileLoadHandler;
} }
public void Disable() public void Disable()
{ {
_loadMtrlShpkHook.Disable(); _loadMtrlShpkHook.Disable();
_loadMtrlTexHook.Disable(); _loadMtrlTexHook.Disable();
Penumbra.ResourceLoader.ResourceLoadCustomization -= MtrlLoadHandler; _apricotResourceLoadHook.Disable();
Penumbra.ResourceLoader.ResourceLoadCustomization -= SubfileLoadHandler;
} }
public void Dispose() public void Dispose()
@ -75,17 +91,21 @@ public unsafe partial class PathResolver
Disable(); Disable();
_loadMtrlShpkHook.Dispose(); _loadMtrlShpkHook.Dispose();
_loadMtrlTexHook.Dispose(); _loadMtrlTexHook.Dispose();
_apricotResourceLoadHook.Dispose();
} }
// We need to set the correct collection for the actual material path that is loaded // We need to set the correct collection for the actual material path that is loaded
// before actually loading the file. // before actually loading the file.
public bool MtrlLoadHandler( ByteString split, ByteString path, ResourceManager* resourceManager, public bool SubfileLoadHandler( ByteString split, ByteString path, ResourceManager* resourceManager,
SeFileDescriptor* fileDescriptor, int priority, bool isSync, out byte ret ) SeFileDescriptor* fileDescriptor, int priority, bool isSync, out byte ret )
{ {
ret = 0; ret = 0;
if( fileDescriptor->ResourceHandle->FileType != ResourceType.Mtrl ) switch( fileDescriptor->ResourceHandle->FileType )
{ {
return false; case ResourceType.Mtrl:
case ResourceType.Avfx:
break;
default: return false;
} }
var lastUnderscore = split.LastIndexOf( ( byte )'_' ); var lastUnderscore = split.LastIndexOf( ( byte )'_' );
@ -94,17 +114,14 @@ public unsafe partial class PathResolver
|| Penumbra.CollectionManager.ByName( name, out collection ) ) || Penumbra.CollectionManager.ByName( name, out collection ) )
{ {
#if DEBUG #if DEBUG
Penumbra.Log.Verbose( $"Using MtrlLoadHandler with collection {name} for path {path}." ); Penumbra.Log.Verbose( $"Using {nameof(SubfileLoadHandler)} with collection {name} for path {path}." );
#endif #endif
_paths.SetCollection( IntPtr.Zero, path, collection );
var objFromObjTable = Dalamud.Objects.FirstOrDefault( f => f.Name.TextValue == name );
var gameObjAddr = objFromObjTable?.Address ?? IntPtr.Zero;
_paths.SetCollection( gameObjAddr, path, collection );
} }
else else
{ {
#if DEBUG #if DEBUG
Penumbra.Log.Verbose( $"Using MtrlLoadHandler with no collection for path {path}." ); Penumbra.Log.Verbose( $"Using {nameof( SubfileLoadHandler )} with no collection for path {path}." );
#endif #endif
} }
@ -124,7 +141,7 @@ public unsafe partial class PathResolver
private byte LoadMtrlTexDetour( IntPtr mtrlResourceHandle ) private byte LoadMtrlTexDetour( IntPtr mtrlResourceHandle )
{ {
LoadMtrlHelper( mtrlResourceHandle ); _mtrlData = LoadFileHelper( mtrlResourceHandle );
var ret = _loadMtrlTexHook.Original( mtrlResourceHandle ); var ret = _loadMtrlTexHook.Original( mtrlResourceHandle );
_mtrlData = ResolveData.Invalid; _mtrlData = ResolveData.Invalid;
return ret; return ret;
@ -136,22 +153,37 @@ public unsafe partial class PathResolver
private byte LoadMtrlShpkDetour( IntPtr mtrlResourceHandle ) private byte LoadMtrlShpkDetour( IntPtr mtrlResourceHandle )
{ {
LoadMtrlHelper( mtrlResourceHandle ); _mtrlData = LoadFileHelper( mtrlResourceHandle );
var ret = _loadMtrlShpkHook.Original( mtrlResourceHandle ); var ret = _loadMtrlShpkHook.Original( mtrlResourceHandle );
_mtrlData = ResolveData.Invalid; _mtrlData = ResolveData.Invalid;
return ret; return ret;
} }
private void LoadMtrlHelper( IntPtr mtrlResourceHandle ) private ResolveData LoadFileHelper( IntPtr resourceHandle )
{ {
if( mtrlResourceHandle == IntPtr.Zero ) if( resourceHandle == IntPtr.Zero )
{ {
return; return ResolveData.Invalid;
} }
var mtrl = ( MtrlResource* )mtrlResourceHandle; var resource = ( ResourceHandle* )resourceHandle;
var mtrlPath = ByteString.FromSpanUnsafe( mtrl->Handle.FileNameSpan(), true, null, true ); var filePath = ByteString.FromSpanUnsafe( resource->FileNameSpan(), true, null, true );
_mtrlData = _paths.TryGetValue( mtrlPath, out var c ) ? c : ResolveData.Invalid; return _paths.TryGetValue( filePath, out var c ) ? c : ResolveData.Invalid;
}
private delegate byte ApricotResourceLoadDelegate( IntPtr handle, IntPtr unk1, byte unk2 );
[Signature( "48 89 74 24 ?? 57 48 83 EC ?? 41 0F B6 F0 48 8B F9", DetourName = nameof( ApricotResourceLoadDetour ) )]
private readonly Hook<ApricotResourceLoadDelegate> _apricotResourceLoadHook = null!;
private byte ApricotResourceLoadDetour( IntPtr handle, IntPtr unk1, byte unk2 )
{
_avfxData = LoadFileHelper( handle );
var ret = _apricotResourceLoadHook.Original( handle, unk1, unk2 );
_avfxData = ResolveData.Invalid;
return ret;
} }
} }
} }

View file

@ -30,7 +30,7 @@ public partial class PathResolver : IDisposable
private readonly AnimationState _animations; private readonly AnimationState _animations;
private readonly PathState _paths; private readonly PathState _paths;
private readonly MetaState _meta; private readonly MetaState _meta;
private readonly MaterialState _materials; private readonly SubfileHelper _subFiles;
static PathResolver() static PathResolver()
=> ValidHumanModels = GetValidHumanModels( Dalamud.GameData ); => ValidHumanModels = GetValidHumanModels( Dalamud.GameData );
@ -42,7 +42,7 @@ public partial class PathResolver : IDisposable
_animations = new AnimationState( DrawObjects ); _animations = new AnimationState( DrawObjects );
_paths = new PathState( this ); _paths = new PathState( this );
_meta = new MetaState( _paths.HumanVTable ); _meta = new MetaState( _paths.HumanVTable );
_materials = new MaterialState( _paths ); _subFiles = new SubfileHelper( _paths );
} }
// The modified resolver that handles game path resolving. // The modified resolver that handles game path resolving.
@ -54,7 +54,7 @@ public partial class PathResolver : IDisposable
// If not use the default collection. // If not use the default collection.
// We can remove paths after they have actually been loaded. // We can remove paths after they have actually been loaded.
// A potential next request will add the path anew. // A potential next request will add the path anew.
var nonDefault = _materials.HandleSubFiles( type, out var resolveData ) var nonDefault = _subFiles.HandleSubFiles( type, out var resolveData )
|| _paths.Consume( gamePath.Path, out resolveData ) || _paths.Consume( gamePath.Path, out resolveData )
|| _animations.HandleFiles( type, gamePath, out resolveData ) || _animations.HandleFiles( type, gamePath, out resolveData )
|| DrawObjects.HandleDecalFile( type, gamePath, out resolveData ); || DrawObjects.HandleDecalFile( type, gamePath, out resolveData );
@ -70,8 +70,7 @@ public partial class PathResolver : IDisposable
// so that the functions loading tex and shpk can find that path and use its collection. // so that the functions loading tex and shpk can find that path and use its collection.
// We also need to handle defaulted materials against a non-default collection. // We also need to handle defaulted materials against a non-default collection.
var path = resolved == null ? gamePath.Path.ToString() : resolved.Value.FullName; var path = resolved == null ? gamePath.Path.ToString() : resolved.Value.FullName;
MaterialState.HandleCollection( resolveData, path, nonDefault, type, resolved, out data ); SubfileHelper.HandleCollection( resolveData, path, nonDefault, type, resolved, out data );
_animations.UpdateAvfx( type, data.Item2 );
return true; return true;
} }
@ -89,7 +88,7 @@ public partial class PathResolver : IDisposable
_animations.Enable(); _animations.Enable();
_paths.Enable(); _paths.Enable();
_meta.Enable(); _meta.Enable();
_materials.Enable(); _subFiles.Enable();
_loader.ResolvePathCustomization += CharacterResolver; _loader.ResolvePathCustomization += CharacterResolver;
Penumbra.Log.Debug( "Character Path Resolver enabled." ); Penumbra.Log.Debug( "Character Path Resolver enabled." );
@ -109,7 +108,7 @@ public partial class PathResolver : IDisposable
IdentifiedCache.Disable(); IdentifiedCache.Disable();
_paths.Disable(); _paths.Disable();
_meta.Disable(); _meta.Disable();
_materials.Disable(); _subFiles.Disable();
_loader.ResolvePathCustomization -= CharacterResolver; _loader.ResolvePathCustomization -= CharacterResolver;
Penumbra.Log.Debug( "Character Path Resolver disabled." ); Penumbra.Log.Debug( "Character Path Resolver disabled." );
@ -124,7 +123,7 @@ public partial class PathResolver : IDisposable
Cutscenes.Dispose(); Cutscenes.Dispose();
IdentifiedCache.Dispose(); IdentifiedCache.Dispose();
_meta.Dispose(); _meta.Dispose();
_materials.Dispose(); _subFiles.Dispose();
} }
public static unsafe (IntPtr, ResolveData) IdentifyDrawObject( IntPtr drawObject ) public static unsafe (IntPtr, ResolveData) IdentifyDrawObject( IntPtr drawObject )