Change subfile handling to maybe retain associated game object for Mare.

This commit is contained in:
Ottermandias 2022-12-29 00:36:35 +01:00
parent 4df9ac4632
commit 87b6fe6aa6
5 changed files with 31 additions and 46 deletions

View file

@ -246,7 +246,7 @@ public unsafe partial class ResourceLoader
private static void LogResource( Structs.ResourceHandle* handle, Utf8GamePath path, FullPath? manipulatedPath, ResolveData _ ) private static void LogResource( Structs.ResourceHandle* handle, Utf8GamePath path, FullPath? manipulatedPath, ResolveData _ )
{ {
var pathString = manipulatedPath != null ? $"custom file {manipulatedPath} instead of {path}" : path.ToString(); var pathString = manipulatedPath != null ? $"custom file {manipulatedPath} instead of {path}" : path.ToString();
Penumbra.Log.Information( $"[ResourceLoader] Loaded {pathString} to 0x{( ulong )handle:X}. (Refcount {handle->RefCount})" ); Penumbra.Log.Information( $"[ResourceLoader] [{handle->FileType}] Loaded {pathString} to 0x{( ulong )handle:X}. (Refcount {handle->RefCount})" );
} }
private static void LogLoadedFile( ByteString path, bool success, bool custom ) private static void LogLoadedFile( ByteString path, bool success, bool custom )

View file

