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 _ )
{
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 )

View file

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

View file

@ -1,9 +1,11 @@
using System;
using System.Collections.Concurrent;
using Dalamud.Hooking;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
using Penumbra.Collections;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Loader;
using Penumbra.Interop.Structs;
using Penumbra.String;
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.
public class SubfileHelper : IDisposable
{
private readonly PathState _paths;
private readonly ResourceLoader _loader;
private ResolveData _mtrlData = 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 );
_paths = paths;
_loader = loader;
}
// 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.
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 )
{
if( nonDefault )
@ -75,7 +80,8 @@ public unsafe partial class PathResolver
_loadMtrlShpkHook.Enable();
_loadMtrlTexHook.Enable();
_apricotResourceLoadHook.Enable();
Penumbra.ResourceLoader.ResourceLoadCustomization += SubfileLoadHandler;
_loader.ResourceLoadCustomization += SubfileLoadHandler;
_loader.ResourceLoaded += SubfileContainerLoaded;
}
public void Disable()
@ -83,7 +89,8 @@ public unsafe partial class PathResolver
_loadMtrlShpkHook.Disable();
_loadMtrlTexHook.Disable();
_apricotResourceLoadHook.Disable();
Penumbra.ResourceLoader.ResourceLoadCustomization -= SubfileLoadHandler;
_loader.ResourceLoadCustomization -= SubfileLoadHandler;
_loader.ResourceLoaded -= SubfileContainerLoaded;
}
public void Dispose()
@ -94,6 +101,17 @@ public unsafe partial class PathResolver
_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
// before actually loading the file.
public bool SubfileLoadHandler( ByteString split, ByteString path, ResourceManager* resourceManager,
@ -108,29 +126,12 @@ public unsafe partial class PathResolver
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,
// or where the difference even comes from.
// Was called with True on my client and with false on other peoples clients,
// which caused problems.
ret = Penumbra.ResourceLoader.DefaultLoadResource( path, resourceManager, fileDescriptor, priority, true );
_paths.Consume( path, out _ );
_subFileCollection.TryRemove( ( IntPtr )fileDescriptor->ResourceHandle, out _ );
return true;
}
@ -166,16 +167,14 @@ public unsafe partial class PathResolver
return 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;
return _subFileCollection.TryGetValue( resourceHandle, 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 readonly Hook< ApricotResourceLoadDelegate > _apricotResourceLoadHook = null!;
private byte ApricotResourceLoadDetour( IntPtr handle, IntPtr unk1, byte unk2 )

View file

@ -42,7 +42,7 @@ public partial class PathResolver : IDisposable
_animations = new AnimationState( DrawObjects );
_paths = new PathState( this );
_meta = new MetaState( _paths.HumanVTable );
_subFiles = new SubfileHelper( _paths );
_subFiles = new SubfileHelper( _loader );
}
// 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
// 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;
var path = resolved == null ? gamePath.Path : resolved.Value.InternalName;
SubfileHelper.HandleCollection( resolveData, path, nonDefault, type, resolved, out data );
return true;
}