mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 21:24:18 +01:00
158 lines
No EOL
5.1 KiB
C#
158 lines
No EOL
5.1 KiB
C#
using System;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
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.Loader;
|
|
|
|
namespace Penumbra.Interop.Resolver;
|
|
|
|
// The Path Resolver handles character collections.
|
|
// It will hook any path resolving functions for humans,
|
|
// as well as DrawObject creation.
|
|
// It links draw objects to actors, and actors to character collections,
|
|
// to resolve paths for character collections.
|
|
public partial class PathResolver : IDisposable
|
|
{
|
|
private readonly ResourceLoader _loader;
|
|
public bool Enabled { get; private set; }
|
|
|
|
public PathResolver( ResourceLoader loader )
|
|
{
|
|
_loader = loader;
|
|
SignatureHelper.Initialise( this );
|
|
SetupHumanHooks();
|
|
SetupWeaponHooks();
|
|
SetupMonsterHooks();
|
|
SetupDemiHooks();
|
|
SetupMetaHooks();
|
|
}
|
|
|
|
// The modified resolver that handles game path resolving.
|
|
private bool CharacterResolver( Utf8GamePath gamePath, ResourceCategory _1, ResourceType type, int _2, out (FullPath?, object?) 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,
|
|
// or if it is a face decal path and the current mod collection is set.
|
|
// 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 = HandleMaterialSubFiles( type, out var collection )
|
|
|| PathCollections.TryRemove( gamePath.Path, out collection )
|
|
|| HandleAnimationFile( type, gamePath, out collection )
|
|
|| HandleDecalFile( type, gamePath, out collection );
|
|
if( !nonDefault || collection == null)
|
|
{
|
|
collection = Penumbra.CollectionManager.Default;
|
|
}
|
|
|
|
// Resolve using character/default collection first, otherwise forced, as usual.
|
|
var resolved = collection.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;
|
|
HandleMtrlCollection( collection, path, nonDefault, type, resolved, out data );
|
|
return true;
|
|
}
|
|
|
|
private bool HandleDecalFile( ResourceType type, Utf8GamePath gamePath, [NotNullWhen(true)] out ModCollection? collection )
|
|
{
|
|
if( type == ResourceType.Tex
|
|
&& _lastCreatedCollection != null
|
|
&& gamePath.Path.Substring( "chara/common/texture/".Length ).StartsWith( 'd', 'e', 'c', 'a', 'l', '_', 'f', 'a', 'c', 'e' ) )
|
|
{
|
|
collection = _lastCreatedCollection;
|
|
return true;
|
|
}
|
|
|
|
collection = null;
|
|
return false;
|
|
}
|
|
|
|
private bool HandleAnimationFile( ResourceType type, Utf8GamePath _, [NotNullWhen(true)] out ModCollection? collection )
|
|
{
|
|
if( _animationLoadCollection != null )
|
|
{
|
|
switch( type )
|
|
{
|
|
case ResourceType.Tmb:
|
|
case ResourceType.Pap:
|
|
case ResourceType.Avfx:
|
|
case ResourceType.Atex:
|
|
collection = _animationLoadCollection;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
collection = null;
|
|
return false;
|
|
}
|
|
|
|
public void Enable()
|
|
{
|
|
if( Enabled )
|
|
{
|
|
return;
|
|
}
|
|
|
|
Enabled = true;
|
|
InitializeDrawObjects();
|
|
|
|
EnableHumanHooks();
|
|
EnableWeaponHooks();
|
|
EnableMonsterHooks();
|
|
EnableDemiHooks();
|
|
EnableMtrlHooks();
|
|
EnableDataHooks();
|
|
EnableMetaHooks();
|
|
|
|
_loader.ResolvePathCustomization += CharacterResolver;
|
|
PluginLog.Debug( "Character Path Resolver enabled." );
|
|
}
|
|
|
|
public void Disable()
|
|
{
|
|
if( !Enabled )
|
|
{
|
|
return;
|
|
}
|
|
|
|
Enabled = false;
|
|
DisableHumanHooks();
|
|
DisableWeaponHooks();
|
|
DisableMonsterHooks();
|
|
DisableDemiHooks();
|
|
DisableMtrlHooks();
|
|
DisableDataHooks();
|
|
DisableMetaHooks();
|
|
|
|
DrawObjectToObject.Clear();
|
|
PathCollections.Clear();
|
|
|
|
_loader.ResolvePathCustomization -= CharacterResolver;
|
|
PluginLog.Debug( "Character Path Resolver disabled." );
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Disable();
|
|
DisposeHumanHooks();
|
|
DisposeWeaponHooks();
|
|
DisposeMonsterHooks();
|
|
DisposeDemiHooks();
|
|
DisposeMtrlHooks();
|
|
DisposeDataHooks();
|
|
DisposeMetaHooks();
|
|
}
|
|
|
|
public unsafe (IntPtr, ModCollection) IdentifyDrawObject( IntPtr drawObject )
|
|
{
|
|
var parent = FindParent( drawObject, out var collection );
|
|
return ( ( IntPtr )parent, collection );
|
|
}
|
|
} |