@ -45,21 +45,7 @@ public unsafe partial class PathResolver
break; break;
case ResourceType.Tmb: case ResourceType.Tmb:
case ResourceType.Pap: case ResourceType.Pap:
if( _animationLoadData.Valid )
{
resolveData = _animationLoadData;
return true;
}
break;
case ResourceType.Avfx: case ResourceType.Avfx:
if( _animationLoadData.Valid )
{
resolveData = _animationLoadData;
return true;
}
break;
case ResourceType.Atex: case ResourceType.Atex:
if( _animationLoadData.Valid ) if( _animationLoadData.Valid )
{ {

View file

@ -31,7 +31,7 @@ public unsafe partial class PathResolver
private readonly ResolverHooks _monster; private readonly ResolverHooks _monster;
// This map links files to their corresponding collection, if it is non-default. // This map links files to their corresponding collection, if it is non-default.
private readonly ConcurrentDictionary< ByteString, ResolveData > _pathCollections = new(); private readonly ConcurrentDictionary< ByteString, ResolveData > _pathCollections = new();
public PathState( PathResolver parent ) public PathState( PathResolver parent )
{ {

View file

@ -1,9 +1,11 @@
using System; using System;
using System.Collections.Concurrent;
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;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.Interop.Loader;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.String; using Penumbra.String;
using Penumbra.String.Classes; using Penumbra.String.Classes;
@ -17,15 +19,18 @@ public unsafe partial class PathResolver
// 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 SubfileHelper : IDisposable public class SubfileHelper : IDisposable
{ {
private readonly PathState _paths; private readonly ResourceLoader _loader;
private ResolveData _mtrlData = ResolveData.Invalid; private ResolveData _mtrlData = ResolveData.Invalid;
private ResolveData _avfxData = ResolveData.Invalid; private ResolveData _avfxData = ResolveData.Invalid;
public SubfileHelper( PathState paths ) private readonly ConcurrentDictionary< IntPtr, ResolveData > _subFileCollection = new();
public SubfileHelper( ResourceLoader loader )
{ {
SignatureHelper.Initialise( this ); SignatureHelper.Initialise( this );
_paths = paths;
_loader = loader;
} }
// 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.
@ -52,7 +57,7 @@ public unsafe partial class PathResolver
} }
// Materials need to be set per collection so they can load their textures independently from each other. // Materials need to be set per collection so they can load their textures independently from each other.
public static void HandleCollection( ResolveData resolveData, string path, bool nonDefault, ResourceType type, FullPath? resolved, public static void HandleCollection( ResolveData resolveData, ByteString path, bool nonDefault, ResourceType type, FullPath? resolved,
out (FullPath?, ResolveData) data ) out (FullPath?, ResolveData) data )
{ {
if( nonDefault ) if( nonDefault )
@ -75,7 +80,8 @@ public unsafe partial class PathResolver
_loadMtrlShpkHook.Enable(); _loadMtrlShpkHook.Enable();
_loadMtrlTexHook.Enable(); _loadMtrlTexHook.Enable();
_apricotResourceLoadHook.Enable(); _apricotResourceLoadHook.Enable();
Penumbra.ResourceLoader.ResourceLoadCustomization += SubfileLoadHandler; _loader.ResourceLoadCustomization += SubfileLoadHandler;
_loader.ResourceLoaded += SubfileContainerLoaded;
} }
public void Disable() public void Disable()
@ -83,7 +89,8 @@ public unsafe partial class PathResolver
_loadMtrlShpkHook.Disable(); _loadMtrlShpkHook.Disable();
_loadMtrlTexHook.Disable(); _loadMtrlTexHook.Disable();
_apricotResourceLoadHook.Disable(); _apricotResourceLoadHook.Disable();
Penumbra.ResourceLoader.ResourceLoadCustomization -= SubfileLoadHandler; _loader.ResourceLoadCustomization -= SubfileLoadHandler;
_loader.ResourceLoaded -= SubfileContainerLoaded;
} }
public void Dispose() public void Dispose()
@ -94,6 +101,17 @@ public unsafe partial class PathResolver
_apricotResourceLoadHook.Dispose(); _apricotResourceLoadHook.Dispose();
} }
private void SubfileContainerLoaded( ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath, ResolveData resolveData )
{
switch( handle->FileType )
{
case ResourceType.Mtrl:
case ResourceType.Avfx:
_subFileCollection[ ( IntPtr )handle ] = resolveData;
break;
}
}
// 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 SubfileLoadHandler( ByteString split, ByteString path, ResourceManager* resourceManager, public bool SubfileLoadHandler( ByteString split, ByteString path, ResourceManager* resourceManager,
@ -108,29 +126,12 @@ public unsafe partial class PathResolver
default: return false; default: return false;
} }
var lastUnderscore = split.LastIndexOf( ( byte )'_' );
var name = lastUnderscore == -1 ? split.ToString() : split.Substring( 0, lastUnderscore ).ToString();
if( Penumbra.TempMods.CollectionByName( name, out var collection )
|| Penumbra.CollectionManager.ByName( name, out collection ) )
{
#if DEBUG
Penumbra.Log.Verbose( $"Using {nameof(SubfileLoadHandler)} with collection {name} for path {path}." );
#endif
_paths.SetCollection( IntPtr.Zero, path, collection );
}
else
{
#if DEBUG
Penumbra.Log.Verbose( $"Using {nameof( SubfileLoadHandler )} with no collection for path {path}." );
#endif
}
// Force isSync = true for this call. I don't really understand why, // Force isSync = true for this call. I don't really understand why,
// or where the difference even comes from. // or where the difference even comes from.
// Was called with True on my client and with false on other peoples clients, // Was called with True on my client and with false on other peoples clients,
// which caused problems. // which caused problems.
ret = Penumbra.ResourceLoader.DefaultLoadResource( path, resourceManager, fileDescriptor, priority, true ); ret = Penumbra.ResourceLoader.DefaultLoadResource( path, resourceManager, fileDescriptor, priority, true );
_paths.Consume( path, out _ ); _subFileCollection.TryRemove( ( IntPtr )fileDescriptor->ResourceHandle, out _ );
return true; return true;
} }
@ -166,16 +167,14 @@ public unsafe partial class PathResolver
return ResolveData.Invalid; return ResolveData.Invalid;
} }
var resource = ( ResourceHandle* )resourceHandle; return _subFileCollection.TryGetValue( resourceHandle, out var c ) ? c : ResolveData.Invalid;
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 ); 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 ) )] [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 readonly Hook< ApricotResourceLoadDelegate > _apricotResourceLoadHook = null!;
private byte ApricotResourceLoadDetour( IntPtr handle, IntPtr unk1, byte unk2 ) private byte ApricotResourceLoadDetour( IntPtr handle, IntPtr unk1, byte unk2 )

View file

@ -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 );
_subFiles = new SubfileHelper( _paths ); _subFiles = new SubfileHelper( _loader );
} }
// The modified resolver that handles game path resolving. // The modified resolver that handles game path resolving.
@ -69,7 +69,7 @@ public partial class PathResolver : IDisposable
// Since mtrl files load their files separately, we need to add the new, resolved path // Since mtrl files load their files separately, we need to add the new, resolved path
// 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 : resolved.Value.InternalName;
SubfileHelper.HandleCollection( resolveData, path, nonDefault, type, resolved, out data ); SubfileHelper.HandleCollection( resolveData, path, nonDefault, type, resolved, out data );
return true; return true;
} }