Rework ResolveData.

This commit is contained in:
Ottermandias 2022-09-05 13:30:07 +02:00
parent 75182d094b
commit d12a3dd152
14 changed files with 199 additions and 170 deletions

View file

@ -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

View file

@ -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)
{
}
}

View 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);
}

View file

@ -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})" );

View file

@ -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();

View file

@ -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;

View file

@ -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;
}
}
}

View file

@ -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.

View file

@ -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 );
}
}

View file

@ -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;
}
}
}

View file

@ -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 );
}

View file

@ -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 );
}
}
}

View file

@ -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 );
}

View file

@ -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