mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +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 );
|
||||
|
||||
public delegate void CreatedCharacterBaseDelegate( IntPtr gameObject, ModCollection collection, IntPtr drawObject );
|
||||
public delegate void GameObjectResourceResolvedDelegate( IntPtr gameObject, string gamePath, string localPath );
|
||||
|
||||
public enum PenumbraApiEc
|
||||
{
|
||||
|
|
@ -79,6 +80,8 @@ public interface IPenumbraApi : IPenumbraApiBase
|
|||
// so you can apply flag changes after finishing.
|
||||
public event CreatedCharacterBaseDelegate? CreatedCharacterBase;
|
||||
|
||||
public event GameObjectResourceResolvedDelegate GameObjectResourceResolved;
|
||||
|
||||
// Queue redrawing of all actors of the given name with the given RedrawType.
|
||||
public void RedrawObject( string name, RedrawType setting );
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using Penumbra.Collections;
|
|||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Interop.Resolver;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods;
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ namespace Penumbra.Api;
|
|||
public class PenumbraApi : IDisposable, IPenumbraApi
|
||||
{
|
||||
public (int, int) ApiVersion
|
||||
=> ( 4, 12 );
|
||||
=> ( 4, 13 );
|
||||
|
||||
private Penumbra? _penumbra;
|
||||
private Lumina.GameData? _lumina;
|
||||
|
|
@ -54,7 +55,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
public bool Valid
|
||||
=> _penumbra != null;
|
||||
|
||||
public PenumbraApi( Penumbra penumbra )
|
||||
public unsafe PenumbraApi( Penumbra penumbra )
|
||||
{
|
||||
_penumbra = penumbra;
|
||||
_lumina = ( Lumina.GameData? )Dalamud.GameData.GetType()
|
||||
|
|
@ -66,10 +67,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
}
|
||||
|
||||
Penumbra.CollectionManager.CollectionChanged += SubscribeToNewCollections;
|
||||
Penumbra.ResourceLoader.ResourceLoaded += OnResourceLoaded;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
Penumbra.ResourceLoader.ResourceLoaded -= OnResourceLoaded;
|
||||
Penumbra.CollectionManager.CollectionChanged -= SubscribeToNewCollections;
|
||||
_penumbra = null;
|
||||
_lumina = null;
|
||||
|
|
@ -90,6 +93,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
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
|
||||
{
|
||||
add => Penumbra.ModManager.ModDirectoryChanged += value;
|
||||
|
|
@ -103,6 +112,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
}
|
||||
|
||||
public event ChangedItemHover? ChangedItemTooltip;
|
||||
public event GameObjectResourceResolvedDelegate? GameObjectResourceResolved;
|
||||
|
||||
public void RedrawObject( int tableIndex, RedrawType setting )
|
||||
{
|
||||
|
|
@ -232,7 +242,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
{
|
||||
CheckInitialized();
|
||||
var (obj, collection) = PathResolver.IdentifyDrawObject( drawObject );
|
||||
return ( obj, collection.Name );
|
||||
return ( obj, collection.ModCollection.Name );
|
||||
}
|
||||
|
||||
public int GetCutsceneParentIndex( int actor )
|
||||
|
|
|
|||
|
|
@ -283,6 +283,7 @@ public partial class PenumbraIpc
|
|||
public const string LabelProviderReverseResolvePlayerPath = "Penumbra.ReverseResolvePlayerPath";
|
||||
public const string LabelProviderCreatingCharacterBase = "Penumbra.CreatingCharacterBase";
|
||||
public const string LabelProviderCreatedCharacterBase = "Penumbra.CreatedCharacterBase";
|
||||
public const string LabelProviderGameObjectResourcePathResolved = "Penumbra.GameObjectResourcePathResolved";
|
||||
|
||||
internal ICallGateProvider< string, string >? ProviderResolveDefault;
|
||||
internal ICallGateProvider< string, string, string >? ProviderResolveCharacter;
|
||||
|
|
@ -293,6 +294,7 @@ public partial class PenumbraIpc
|
|||
internal ICallGateProvider< string, string[] >? ProviderReverseResolvePathPlayer;
|
||||
internal ICallGateProvider< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >? ProviderCreatingCharacterBase;
|
||||
internal ICallGateProvider< IntPtr, string, IntPtr, object? >? ProviderCreatedCharacterBase;
|
||||
internal ICallGateProvider<IntPtr, string, string, object?>? ProviderGameObjectResourcePathResolved;
|
||||
|
||||
private void InitializeResolveProviders( DalamudPluginInterface pi )
|
||||
{
|
||||
|
|
@ -387,6 +389,21 @@ public partial class PenumbraIpc
|
|||
{
|
||||
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()
|
||||
|
|
|
|||
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.Handle;
|
||||
using FFXIVClientStructs.STD;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
|
|
@ -69,7 +70,7 @@ public unsafe partial class ResourceLoader
|
|||
}
|
||||
|
||||
private void AddModifiedDebugInfo( Structs.ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
|
||||
object? resolverInfo )
|
||||
LinkedModCollection? resolverInfo )
|
||||
{
|
||||
if( manipulatedPath == null || manipulatedPath.Value.Crc64 == 0 )
|
||||
{
|
||||
|
|
@ -243,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, 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();
|
||||
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.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Interop.Structs;
|
||||
|
|
@ -113,14 +114,14 @@ public unsafe partial class ResourceLoader
|
|||
|
||||
|
||||
// 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 );
|
||||
return ( resolved, null );
|
||||
return ( resolved, new LinkedModCollection( Penumbra.CollectionManager.Default ) );
|
||||
}
|
||||
|
||||
// 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 )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using Dalamud.Hooking;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Enums;
|
||||
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.
|
||||
// resolveData is additional data returned by the current ResolvePath function and is user-defined.
|
||||
public delegate void ResourceLoadedDelegate( ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
|
||||
object? resolveData );
|
||||
LinkedModCollection? resolveData );
|
||||
|
||||
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,
|
||||
// or uses default resolving if none return true.
|
||||
public delegate bool ResolvePathDelegate( Utf8GamePath path, ResourceCategory category, ResourceType type, int hash,
|
||||
out (FullPath?, object?) ret );
|
||||
out (FullPath?, LinkedModCollection?) ret );
|
||||
|
||||
public event ResolvePathDelegate? ResolvePathCustomization;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ public unsafe partial class PathResolver
|
|||
{
|
||||
private readonly DrawObjectState _drawObjectState;
|
||||
|
||||
private ModCollection? _animationLoadCollection;
|
||||
private ModCollection? _lastAvfxCollection;
|
||||
private LinkedModCollection? _animationLoadCollection;
|
||||
private LinkedModCollection? _lastAvfxCollection;
|
||||
|
||||
public AnimationState( DrawObjectState drawObjectState )
|
||||
{
|
||||
|
|
@ -24,7 +24,7 @@ public unsafe partial class PathResolver
|
|||
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 )
|
||||
{
|
||||
|
|
@ -39,7 +39,7 @@ public unsafe partial class PathResolver
|
|||
|
||||
break;
|
||||
case ResourceType.Avfx:
|
||||
_lastAvfxCollection = _animationLoadCollection ?? Penumbra.CollectionManager.Default;
|
||||
_lastAvfxCollection = _animationLoadCollection ?? new LinkedModCollection(Penumbra.CollectionManager.Default);
|
||||
if( _animationLoadCollection != null )
|
||||
{
|
||||
collection = _animationLoadCollection;
|
||||
|
|
@ -147,7 +147,7 @@ public unsafe partial class PathResolver
|
|||
{
|
||||
var last = _animationLoadCollection;
|
||||
_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 );
|
||||
_animationLoadCollection = last;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ public unsafe partial class PathResolver
|
|||
public static event CreatingCharacterBaseDelegate? CreatingCharacterBase;
|
||||
public static event CreatedCharacterBaseDelegate? CreatedCharacterBase;
|
||||
|
||||
public IEnumerable< KeyValuePair< IntPtr, (ModCollection, int) > > DrawObjects
|
||||
public IEnumerable< KeyValuePair< IntPtr, (LinkedModCollection, int) > > DrawObjects
|
||||
=> _drawObjectToObject;
|
||||
|
||||
public int 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;
|
||||
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 ModCollection? CheckParentDrawObject( IntPtr drawObject, IntPtr parentObject )
|
||||
public LinkedModCollection? CheckParentDrawObject( IntPtr drawObject, IntPtr parentObject )
|
||||
{
|
||||
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
|
||||
&& LastCreatedCollection != null
|
||||
|
|
@ -68,7 +68,7 @@ public unsafe partial class PathResolver
|
|||
}
|
||||
|
||||
|
||||
public ModCollection? LastCreatedCollection
|
||||
public LinkedModCollection? 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, (ModCollection, int) > _drawObjectToObject = new();
|
||||
private ModCollection? _lastCreatedCollection;
|
||||
private readonly Dictionary< IntPtr, (LinkedModCollection, int) > _drawObjectToObject = new();
|
||||
private LinkedModCollection? _lastCreatedCollection;
|
||||
|
||||
// Keep track of created DrawObjects that are CharacterBase,
|
||||
// and use the last game object that called EnableDraw to link them.
|
||||
|
|
@ -141,14 +141,14 @@ public unsafe partial class PathResolver
|
|||
if( LastGameObject != null )
|
||||
{
|
||||
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 );
|
||||
if( LastGameObject != null )
|
||||
{
|
||||
_drawObjectToObject[ ret ] = ( _lastCreatedCollection!, LastGameObject->ObjectIndex );
|
||||
CreatedCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!, ret );
|
||||
CreatedCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection, ret );
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -139,11 +139,11 @@ public unsafe partial class PathResolver
|
|||
}
|
||||
|
||||
// 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 )
|
||||
{
|
||||
return Penumbra.CollectionManager.Default;
|
||||
return new LinkedModCollection(Penumbra.CollectionManager.Default);
|
||||
}
|
||||
|
||||
try
|
||||
|
|
@ -153,8 +153,8 @@ public unsafe partial class PathResolver
|
|||
// Actors are also not named. So use Yourself > Players > Racial > Default.
|
||||
if( !Dalamud.ClientState.IsLoggedIn )
|
||||
{
|
||||
return Penumbra.CollectionManager.ByType( CollectionType.Yourself )
|
||||
?? ( CollectionByActor( string.Empty, gameObject, out var c ) ? c : Penumbra.CollectionManager.Default );
|
||||
return new LinkedModCollection(gameObject, Penumbra.CollectionManager.ByType( CollectionType.Yourself )
|
||||
?? ( CollectionByActor( string.Empty, gameObject, out var c ) ? c : Penumbra.CollectionManager.Default ));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -163,7 +163,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 Penumbra.CollectionManager.Default;
|
||||
return new LinkedModCollection((IntPtr)gameObject, Penumbra.CollectionManager.Default);
|
||||
}
|
||||
|
||||
string? actorName = null;
|
||||
|
|
@ -174,7 +174,7 @@ public unsafe partial class PathResolver
|
|||
if( actorName.Length > 0
|
||||
&& 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();
|
||||
|
||||
// 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
|
||||
: CollectionByActor( actualName, gameObject, out c )
|
||||
? c
|
||||
: Penumbra.CollectionManager.Default;
|
||||
: Penumbra.CollectionManager.Default);
|
||||
}
|
||||
}
|
||||
catch( Exception 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.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||
using OtterGui;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
|
@ -20,7 +21,7 @@ public unsafe partial class PathResolver
|
|||
{
|
||||
private readonly PathState _paths;
|
||||
|
||||
private ModCollection? _mtrlCollection;
|
||||
private LinkedModCollection? _mtrlCollection;
|
||||
|
||||
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.
|
||||
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 )
|
||||
{
|
||||
|
|
@ -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.
|
||||
public static void HandleCollection( ModCollection collection, string path, bool nonDefault, ResourceType type, FullPath? resolved,
|
||||
out (FullPath?, object?) data )
|
||||
public static void HandleCollection( LinkedModCollection collection, string path, bool nonDefault, ResourceType type, FullPath? resolved,
|
||||
out (FullPath?, LinkedModCollection?) data )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
else
|
||||
|
|
@ -96,7 +97,12 @@ public unsafe partial class PathResolver
|
|||
#if DEBUG
|
||||
PluginLog.Verbose( "Using MtrlLoadHandler with collection {$Split:l} for path {$Path:l}.", name, path );
|
||||
#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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@ public unsafe partial class PathResolver
|
|||
var collection = GetCollection( drawObject );
|
||||
if( collection != null )
|
||||
{
|
||||
using var eqp = MetaChanger.ChangeEqp( collection );
|
||||
using var eqdp = MetaChanger.ChangeEqdp( collection );
|
||||
using var eqp = MetaChanger.ChangeEqp( collection.ModCollection );
|
||||
using var eqdp = MetaChanger.ChangeEqdp( collection.ModCollection );
|
||||
_onModelLoadCompleteHook.Original.Invoke( drawObject );
|
||||
}
|
||||
else
|
||||
|
|
@ -109,8 +109,8 @@ public unsafe partial class PathResolver
|
|||
var collection = GetCollection( drawObject );
|
||||
if( collection != null )
|
||||
{
|
||||
using var eqp = MetaChanger.ChangeEqp( collection );
|
||||
using var eqdp = MetaChanger.ChangeEqdp( collection );
|
||||
using var eqp = MetaChanger.ChangeEqp( collection.ModCollection );
|
||||
using var eqdp = MetaChanger.ChangeEqdp( collection.ModCollection );
|
||||
_updateModelsHook.Original.Invoke( drawObject );
|
||||
}
|
||||
else
|
||||
|
|
@ -217,7 +217,7 @@ public unsafe partial class PathResolver
|
|||
var collection = GetCollection( drawObject );
|
||||
if( collection != null )
|
||||
{
|
||||
return ChangeEqp( collection );
|
||||
return ChangeEqp( collection.ModCollection );
|
||||
}
|
||||
|
||||
return new MetaChanger( MetaManipulation.Type.Unknown );
|
||||
|
|
@ -231,7 +231,7 @@ public unsafe partial class PathResolver
|
|||
var collection = GetCollection( drawObject );
|
||||
if( collection != null )
|
||||
{
|
||||
return ChangeEqdp( collection );
|
||||
return ChangeEqdp( collection.ModCollection );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -249,7 +249,7 @@ public unsafe partial class PathResolver
|
|||
var collection = GetCollection( drawObject );
|
||||
if( collection != null )
|
||||
{
|
||||
collection.SetGmpFiles();
|
||||
collection.ModCollection.SetGmpFiles();
|
||||
return new MetaChanger( MetaManipulation.Type.Gmp );
|
||||
}
|
||||
|
||||
|
|
@ -261,21 +261,21 @@ public unsafe partial class PathResolver
|
|||
var collection = GetCollection( drawObject );
|
||||
if( collection != null )
|
||||
{
|
||||
collection.SetEstFiles();
|
||||
collection.ModCollection.SetEstFiles();
|
||||
return new MetaChanger( MetaManipulation.Type.Est );
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
|
@ -292,7 +292,7 @@ public unsafe partial class PathResolver
|
|||
var collection = GetCollection( drawObject );
|
||||
if( collection != null )
|
||||
{
|
||||
collection.SetCmpFiles();
|
||||
collection.ModCollection.SetCmpFiles();
|
||||
return new MetaChanger( MetaManipulation.Type.Rsp );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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, ModCollection > _pathCollections = new();
|
||||
private readonly ConcurrentDictionary< Utf8String, LinkedModCollection > _pathCollections = new();
|
||||
|
||||
public PathState( PathResolver parent )
|
||||
{
|
||||
|
|
@ -70,18 +70,18 @@ public unsafe partial class PathResolver
|
|||
public int Count
|
||||
=> _pathCollections.Count;
|
||||
|
||||
public IEnumerable< KeyValuePair< Utf8String, ModCollection > > Paths
|
||||
public IEnumerable< KeyValuePair< Utf8String, LinkedModCollection > > Paths
|
||||
=> _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 );
|
||||
|
||||
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 );
|
||||
|
||||
// Just add or remove the resolved path.
|
||||
[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 )
|
||||
{
|
||||
|
|
@ -89,20 +89,20 @@ public unsafe partial class PathResolver
|
|||
}
|
||||
|
||||
var gamePath = new Utf8String( ( byte* )path );
|
||||
SetCollection( gamePath, collection );
|
||||
SetCollection( gameObject, gamePath, collection );
|
||||
return path;
|
||||
}
|
||||
|
||||
// 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 )
|
||||
{
|
||||
_pathCollections[ path ] = collection;
|
||||
_pathCollections[ path ] = new LinkedModCollection(gameObject, collection);
|
||||
}
|
||||
else
|
||||
{
|
||||
_pathCollections[ path.Clone() ] = collection;
|
||||
_pathCollections[ path.Clone() ] = new LinkedModCollection(gameObject, collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Runtime.CompilerServices;
|
||||
using Dalamud.Hooking;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using Penumbra.Collections;
|
||||
|
||||
namespace Penumbra.Interop.Resolver;
|
||||
|
||||
|
|
@ -226,9 +227,9 @@ public partial class PathResolver
|
|||
// Implementation
|
||||
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
|
||||
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
|
||||
: collection, path );
|
||||
: collection.ModCollection, path );
|
||||
|
||||
// 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
|
||||
|
|
@ -239,20 +240,20 @@ public partial class PathResolver
|
|||
var parent = FindParent( drawObject, out var collection );
|
||||
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 parentCollection = DrawObjects.CheckParentDrawObject( drawObject, parentObject );
|
||||
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 );
|
||||
return _parent._paths.ResolvePath( parent == null
|
||||
return _parent._paths.ResolvePath( (IntPtr?)parent, parent == null
|
||||
? 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.
|
||||
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,
|
||||
// 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 );
|
||||
if( !nonDefault || collection == null )
|
||||
{
|
||||
collection = Penumbra.CollectionManager.Default;
|
||||
collection = new LinkedModCollection(Penumbra.CollectionManager.Default);
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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();
|
||||
}
|
||||
|
||||
public static unsafe (IntPtr, ModCollection) IdentifyDrawObject( IntPtr drawObject )
|
||||
public static unsafe (IntPtr, LinkedModCollection) IdentifyDrawObject( IntPtr drawObject )
|
||||
{
|
||||
var parent = FindParent( drawObject, out var collection );
|
||||
return ( ( IntPtr )parent, collection );
|
||||
|
|
@ -127,7 +127,7 @@ public partial class PathResolver : IDisposable
|
|||
=> 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 ModCollection collection )
|
||||
public static unsafe GameObject* FindParent( IntPtr drawObject, out LinkedModCollection collection )
|
||||
{
|
||||
if( DrawObjects.TryGetValue( drawObject, out var data, out var gameObject ) )
|
||||
{
|
||||
|
|
@ -146,21 +146,21 @@ public partial class PathResolver : IDisposable
|
|||
return null;
|
||||
}
|
||||
|
||||
private static unsafe ModCollection? GetCollection( IntPtr drawObject )
|
||||
private static unsafe LinkedModCollection? GetCollection( IntPtr drawObject )
|
||||
{
|
||||
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 collection.HasCache ? collection : null;
|
||||
return collection.ModCollection.HasCache ? collection : null;
|
||||
}
|
||||
|
||||
internal IEnumerable< KeyValuePair< Utf8String, ModCollection > > PathCollections
|
||||
internal IEnumerable< KeyValuePair< Utf8String, LinkedModCollection > > PathCollections
|
||||
=> _paths.Paths;
|
||||
|
||||
internal IEnumerable< KeyValuePair< IntPtr, (ModCollection, int) > > DrawObjectMap
|
||||
internal IEnumerable< KeyValuePair< IntPtr, (LinkedModCollection, int) > > DrawObjectMap
|
||||
=> DrawObjects.DrawObjects;
|
||||
|
||||
internal IEnumerable< KeyValuePair< int, global::Dalamud.Game.ClientState.Objects.Types.GameObject > > CutsceneActors
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ public partial class ConfigWindow
|
|||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted( name );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted( c.Name );
|
||||
ImGui.TextUnformatted( c.ModCollection.Name );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -195,7 +195,7 @@ public partial class ConfigWindow
|
|||
ImGui.TableNextColumn();
|
||||
ImGuiNative.igTextUnformatted( path.Path, path.Path + path.Length );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted( collection.Name );
|
||||
ImGui.TextUnformatted( collection.ModCollection.Name );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue