mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Rework ResolveData.
This commit is contained in:
parent
75182d094b
commit
d12a3dd152
14 changed files with 199 additions and 170 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
46
Penumbra/Collections/ResolveData.cs
Normal file
46
Penumbra/Collections/ResolveData.cs
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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})" );
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue