mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-14 12:44:19 +01:00
add LinkedModCollection to be able to retrospectively verify which gamepath was resolved for which game object
This commit is contained in:
parent
07af64feed
commit
dcdc6d1be1
16 changed files with 151 additions and 79 deletions
|
|
@ -27,6 +27,7 @@ public delegate void CreatingCharacterBaseDelegate( IntPtr gameObject, ModCollec
|
||||||
IntPtr equipData );
|
IntPtr equipData );
|
||||||
|
|
||||||
public delegate void CreatedCharacterBaseDelegate( IntPtr gameObject, ModCollection collection, IntPtr drawObject );
|
public delegate void CreatedCharacterBaseDelegate( IntPtr gameObject, ModCollection collection, IntPtr drawObject );
|
||||||
|
public delegate void GameObjectResourceResolvedDelegate( IntPtr gameObject, string gamePath, string localPath );
|
||||||
|
|
||||||
public enum PenumbraApiEc
|
public enum PenumbraApiEc
|
||||||
{
|
{
|
||||||
|
|
@ -79,6 +80,8 @@ public interface IPenumbraApi : IPenumbraApiBase
|
||||||
// so you can apply flag changes after finishing.
|
// so you can apply flag changes after finishing.
|
||||||
public event CreatedCharacterBaseDelegate? CreatedCharacterBase;
|
public event CreatedCharacterBaseDelegate? CreatedCharacterBase;
|
||||||
|
|
||||||
|
public event GameObjectResourceResolvedDelegate GameObjectResourceResolved;
|
||||||
|
|
||||||
// Queue redrawing of all actors of the given name with the given RedrawType.
|
// Queue redrawing of all actors of the given name with the given RedrawType.
|
||||||
public void RedrawObject( string name, RedrawType setting );
|
public void RedrawObject( string name, RedrawType setting );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ using Penumbra.Collections;
|
||||||
using Penumbra.GameData.ByteString;
|
using Penumbra.GameData.ByteString;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.Interop.Resolver;
|
using Penumbra.Interop.Resolver;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
using Penumbra.Meta.Manipulations;
|
using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.Mods;
|
using Penumbra.Mods;
|
||||||
|
|
||||||
|
|
@ -21,7 +22,7 @@ namespace Penumbra.Api;
|
||||||
public class PenumbraApi : IDisposable, IPenumbraApi
|
public class PenumbraApi : IDisposable, IPenumbraApi
|
||||||
{
|
{
|
||||||
public (int, int) ApiVersion
|
public (int, int) ApiVersion
|
||||||
=> ( 4, 12 );
|
=> ( 4, 13 );
|
||||||
|
|
||||||
private Penumbra? _penumbra;
|
private Penumbra? _penumbra;
|
||||||
private Lumina.GameData? _lumina;
|
private Lumina.GameData? _lumina;
|
||||||
|
|
@ -54,7 +55,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
||||||
public bool Valid
|
public bool Valid
|
||||||
=> _penumbra != null;
|
=> _penumbra != null;
|
||||||
|
|
||||||
public PenumbraApi( Penumbra penumbra )
|
public unsafe PenumbraApi( Penumbra penumbra )
|
||||||
{
|
{
|
||||||
_penumbra = penumbra;
|
_penumbra = penumbra;
|
||||||
_lumina = ( Lumina.GameData? )Dalamud.GameData.GetType()
|
_lumina = ( Lumina.GameData? )Dalamud.GameData.GetType()
|
||||||
|
|
@ -66,10 +67,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
||||||
}
|
}
|
||||||
|
|
||||||
Penumbra.CollectionManager.CollectionChanged += SubscribeToNewCollections;
|
Penumbra.CollectionManager.CollectionChanged += SubscribeToNewCollections;
|
||||||
|
Penumbra.ResourceLoader.ResourceLoaded += OnResourceLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public unsafe void Dispose()
|
||||||
{
|
{
|
||||||
|
Penumbra.ResourceLoader.ResourceLoaded -= OnResourceLoaded;
|
||||||
Penumbra.CollectionManager.CollectionChanged -= SubscribeToNewCollections;
|
Penumbra.CollectionManager.CollectionChanged -= SubscribeToNewCollections;
|
||||||
_penumbra = null;
|
_penumbra = null;
|
||||||
_lumina = null;
|
_lumina = null;
|
||||||
|
|
@ -90,6 +93,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
||||||
return Penumbra.Config.ModDirectory;
|
return Penumbra.Config.ModDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private unsafe void OnResourceLoaded( ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath, LinkedModCollection? resolveData )
|
||||||
|
{
|
||||||
|
if( resolveData == null ) return;
|
||||||
|
GameObjectResourceResolved?.Invoke( resolveData.AssociatedGameObject, originalPath.ToString(), manipulatedPath?.ToString() ?? originalPath.ToString() );
|
||||||
|
}
|
||||||
|
|
||||||
public event Action< string, bool >? ModDirectoryChanged
|
public event Action< string, bool >? ModDirectoryChanged
|
||||||
{
|
{
|
||||||
add => Penumbra.ModManager.ModDirectoryChanged += value;
|
add => Penumbra.ModManager.ModDirectoryChanged += value;
|
||||||
|
|
@ -103,6 +112,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
||||||
}
|
}
|
||||||
|
|
||||||
public event ChangedItemHover? ChangedItemTooltip;
|
public event ChangedItemHover? ChangedItemTooltip;
|
||||||
|
public event GameObjectResourceResolvedDelegate? GameObjectResourceResolved;
|
||||||
|
|
||||||
public void RedrawObject( int tableIndex, RedrawType setting )
|
public void RedrawObject( int tableIndex, RedrawType setting )
|
||||||
{
|
{
|
||||||
|
|
@ -232,7 +242,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
||||||
{
|
{
|
||||||
CheckInitialized();
|
CheckInitialized();
|
||||||
var (obj, collection) = PathResolver.IdentifyDrawObject( drawObject );
|
var (obj, collection) = PathResolver.IdentifyDrawObject( drawObject );
|
||||||
return ( obj, collection.Name );
|
return ( obj, collection.ModCollection.Name );
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetCutsceneParentIndex( int actor )
|
public int GetCutsceneParentIndex( int actor )
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,7 @@ public partial class PenumbraIpc
|
||||||
public const string LabelProviderReverseResolvePlayerPath = "Penumbra.ReverseResolvePlayerPath";
|
public const string LabelProviderReverseResolvePlayerPath = "Penumbra.ReverseResolvePlayerPath";
|
||||||
public const string LabelProviderCreatingCharacterBase = "Penumbra.CreatingCharacterBase";
|
public const string LabelProviderCreatingCharacterBase = "Penumbra.CreatingCharacterBase";
|
||||||
public const string LabelProviderCreatedCharacterBase = "Penumbra.CreatedCharacterBase";
|
public const string LabelProviderCreatedCharacterBase = "Penumbra.CreatedCharacterBase";
|
||||||
|
public const string LabelProviderGameObjectResourcePathResolved = "Penumbra.GameObjectResourcePathResolved";
|
||||||
|
|
||||||
internal ICallGateProvider< string, string >? ProviderResolveDefault;
|
internal ICallGateProvider< string, string >? ProviderResolveDefault;
|
||||||
internal ICallGateProvider< string, string, string >? ProviderResolveCharacter;
|
internal ICallGateProvider< string, string, string >? ProviderResolveCharacter;
|
||||||
|
|
@ -293,6 +294,7 @@ public partial class PenumbraIpc
|
||||||
internal ICallGateProvider< string, string[] >? ProviderReverseResolvePathPlayer;
|
internal ICallGateProvider< string, string[] >? ProviderReverseResolvePathPlayer;
|
||||||
internal ICallGateProvider< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >? ProviderCreatingCharacterBase;
|
internal ICallGateProvider< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >? ProviderCreatingCharacterBase;
|
||||||
internal ICallGateProvider< IntPtr, string, IntPtr, object? >? ProviderCreatedCharacterBase;
|
internal ICallGateProvider< IntPtr, string, IntPtr, object? >? ProviderCreatedCharacterBase;
|
||||||
|
internal ICallGateProvider<IntPtr, string, string, object?>? ProviderGameObjectResourcePathResolved;
|
||||||
|
|
||||||
private void InitializeResolveProviders( DalamudPluginInterface pi )
|
private void InitializeResolveProviders( DalamudPluginInterface pi )
|
||||||
{
|
{
|
||||||
|
|
@ -387,6 +389,21 @@ public partial class PenumbraIpc
|
||||||
{
|
{
|
||||||
PluginLog.Error( $"Error registering IPC provider for {LabelProviderCreatedCharacterBase}:\n{e}" );
|
PluginLog.Error( $"Error registering IPC provider for {LabelProviderCreatedCharacterBase}:\n{e}" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ProviderGameObjectResourcePathResolved = pi.GetIpcProvider<IntPtr, string, string, object?>( LabelProviderGameObjectResourcePathResolved );
|
||||||
|
Api.GameObjectResourceResolved += GameObjectResourceResolvdedEvent;
|
||||||
|
}
|
||||||
|
catch( Exception e )
|
||||||
|
{
|
||||||
|
PluginLog.Error( $"Error registering IPC provider for {LabelProviderGameObjectResourcePathResolved}:\n{e}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GameObjectResourceResolvdedEvent( IntPtr gameObject, string gamePath, string localPath )
|
||||||
|
{
|
||||||
|
ProviderGameObjectResourcePathResolved?.SendMessage( gameObject, gamePath, localPath );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisposeResolveProviders()
|
private void DisposeResolveProviders()
|
||||||
|
|
|
||||||
32
Penumbra/Collections/LinkedModCollection.cs
Normal file
32
Penumbra/Collections/LinkedModCollection.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
using System;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
|
|
||||||
|
namespace Penumbra.Collections;
|
||||||
|
|
||||||
|
public class LinkedModCollection
|
||||||
|
{
|
||||||
|
private IntPtr? _associatedGameObject;
|
||||||
|
public IntPtr AssociatedGameObject
|
||||||
|
{
|
||||||
|
get => _associatedGameObject ?? IntPtr.Zero;
|
||||||
|
set => _associatedGameObject = value;
|
||||||
|
}
|
||||||
|
public ModCollection ModCollection;
|
||||||
|
|
||||||
|
public LinkedModCollection(ModCollection modCollection)
|
||||||
|
{
|
||||||
|
ModCollection = modCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinkedModCollection(IntPtr? gameObject, ModCollection collection)
|
||||||
|
{
|
||||||
|
AssociatedGameObject = gameObject ?? IntPtr.Zero;
|
||||||
|
ModCollection = collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe LinkedModCollection(GameObject* gameObject, ModCollection collection)
|
||||||
|
{
|
||||||
|
AssociatedGameObject = ( IntPtr )gameObject;
|
||||||
|
ModCollection = collection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ using Dalamud.Utility.Signatures;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||||
using FFXIVClientStructs.STD;
|
using FFXIVClientStructs.STD;
|
||||||
|
using Penumbra.Collections;
|
||||||
using Penumbra.GameData.ByteString;
|
using Penumbra.GameData.ByteString;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
|
||||||
|
|
@ -69,7 +70,7 @@ public unsafe partial class ResourceLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddModifiedDebugInfo( Structs.ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
|
private void AddModifiedDebugInfo( Structs.ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
|
||||||
object? resolverInfo )
|
LinkedModCollection? resolverInfo )
|
||||||
{
|
{
|
||||||
if( manipulatedPath == null || manipulatedPath.Value.Crc64 == 0 )
|
if( manipulatedPath == null || manipulatedPath.Value.Crc64 == 0 )
|
||||||
{
|
{
|
||||||
|
|
@ -243,7 +244,7 @@ public unsafe partial class ResourceLoader
|
||||||
private static void LogPath( Utf8GamePath path, bool synchronous )
|
private static void LogPath( Utf8GamePath path, bool synchronous )
|
||||||
=> PluginLog.Information( $"[ResourceLoader] Requested {path} {( synchronous ? "synchronously." : "asynchronously." )}" );
|
=> PluginLog.Information( $"[ResourceLoader] Requested {path} {( synchronous ? "synchronously." : "asynchronously." )}" );
|
||||||
|
|
||||||
private static void LogResource( Structs.ResourceHandle* handle, Utf8GamePath path, FullPath? manipulatedPath, object? _ )
|
private static void LogResource( Structs.ResourceHandle* handle, Utf8GamePath path, FullPath? manipulatedPath, LinkedModCollection? _ )
|
||||||
{
|
{
|
||||||
var pathString = manipulatedPath != null ? $"custom file {manipulatedPath} instead of {path}" : path.ToString();
|
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})" );
|
PluginLog.Information( $"[ResourceLoader] Loaded {pathString} to 0x{( ulong )handle:X}. (Refcount {handle->RefCount})" );
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using Dalamud.Hooking;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
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.GameData.ByteString;
|
using Penumbra.GameData.ByteString;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
|
|
@ -113,14 +114,14 @@ public unsafe partial class ResourceLoader
|
||||||
|
|
||||||
|
|
||||||
// Use the default method of path replacement.
|
// Use the default method of path replacement.
|
||||||
public static (FullPath?, object?) DefaultResolver( Utf8GamePath path )
|
public static (FullPath?, LinkedModCollection?) DefaultResolver( Utf8GamePath path )
|
||||||
{
|
{
|
||||||
var resolved = Penumbra.CollectionManager.Default.ResolvePath( path );
|
var resolved = Penumbra.CollectionManager.Default.ResolvePath( path );
|
||||||
return ( resolved, null );
|
return ( resolved, new LinkedModCollection( Penumbra.CollectionManager.Default ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try all resolve path subscribers or use the default replacer.
|
// Try all resolve path subscribers or use the default replacer.
|
||||||
private (FullPath?, object?) ResolvePath( Utf8GamePath path, ResourceCategory category, ResourceType resourceType, int resourceHash )
|
private (FullPath?, LinkedModCollection?) ResolvePath( Utf8GamePath path, ResourceCategory category, ResourceType resourceType, int resourceHash )
|
||||||
{
|
{
|
||||||
if( !DoReplacements || _incMode.Value )
|
if( !DoReplacements || _incMode.Value )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ using System;
|
||||||
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.GameData.ByteString;
|
using Penumbra.GameData.ByteString;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
|
|
@ -118,7 +119,7 @@ public unsafe partial class ResourceLoader : IDisposable
|
||||||
// If the path was manipulated by penumbra, manipulatedPath will be the file path of the loaded resource.
|
// 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 and is user-defined.
|
||||||
public delegate void ResourceLoadedDelegate( ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
|
public delegate void ResourceLoadedDelegate( ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
|
||||||
object? resolveData );
|
LinkedModCollection? resolveData );
|
||||||
|
|
||||||
public event ResourceLoadedDelegate? ResourceLoaded;
|
public event ResourceLoadedDelegate? ResourceLoaded;
|
||||||
|
|
||||||
|
|
@ -133,7 +134,7 @@ public unsafe partial class ResourceLoader : IDisposable
|
||||||
// Resolving goes through all subscribed functions in arbitrary order until one returns true,
|
// Resolving goes through all subscribed functions in arbitrary order until one returns true,
|
||||||
// or uses default resolving if none return true.
|
// or uses default resolving if none return true.
|
||||||
public delegate bool ResolvePathDelegate( Utf8GamePath path, ResourceCategory category, ResourceType type, int hash,
|
public delegate bool ResolvePathDelegate( Utf8GamePath path, ResourceCategory category, ResourceType type, int hash,
|
||||||
out (FullPath?, object?) ret );
|
out (FullPath?, LinkedModCollection?) ret );
|
||||||
|
|
||||||
public event ResolvePathDelegate? ResolvePathCustomization;
|
public event ResolvePathDelegate? ResolvePathCustomization;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ public unsafe partial class PathResolver
|
||||||
{
|
{
|
||||||
private readonly DrawObjectState _drawObjectState;
|
private readonly DrawObjectState _drawObjectState;
|
||||||
|
|
||||||
private ModCollection? _animationLoadCollection;
|
private LinkedModCollection? _animationLoadCollection;
|
||||||
private ModCollection? _lastAvfxCollection;
|
private LinkedModCollection? _lastAvfxCollection;
|
||||||
|
|
||||||
public AnimationState( DrawObjectState drawObjectState )
|
public AnimationState( DrawObjectState drawObjectState )
|
||||||
{
|
{
|
||||||
|
|
@ -24,7 +24,7 @@ public unsafe partial class PathResolver
|
||||||
SignatureHelper.Initialise( this );
|
SignatureHelper.Initialise( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HandleFiles( ResourceType type, Utf8GamePath _, [NotNullWhen( true )] out ModCollection? collection )
|
public bool HandleFiles( ResourceType type, Utf8GamePath _, [NotNullWhen( true )] out LinkedModCollection? collection )
|
||||||
{
|
{
|
||||||
switch( type )
|
switch( type )
|
||||||
{
|
{
|
||||||
|
|
@ -39,7 +39,7 @@ public unsafe partial class PathResolver
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ResourceType.Avfx:
|
case ResourceType.Avfx:
|
||||||
_lastAvfxCollection = _animationLoadCollection ?? Penumbra.CollectionManager.Default;
|
_lastAvfxCollection = _animationLoadCollection ?? new LinkedModCollection(Penumbra.CollectionManager.Default);
|
||||||
if( _animationLoadCollection != null )
|
if( _animationLoadCollection != null )
|
||||||
{
|
{
|
||||||
collection = _animationLoadCollection;
|
collection = _animationLoadCollection;
|
||||||
|
|
@ -147,7 +147,7 @@ public unsafe partial class PathResolver
|
||||||
{
|
{
|
||||||
var last = _animationLoadCollection;
|
var last = _animationLoadCollection;
|
||||||
_animationLoadCollection = _drawObjectState.LastCreatedCollection
|
_animationLoadCollection = _drawObjectState.LastCreatedCollection
|
||||||
?? ( FindParent( drawObject, out var collection ) != null ? collection : Penumbra.CollectionManager.Default );
|
?? ( FindParent( drawObject, out var collection ) != null ? collection : new LinkedModCollection(Penumbra.CollectionManager.Default) );
|
||||||
_characterBaseLoadAnimationHook.Original( drawObject );
|
_characterBaseLoadAnimationHook.Original( drawObject );
|
||||||
_animationLoadCollection = last;
|
_animationLoadCollection = last;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,13 @@ public unsafe partial class PathResolver
|
||||||
public static event CreatingCharacterBaseDelegate? CreatingCharacterBase;
|
public static event CreatingCharacterBaseDelegate? CreatingCharacterBase;
|
||||||
public static event CreatedCharacterBaseDelegate? CreatedCharacterBase;
|
public static event CreatedCharacterBaseDelegate? CreatedCharacterBase;
|
||||||
|
|
||||||
public IEnumerable< KeyValuePair< IntPtr, (ModCollection, int) > > DrawObjects
|
public IEnumerable< KeyValuePair< IntPtr, (LinkedModCollection, int) > > DrawObjects
|
||||||
=> _drawObjectToObject;
|
=> _drawObjectToObject;
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
=> _drawObjectToObject.Count;
|
=> _drawObjectToObject.Count;
|
||||||
|
|
||||||
public bool TryGetValue( IntPtr drawObject, out (ModCollection, int) value, out GameObject* gameObject )
|
public bool TryGetValue( IntPtr drawObject, out (LinkedModCollection, int) value, out GameObject* gameObject )
|
||||||
{
|
{
|
||||||
gameObject = null;
|
gameObject = null;
|
||||||
if( !_drawObjectToObject.TryGetValue( drawObject, out value ) )
|
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.
|
// Set and update a parent object if it exists and a last game object is set.
|
||||||
public ModCollection? CheckParentDrawObject( IntPtr drawObject, IntPtr parentObject )
|
public LinkedModCollection? CheckParentDrawObject( IntPtr drawObject, IntPtr parentObject )
|
||||||
{
|
{
|
||||||
if( parentObject == IntPtr.Zero && LastGameObject != null )
|
if( parentObject == IntPtr.Zero && LastGameObject != null )
|
||||||
{
|
{
|
||||||
|
|
@ -53,7 +53,7 @@ public unsafe partial class PathResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public bool HandleDecalFile( ResourceType type, Utf8GamePath gamePath, [NotNullWhen( true )] out ModCollection? collection )
|
public bool HandleDecalFile( ResourceType type, Utf8GamePath gamePath, [NotNullWhen( true )] out LinkedModCollection? collection )
|
||||||
{
|
{
|
||||||
if( type == ResourceType.Tex
|
if( type == ResourceType.Tex
|
||||||
&& LastCreatedCollection != null
|
&& LastCreatedCollection != null
|
||||||
|
|
@ -68,7 +68,7 @@ public unsafe partial class PathResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ModCollection? LastCreatedCollection
|
public LinkedModCollection? LastCreatedCollection
|
||||||
=> _lastCreatedCollection;
|
=> _lastCreatedCollection;
|
||||||
|
|
||||||
public GameObject* LastGameObject { get; private set; }
|
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.
|
// 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.
|
// It contains any DrawObjects that correspond to a human actor, even those without specific collections.
|
||||||
private readonly Dictionary< IntPtr, (ModCollection, int) > _drawObjectToObject = new();
|
private readonly Dictionary< IntPtr, (LinkedModCollection, int) > _drawObjectToObject = new();
|
||||||
private ModCollection? _lastCreatedCollection;
|
private LinkedModCollection? _lastCreatedCollection;
|
||||||
|
|
||||||
// Keep track of created DrawObjects that are CharacterBase,
|
// Keep track of created DrawObjects that are CharacterBase,
|
||||||
// and use the last game object that called EnableDraw to link them.
|
// and use the last game object that called EnableDraw to link them.
|
||||||
|
|
@ -141,14 +141,14 @@ public unsafe partial class PathResolver
|
||||||
if( LastGameObject != null )
|
if( LastGameObject != null )
|
||||||
{
|
{
|
||||||
var modelPtr = &a;
|
var modelPtr = &a;
|
||||||
CreatingCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!, ( IntPtr )modelPtr, b, c );
|
CreatingCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection, ( IntPtr )modelPtr, b, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = _characterBaseCreateHook.Original( a, b, c, d );
|
var ret = _characterBaseCreateHook.Original( a, b, c, d );
|
||||||
if( LastGameObject != null )
|
if( LastGameObject != null )
|
||||||
{
|
{
|
||||||
_drawObjectToObject[ ret ] = ( _lastCreatedCollection!, LastGameObject->ObjectIndex );
|
_drawObjectToObject[ ret ] = ( _lastCreatedCollection!, LastGameObject->ObjectIndex );
|
||||||
CreatedCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!, ret );
|
CreatedCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection, ret );
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
||||||
|
|
@ -139,11 +139,11 @@ public unsafe partial class PathResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identify the correct collection for a GameObject by index and name.
|
// Identify the correct collection for a GameObject by index and name.
|
||||||
private static ModCollection IdentifyCollection( GameObject* gameObject )
|
private static LinkedModCollection IdentifyCollection( GameObject* gameObject )
|
||||||
{
|
{
|
||||||
if( gameObject == null )
|
if( gameObject == null )
|
||||||
{
|
{
|
||||||
return Penumbra.CollectionManager.Default;
|
return new LinkedModCollection(Penumbra.CollectionManager.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|
@ -153,8 +153,8 @@ public unsafe partial class PathResolver
|
||||||
// Actors are also not named. So use Yourself > Players > Racial > Default.
|
// Actors are also not named. So use Yourself > Players > Racial > Default.
|
||||||
if( !Dalamud.ClientState.IsLoggedIn )
|
if( !Dalamud.ClientState.IsLoggedIn )
|
||||||
{
|
{
|
||||||
return Penumbra.CollectionManager.ByType( CollectionType.Yourself )
|
return new LinkedModCollection(gameObject, Penumbra.CollectionManager.ByType( CollectionType.Yourself )
|
||||||
?? ( CollectionByActor( string.Empty, gameObject, out var c ) ? c : Penumbra.CollectionManager.Default );
|
?? ( CollectionByActor( string.Empty, gameObject, out var c ) ? c : Penumbra.CollectionManager.Default ));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -163,7 +163,7 @@ public unsafe partial class PathResolver
|
||||||
&& gameObject->ObjectKind == ( byte )ObjectKind.EventNpc
|
&& gameObject->ObjectKind == ( byte )ObjectKind.EventNpc
|
||||||
&& gameObject->DataID is 1011832 or 1011021 ) // cf. "E8 ?? ?? ?? ?? 0F B6 F8 88 45", male or female retainer
|
&& gameObject->DataID is 1011832 or 1011021 ) // cf. "E8 ?? ?? ?? ?? 0F B6 F8 88 45", male or female retainer
|
||||||
{
|
{
|
||||||
return Penumbra.CollectionManager.Default;
|
return new LinkedModCollection((IntPtr)gameObject, Penumbra.CollectionManager.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
string? actorName = null;
|
string? actorName = null;
|
||||||
|
|
@ -174,7 +174,7 @@ public unsafe partial class PathResolver
|
||||||
if( actorName.Length > 0
|
if( actorName.Length > 0
|
||||||
&& CollectionByActorName( actorName, out var actorCollection ) )
|
&& CollectionByActorName( actorName, out var actorCollection ) )
|
||||||
{
|
{
|
||||||
return actorCollection;
|
return new LinkedModCollection(gameObject, actorCollection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,17 +193,17 @@ public unsafe partial class PathResolver
|
||||||
?? GetOwnerName( gameObject ) ?? actorName ?? new Utf8String( gameObject->Name ).ToString();
|
?? GetOwnerName( gameObject ) ?? actorName ?? new Utf8String( gameObject->Name ).ToString();
|
||||||
|
|
||||||
// First check temporary character collections, then the own configuration, then special collections.
|
// First check temporary character collections, then the own configuration, then special collections.
|
||||||
return CollectionByActorName( actualName, out var c )
|
return new LinkedModCollection(gameObject, CollectionByActorName( actualName, out var c )
|
||||||
? c
|
? c
|
||||||
: CollectionByActor( actualName, gameObject, out c )
|
: CollectionByActor( actualName, gameObject, out c )
|
||||||
? c
|
? c
|
||||||
: Penumbra.CollectionManager.Default;
|
: Penumbra.CollectionManager.Default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch( Exception e )
|
catch( Exception e )
|
||||||
{
|
{
|
||||||
PluginLog.Error( $"Error identifying collection:\n{e}" );
|
PluginLog.Error( $"Error identifying collection:\n{e}" );
|
||||||
return Penumbra.CollectionManager.Default;
|
return new LinkedModCollection(gameObject, Penumbra.CollectionManager.Default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using Dalamud.Hooking;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
using Dalamud.Utility.Signatures;
|
using Dalamud.Utility.Signatures;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||||
|
using OtterGui;
|
||||||
using Penumbra.Collections;
|
using Penumbra.Collections;
|
||||||
using Penumbra.GameData.ByteString;
|
using Penumbra.GameData.ByteString;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
|
@ -20,7 +21,7 @@ public unsafe partial class PathResolver
|
||||||
{
|
{
|
||||||
private readonly PathState _paths;
|
private readonly PathState _paths;
|
||||||
|
|
||||||
private ModCollection? _mtrlCollection;
|
private LinkedModCollection? _mtrlCollection;
|
||||||
|
|
||||||
public MaterialState( PathState paths )
|
public MaterialState( PathState paths )
|
||||||
{
|
{
|
||||||
|
|
@ -29,7 +30,7 @@ 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, [NotNullWhen( true )] out ModCollection? collection )
|
public bool HandleSubFiles( ResourceType type, [NotNullWhen( true )] out LinkedModCollection? collection )
|
||||||
{
|
{
|
||||||
if( _mtrlCollection != null && type is ResourceType.Tex or ResourceType.Shpk )
|
if( _mtrlCollection != null && type is ResourceType.Tex or ResourceType.Shpk )
|
||||||
{
|
{
|
||||||
|
|
@ -42,12 +43,12 @@ 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( ModCollection collection, string path, bool nonDefault, ResourceType type, FullPath? resolved,
|
public static void HandleCollection( LinkedModCollection collection, string path, bool nonDefault, ResourceType type, FullPath? resolved,
|
||||||
out (FullPath?, object?) data )
|
out (FullPath?, LinkedModCollection?) data )
|
||||||
{
|
{
|
||||||
if( nonDefault && type == ResourceType.Mtrl )
|
if( nonDefault && type == ResourceType.Mtrl )
|
||||||
{
|
{
|
||||||
var fullPath = new FullPath( $"|{collection.Name}_{collection.ChangeCounter}|{path}" );
|
var fullPath = new FullPath( $"|{collection.ModCollection.Name}_{collection.ModCollection.ChangeCounter}|{path}" );
|
||||||
data = ( fullPath, collection );
|
data = ( fullPath, collection );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -96,7 +97,12 @@ public unsafe partial class PathResolver
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
PluginLog.Verbose( "Using MtrlLoadHandler with collection {$Split:l} for path {$Path:l}.", name, path );
|
PluginLog.Verbose( "Using MtrlLoadHandler with collection {$Split:l} for path {$Path:l}.", name, path );
|
||||||
#endif
|
#endif
|
||||||
_paths.SetCollection( path, collection );
|
IntPtr gameObjAddr = IntPtr.Zero;
|
||||||
|
if ( Dalamud.Objects.FindFirst(f => f.Name.TextValue == name, out var gameObj ) )
|
||||||
|
{
|
||||||
|
gameObjAddr = gameObj.Address;
|
||||||
|
}
|
||||||
|
_paths.SetCollection( gameObjAddr, path, collection );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,8 @@ public unsafe partial class PathResolver
|
||||||
var collection = GetCollection( drawObject );
|
var collection = GetCollection( drawObject );
|
||||||
if( collection != null )
|
if( collection != null )
|
||||||
{
|
{
|
||||||
using var eqp = MetaChanger.ChangeEqp( collection );
|
using var eqp = MetaChanger.ChangeEqp( collection.ModCollection );
|
||||||
using var eqdp = MetaChanger.ChangeEqdp( collection );
|
using var eqdp = MetaChanger.ChangeEqdp( collection.ModCollection );
|
||||||
_onModelLoadCompleteHook.Original.Invoke( drawObject );
|
_onModelLoadCompleteHook.Original.Invoke( drawObject );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -109,8 +109,8 @@ public unsafe partial class PathResolver
|
||||||
var collection = GetCollection( drawObject );
|
var collection = GetCollection( drawObject );
|
||||||
if( collection != null )
|
if( collection != null )
|
||||||
{
|
{
|
||||||
using var eqp = MetaChanger.ChangeEqp( collection );
|
using var eqp = MetaChanger.ChangeEqp( collection.ModCollection );
|
||||||
using var eqdp = MetaChanger.ChangeEqdp( collection );
|
using var eqdp = MetaChanger.ChangeEqdp( collection.ModCollection );
|
||||||
_updateModelsHook.Original.Invoke( drawObject );
|
_updateModelsHook.Original.Invoke( drawObject );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -217,7 +217,7 @@ public unsafe partial class PathResolver
|
||||||
var collection = GetCollection( drawObject );
|
var collection = GetCollection( drawObject );
|
||||||
if( collection != null )
|
if( collection != null )
|
||||||
{
|
{
|
||||||
return ChangeEqp( collection );
|
return ChangeEqp( collection.ModCollection );
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MetaChanger( MetaManipulation.Type.Unknown );
|
return new MetaChanger( MetaManipulation.Type.Unknown );
|
||||||
|
|
@ -231,7 +231,7 @@ public unsafe partial class PathResolver
|
||||||
var collection = GetCollection( drawObject );
|
var collection = GetCollection( drawObject );
|
||||||
if( collection != null )
|
if( collection != null )
|
||||||
{
|
{
|
||||||
return ChangeEqdp( collection );
|
return ChangeEqdp( collection.ModCollection );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,7 +249,7 @@ public unsafe partial class PathResolver
|
||||||
var collection = GetCollection( drawObject );
|
var collection = GetCollection( drawObject );
|
||||||
if( collection != null )
|
if( collection != null )
|
||||||
{
|
{
|
||||||
collection.SetGmpFiles();
|
collection.ModCollection.SetGmpFiles();
|
||||||
return new MetaChanger( MetaManipulation.Type.Gmp );
|
return new MetaChanger( MetaManipulation.Type.Gmp );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,21 +261,21 @@ public unsafe partial class PathResolver
|
||||||
var collection = GetCollection( drawObject );
|
var collection = GetCollection( drawObject );
|
||||||
if( collection != null )
|
if( collection != null )
|
||||||
{
|
{
|
||||||
collection.SetEstFiles();
|
collection.ModCollection.SetEstFiles();
|
||||||
return new MetaChanger( MetaManipulation.Type.Est );
|
return new MetaChanger( MetaManipulation.Type.Est );
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MetaChanger( MetaManipulation.Type.Unknown );
|
return new MetaChanger( MetaManipulation.Type.Unknown );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MetaChanger ChangeCmp( GameObject* gameObject, out ModCollection? collection )
|
public static MetaChanger ChangeCmp( GameObject* gameObject, out LinkedModCollection? collection )
|
||||||
{
|
{
|
||||||
if( gameObject != null )
|
if( gameObject != null )
|
||||||
{
|
{
|
||||||
collection = IdentifyCollection( gameObject );
|
collection = IdentifyCollection( gameObject );
|
||||||
if( collection != Penumbra.CollectionManager.Default && collection.HasCache )
|
if( collection.ModCollection != Penumbra.CollectionManager.Default && collection.ModCollection.HasCache )
|
||||||
{
|
{
|
||||||
collection.SetCmpFiles();
|
collection.ModCollection.SetCmpFiles();
|
||||||
return new MetaChanger( MetaManipulation.Type.Rsp );
|
return new MetaChanger( MetaManipulation.Type.Rsp );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -292,7 +292,7 @@ public unsafe partial class PathResolver
|
||||||
var collection = GetCollection( drawObject );
|
var collection = GetCollection( drawObject );
|
||||||
if( collection != null )
|
if( collection != null )
|
||||||
{
|
{
|
||||||
collection.SetCmpFiles();
|
collection.ModCollection.SetCmpFiles();
|
||||||
return new MetaChanger( MetaManipulation.Type.Rsp );
|
return new MetaChanger( MetaManipulation.Type.Rsp );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,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< Utf8String, ModCollection > _pathCollections = new();
|
private readonly ConcurrentDictionary< Utf8String, LinkedModCollection > _pathCollections = new();
|
||||||
|
|
||||||
public PathState( PathResolver parent )
|
public PathState( PathResolver parent )
|
||||||
{
|
{
|
||||||
|
|
@ -70,18 +70,18 @@ public unsafe partial class PathResolver
|
||||||
public int Count
|
public int Count
|
||||||
=> _pathCollections.Count;
|
=> _pathCollections.Count;
|
||||||
|
|
||||||
public IEnumerable< KeyValuePair< Utf8String, ModCollection > > Paths
|
public IEnumerable< KeyValuePair< Utf8String, LinkedModCollection > > Paths
|
||||||
=> _pathCollections;
|
=> _pathCollections;
|
||||||
|
|
||||||
public bool TryGetValue( Utf8String path, [NotNullWhen( true )] out ModCollection? collection )
|
public bool TryGetValue( Utf8String path, [NotNullWhen( true )] out LinkedModCollection? collection )
|
||||||
=> _pathCollections.TryGetValue( path, out collection );
|
=> _pathCollections.TryGetValue( path, out collection );
|
||||||
|
|
||||||
public bool Consume( Utf8String path, [NotNullWhen( true )] out ModCollection? collection )
|
public bool Consume( Utf8String path, [NotNullWhen( true )] out LinkedModCollection? collection )
|
||||||
=> _pathCollections.TryRemove( path, out collection );
|
=> _pathCollections.TryRemove( path, out collection );
|
||||||
|
|
||||||
// Just add or remove the resolved path.
|
// Just add or remove the resolved path.
|
||||||
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
|
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
|
||||||
public IntPtr ResolvePath( ModCollection collection, IntPtr path )
|
public IntPtr ResolvePath( IntPtr? gameObject, ModCollection collection, IntPtr path )
|
||||||
{
|
{
|
||||||
if( path == IntPtr.Zero )
|
if( path == IntPtr.Zero )
|
||||||
{
|
{
|
||||||
|
|
@ -89,20 +89,20 @@ public unsafe partial class PathResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
var gamePath = new Utf8String( ( byte* )path );
|
var gamePath = new Utf8String( ( byte* )path );
|
||||||
SetCollection( gamePath, collection );
|
SetCollection( gameObject, gamePath, collection );
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special handling for paths so that we do not store non-owned temporary strings in the dictionary.
|
// Special handling for paths so that we do not store non-owned temporary strings in the dictionary.
|
||||||
public void SetCollection( Utf8String path, ModCollection collection )
|
public void SetCollection( IntPtr? gameObject, Utf8String path, ModCollection collection )
|
||||||
{
|
{
|
||||||
if( _pathCollections.ContainsKey( path ) || path.IsOwned )
|
if( _pathCollections.ContainsKey( path ) || path.IsOwned )
|
||||||
{
|
{
|
||||||
_pathCollections[ path ] = collection;
|
_pathCollections[ path ] = new LinkedModCollection(gameObject, collection);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_pathCollections[ path.Clone() ] = collection;
|
_pathCollections[ path.Clone() ] = new LinkedModCollection(gameObject, collection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
|
using Penumbra.Collections;
|
||||||
|
|
||||||
namespace Penumbra.Interop.Resolver;
|
namespace Penumbra.Interop.Resolver;
|
||||||
|
|
||||||
|
|
@ -226,9 +227,9 @@ public partial class PathResolver
|
||||||
// Implementation
|
// Implementation
|
||||||
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
|
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
|
||||||
private IntPtr ResolvePath( IntPtr drawObject, IntPtr path )
|
private IntPtr ResolvePath( IntPtr drawObject, IntPtr path )
|
||||||
=> _parent._paths.ResolvePath( FindParent( drawObject, out var collection ) == null
|
=> _parent._paths.ResolvePath( (IntPtr?)FindParent( drawObject, out _), FindParent( drawObject, out var collection ) == null
|
||||||
? Penumbra.CollectionManager.Default
|
? Penumbra.CollectionManager.Default
|
||||||
: collection, path );
|
: collection.ModCollection, path );
|
||||||
|
|
||||||
// Weapons have the characters DrawObject as a parent,
|
// Weapons have the characters DrawObject as a parent,
|
||||||
// but that may not be set yet when creating a new object, so we have to do the same detour
|
// but that may not be set yet when creating a new object, so we have to do the same detour
|
||||||
|
|
@ -239,20 +240,20 @@ public partial class PathResolver
|
||||||
var parent = FindParent( drawObject, out var collection );
|
var parent = FindParent( drawObject, out var collection );
|
||||||
if( parent != null )
|
if( parent != null )
|
||||||
{
|
{
|
||||||
return _parent._paths.ResolvePath( collection, path );
|
return _parent._paths.ResolvePath( (IntPtr)parent, collection.ModCollection, path );
|
||||||
}
|
}
|
||||||
|
|
||||||
var parentObject = ( IntPtr )( ( DrawObject* )drawObject )->Object.ParentObject;
|
var parentObject = ( IntPtr )( ( DrawObject* )drawObject )->Object.ParentObject;
|
||||||
var parentCollection = DrawObjects.CheckParentDrawObject( drawObject, parentObject );
|
var parentCollection = DrawObjects.CheckParentDrawObject( drawObject, parentObject );
|
||||||
if( parentCollection != null )
|
if( parentCollection != null )
|
||||||
{
|
{
|
||||||
return _parent._paths.ResolvePath( parentCollection, path );
|
return _parent._paths.ResolvePath( (IntPtr)FindParent(parentObject, out _), parentCollection.ModCollection, path );
|
||||||
}
|
}
|
||||||
|
|
||||||
parent = FindParent( parentObject, out collection );
|
parent = FindParent( parentObject, out collection );
|
||||||
return _parent._paths.ResolvePath( parent == null
|
return _parent._paths.ResolvePath( (IntPtr?)parent, parent == null
|
||||||
? Penumbra.CollectionManager.Default
|
? Penumbra.CollectionManager.Default
|
||||||
: collection, path );
|
: collection.ModCollection, path );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +40,7 @@ public partial class PathResolver : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
// The modified resolver that handles game path resolving.
|
// The modified resolver that handles game path resolving.
|
||||||
private bool CharacterResolver( Utf8GamePath gamePath, ResourceCategory _1, ResourceType type, int _2, out (FullPath?, object?) data )
|
private bool CharacterResolver( Utf8GamePath gamePath, ResourceCategory _1, ResourceType type, int _2, out (FullPath?, LinkedModCollection?) data )
|
||||||
{
|
{
|
||||||
// Check if the path was marked for a specific collection,
|
// 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,
|
// or if it is a file loaded by a material, and if we are currently in a material load,
|
||||||
|
|
@ -54,11 +54,11 @@ public partial class PathResolver : IDisposable
|
||||||
|| DrawObjects.HandleDecalFile( type, gamePath, out collection );
|
|| DrawObjects.HandleDecalFile( type, gamePath, out collection );
|
||||||
if( !nonDefault || collection == null )
|
if( !nonDefault || collection == null )
|
||||||
{
|
{
|
||||||
collection = Penumbra.CollectionManager.Default;
|
collection = new LinkedModCollection(Penumbra.CollectionManager.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve using character/default collection first, otherwise forced, as usual.
|
// Resolve using character/default collection first, otherwise forced, as usual.
|
||||||
var resolved = collection.ResolvePath( gamePath );
|
var resolved = collection.ModCollection.ResolvePath( gamePath );
|
||||||
|
|
||||||
// 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.
|
||||||
|
|
@ -117,7 +117,7 @@ public partial class PathResolver : IDisposable
|
||||||
_materials.Dispose();
|
_materials.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe (IntPtr, ModCollection) IdentifyDrawObject( IntPtr drawObject )
|
public static unsafe (IntPtr, LinkedModCollection) IdentifyDrawObject( IntPtr drawObject )
|
||||||
{
|
{
|
||||||
var parent = FindParent( drawObject, out var collection );
|
var parent = FindParent( drawObject, out var collection );
|
||||||
return ( ( IntPtr )parent, collection );
|
return ( ( IntPtr )parent, collection );
|
||||||
|
|
@ -127,7 +127,7 @@ public partial class PathResolver : IDisposable
|
||||||
=> Cutscenes.GetParentIndex( idx );
|
=> Cutscenes.GetParentIndex( idx );
|
||||||
|
|
||||||
// Use the stored information to find the GameObject and Collection linked to a DrawObject.
|
// Use the stored information to find the GameObject and Collection linked to a DrawObject.
|
||||||
public static unsafe GameObject* FindParent( IntPtr drawObject, out ModCollection collection )
|
public static unsafe GameObject* FindParent( IntPtr drawObject, out LinkedModCollection collection )
|
||||||
{
|
{
|
||||||
if( DrawObjects.TryGetValue( drawObject, out var data, out var gameObject ) )
|
if( DrawObjects.TryGetValue( drawObject, out var data, out var gameObject ) )
|
||||||
{
|
{
|
||||||
|
|
@ -146,21 +146,21 @@ public partial class PathResolver : IDisposable
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe ModCollection? GetCollection( IntPtr drawObject )
|
private static unsafe LinkedModCollection? GetCollection( IntPtr drawObject )
|
||||||
{
|
{
|
||||||
var parent = FindParent( drawObject, out var collection );
|
var parent = FindParent( drawObject, out var collection );
|
||||||
if( parent == null || collection == Penumbra.CollectionManager.Default )
|
if( parent == null || collection.ModCollection == Penumbra.CollectionManager.Default )
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return collection.HasCache ? collection : null;
|
return collection.ModCollection.HasCache ? collection : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IEnumerable< KeyValuePair< Utf8String, ModCollection > > PathCollections
|
internal IEnumerable< KeyValuePair< Utf8String, LinkedModCollection > > PathCollections
|
||||||
=> _paths.Paths;
|
=> _paths.Paths;
|
||||||
|
|
||||||
internal IEnumerable< KeyValuePair< IntPtr, (ModCollection, int) > > DrawObjectMap
|
internal IEnumerable< KeyValuePair< IntPtr, (LinkedModCollection, int) > > DrawObjectMap
|
||||||
=> DrawObjects.DrawObjects;
|
=> DrawObjects.DrawObjects;
|
||||||
|
|
||||||
internal IEnumerable< KeyValuePair< int, global::Dalamud.Game.ClientState.Objects.Types.GameObject > > CutsceneActors
|
internal IEnumerable< KeyValuePair< int, global::Dalamud.Game.ClientState.Objects.Types.GameObject > > CutsceneActors
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ public partial class ConfigWindow
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted( name );
|
ImGui.TextUnformatted( name );
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted( c.Name );
|
ImGui.TextUnformatted( c.ModCollection.Name );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -195,7 +195,7 @@ public partial class ConfigWindow
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGuiNative.igTextUnformatted( path.Path, path.Path + path.Length );
|
ImGuiNative.igTextUnformatted( path.Path, path.Path + path.Length );
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted( collection.Name );
|
ImGui.TextUnformatted( collection.ModCollection.Name );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue