diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 0546ac53..f6292d82 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -67,12 +67,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi } Penumbra.CollectionManager.CollectionChanged += SubscribeToNewCollections; - Penumbra.ResourceLoader.ResourceLoaded += OnResourceLoaded; + Penumbra.ResourceLoader.ResourceLoaded += OnResourceLoaded; } public unsafe void Dispose() { - Penumbra.ResourceLoader.ResourceLoaded -= OnResourceLoaded; + Penumbra.ResourceLoader.ResourceLoaded -= OnResourceLoaded; Penumbra.CollectionManager.CollectionChanged -= SubscribeToNewCollections; _penumbra = null; _lumina = null; @@ -93,10 +93,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi return Penumbra.Config.ModDirectory; } - private unsafe void OnResourceLoaded( ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath, LinkedModCollection? resolveData ) + private unsafe void OnResourceLoaded( ResourceHandle* _, Utf8GamePath originalPath, FullPath? manipulatedPath, + ResolveData resolveData ) { - if( resolveData == null ) return; - GameObjectResourceResolved?.Invoke( resolveData.AssociatedGameObject, originalPath.ToString(), manipulatedPath?.ToString() ?? originalPath.ToString() ); + GameObjectResourceResolved?.Invoke( resolveData.AssociatedGameObject, originalPath.ToString(), + manipulatedPath?.ToString() ?? originalPath.ToString() ); } public event Action< string, bool >? ModDirectoryChanged diff --git a/Penumbra/Collections/LinkedModCollection.cs b/Penumbra/Collections/LinkedModCollection.cs deleted file mode 100644 index 93575544..00000000 --- a/Penumbra/Collections/LinkedModCollection.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using FFXIVClientStructs.FFXIV.Client.Game.Object; - -namespace Penumbra.Collections; - -public class LinkedModCollection -{ - private IntPtr? _associatedGameObject; - public IntPtr AssociatedGameObject = IntPtr.Zero; - public ModCollection ModCollection; - - public LinkedModCollection(ModCollection modCollection) - { - ModCollection = modCollection; - } - - public LinkedModCollection(IntPtr gameObject, ModCollection collection) - { - AssociatedGameObject = gameObject; - ModCollection = collection; - } - - public unsafe LinkedModCollection(GameObject* gameObject, ModCollection collection) : this((IntPtr)gameObject, collection) - { - } -} diff --git a/Penumbra/Collections/ResolveData.cs b/Penumbra/Collections/ResolveData.cs new file mode 100644 index 00000000..348b06a9 --- /dev/null +++ b/Penumbra/Collections/ResolveData.cs @@ -0,0 +1,46 @@ +using System; +using FFXIVClientStructs.FFXIV.Client.Game.Object; + +namespace Penumbra.Collections; + +public readonly struct ResolveData +{ + public static readonly ResolveData Invalid = new(ModCollection.Empty); + + public readonly ModCollection ModCollection; + public readonly IntPtr AssociatedGameObject; + + public bool Valid + => ModCollection != ModCollection.Empty; + + public ResolveData() + { + ModCollection = ModCollection.Empty; + AssociatedGameObject = IntPtr.Zero; + } + + public ResolveData( ModCollection collection, IntPtr gameObject ) + { + ModCollection = collection; + AssociatedGameObject = gameObject; + } + + public ResolveData( ModCollection collection ) + : this( collection, IntPtr.Zero ) + { } + + public override string ToString() + => ModCollection.Name; +} + +public static class ResolveDataExtensions +{ + public static ResolveData ToResolveData( this ModCollection collection ) + => new(collection); + + public static ResolveData ToResolveData( this ModCollection collection, IntPtr ptr ) + => new(collection, ptr); + + public static unsafe ResolveData ToResolveData( this ModCollection collection, void* ptr ) + => new(collection, ( IntPtr )ptr); +} \ No newline at end of file diff --git a/Penumbra/Interop/Loader/ResourceLoader.Debug.cs b/Penumbra/Interop/Loader/ResourceLoader.Debug.cs index 4e162bca..ada91c0a 100644 --- a/Penumbra/Interop/Loader/ResourceLoader.Debug.cs +++ b/Penumbra/Interop/Loader/ResourceLoader.Debug.cs @@ -48,7 +48,7 @@ public unsafe partial class ResourceLoader public Utf8GamePath OriginalPath; public FullPath ManipulatedPath; public ResourceCategory Category; - public object? ResolverInfo; + public ResolveData ResolverInfo; public ResourceType Extension; } @@ -59,18 +59,18 @@ public unsafe partial class ResourceLoader public void EnableDebug() { - _decRefHook?.Enable(); + _decRefHook.Enable(); ResourceLoaded += AddModifiedDebugInfo; } public void DisableDebug() { - _decRefHook?.Disable(); + _decRefHook.Disable(); ResourceLoaded -= AddModifiedDebugInfo; } private void AddModifiedDebugInfo( Structs.ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath, - LinkedModCollection? resolverInfo ) + ResolveData resolverInfo ) { if( manipulatedPath == null || manipulatedPath.Value.Crc64 == 0 ) { @@ -244,7 +244,7 @@ public unsafe partial class ResourceLoader private static void LogPath( Utf8GamePath path, bool synchronous ) => PluginLog.Information( $"[ResourceLoader] Requested {path} {( synchronous ? "synchronously." : "asynchronously." )}" ); - private static void LogResource( Structs.ResourceHandle* handle, Utf8GamePath path, FullPath? manipulatedPath, LinkedModCollection? _ ) + private static void LogResource( Structs.ResourceHandle* handle, Utf8GamePath path, FullPath? manipulatedPath, ResolveData _ ) { var pathString = manipulatedPath != null ? $"custom file {manipulatedPath} instead of {path}" : path.ToString(); PluginLog.Information( $"[ResourceLoader] Loaded {pathString} to 0x{( ulong )handle:X}. (Refcount {handle->RefCount})" ); diff --git a/Penumbra/Interop/Loader/ResourceLoader.Replacement.cs b/Penumbra/Interop/Loader/ResourceLoader.Replacement.cs index a9a996f6..f14f19b5 100644 --- a/Penumbra/Interop/Loader/ResourceLoader.Replacement.cs +++ b/Penumbra/Interop/Loader/ResourceLoader.Replacement.cs @@ -114,18 +114,18 @@ public unsafe partial class ResourceLoader // Use the default method of path replacement. - public static (FullPath?, LinkedModCollection?) DefaultResolver( Utf8GamePath path ) + public static (FullPath?, ResolveData) DefaultResolver( Utf8GamePath path ) { var resolved = Penumbra.CollectionManager.Default.ResolvePath( path ); - return ( resolved, new LinkedModCollection( Penumbra.CollectionManager.Default ) ); + return ( resolved, Penumbra.CollectionManager.Default.ToResolveData() ); } // Try all resolve path subscribers or use the default replacer. - private (FullPath?, LinkedModCollection?) ResolvePath( Utf8GamePath path, ResourceCategory category, ResourceType resourceType, int resourceHash ) + private (FullPath?, ResolveData) ResolvePath( Utf8GamePath path, ResourceCategory category, ResourceType resourceType, int resourceHash ) { if( !DoReplacements || _incMode.Value ) { - return ( null, null ); + return ( null, ResolveData.Invalid ); } path = path.ToLower(); diff --git a/Penumbra/Interop/Loader/ResourceLoader.cs b/Penumbra/Interop/Loader/ResourceLoader.cs index 8f23d71f..44d55214 100644 --- a/Penumbra/Interop/Loader/ResourceLoader.cs +++ b/Penumbra/Interop/Loader/ResourceLoader.cs @@ -117,9 +117,9 @@ public unsafe partial class ResourceLoader : IDisposable // Event fired whenever a resource is returned. // If the path was manipulated by penumbra, manipulatedPath will be the file path of the loaded resource. - // resolveData is additional data returned by the current ResolvePath function and is user-defined. + // resolveData is additional data returned by the current ResolvePath function which can contain the collection and associated game object. public delegate void ResourceLoadedDelegate( ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath, - LinkedModCollection? resolveData ); + ResolveData resolveData ); public event ResourceLoadedDelegate? ResourceLoaded; @@ -134,7 +134,7 @@ public unsafe partial class ResourceLoader : IDisposable // Resolving goes through all subscribed functions in arbitrary order until one returns true, // or uses default resolving if none return true. public delegate bool ResolvePathDelegate( Utf8GamePath path, ResourceCategory category, ResourceType type, int hash, - out (FullPath?, LinkedModCollection?) ret ); + out (FullPath?, ResolveData) ret ); public event ResolvePathDelegate? ResolvePathCustomization; diff --git a/Penumbra/Interop/Resolver/PathResolver.AnimationState.cs b/Penumbra/Interop/Resolver/PathResolver.AnimationState.cs index d6106a87..7211d109 100644 --- a/Penumbra/Interop/Resolver/PathResolver.AnimationState.cs +++ b/Penumbra/Interop/Resolver/PathResolver.AnimationState.cs @@ -15,8 +15,8 @@ public unsafe partial class PathResolver { private readonly DrawObjectState _drawObjectState; - private LinkedModCollection? _animationLoadCollection; - private LinkedModCollection? _lastAvfxCollection; + private ResolveData _animationLoadData = ResolveData.Invalid; + private ResolveData _lastAvfxData = ResolveData.Invalid; public AnimationState( DrawObjectState drawObjectState ) { @@ -24,46 +24,48 @@ public unsafe partial class PathResolver SignatureHelper.Initialise( this ); } - public bool HandleFiles( ResourceType type, Utf8GamePath _, [NotNullWhen( true )] out LinkedModCollection? collection ) + public bool HandleFiles( ResourceType type, Utf8GamePath _, out ResolveData resolveData ) { switch( type ) { case ResourceType.Tmb: case ResourceType.Pap: case ResourceType.Scd: - if( _animationLoadCollection != null ) + if( _animationLoadData.Valid ) { - collection = _animationLoadCollection; + resolveData = _animationLoadData; return true; } break; case ResourceType.Avfx: - _lastAvfxCollection = _animationLoadCollection ?? new LinkedModCollection(Penumbra.CollectionManager.Default); - if( _animationLoadCollection != null ) + _lastAvfxData = _animationLoadData.Valid + ? _animationLoadData + : Penumbra.CollectionManager.Default.ToResolveData(); + if( _animationLoadData.Valid ) { - collection = _animationLoadCollection; + resolveData = _animationLoadData; return true; } break; case ResourceType.Atex: - if( _lastAvfxCollection != null ) + if( _lastAvfxData.Valid ) { - collection = _lastAvfxCollection; + resolveData = _lastAvfxData; return true; } - if( _animationLoadCollection != null ) + if( _animationLoadData.Valid ) { - collection = _animationLoadCollection; + resolveData = _animationLoadData; return true; } break; } - collection = null; + resolveData = ResolveData.Invalid; return false; } @@ -107,7 +109,7 @@ public unsafe partial class PathResolver private ulong LoadTimelineResourcesDetour( IntPtr timeline ) { ulong ret; - var old = _animationLoadCollection; + var old = _animationLoadData; try { if( timeline != IntPtr.Zero ) @@ -117,11 +119,11 @@ public unsafe partial class PathResolver if( idx >= 0 && idx < Dalamud.Objects.Length ) { var obj = Dalamud.Objects[ idx ]; - _animationLoadCollection = obj != null ? IdentifyCollection( ( GameObject* )obj.Address ) : null; + _animationLoadData = obj != null ? IdentifyCollection( ( GameObject* )obj.Address ) : ResolveData.Invalid; } else { - _animationLoadCollection = null; + _animationLoadData = ResolveData.Invalid; } } } @@ -130,7 +132,7 @@ public unsafe partial class PathResolver ret = _loadTimelineResourcesHook.Original( timeline ); } - _animationLoadCollection = old; + _animationLoadData = old; return ret; } @@ -145,11 +147,14 @@ public unsafe partial class PathResolver private void CharacterBaseLoadAnimationDetour( IntPtr drawObject ) { - var last = _animationLoadCollection; - _animationLoadCollection = _drawObjectState.LastCreatedCollection - ?? ( FindParent( drawObject, out var collection ) != null ? collection : new LinkedModCollection(Penumbra.CollectionManager.Default) ); + var last = _animationLoadData; + _animationLoadData = _drawObjectState.LastCreatedCollection.Valid + ? _drawObjectState.LastCreatedCollection + : FindParent( drawObject, out var collection ) != null + ? collection + : Penumbra.CollectionManager.Default.ToResolveData(); _characterBaseLoadAnimationHook.Original( drawObject ); - _animationLoadCollection = last; + _animationLoadData = last; } @@ -160,10 +165,10 @@ public unsafe partial class PathResolver private ulong LoadSomeAvfxDetour( uint a1, IntPtr gameObject, IntPtr gameObject2, float unk1, IntPtr unk2, IntPtr unk3 ) { - var last = _animationLoadCollection; - _animationLoadCollection = IdentifyCollection( ( GameObject* )gameObject ); + var last = _animationLoadData; + _animationLoadData = IdentifyCollection( ( GameObject* )gameObject ); var ret = _loadSomeAvfxHook.Original( a1, gameObject, gameObject2, unk1, unk2, unk3 ); - _animationLoadCollection = last; + _animationLoadData = last; return ret; } @@ -177,18 +182,18 @@ public unsafe partial class PathResolver private void LoadSomePapDetour( IntPtr a1, int a2, IntPtr a3, int a4 ) { var timelinePtr = a1 + 0x50; - var last = _animationLoadCollection; + var last = _animationLoadData; if( timelinePtr != IntPtr.Zero ) { var actorIdx = ( int )( *( *( ulong** )timelinePtr + 1 ) >> 3 ); if( actorIdx >= 0 && actorIdx < Dalamud.Objects.Length ) { - _animationLoadCollection = IdentifyCollection( ( GameObject* )( Dalamud.Objects[ actorIdx ]?.Address ?? IntPtr.Zero ) ); + _animationLoadData = IdentifyCollection( ( GameObject* )( Dalamud.Objects[ actorIdx ]?.Address ?? IntPtr.Zero ) ); } } _loadSomePapHook.Original( a1, a2, a3, a4 ); - _animationLoadCollection = last; + _animationLoadData = last; } // Seems to load character actions when zoning or changing class, maybe. @@ -197,10 +202,10 @@ public unsafe partial class PathResolver private void SomeActionLoadDetour( IntPtr gameObject ) { - var last = _animationLoadCollection; - _animationLoadCollection = IdentifyCollection( ( GameObject* )gameObject ); + var last = _animationLoadData; + _animationLoadData = IdentifyCollection( ( GameObject* )gameObject ); _someActionLoadHook.Original( gameObject ); - _animationLoadCollection = last; + _animationLoadData = last; } [Signature( "E8 ?? ?? ?? ?? 44 84 A3", DetourName = nameof( SomeOtherAvfxDetour ) )] @@ -208,11 +213,11 @@ public unsafe partial class PathResolver private void SomeOtherAvfxDetour( IntPtr unk ) { - var last = _animationLoadCollection; + var last = _animationLoadData; var gameObject = ( GameObject* )( unk - 0x8D0 ); - _animationLoadCollection = IdentifyCollection( gameObject ); + _animationLoadData = IdentifyCollection( gameObject ); _someOtherAvfxHook.Original( unk ); - _animationLoadCollection = last; + _animationLoadData = last; } } } \ No newline at end of file diff --git a/Penumbra/Interop/Resolver/PathResolver.DrawObjectState.cs b/Penumbra/Interop/Resolver/PathResolver.DrawObjectState.cs index 1e6ed477..7c9690fc 100644 --- a/Penumbra/Interop/Resolver/PathResolver.DrawObjectState.cs +++ b/Penumbra/Interop/Resolver/PathResolver.DrawObjectState.cs @@ -20,13 +20,13 @@ public unsafe partial class PathResolver public static event CreatingCharacterBaseDelegate? CreatingCharacterBase; public static event CreatedCharacterBaseDelegate? CreatedCharacterBase; - public IEnumerable< KeyValuePair< IntPtr, (LinkedModCollection, int) > > DrawObjects + public IEnumerable< KeyValuePair< IntPtr, (ResolveData, int) > > DrawObjects => _drawObjectToObject; public int Count => _drawObjectToObject.Count; - public bool TryGetValue( IntPtr drawObject, out (LinkedModCollection, int) value, out GameObject* gameObject ) + public bool TryGetValue( IntPtr drawObject, out (ResolveData, int) value, out GameObject* gameObject ) { gameObject = null; if( !_drawObjectToObject.TryGetValue( drawObject, out value ) ) @@ -40,7 +40,7 @@ public unsafe partial class PathResolver // Set and update a parent object if it exists and a last game object is set. - public LinkedModCollection? CheckParentDrawObject( IntPtr drawObject, IntPtr parentObject ) + public ResolveData CheckParentDrawObject( IntPtr drawObject, IntPtr parentObject ) { if( parentObject == IntPtr.Zero && LastGameObject != null ) { @@ -49,26 +49,26 @@ public unsafe partial class PathResolver return collection; } - return null; + return ResolveData.Invalid; } - public bool HandleDecalFile( ResourceType type, Utf8GamePath gamePath, [NotNullWhen( true )] out LinkedModCollection? collection ) + public bool HandleDecalFile( ResourceType type, Utf8GamePath gamePath, out ResolveData resolveData ) { if( type == ResourceType.Tex - && LastCreatedCollection != null + && LastCreatedCollection.Valid && gamePath.Path.Substring( "chara/common/texture/".Length ).StartsWith( 'd', 'e', 'c', 'a', 'l', '_', 'f', 'a', 'c', 'e' ) ) { - collection = LastCreatedCollection!; + resolveData = LastCreatedCollection; return true; } - collection = null; + resolveData = ResolveData.Invalid; return false; } - public LinkedModCollection? LastCreatedCollection + public ResolveData LastCreatedCollection => _lastCreatedCollection; public GameObject* LastGameObject { get; private set; } @@ -124,8 +124,8 @@ public unsafe partial class PathResolver // This map links DrawObjects directly to Actors (by ObjectTable index) and their collections. // It contains any DrawObjects that correspond to a human actor, even those without specific collections. - private readonly Dictionary< IntPtr, (LinkedModCollection, int) > _drawObjectToObject = new(); - private LinkedModCollection? _lastCreatedCollection; + private readonly Dictionary< IntPtr, (ResolveData, int) > _drawObjectToObject = new(); + private ResolveData _lastCreatedCollection = ResolveData.Invalid; // Keep track of created DrawObjects that are CharacterBase, // and use the last game object that called EnableDraw to link them. diff --git a/Penumbra/Interop/Resolver/PathResolver.Identification.cs b/Penumbra/Interop/Resolver/PathResolver.Identification.cs index 9b0586dd..39efe25b 100644 --- a/Penumbra/Interop/Resolver/PathResolver.Identification.cs +++ b/Penumbra/Interop/Resolver/PathResolver.Identification.cs @@ -40,7 +40,7 @@ public unsafe partial class PathResolver return null; } - var ui = ( AtkUnitBase* )addon; + var ui = ( AtkUnitBase* )addon; var nodeId = Dalamud.GameData.GetExcelSheet< Title >()?.GetRow( *_inspectTitleId )?.IsPrefix == true ? 2u : 6u; var text = ( AtkTextNode* )ui->UldManager.SearchNodeById( nodeId ); @@ -61,7 +61,8 @@ public unsafe partial class PathResolver { return null; } - var data = *( byte** )( (byte*) agent + 0x28 ); + + var data = *( byte** )( ( byte* )agent + 0x28 ); if( data == null ) { return null; @@ -139,11 +140,11 @@ public unsafe partial class PathResolver } // Identify the correct collection for a GameObject by index and name. - private static LinkedModCollection IdentifyCollection( GameObject* gameObject ) + private static ResolveData IdentifyCollection( GameObject* gameObject ) { if( gameObject == null ) { - return new LinkedModCollection(Penumbra.CollectionManager.Default); + return new ResolveData( Penumbra.CollectionManager.Default ); } try @@ -153,8 +154,9 @@ public unsafe partial class PathResolver // Actors are also not named. So use Yourself > Players > Racial > Default. if( !Dalamud.ClientState.IsLoggedIn ) { - return new LinkedModCollection(gameObject, Penumbra.CollectionManager.ByType( CollectionType.Yourself ) - ?? ( CollectionByActor( string.Empty, gameObject, out var c ) ? c : Penumbra.CollectionManager.Default )); + var collection = Penumbra.CollectionManager.ByType( CollectionType.Yourself ) + ?? ( CollectionByActor( string.Empty, gameObject, out var c ) ? c : Penumbra.CollectionManager.Default ); + return collection.ToResolveData( gameObject ); } else { @@ -163,7 +165,7 @@ public unsafe partial class PathResolver && gameObject->ObjectKind == ( byte )ObjectKind.EventNpc && gameObject->DataID is 1011832 or 1011021 ) // cf. "E8 ?? ?? ?? ?? 0F B6 F8 88 45", male or female retainer { - return new LinkedModCollection((IntPtr)gameObject, Penumbra.CollectionManager.Default); + return Penumbra.CollectionManager.Default.ToResolveData( gameObject ); } string? actorName = null; @@ -174,7 +176,7 @@ public unsafe partial class PathResolver if( actorName.Length > 0 && CollectionByActorName( actorName, out var actorCollection ) ) { - return new LinkedModCollection(gameObject, actorCollection); + return actorCollection.ToResolveData( gameObject ); } } @@ -193,17 +195,18 @@ public unsafe partial class PathResolver ?? GetOwnerName( gameObject ) ?? actorName ?? new Utf8String( gameObject->Name ).ToString(); // First check temporary character collections, then the own configuration, then special collections. - return new LinkedModCollection(gameObject, CollectionByActorName( actualName, out var c ) + var collection = CollectionByActorName( actualName, out var c ) ? c : CollectionByActor( actualName, gameObject, out c ) ? c - : Penumbra.CollectionManager.Default); + : Penumbra.CollectionManager.Default; + return collection.ToResolveData( gameObject ); } } catch( Exception e ) { PluginLog.Error( $"Error identifying collection:\n{e}" ); - return new LinkedModCollection(gameObject, Penumbra.CollectionManager.Default); + return Penumbra.CollectionManager.Default.ToResolveData( gameObject ); } } diff --git a/Penumbra/Interop/Resolver/PathResolver.Material.cs b/Penumbra/Interop/Resolver/PathResolver.Material.cs index b4a6c494..6e81dbac 100644 --- a/Penumbra/Interop/Resolver/PathResolver.Material.cs +++ b/Penumbra/Interop/Resolver/PathResolver.Material.cs @@ -21,7 +21,7 @@ public unsafe partial class PathResolver { private readonly PathState _paths; - private LinkedModCollection? _mtrlCollection; + private ResolveData _mtrlData = ResolveData.Invalid; public MaterialState( PathState paths ) { @@ -30,30 +30,30 @@ 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, [NotNullWhen( true )] out LinkedModCollection? collection ) + public bool HandleSubFiles( ResourceType type, out ResolveData collection ) { - if( _mtrlCollection != null && type is ResourceType.Tex or ResourceType.Shpk ) + if( _mtrlData.Valid && type is ResourceType.Tex or ResourceType.Shpk ) { - collection = _mtrlCollection; + collection = _mtrlData; return true; } - collection = null; + collection = ResolveData.Invalid; return false; } // Materials need to be set per collection so they can load their textures independently from each other. - public static void HandleCollection( LinkedModCollection collection, string path, bool nonDefault, ResourceType type, FullPath? resolved, - out (FullPath?, LinkedModCollection?) data ) + public static void HandleCollection( ResolveData resolveData, string path, bool nonDefault, ResourceType type, FullPath? resolved, + out (FullPath?, ResolveData) data ) { if( nonDefault && type == ResourceType.Mtrl ) { - var fullPath = new FullPath( $"|{collection.ModCollection.Name}_{collection.ModCollection.ChangeCounter}|{path}" ); - data = ( fullPath, collection ); + var fullPath = new FullPath( $"|{resolveData.ModCollection.Name}_{resolveData.ModCollection.ChangeCounter}|{path}" ); + data = ( fullPath, resolveData ); } else { - data = ( resolved, collection ); + data = ( resolved, resolveData ); } } @@ -74,8 +74,8 @@ public unsafe partial class PathResolver public void Dispose() { Disable(); - _loadMtrlShpkHook?.Dispose(); - _loadMtrlTexHook?.Dispose(); + _loadMtrlShpkHook.Dispose(); + _loadMtrlTexHook.Dispose(); } // We need to set the correct collection for the actual material path that is loaded @@ -97,9 +97,9 @@ public unsafe partial class PathResolver #if DEBUG PluginLog.Verbose( "Using MtrlLoadHandler with collection {$Split:l} for path {$Path:l}.", name, path ); #endif - + var objFromObjTable = Dalamud.Objects.FirstOrDefault( f => f.Name.TextValue == name ); - IntPtr gameObjAddr = objFromObjTable?.Address ?? IntPtr.Zero; + var gameObjAddr = objFromObjTable?.Address ?? IntPtr.Zero; _paths.SetCollection( gameObjAddr, path, collection ); } else @@ -127,7 +127,7 @@ public unsafe partial class PathResolver { LoadMtrlHelper( mtrlResourceHandle ); var ret = _loadMtrlTexHook.Original( mtrlResourceHandle ); - _mtrlCollection = null; + _mtrlData = ResolveData.Invalid; return ret; } @@ -139,7 +139,7 @@ public unsafe partial class PathResolver { LoadMtrlHelper( mtrlResourceHandle ); var ret = _loadMtrlShpkHook.Original( mtrlResourceHandle ); - _mtrlCollection = null; + _mtrlData = ResolveData.Invalid; return ret; } @@ -152,7 +152,7 @@ public unsafe partial class PathResolver var mtrl = ( MtrlResource* )mtrlResourceHandle; var mtrlPath = Utf8String.FromSpanUnsafe( mtrl->Handle.FileNameSpan(), true, null, true ); - _mtrlCollection = _paths.TryGetValue( mtrlPath, out var c ) ? c : null; + _mtrlData = _paths.TryGetValue( mtrlPath, out var c ) ? c : ResolveData.Invalid; } } } \ No newline at end of file diff --git a/Penumbra/Interop/Resolver/PathResolver.Meta.cs b/Penumbra/Interop/Resolver/PathResolver.Meta.cs index a2699544..e305b6db 100644 --- a/Penumbra/Interop/Resolver/PathResolver.Meta.cs +++ b/Penumbra/Interop/Resolver/PathResolver.Meta.cs @@ -79,8 +79,8 @@ public unsafe partial class PathResolver private void OnModelLoadCompleteDetour( IntPtr drawObject ) { - var collection = GetCollection( drawObject ); - if( collection != null ) + var collection = GetResolveData( drawObject ); + if( collection.Valid ) { using var eqp = MetaChanger.ChangeEqp( collection.ModCollection ); using var eqdp = MetaChanger.ChangeEqdp( collection.ModCollection ); @@ -106,8 +106,8 @@ public unsafe partial class PathResolver return; } - var collection = GetCollection( drawObject ); - if( collection != null ) + var collection = GetResolveData( drawObject ); + if( collection.Valid ) { using var eqp = MetaChanger.ChangeEqp( collection.ModCollection ); using var eqdp = MetaChanger.ChangeEqdp( collection.ModCollection ); @@ -212,24 +212,24 @@ public unsafe partial class PathResolver return new MetaChanger( MetaManipulation.Type.Eqp ); } - public static MetaChanger ChangeEqp( PathResolver resolver, IntPtr drawObject ) + public static MetaChanger ChangeEqp( PathResolver _, IntPtr drawObject ) { - var collection = GetCollection( drawObject ); - if( collection != null ) + var resolveData = GetResolveData( drawObject ); + if( resolveData.Valid ) { - return ChangeEqp( collection.ModCollection ); + return ChangeEqp( resolveData.ModCollection ); } return new MetaChanger( MetaManipulation.Type.Unknown ); } // We only need to change anything if it is actually equipment here. - public static MetaChanger ChangeEqdp( PathResolver resolver, IntPtr drawObject, uint modelType ) + public static MetaChanger ChangeEqdp( PathResolver _, IntPtr drawObject, uint modelType ) { if( modelType < 10 ) { - var collection = GetCollection( drawObject ); - if( collection != null ) + var collection = GetResolveData( drawObject ); + if( collection.Valid ) { return ChangeEqdp( collection.ModCollection ); } @@ -246,10 +246,10 @@ public unsafe partial class PathResolver public static MetaChanger ChangeGmp( PathResolver resolver, IntPtr drawObject ) { - var collection = GetCollection( drawObject ); - if( collection != null ) + var resolveData = GetResolveData( drawObject ); + if( resolveData.Valid ) { - collection.ModCollection.SetGmpFiles(); + resolveData.ModCollection.SetGmpFiles(); return new MetaChanger( MetaManipulation.Type.Gmp ); } @@ -258,30 +258,30 @@ public unsafe partial class PathResolver public static MetaChanger ChangeEst( PathResolver resolver, IntPtr drawObject ) { - var collection = GetCollection( drawObject ); - if( collection != null ) + var resolveData = GetResolveData( drawObject ); + if( resolveData.Valid ) { - collection.ModCollection.SetEstFiles(); + resolveData.ModCollection.SetEstFiles(); return new MetaChanger( MetaManipulation.Type.Est ); } return new MetaChanger( MetaManipulation.Type.Unknown ); } - public static MetaChanger ChangeCmp( GameObject* gameObject, out LinkedModCollection? collection ) + public static MetaChanger ChangeCmp( GameObject* gameObject, out ResolveData resolveData ) { if( gameObject != null ) { - collection = IdentifyCollection( gameObject ); - if( collection.ModCollection != Penumbra.CollectionManager.Default && collection.ModCollection.HasCache ) + resolveData = IdentifyCollection( gameObject ); + if( resolveData.ModCollection != Penumbra.CollectionManager.Default && resolveData.ModCollection.HasCache ) { - collection.ModCollection.SetCmpFiles(); + resolveData.ModCollection.SetCmpFiles(); return new MetaChanger( MetaManipulation.Type.Rsp ); } } else { - collection = null; + resolveData = ResolveData.Invalid; } return new MetaChanger( MetaManipulation.Type.Unknown ); @@ -289,10 +289,10 @@ public unsafe partial class PathResolver public static MetaChanger ChangeCmp( PathResolver resolver, IntPtr drawObject ) { - var collection = GetCollection( drawObject ); - if( collection != null ) + var resolveData = GetResolveData( drawObject ); + if( resolveData.Valid ) { - collection.ModCollection.SetCmpFiles(); + resolveData.ModCollection.SetCmpFiles(); return new MetaChanger( MetaManipulation.Type.Rsp ); } diff --git a/Penumbra/Interop/Resolver/PathResolver.PathState.cs b/Penumbra/Interop/Resolver/PathResolver.PathState.cs index 6a1d745a..f5c11771 100644 --- a/Penumbra/Interop/Resolver/PathResolver.PathState.cs +++ b/Penumbra/Interop/Resolver/PathResolver.PathState.cs @@ -32,7 +32,7 @@ public unsafe partial class PathResolver private readonly ResolverHooks _monster; // This map links files to their corresponding collection, if it is non-default. - private readonly ConcurrentDictionary< Utf8String, LinkedModCollection > _pathCollections = new(); + private readonly ConcurrentDictionary< Utf8String, ResolveData > _pathCollections = new(); public PathState( PathResolver parent ) { @@ -70,13 +70,13 @@ public unsafe partial class PathResolver public int Count => _pathCollections.Count; - public IEnumerable< KeyValuePair< Utf8String, LinkedModCollection > > Paths + public IEnumerable< KeyValuePair< Utf8String, ResolveData > > Paths => _pathCollections; - public bool TryGetValue( Utf8String path, [NotNullWhen( true )] out LinkedModCollection? collection ) + public bool TryGetValue( Utf8String path, out ResolveData collection ) => _pathCollections.TryGetValue( path, out collection ); - public bool Consume( Utf8String path, [NotNullWhen( true )] out LinkedModCollection? collection ) + public bool Consume( Utf8String path, out ResolveData collection ) => _pathCollections.TryRemove( path, out collection ); // Just add or remove the resolved path. @@ -98,11 +98,11 @@ public unsafe partial class PathResolver { if( _pathCollections.ContainsKey( path ) || path.IsOwned ) { - _pathCollections[ path ] = new LinkedModCollection(gameObject, collection); + _pathCollections[ path ] = collection.ToResolveData( gameObject ); } else { - _pathCollections[ path.Clone() ] = new LinkedModCollection(gameObject, collection); + _pathCollections[ path.Clone() ] = collection.ToResolveData( gameObject ); } } } diff --git a/Penumbra/Interop/Resolver/PathResolver.ResolverHooks.cs b/Penumbra/Interop/Resolver/PathResolver.ResolverHooks.cs index adc3dfeb..02729c07 100644 --- a/Penumbra/Interop/Resolver/PathResolver.ResolverHooks.cs +++ b/Penumbra/Interop/Resolver/PathResolver.ResolverHooks.cs @@ -245,7 +245,7 @@ public partial class PathResolver var parentObject = ( IntPtr )( ( DrawObject* )drawObject )->Object.ParentObject; var parentCollection = DrawObjects.CheckParentDrawObject( drawObject, parentObject ); - if( parentCollection != null ) + if( parentCollection.Valid ) { return _parent._paths.ResolvePath( (IntPtr)FindParent(parentObject, out _), parentCollection.ModCollection, path ); } diff --git a/Penumbra/Interop/Resolver/PathResolver.cs b/Penumbra/Interop/Resolver/PathResolver.cs index 76164318..06536b30 100644 --- a/Penumbra/Interop/Resolver/PathResolver.cs +++ b/Penumbra/Interop/Resolver/PathResolver.cs @@ -40,7 +40,7 @@ public partial class PathResolver : IDisposable } // The modified resolver that handles game path resolving. - private bool CharacterResolver( Utf8GamePath gamePath, ResourceCategory _1, ResourceType type, int _2, out (FullPath?, LinkedModCollection?) data ) + private bool CharacterResolver( Utf8GamePath gamePath, ResourceCategory _1, ResourceType type, int _2, out (FullPath?, ResolveData) data ) { // Check if the path was marked for a specific collection, // or if it is a file loaded by a material, and if we are currently in a material load, @@ -48,23 +48,23 @@ 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 collection ) - || _paths.Consume( gamePath.Path, out collection ) - || _animations.HandleFiles( type, gamePath, out collection ) - || DrawObjects.HandleDecalFile( type, gamePath, out collection ); - if( !nonDefault || collection == null ) + var nonDefault = _materials.HandleSubFiles( type, out var resolveData ) + || _paths.Consume( gamePath.Path, out resolveData ) + || _animations.HandleFiles( type, gamePath, out resolveData ) + || DrawObjects.HandleDecalFile( type, gamePath, out resolveData ); + if( !nonDefault || !resolveData.Valid ) { - collection = new LinkedModCollection(Penumbra.CollectionManager.Default); + resolveData = Penumbra.CollectionManager.Default.ToResolveData(); } // Resolve using character/default collection first, otherwise forced, as usual. - var resolved = collection.ModCollection.ResolvePath( gamePath ); + var resolved = resolveData.ModCollection.ResolvePath( gamePath ); // 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; - MaterialState.HandleCollection( collection, path, nonDefault, type, resolved, out data ); + MaterialState.HandleCollection( resolveData, path, nonDefault, type, resolved, out data ); return true; } @@ -117,50 +117,50 @@ public partial class PathResolver : IDisposable _materials.Dispose(); } - public static unsafe (IntPtr, LinkedModCollection) IdentifyDrawObject( IntPtr drawObject ) + public static unsafe (IntPtr, ResolveData) IdentifyDrawObject( IntPtr drawObject ) { - var parent = FindParent( drawObject, out var collection ); - return ( ( IntPtr )parent, collection ); + var parent = FindParent( drawObject, out var resolveData ); + return ( ( IntPtr )parent, resolveData ); } public int CutsceneActor( int idx ) => Cutscenes.GetParentIndex( idx ); // Use the stored information to find the GameObject and Collection linked to a DrawObject. - public static unsafe GameObject* FindParent( IntPtr drawObject, out LinkedModCollection collection ) + public static unsafe GameObject* FindParent( IntPtr drawObject, out ResolveData resolveData ) { if( DrawObjects.TryGetValue( drawObject, out var data, out var gameObject ) ) { - collection = data.Item1; + resolveData = data.Item1; return gameObject; } if( DrawObjects.LastGameObject != null && ( DrawObjects.LastGameObject->DrawObject == null || DrawObjects.LastGameObject->DrawObject == ( DrawObject* )drawObject ) ) { - collection = IdentifyCollection( DrawObjects.LastGameObject ); + resolveData = IdentifyCollection( DrawObjects.LastGameObject ); return DrawObjects.LastGameObject; } - collection = IdentifyCollection( null ); + resolveData = IdentifyCollection( null ); return null; } - private static unsafe LinkedModCollection? GetCollection( IntPtr drawObject ) + private static unsafe ResolveData GetResolveData( IntPtr drawObject ) { - var parent = FindParent( drawObject, out var collection ); - if( parent == null || collection.ModCollection == Penumbra.CollectionManager.Default ) + var parent = FindParent( drawObject, out var resolveData ); + if( parent == null || resolveData.ModCollection == Penumbra.CollectionManager.Default ) { - return null; + return ResolveData.Invalid; } - return collection.ModCollection.HasCache ? collection : null; + return resolveData.ModCollection.HasCache ? resolveData : ResolveData.Invalid; } - internal IEnumerable< KeyValuePair< Utf8String, LinkedModCollection > > PathCollections + internal IEnumerable< KeyValuePair< Utf8String, ResolveData > > PathCollections => _paths.Paths; - internal IEnumerable< KeyValuePair< IntPtr, (LinkedModCollection, int) > > DrawObjectMap + internal IEnumerable< KeyValuePair< IntPtr, (ResolveData, int) > > DrawObjectMap => DrawObjects.DrawObjects; internal IEnumerable< KeyValuePair< int, global::Dalamud.Game.ClientState.Objects.Types.GameObject > > CutsceneActors