mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 13:14:17 +01:00
Merge branch 'xivdev:master' into master
This commit is contained in:
commit
f910211394
25 changed files with 309 additions and 54 deletions
|
|
@ -20,6 +20,7 @@ public interface IPenumbraApiBase
|
|||
|
||||
public delegate void ChangedItemHover( object? item );
|
||||
public delegate void ChangedItemClick( MouseButton button, object? item );
|
||||
public delegate void GameObjectRedrawn( IntPtr objectPtr, int objectTableIndex );
|
||||
|
||||
public enum PenumbraApiEc
|
||||
{
|
||||
|
|
@ -51,6 +52,7 @@ public interface IPenumbraApi : IPenumbraApiBase
|
|||
|
||||
// Triggered when the user clicks a listed changed object in a mod tab.
|
||||
public event ChangedItemClick? ChangedItemClicked;
|
||||
public event GameObjectRedrawn? GameObjectRedrawn;
|
||||
|
||||
// Queue redrawing of all actors of the given name with the given RedrawType.
|
||||
public void RedrawObject( string name, RedrawType setting );
|
||||
|
|
@ -72,6 +74,9 @@ public interface IPenumbraApi : IPenumbraApiBase
|
|||
// Returns the given gamePath if penumbra would not manipulate it.
|
||||
public string ResolvePath( string gamePath, string characterName );
|
||||
|
||||
// Reverse resolves a given modded local path into its replacement in form of all applicable game path for given character
|
||||
public IList<string> ReverseResolvePath( string moddedPath, string characterName );
|
||||
|
||||
// Try to load a given gamePath with the resolved path from Penumbra.
|
||||
public T? GetFile< T >( string gamePath ) where T : FileResource;
|
||||
|
||||
|
|
@ -104,12 +109,12 @@ public interface IPenumbraApi : IPenumbraApiBase
|
|||
|
||||
// Obtain the potential settings of a mod specified by its directory name first or mod name second.
|
||||
// Returns null if the mod could not be found.
|
||||
public Dictionary< string, (string[], SelectType) >? GetAvailableModSettings( string modDirectory, string modName );
|
||||
public IDictionary< string, (IList<string>, SelectType) >? GetAvailableModSettings( string modDirectory, string modName );
|
||||
|
||||
// Obtain the enabled state, the priority, the settings of a mod specified by its directory name first or mod name second,
|
||||
// and whether these settings are inherited, or null if the collection does not set them at all.
|
||||
// If allowInheritance is false, only the collection itself will be checked.
|
||||
public (PenumbraApiEc, (bool, int, Dictionary< string, string[] >, bool)?) GetCurrentModSettings( string collectionName,
|
||||
public (PenumbraApiEc, (bool, int, IDictionary< string, IList<string> >, bool)?) GetCurrentModSettings( string collectionName,
|
||||
string modDirectory, string modName, bool allowInheritance );
|
||||
|
||||
// Try to set the inheritance state in the given collection of a mod specified by its directory name first or mod name second.
|
||||
|
|
@ -131,7 +136,7 @@ public interface IPenumbraApi : IPenumbraApiBase
|
|||
public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName, string option );
|
||||
|
||||
public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName,
|
||||
string[] options );
|
||||
IReadOnlyList<string> options );
|
||||
|
||||
|
||||
// Create a temporary collection without actual settings but with a cache.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
public int ApiVersion { get; } = 4;
|
||||
private Penumbra? _penumbra;
|
||||
private Lumina.GameData? _lumina;
|
||||
public event GameObjectRedrawn? GameObjectRedrawn;
|
||||
|
||||
public bool Valid
|
||||
=> _penumbra != null;
|
||||
|
|
@ -30,10 +31,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
_lumina = ( Lumina.GameData? )Dalamud.GameData.GetType()
|
||||
.GetField( "gameData", BindingFlags.Instance | BindingFlags.NonPublic )
|
||||
?.GetValue( Dalamud.GameData );
|
||||
_penumbra.ObjectReloader.GameObjectRedrawn += OnGameObjectRedrawn;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_penumbra!.ObjectReloader.GameObjectRedrawn -= OnGameObjectRedrawn;
|
||||
_penumbra = null;
|
||||
_lumina = null;
|
||||
}
|
||||
|
|
@ -90,13 +93,18 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
_penumbra!.ObjectReloader.RedrawObject( gameObject, setting );
|
||||
}
|
||||
|
||||
private void OnGameObjectRedrawn( IntPtr objectAddress, int objectTableIndex )
|
||||
{
|
||||
GameObjectRedrawn?.Invoke( objectAddress, objectTableIndex );
|
||||
}
|
||||
|
||||
public void RedrawAll( RedrawType setting )
|
||||
{
|
||||
CheckInitialized();
|
||||
_penumbra!.ObjectReloader.RedrawAll( setting );
|
||||
}
|
||||
|
||||
private static string ResolvePath( string path, Mods.Mod.Manager _, ModCollection collection )
|
||||
private static string ResolvePath( string path, Mod.Manager _, ModCollection collection )
|
||||
{
|
||||
if( !Penumbra.Config.EnableMods )
|
||||
{
|
||||
|
|
@ -121,6 +129,18 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
Penumbra.CollectionManager.Character( characterName ) );
|
||||
}
|
||||
|
||||
public IList< string > ReverseResolvePath( string path, string characterName )
|
||||
{
|
||||
CheckInitialized();
|
||||
if( !Penumbra.Config.EnableMods )
|
||||
{
|
||||
return new[] { path };
|
||||
}
|
||||
|
||||
var ret = Penumbra.CollectionManager.Character( characterName ).ReverseResolvePath( new FullPath( path ) );
|
||||
return ret.Select( r => r.ToString() ).ToList();
|
||||
}
|
||||
|
||||
private T? GetFileIntern< T >( string resolvedPath ) where T : FileResource
|
||||
{
|
||||
CheckInitialized();
|
||||
|
|
@ -210,10 +230,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
return Penumbra.ModManager.Select( m => ( m.ModPath.Name, m.Name.Text ) ).ToArray();
|
||||
}
|
||||
|
||||
public Dictionary< string, (string[], SelectType) >? GetAvailableModSettings( string modDirectory, string modName )
|
||||
public IDictionary< string, (IList< string >, SelectType) >? GetAvailableModSettings( string modDirectory, string modName )
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public (PenumbraApiEc, (bool, int, Dictionary< string, string[] >, bool)?) GetCurrentModSettings( string collectionName, string modDirectory, string modName,
|
||||
public (PenumbraApiEc, (bool, int, IDictionary< string, IList< string > >, bool)?) GetCurrentModSettings( string collectionName,
|
||||
string modDirectory, string modName,
|
||||
bool allowInheritance )
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
|
|
@ -229,7 +250,8 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName, string option )
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName, string[] options )
|
||||
public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName,
|
||||
IReadOnlyList< string > options )
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public PenumbraApiEc CreateTemporaryCollection( string collectionName, string? character, bool forceOverwriteCharacter )
|
||||
|
|
|
|||
|
|
@ -115,11 +115,13 @@ public partial class PenumbraIpc
|
|||
public const string LabelProviderRedrawIndex = "Penumbra.RedrawObjectByIndex";
|
||||
public const string LabelProviderRedrawObject = "Penumbra.RedrawObject";
|
||||
public const string LabelProviderRedrawAll = "Penumbra.RedrawAll";
|
||||
public const string LabelProviderGameObjectRedrawn = "Penumbra.GameObjectRedrawn";
|
||||
|
||||
internal ICallGateProvider< string, int, object >? ProviderRedrawName;
|
||||
internal ICallGateProvider< int, int, object >? ProviderRedrawIndex;
|
||||
internal ICallGateProvider< GameObject, int, object >? ProviderRedrawObject;
|
||||
internal ICallGateProvider< int, object >? ProviderRedrawAll;
|
||||
internal ICallGateProvider< IntPtr, int, object? >? ProviderGameObjectRedrawn;
|
||||
|
||||
private static RedrawType CheckRedrawType( int value )
|
||||
{
|
||||
|
|
@ -146,7 +148,7 @@ public partial class PenumbraIpc
|
|||
|
||||
try
|
||||
{
|
||||
ProviderRedrawIndex = pi.GetIpcProvider<int, int, object>( LabelProviderRedrawIndex );
|
||||
ProviderRedrawIndex = pi.GetIpcProvider< int, int, object >( LabelProviderRedrawIndex );
|
||||
ProviderRedrawIndex.RegisterAction( ( idx, i ) => Api.RedrawObject( idx, CheckRedrawType( i ) ) );
|
||||
}
|
||||
catch( Exception e )
|
||||
|
|
@ -173,7 +175,20 @@ public partial class PenumbraIpc
|
|||
{
|
||||
PluginLog.Error( $"Error registering IPC provider for {LabelProviderRedrawAll}:\n{e}" );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderGameObjectRedrawn = pi.GetIpcProvider< IntPtr, int, object? >( LabelProviderGameObjectRedrawn );
|
||||
Api.GameObjectRedrawn += OnGameObjectRedrawn;
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Error registering IPC provider for {LabelProviderGameObjectRedrawn}:\n{e}" );
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGameObjectRedrawn( IntPtr objectAddress, int objectTableIndex )
|
||||
=> ProviderGameObjectRedrawn?.SendMessage( objectAddress, objectTableIndex );
|
||||
|
||||
private void DisposeRedrawProviders()
|
||||
{
|
||||
|
|
@ -181,6 +196,7 @@ public partial class PenumbraIpc
|
|||
ProviderRedrawIndex?.UnregisterAction();
|
||||
ProviderRedrawObject?.UnregisterAction();
|
||||
ProviderRedrawAll?.UnregisterAction();
|
||||
Api.GameObjectRedrawn -= OnGameObjectRedrawn;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -189,10 +205,12 @@ public partial class PenumbraIpc
|
|||
public const string LabelProviderResolveDefault = "Penumbra.ResolveDefaultPath";
|
||||
public const string LabelProviderResolveCharacter = "Penumbra.ResolveCharacterPath";
|
||||
public const string LabelProviderGetDrawObjectInfo = "Penumbra.GetDrawObjectInfo";
|
||||
public const string LabelProviderReverseResolvePath = "Penumbra.ReverseResolvePath";
|
||||
|
||||
internal ICallGateProvider< string, string >? ProviderResolveDefault;
|
||||
internal ICallGateProvider< string, string, string >? ProviderResolveCharacter;
|
||||
internal ICallGateProvider< IntPtr, (IntPtr, string) >? ProviderGetDrawObjectInfo;
|
||||
internal ICallGateProvider< string, string, IList< string > >? ProviderReverseResolvePath;
|
||||
|
||||
private void InitializeResolveProviders( DalamudPluginInterface pi )
|
||||
{
|
||||
|
|
@ -225,6 +243,16 @@ public partial class PenumbraIpc
|
|||
{
|
||||
PluginLog.Error( $"Error registering IPC provider for {LabelProviderGetDrawObjectInfo}:\n{e}" );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderReverseResolvePath = pi.GetIpcProvider< string, string, IList< string > >( LabelProviderReverseResolvePath );
|
||||
ProviderReverseResolvePath.RegisterFunc( Api.ReverseResolvePath );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Error registering IPC provider for {LabelProviderGetDrawObjectInfo}:\n{e}" );
|
||||
}
|
||||
}
|
||||
|
||||
private void DisposeResolveProviders()
|
||||
|
|
@ -232,6 +260,7 @@ public partial class PenumbraIpc
|
|||
ProviderGetDrawObjectInfo?.UnregisterFunc();
|
||||
ProviderResolveDefault?.UnregisterFunc();
|
||||
ProviderResolveCharacter?.UnregisterFunc();
|
||||
ProviderReverseResolvePath?.UnregisterFunc();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -356,7 +385,7 @@ public partial class PenumbraIpc
|
|||
|
||||
try
|
||||
{
|
||||
ProviderCharacterCollectionName = pi.GetIpcProvider< string, ( string, bool) >( LabelProviderCharacterCollectionName );
|
||||
ProviderCharacterCollectionName = pi.GetIpcProvider< string, (string, bool) >( LabelProviderCharacterCollectionName );
|
||||
ProviderCharacterCollectionName.RegisterFunc( Api.GetCharacterCollection );
|
||||
}
|
||||
catch( Exception e )
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ public partial class ModCollection
|
|||
// The collection currently selected for changing settings.
|
||||
public ModCollection Current { get; private set; } = Empty;
|
||||
|
||||
// The collection currently selected is in use either as an active collection or through inheritance.
|
||||
public bool CurrentCollectionInUse { get; private set; }
|
||||
|
||||
// The collection used for general file redirections and all characters not specifically named.
|
||||
public ModCollection Default { get; private set; } = Empty;
|
||||
|
||||
|
|
@ -78,6 +81,8 @@ public partial class ModCollection
|
|||
break;
|
||||
}
|
||||
|
||||
CurrentCollectionInUse = Characters.Values.Prepend( Default ).SelectMany( c => c.GetFlattenedInheritance() ).Contains( Current );
|
||||
|
||||
CollectionChanged.Invoke( type, this[ oldCollectionIdx ], newCollection, characterName );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ public partial class ModCollection
|
|||
PluginLog.Verbose( "Cleared cache of collection {Name:l}.", Name );
|
||||
}
|
||||
|
||||
public IEnumerable< Utf8GamePath > ReverseResolvePath( FullPath path )
|
||||
=> _cache?.ReverseResolvePath( path ) ?? Array.Empty< Utf8GamePath >();
|
||||
|
||||
public FullPath? ResolvePath( Utf8GamePath path )
|
||||
=> _cache?.ResolvePath( path );
|
||||
|
|
|
|||
|
|
@ -79,6 +79,28 @@ public partial class ModCollection
|
|||
return candidate.Path;
|
||||
}
|
||||
|
||||
// For a given full path, find all game paths that currently use this file.
|
||||
public IEnumerable< Utf8GamePath > ReverseResolvePath( FullPath localFilePath )
|
||||
{
|
||||
var needle = localFilePath.FullName.ToLower();
|
||||
if( localFilePath.IsRooted )
|
||||
{
|
||||
needle = needle.Replace( '/', '\\' );
|
||||
}
|
||||
|
||||
var iterator = ResolvedFiles
|
||||
.Where( f => string.Equals( f.Value.Path.FullName, needle, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
.Select( kvp => kvp.Key );
|
||||
|
||||
// For files that are not rooted, try to add themselves.
|
||||
if( !localFilePath.IsRooted && Utf8GamePath.FromString( localFilePath.FullName, out var utf8 ) )
|
||||
{
|
||||
iterator = iterator.Prepend( utf8 );
|
||||
}
|
||||
|
||||
return iterator;
|
||||
}
|
||||
|
||||
private void OnModSettingChange( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool _ )
|
||||
{
|
||||
switch( type )
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ public partial class Configuration : IPluginConfiguration
|
|||
public bool UseCharacterCollectionInTryOn { get; set; } = true;
|
||||
public bool UseOwnerNameForCharacterCollection { get; set; } = true;
|
||||
public bool PreferNamedCollectionsOverOwners { get; set; } = true;
|
||||
public bool UseDefaultCollectionForRetainers { get; set; } = false;
|
||||
|
||||
#if DEBUG
|
||||
public bool DebugMode { get; set; } = true;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ public unsafe partial class ObjectReloader
|
|||
private static void EnableDraw( GameObject actor )
|
||||
=> ( ( delegate* unmanaged< IntPtr, void >** )actor.Address )[ 0 ][ 16 ]( actor.Address );
|
||||
|
||||
|
||||
// Check whether we currently are in GPose.
|
||||
// Also clear the name list.
|
||||
private void SetGPose()
|
||||
|
|
@ -106,6 +105,8 @@ public sealed unsafe partial class ObjectReloader : IDisposable
|
|||
private readonly List< int > _afterGPoseQueue = new(GPoseSlots);
|
||||
private int _target = -1;
|
||||
|
||||
public event Action< IntPtr, int >? GameObjectRedrawn;
|
||||
|
||||
public ObjectReloader()
|
||||
=> Dalamud.Framework.Update += OnUpdateEvent;
|
||||
|
||||
|
|
@ -133,7 +134,7 @@ public sealed unsafe partial class ObjectReloader : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private static void WriteVisible( GameObject? actor )
|
||||
private void WriteVisible( GameObject? actor )
|
||||
{
|
||||
if( BadRedrawIndices( actor, out var tableIndex ) )
|
||||
{
|
||||
|
|
@ -141,11 +142,12 @@ public sealed unsafe partial class ObjectReloader : IDisposable
|
|||
}
|
||||
|
||||
*ActorDrawState( actor! ) &= ~DrawState.Invisibility;
|
||||
|
||||
if( IsGPoseActor( tableIndex ) )
|
||||
{
|
||||
EnableDraw( actor! );
|
||||
}
|
||||
|
||||
GameObjectRedrawn?.Invoke( actor!.Address, tableIndex );
|
||||
}
|
||||
|
||||
private void ReloadActor( GameObject? actor )
|
||||
|
|
|
|||
|
|
@ -271,6 +271,10 @@ public unsafe partial class PathResolver
|
|||
return Penumbra.CollectionManager.Default;
|
||||
}
|
||||
|
||||
// Housing Retainers
|
||||
if( Penumbra.Config.UseDefaultCollectionForRetainers && gameObject->ObjectKind == (byte) ObjectKind.EventNpc && gameObject->DataID == 1011832 )
|
||||
return Penumbra.CollectionManager.Default;
|
||||
|
||||
string? actorName = null;
|
||||
if( Penumbra.Config.PreferNamedCollectionsOverOwners )
|
||||
{
|
||||
|
|
|
|||
88
Penumbra/Interop/Resolver/PathResolver.Demihuman.cs
Normal file
88
Penumbra/Interop/Resolver/PathResolver.Demihuman.cs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
using System;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Utility.Signatures;
|
||||
|
||||
namespace Penumbra.Interop.Resolver;
|
||||
|
||||
public unsafe partial class PathResolver
|
||||
{
|
||||
[Signature( "48 8D 05 ?? ?? ?? ?? 45 33 C0 48 89 03 BA", ScanType = ScanType.StaticAddress )]
|
||||
public IntPtr* DrawObjectDemiVTable;
|
||||
|
||||
public Hook< GeneralResolveDelegate >? ResolveDemiDecalPathHook;
|
||||
public Hook< EidResolveDelegate >? ResolveDemiEidPathHook;
|
||||
public Hook< GeneralResolveDelegate >? ResolveDemiImcPathHook;
|
||||
public Hook< MPapResolveDelegate >? ResolveDemiMPapPathHook;
|
||||
public Hook< GeneralResolveDelegate >? ResolveDemiMdlPathHook;
|
||||
public Hook< MaterialResolveDetour >? ResolveDemiMtrlPathHook;
|
||||
public Hook< MaterialResolveDetour >? ResolveDemiPapPathHook;
|
||||
public Hook< GeneralResolveDelegate >? ResolveDemiPhybPathHook;
|
||||
public Hook< GeneralResolveDelegate >? ResolveDemiSklbPathHook;
|
||||
public Hook< GeneralResolveDelegate >? ResolveDemiSkpPathHook;
|
||||
public Hook< EidResolveDelegate >? ResolveDemiTmbPathHook;
|
||||
public Hook< MaterialResolveDetour >? ResolveDemiVfxPathHook;
|
||||
|
||||
private void SetupDemiHooks()
|
||||
{
|
||||
ResolveDemiDecalPathHook = new Hook< GeneralResolveDelegate >( DrawObjectDemiVTable[ ResolveDecalIdx ], ResolveDemiDecalDetour );
|
||||
ResolveDemiEidPathHook = new Hook< EidResolveDelegate >( DrawObjectDemiVTable[ ResolveEidIdx ], ResolveDemiEidDetour );
|
||||
ResolveDemiImcPathHook = new Hook< GeneralResolveDelegate >( DrawObjectDemiVTable[ ResolveImcIdx ], ResolveDemiImcDetour );
|
||||
ResolveDemiMPapPathHook = new Hook< MPapResolveDelegate >( DrawObjectDemiVTable[ ResolveMPapIdx ], ResolveDemiMPapDetour );
|
||||
ResolveDemiMdlPathHook = new Hook< GeneralResolveDelegate >( DrawObjectDemiVTable[ ResolveMdlIdx ], ResolveDemiMdlDetour );
|
||||
ResolveDemiMtrlPathHook = new Hook< MaterialResolveDetour >( DrawObjectDemiVTable[ ResolveMtrlIdx ], ResolveDemiMtrlDetour );
|
||||
ResolveDemiPapPathHook = new Hook< MaterialResolveDetour >( DrawObjectDemiVTable[ ResolvePapIdx ], ResolveDemiPapDetour );
|
||||
ResolveDemiPhybPathHook = new Hook< GeneralResolveDelegate >( DrawObjectDemiVTable[ ResolvePhybIdx ], ResolveDemiPhybDetour );
|
||||
ResolveDemiSklbPathHook = new Hook< GeneralResolveDelegate >( DrawObjectDemiVTable[ ResolveSklbIdx ], ResolveDemiSklbDetour );
|
||||
ResolveDemiSkpPathHook = new Hook< GeneralResolveDelegate >( DrawObjectDemiVTable[ ResolveSkpIdx ], ResolveDemiSkpDetour );
|
||||
ResolveDemiTmbPathHook = new Hook< EidResolveDelegate >( DrawObjectDemiVTable[ ResolveTmbIdx ], ResolveDemiTmbDetour );
|
||||
ResolveDemiVfxPathHook = new Hook< MaterialResolveDetour >( DrawObjectDemiVTable[ ResolveVfxIdx ], ResolveDemiVfxDetour );
|
||||
}
|
||||
|
||||
private void EnableDemiHooks()
|
||||
{
|
||||
ResolveDemiDecalPathHook?.Enable();
|
||||
ResolveDemiEidPathHook?.Enable();
|
||||
ResolveDemiImcPathHook?.Enable();
|
||||
ResolveDemiMPapPathHook?.Enable();
|
||||
ResolveDemiMdlPathHook?.Enable();
|
||||
ResolveDemiMtrlPathHook?.Enable();
|
||||
ResolveDemiPapPathHook?.Enable();
|
||||
ResolveDemiPhybPathHook?.Enable();
|
||||
ResolveDemiSklbPathHook?.Enable();
|
||||
ResolveDemiSkpPathHook?.Enable();
|
||||
ResolveDemiTmbPathHook?.Enable();
|
||||
ResolveDemiVfxPathHook?.Enable();
|
||||
}
|
||||
|
||||
private void DisableDemiHooks()
|
||||
{
|
||||
ResolveDemiDecalPathHook?.Disable();
|
||||
ResolveDemiEidPathHook?.Disable();
|
||||
ResolveDemiImcPathHook?.Disable();
|
||||
ResolveDemiMPapPathHook?.Disable();
|
||||
ResolveDemiMdlPathHook?.Disable();
|
||||
ResolveDemiMtrlPathHook?.Disable();
|
||||
ResolveDemiPapPathHook?.Disable();
|
||||
ResolveDemiPhybPathHook?.Disable();
|
||||
ResolveDemiSklbPathHook?.Disable();
|
||||
ResolveDemiSkpPathHook?.Disable();
|
||||
ResolveDemiTmbPathHook?.Disable();
|
||||
ResolveDemiVfxPathHook?.Disable();
|
||||
}
|
||||
|
||||
private void DisposeDemiHooks()
|
||||
{
|
||||
ResolveDemiDecalPathHook?.Dispose();
|
||||
ResolveDemiEidPathHook?.Dispose();
|
||||
ResolveDemiImcPathHook?.Dispose();
|
||||
ResolveDemiMPapPathHook?.Dispose();
|
||||
ResolveDemiMdlPathHook?.Dispose();
|
||||
ResolveDemiMtrlPathHook?.Dispose();
|
||||
ResolveDemiPapPathHook?.Dispose();
|
||||
ResolveDemiPhybPathHook?.Dispose();
|
||||
ResolveDemiSklbPathHook?.Dispose();
|
||||
ResolveDemiSkpPathHook?.Dispose();
|
||||
ResolveDemiTmbPathHook?.Dispose();
|
||||
ResolveDemiVfxPathHook?.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -14,12 +14,6 @@ public unsafe partial class PathResolver
|
|||
// [Signature( "48 8D 1D ?? ?? ?? ?? 48 C7 41", ScanType = ScanType.StaticAddress )]
|
||||
// public IntPtr* DrawObjectVTable;
|
||||
//
|
||||
// [Signature( "48 8D 05 ?? ?? ?? ?? 45 33 C0 48 89 03 BA", ScanType = ScanType.StaticAddress )]
|
||||
// public IntPtr* DrawObjectDemihumanVTable;
|
||||
//
|
||||
// [Signature( "48 8D 05 ?? ?? ?? ?? 48 89 03 33 C0 48 89 83 ?? ?? ?? ?? 48 89 83 ?? ?? ?? ?? C7 83", ScanType = ScanType.StaticAddress )]
|
||||
// public IntPtr* DrawObjectMonsterVTable;
|
||||
//
|
||||
// public const int ResolveRootIdx = 71;
|
||||
|
||||
public const int ResolveSklbIdx = 72;
|
||||
|
|
|
|||
|
|
@ -136,6 +136,43 @@ public unsafe partial class PathResolver
|
|||
private IntPtr ResolveMonsterVfxDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4, ulong unk5 )
|
||||
=> ResolvePathDetour( drawObject, ResolveMonsterVfxPathHook!.Original( drawObject, path, unk3, unk4, unk5 ) );
|
||||
|
||||
// Demihumans
|
||||
private IntPtr ResolveDemiDecalDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4 )
|
||||
=> ResolvePathDetour( drawObject, ResolveDemiDecalPathHook!.Original( drawObject, path, unk3, unk4 ) );
|
||||
|
||||
private IntPtr ResolveDemiEidDetour( IntPtr drawObject, IntPtr path, IntPtr unk3 )
|
||||
=> ResolvePathDetour( drawObject, ResolveDemiEidPathHook!.Original( drawObject, path, unk3 ) );
|
||||
|
||||
private IntPtr ResolveDemiImcDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4 )
|
||||
=> ResolvePathDetour( drawObject, ResolveDemiImcPathHook!.Original( drawObject, path, unk3, unk4 ) );
|
||||
|
||||
private IntPtr ResolveDemiMPapDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4, uint unk5 )
|
||||
=> ResolvePathDetour( drawObject, ResolveDemiMPapPathHook!.Original( drawObject, path, unk3, unk4, unk5 ) );
|
||||
|
||||
private IntPtr ResolveDemiMdlDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint modelType )
|
||||
=> ResolvePathDetour( drawObject, ResolveDemiMdlPathHook!.Original( drawObject, path, unk3, modelType ) );
|
||||
|
||||
private IntPtr ResolveDemiMtrlDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4, ulong unk5 )
|
||||
=> ResolvePathDetour( drawObject, ResolveDemiMtrlPathHook!.Original( drawObject, path, unk3, unk4, unk5 ) );
|
||||
|
||||
private IntPtr ResolveDemiPapDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4, ulong unk5 )
|
||||
=> ResolvePathDetour( drawObject, ResolveDemiPapPathHook!.Original( drawObject, path, unk3, unk4, unk5 ) );
|
||||
|
||||
private IntPtr ResolveDemiPhybDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4 )
|
||||
=> ResolvePathDetour( drawObject, ResolveDemiPhybPathHook!.Original( drawObject, path, unk3, unk4 ) );
|
||||
|
||||
private IntPtr ResolveDemiSklbDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4 )
|
||||
=> ResolvePathDetour( drawObject, ResolveDemiSklbPathHook!.Original( drawObject, path, unk3, unk4 ) );
|
||||
|
||||
private IntPtr ResolveDemiSkpDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4 )
|
||||
=> ResolvePathDetour( drawObject, ResolveDemiSkpPathHook!.Original( drawObject, path, unk3, unk4 ) );
|
||||
|
||||
private IntPtr ResolveDemiTmbDetour( IntPtr drawObject, IntPtr path, IntPtr unk3 )
|
||||
=> ResolvePathDetour( drawObject, ResolveDemiTmbPathHook!.Original( drawObject, path, unk3 ) );
|
||||
|
||||
private IntPtr ResolveDemiVfxDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4, ulong unk5 )
|
||||
=> ResolvePathDetour( drawObject, ResolveDemiVfxPathHook!.Original( drawObject, path, unk3, unk4, unk5 ) );
|
||||
|
||||
|
||||
// Implementation
|
||||
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ public partial class PathResolver : IDisposable
|
|||
SetupHumanHooks();
|
||||
SetupWeaponHooks();
|
||||
SetupMonsterHooks();
|
||||
SetupDemiHooks();
|
||||
SetupMetaHooks();
|
||||
}
|
||||
|
||||
|
|
@ -105,6 +106,7 @@ public partial class PathResolver : IDisposable
|
|||
EnableHumanHooks();
|
||||
EnableWeaponHooks();
|
||||
EnableMonsterHooks();
|
||||
EnableDemiHooks();
|
||||
EnableMtrlHooks();
|
||||
EnableDataHooks();
|
||||
EnableMetaHooks();
|
||||
|
|
@ -124,6 +126,7 @@ public partial class PathResolver : IDisposable
|
|||
DisableHumanHooks();
|
||||
DisableWeaponHooks();
|
||||
DisableMonsterHooks();
|
||||
DisableDemiHooks();
|
||||
DisableMtrlHooks();
|
||||
DisableDataHooks();
|
||||
DisableMetaHooks();
|
||||
|
|
@ -141,6 +144,7 @@ public partial class PathResolver : IDisposable
|
|||
DisposeHumanHooks();
|
||||
DisposeWeaponHooks();
|
||||
DisposeMonsterHooks();
|
||||
DisposeDemiHooks();
|
||||
DisposeMtrlHooks();
|
||||
DisposeDataHooks();
|
||||
DisposeMetaHooks();
|
||||
|
|
|
|||
|
|
@ -152,6 +152,10 @@ public class Penumbra : IDisposable
|
|||
{
|
||||
PluginLog.Error( $"{ImcExceptions} IMC Exceptions thrown. Please repair your game files." );
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginLog.Information( $"Penumbra Version {Version}, Commit #{CommitHash} successfully Loaded." );
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupInterface( out ConfigWindow cfg, out LaunchButton btn, out WindowSystem system )
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public partial class ModEditWindow
|
|||
private int _offsetX = 0;
|
||||
private int _offsetY = 0;
|
||||
|
||||
private readonly FileDialogManager _dialogManager = new();
|
||||
private readonly FileDialogManager _dialogManager = ConfigWindow.SetupFileManager();
|
||||
|
||||
private static bool DragFloat( string label, float width, ref float value )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace Penumbra.UI.Classes;
|
|||
|
||||
public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, ModFileSystemSelector.ModState >
|
||||
{
|
||||
private readonly FileDialogManager _fileManager = new();
|
||||
private readonly FileDialogManager _fileManager = ConfigWindow.SetupFileManager();
|
||||
private TexToolsImporter? _import;
|
||||
public ModSettings SelectedSettings { get; private set; } = ModSettings.Empty;
|
||||
public ModCollection SelectedSettingCollection { get; private set; } = ModCollection.Empty;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ public partial class ConfigWindow
|
|||
ImGui.TableSetupColumn( "mods", flags, varWidth - 100 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.TableSetupColumn( "id", flags, 100 * ImGuiHelpers.GlobalScale );
|
||||
|
||||
var items = Penumbra.CollectionManager.Default.ChangedItems;
|
||||
var items = Penumbra.CollectionManager.Current.ChangedItems;
|
||||
var rest = _changedItemFilter.IsEmpty && _changedItemModFilter.IsEmpty
|
||||
? ImGuiClip.ClippedDraw( items, skips, DrawChangedItemColumn, items.Count )
|
||||
: ImGuiClip.FilteredClippedDraw( items, skips, FilterChangedItem, DrawChangedItemColumn );
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public partial class ConfigWindow
|
|||
ImGui.TableSetupColumn( string.Empty, ImGuiTableColumnFlags.WidthFixed, _effectiveArrowLength );
|
||||
ImGui.TableSetupColumn( "##file", ImGuiTableColumnFlags.WidthFixed, _effectiveRightTextLength );
|
||||
|
||||
DrawEffectiveRows( Penumbra.CollectionManager.Default, skips, height,
|
||||
DrawEffectiveRows( Penumbra.CollectionManager.Current, skips, height,
|
||||
_effectiveFilePathFilter.Length > 0 || _effectiveGamePathFilter.Length > 0 );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using ImGuiNET;
|
||||
using Lumina.Data.Parsing;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
|
@ -11,6 +13,7 @@ using Penumbra.GameData.ByteString;
|
|||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.UI.Classes;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.UI;
|
||||
|
||||
|
|
@ -102,11 +105,8 @@ public partial class ConfigWindow
|
|||
};
|
||||
|
||||
using var combo = ImRaii.Combo( label, current.Name );
|
||||
if( !combo )
|
||||
if( combo )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach( var collection in Penumbra.CollectionManager.GetEnumeratorWithEmpty().Skip( withEmpty ? 0 : 1 ).OrderBy( c => c.Name ) )
|
||||
{
|
||||
using var id = ImRaii.PushId( collection.Index );
|
||||
|
|
@ -116,4 +116,33 @@ public partial class ConfigWindow
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set up the file selector with the right flags and custom side bar items.
|
||||
public static FileDialogManager SetupFileManager()
|
||||
{
|
||||
var fileManager = new FileDialogManager
|
||||
{
|
||||
AddedWindowFlags = ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoDocking,
|
||||
};
|
||||
|
||||
if( Functions.GetDownloadsFolder( out var downloadsFolder ) )
|
||||
{
|
||||
fileManager.CustomSideBarItems.Add( ("Downloads", downloadsFolder, FontAwesomeIcon.Download, -1) );
|
||||
}
|
||||
|
||||
if( Functions.GetQuickAccessFolders( out var folders ) )
|
||||
{
|
||||
foreach( var ((name, path), idx) in folders.WithIndex() )
|
||||
{
|
||||
fileManager.CustomSideBarItems.Add( ($"{name}##{idx}", path, FontAwesomeIcon.Folder, -1) );
|
||||
}
|
||||
}
|
||||
|
||||
// Remove Videos and Music.
|
||||
fileManager.CustomSideBarItems.Add( ("Videos", string.Empty, 0, -1) );
|
||||
fileManager.CustomSideBarItems.Add( ("Music", string.Empty, 0, -1) );
|
||||
|
||||
return fileManager;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,17 +12,12 @@ namespace Penumbra.UI;
|
|||
|
||||
public partial class ConfigWindow
|
||||
{
|
||||
private partial class ModPanel : IDisposable
|
||||
private partial class ModPanel
|
||||
{
|
||||
// We use a big, nice game font for the title.
|
||||
private readonly GameFontHandle _nameFont =
|
||||
Dalamud.PluginInterface.UiBuilder.GetGameFontHandle( new GameFontStyle( GameFontFamilyAndSize.Jupiter23 ) );
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_nameFont.Dispose();
|
||||
}
|
||||
|
||||
// Header data.
|
||||
private string _modName = string.Empty;
|
||||
private string _modAuthor = string.Empty;
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ public partial class ConfigWindow
|
|||
DrawChangedItemsTab();
|
||||
DrawConflictsTab();
|
||||
DrawEditModTab();
|
||||
if( ImGui.TabItemButton( "Advanced Editing", ImGuiTabItemFlags.Trailing | ImGuiTabItemFlags.NoTooltip ) )
|
||||
if( Penumbra.Config.ShowAdvanced && ImGui.TabItemButton( "Advanced Editing", ImGuiTabItemFlags.Trailing | ImGuiTabItemFlags.NoTooltip ) )
|
||||
{
|
||||
_window.ModEditPopup.ChangeMod( _mod );
|
||||
_window.ModEditPopup.ChangeOption( -1, 0 );
|
||||
|
|
|
|||
|
|
@ -63,6 +63,10 @@ public partial class ConfigWindow
|
|||
DrawInheritedCollectionButton( 3 * buttonSize );
|
||||
ImGui.SameLine();
|
||||
DrawCollectionSelector( "##collectionSelector", 2 * buttonSize.X, ModCollection.Type.Current, false, null );
|
||||
if( !Penumbra.CollectionManager.CurrentCollectionInUse )
|
||||
{
|
||||
ImGuiUtil.DrawTextButton( "The currently selected collection is not used in any way.", -Vector2.UnitX, Colors.PressEnterWarningBg );
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawDefaultCollectionButton( Vector2 width )
|
||||
|
|
@ -112,7 +116,7 @@ public partial class ConfigWindow
|
|||
|
||||
// The basic setup for the mod panel.
|
||||
// Details are in other files.
|
||||
private partial class ModPanel
|
||||
private partial class ModPanel : IDisposable
|
||||
{
|
||||
private readonly ConfigWindow _window;
|
||||
|
||||
|
|
@ -123,6 +127,11 @@ public partial class ConfigWindow
|
|||
public ModPanel( ConfigWindow window )
|
||||
=> _window = window;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_nameFont.Dispose();
|
||||
}
|
||||
|
||||
public void Draw( ModFileSystemSelector selector )
|
||||
{
|
||||
Init( selector );
|
||||
|
|
|
|||
|
|
@ -75,6 +75,10 @@ public partial class ConfigWindow
|
|||
"If you have a character collection set to a specific name for a companion or combat pet, prefer this collection over the owner's collection.\n"
|
||||
+ "That is, if you have a 'Topaz Carbuncle' collection, it will use this one instead of the one for its owner.",
|
||||
Penumbra.Config.PreferNamedCollectionsOverOwners, v => Penumbra.Config.PreferNamedCollectionsOverOwners = v );
|
||||
Checkbox( "Use Default Collection for Housing Retainers",
|
||||
"Housing Retainers use the name of their owner instead of their own, you can decide to let them use their owners character collection or the default collection.\n"
|
||||
+ "It is not possible to make them have their own collection, since they have no connection to their actual name.",
|
||||
Penumbra.Config.UseDefaultCollectionForRetainers, v => Penumbra.Config.UseDefaultCollectionForRetainers = v );
|
||||
ImGui.Dummy( _window._defaultSpace );
|
||||
DrawFolderSortType();
|
||||
DrawAbsoluteSizeSelector();
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public partial class ConfigWindow
|
|||
|
||||
// Changing the base mod directory.
|
||||
private string? _newModDirectory;
|
||||
private readonly FileDialogManager _dialogManager = new();
|
||||
private readonly FileDialogManager _dialogManager = SetupFileManager();
|
||||
private bool _dialogOpen; // For toggling on/off.
|
||||
|
||||
// Do not change the directory without explicitly pressing enter or this button.
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ public sealed partial class ConfigWindow : Window, IDisposable
|
|||
_effectiveTab = new EffectiveTab();
|
||||
_debugTab = new DebugTab( this );
|
||||
_resourceTab = new ResourceTab( this );
|
||||
Flags |= ImGuiWindowFlags.NoDocking;
|
||||
if( Penumbra.Config.FixMainWindow )
|
||||
{
|
||||
Flags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue