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.Collections.Generic;
using Dalamud.Hooking;
using Dalamud.Utility.Signatures;
using Penumbra.Collections;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs;
using Penumbra.String;
using Penumbra.String.Classes;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
@ -14,9 +17,8 @@ public unsafe partial class PathResolver
{
private readonly DrawObjectState _drawObjectState;
private ResolveData _animationLoadData = ResolveData.Invalid;
private ResolveData _lastAvfxData = ResolveData.Invalid;
private ResolveData _characterSoundData = ResolveData.Invalid;
private ResolveData _animationLoadData = ResolveData.Invalid;
private ResolveData _characterSoundData = ResolveData.Invalid;
public AnimationState( DrawObjectState drawObjectState )
{
@ -24,15 +26,7 @@ public unsafe partial class PathResolver
SignatureHelper.Initialise( this );
}
public void UpdateAvfx( ResourceType type, ResolveData data )
{
if( type == ResourceType.Avfx )
{
_lastAvfxData = data;
}
}
public bool HandleFiles( ResourceType type, Utf8GamePath _, out ResolveData resolveData )
public bool HandleFiles( ResourceType type, Utf8GamePath path, out ResolveData resolveData )
{
switch( type )
{
@ -60,9 +54,6 @@ public unsafe partial class PathResolver
break;
case ResourceType.Avfx:
_lastAvfxData = _animationLoadData.Valid
? _animationLoadData
: Penumbra.CollectionManager.Default.ToResolveData();
if( _animationLoadData.Valid )
{
resolveData = _animationLoadData;
@ -71,12 +62,6 @@ public unsafe partial class PathResolver
break;
case ResourceType.Atex:
if( _lastAvfxData.Valid )
{
resolveData = _lastAvfxData;
return true;
}
if( _animationLoadData.Valid )
{
resolveData = _animationLoadData;
@ -99,7 +84,6 @@ public unsafe partial class PathResolver
_someActionLoadHook.Enable();
_someOtherAvfxHook.Enable();
_loadCharacterSoundHook.Enable();
//_apricotResourceLoadHook.Enable();
}
public void Disable()
@ -111,7 +95,6 @@ public unsafe partial class PathResolver
_someActionLoadHook.Disable();
_someOtherAvfxHook.Disable();
_loadCharacterSoundHook.Disable();
//_apricotResourceLoadHook.Disable();
}
public void Dispose()
@ -123,7 +106,6 @@ public unsafe partial class PathResolver
_someActionLoadHook.Dispose();
_someOtherAvfxHook.Dispose();
_loadCharacterSoundHook.Dispose();
//_apricotResourceLoadHook.Dispose();
}
// Characters load some of their voice lines or whatever with this function.
@ -261,17 +243,5 @@ public unsafe partial class PathResolver
_someOtherAvfxHook.Original( unk );
_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.Linq;
using Dalamud.Hooking;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
@ -13,16 +12,17 @@ namespace Penumbra.Interop.Resolver;
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.
// 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 ResolveData _mtrlData = ResolveData.Invalid;
private ResolveData _avfxData = ResolveData.Invalid;
public MaterialState( PathState paths )
public SubfileHelper( PathState paths )
{
SignatureHelper.Initialise( this );
_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.
public bool HandleSubFiles( ResourceType type, out ResolveData collection )
{
if( _mtrlData.Valid && type is ResourceType.Tex or ResourceType.Shpk )
switch( type )
{
collection = _mtrlData;
return true;
case ResourceType.Tex:
case ResourceType.Shpk:
if( _mtrlData.Valid )
{
collection = _mtrlData;
return true;
}
break;
case ResourceType.Atex when _avfxData.Valid:
collection = _avfxData;
return true;
}
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,
out (FullPath?, ResolveData) data )
{
if( nonDefault && type == ResourceType.Mtrl )
if( nonDefault )
{
var fullPath = new FullPath( $"|{resolveData.ModCollection.Name}_{resolveData.ModCollection.ChangeCounter}|{path}" );
data = ( fullPath, resolveData );
}
else
{
data = ( resolved, resolveData );
switch( type )
{
case ResourceType.Mtrl:
case ResourceType.Avfx:
var fullPath = new FullPath( $"|{resolveData.ModCollection.Name}_{resolveData.ModCollection.ChangeCounter}|{path}" );
data = ( fullPath, resolveData );
return;
}
}
data = ( resolved, resolveData );
}
public void Enable()
{
_loadMtrlShpkHook.Enable();
_loadMtrlTexHook.Enable();
Penumbra.ResourceLoader.ResourceLoadCustomization += MtrlLoadHandler;
_apricotResourceLoadHook.Enable();
Penumbra.ResourceLoader.ResourceLoadCustomization += SubfileLoadHandler;
}
public void Disable()
{
_loadMtrlShpkHook.Disable();
_loadMtrlTexHook.Disable();
Penumbra.ResourceLoader.ResourceLoadCustomization -= MtrlLoadHandler;
_apricotResourceLoadHook.Disable();
Penumbra.ResourceLoader.ResourceLoadCustomization -= SubfileLoadHandler;
}
public void Dispose()
@ -75,17 +91,21 @@ public unsafe partial class PathResolver
Disable();
_loadMtrlShpkHook.Dispose();
_loadMtrlTexHook.Dispose();
_apricotResourceLoadHook.Dispose();
}
// We need to set the correct collection for the actual material path that is loaded
// 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 )
{
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 )'_' );
@ -94,17 +114,14 @@ public unsafe partial class PathResolver
|| Penumbra.CollectionManager.ByName( name, out collection ) )
{
#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
var objFromObjTable = Dalamud.Objects.FirstOrDefault( f => f.Name.TextValue == name );
var gameObjAddr = objFromObjTable?.Address ?? IntPtr.Zero;
_paths.SetCollection( gameObjAddr, path, collection );
_paths.SetCollection( IntPtr.Zero, path, collection );
}
else
{
#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
}
@ -124,7 +141,7 @@ public unsafe partial class PathResolver
private byte LoadMtrlTexDetour( IntPtr mtrlResourceHandle )
{
LoadMtrlHelper( mtrlResourceHandle );
_mtrlData = LoadFileHelper( mtrlResourceHandle );
var ret = _loadMtrlTexHook.Original( mtrlResourceHandle );
_mtrlData = ResolveData.Invalid;
return ret;
@ -136,22 +153,37 @@ public unsafe partial class PathResolver
private byte LoadMtrlShpkDetour( IntPtr mtrlResourceHandle )
{
LoadMtrlHelper( mtrlResourceHandle );
_mtrlData = LoadFileHelper( mtrlResourceHandle );
var ret = _loadMtrlShpkHook.Original( mtrlResourceHandle );
_mtrlData = ResolveData.Invalid;
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 mtrlPath = ByteString.FromSpanUnsafe( mtrl->Handle.FileNameSpan(), true, null, true );
_mtrlData = _paths.TryGetValue( mtrlPath, out var c ) ? c : ResolveData.Invalid;
var resource = ( ResourceHandle* )resourceHandle;
var filePath = ByteString.FromSpanUnsafe( resource->FileNameSpan(), true, null, true );
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 PathState _paths;
private readonly MetaState _meta;
private readonly MaterialState _materials;
private readonly SubfileHelper _subFiles;
static PathResolver()
=> ValidHumanModels = GetValidHumanModels( Dalamud.GameData );
@ -42,7 +42,7 @@ public partial class PathResolver : IDisposable
_animations = new AnimationState( DrawObjects );
_paths = new PathState( this );
_meta = new MetaState( _paths.HumanVTable );
_materials = new MaterialState( _paths );
_subFiles = new SubfileHelper( _paths );
}
// The modified resolver that handles game path resolving.
@ -54,7 +54,7 @@ public partial class PathResolver : IDisposable
// If not use the default collection.
// We can remove paths after they have actually been loaded.
// 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 )
|| _animations.HandleFiles( 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.
// We also need to handle defaulted materials against a non-default collection.
var path = resolved == null ? gamePath.Path.ToString() : resolved.Value.FullName;
MaterialState.HandleCollection( resolveData, path, nonDefault, type, resolved, out data );
_animations.UpdateAvfx( type, data.Item2 );
SubfileHelper.HandleCollection( resolveData, path, nonDefault, type, resolved, out data );
return true;
}
@ -89,7 +88,7 @@ public partial class PathResolver : IDisposable
_animations.Enable();
_paths.Enable();
_meta.Enable();
_materials.Enable();
_subFiles.Enable();
_loader.ResolvePathCustomization += CharacterResolver;
Penumbra.Log.Debug( "Character Path Resolver enabled." );
@ -109,7 +108,7 @@ public partial class PathResolver : IDisposable
IdentifiedCache.Disable();
_paths.Disable();
_meta.Disable();
_materials.Disable();
_subFiles.Disable();
_loader.ResolvePathCustomization -= CharacterResolver;
Penumbra.Log.Debug( "Character Path Resolver disabled." );
@ -124,7 +123,7 @@ public partial class PathResolver : IDisposable
Cutscenes.Dispose();
IdentifiedCache.Dispose();
_meta.Dispose();
_materials.Dispose();
_subFiles.Dispose();
}
public static unsafe (IntPtr, ResolveData) IdentifyDrawObject( IntPtr drawObject )