Merge branch 'xivdev:master' into master

This commit is contained in:
rootdarkarchon 2022-06-18 13:11:52 +02:00 committed by GitHub
commit f910211394
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 309 additions and 54 deletions

View file

@ -20,6 +20,7 @@ public interface IPenumbraApiBase
public delegate void ChangedItemHover( object? item ); public delegate void ChangedItemHover( object? item );
public delegate void ChangedItemClick( MouseButton button, object? item ); public delegate void ChangedItemClick( MouseButton button, object? item );
public delegate void GameObjectRedrawn( IntPtr objectPtr, int objectTableIndex );
public enum PenumbraApiEc public enum PenumbraApiEc
{ {
@ -51,6 +52,7 @@ public interface IPenumbraApi : IPenumbraApiBase
// Triggered when the user clicks a listed changed object in a mod tab. // Triggered when the user clicks a listed changed object in a mod tab.
public event ChangedItemClick? ChangedItemClicked; public event ChangedItemClick? ChangedItemClicked;
public event GameObjectRedrawn? GameObjectRedrawn;
// Queue redrawing of all actors of the given name with the given RedrawType. // Queue redrawing of all actors of the given name with the given RedrawType.
public void RedrawObject( string name, RedrawType setting ); 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. // Returns the given gamePath if penumbra would not manipulate it.
public string ResolvePath( string gamePath, string characterName ); 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. // Try to load a given gamePath with the resolved path from Penumbra.
public T? GetFile< T >( string gamePath ) where T : FileResource; 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. // 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. // 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, // 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. // 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. // 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 ); 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. // 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 option );
public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName, 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. // Create a temporary collection without actual settings but with a cache.

View file

@ -20,6 +20,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
public int ApiVersion { get; } = 4; public int ApiVersion { get; } = 4;
private Penumbra? _penumbra; private Penumbra? _penumbra;
private Lumina.GameData? _lumina; private Lumina.GameData? _lumina;
public event GameObjectRedrawn? GameObjectRedrawn;
public bool Valid public bool Valid
=> _penumbra != null; => _penumbra != null;
@ -30,10 +31,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi
_lumina = ( Lumina.GameData? )Dalamud.GameData.GetType() _lumina = ( Lumina.GameData? )Dalamud.GameData.GetType()
.GetField( "gameData", BindingFlags.Instance | BindingFlags.NonPublic ) .GetField( "gameData", BindingFlags.Instance | BindingFlags.NonPublic )
?.GetValue( Dalamud.GameData ); ?.GetValue( Dalamud.GameData );
_penumbra.ObjectReloader.GameObjectRedrawn += OnGameObjectRedrawn;
} }
public void Dispose() public void Dispose()
{ {
_penumbra!.ObjectReloader.GameObjectRedrawn -= OnGameObjectRedrawn;
_penumbra = null; _penumbra = null;
_lumina = null; _lumina = null;
} }
@ -90,13 +93,18 @@ public class PenumbraApi : IDisposable, IPenumbraApi
_penumbra!.ObjectReloader.RedrawObject( gameObject, setting ); _penumbra!.ObjectReloader.RedrawObject( gameObject, setting );
} }
private void OnGameObjectRedrawn( IntPtr objectAddress, int objectTableIndex )
{
GameObjectRedrawn?.Invoke( objectAddress, objectTableIndex );
}
public void RedrawAll( RedrawType setting ) public void RedrawAll( RedrawType setting )
{ {
CheckInitialized(); CheckInitialized();
_penumbra!.ObjectReloader.RedrawAll( setting ); _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 ) if( !Penumbra.Config.EnableMods )
{ {
@ -121,6 +129,18 @@ public class PenumbraApi : IDisposable, IPenumbraApi
Penumbra.CollectionManager.Character( characterName ) ); 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 private T? GetFileIntern< T >( string resolvedPath ) where T : FileResource
{ {
CheckInitialized(); CheckInitialized();
@ -210,10 +230,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return Penumbra.ModManager.Select( m => ( m.ModPath.Name, m.Name.Text ) ).ToArray(); 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(); => 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 ) bool allowInheritance )
=> throw new NotImplementedException(); => 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 ) public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName, string option )
=> throw new NotImplementedException(); => 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(); => throw new NotImplementedException();
public PenumbraApiEc CreateTemporaryCollection( string collectionName, string? character, bool forceOverwriteCharacter ) public PenumbraApiEc CreateTemporaryCollection( string collectionName, string? character, bool forceOverwriteCharacter )

View file

@ -115,11 +115,13 @@ public partial class PenumbraIpc
public const string LabelProviderRedrawIndex = "Penumbra.RedrawObjectByIndex"; public const string LabelProviderRedrawIndex = "Penumbra.RedrawObjectByIndex";
public const string LabelProviderRedrawObject = "Penumbra.RedrawObject"; public const string LabelProviderRedrawObject = "Penumbra.RedrawObject";
public const string LabelProviderRedrawAll = "Penumbra.RedrawAll"; public const string LabelProviderRedrawAll = "Penumbra.RedrawAll";
public const string LabelProviderGameObjectRedrawn = "Penumbra.GameObjectRedrawn";
internal ICallGateProvider< string, int, object >? ProviderRedrawName; internal ICallGateProvider< string, int, object >? ProviderRedrawName;
internal ICallGateProvider< int, int, object >? ProviderRedrawIndex; internal ICallGateProvider< int, int, object >? ProviderRedrawIndex;
internal ICallGateProvider< GameObject, int, object >? ProviderRedrawObject; internal ICallGateProvider< GameObject, int, object >? ProviderRedrawObject;
internal ICallGateProvider< int, object >? ProviderRedrawAll; internal ICallGateProvider< int, object >? ProviderRedrawAll;
internal ICallGateProvider< IntPtr, int, object? >? ProviderGameObjectRedrawn;
private static RedrawType CheckRedrawType( int value ) private static RedrawType CheckRedrawType( int value )
{ {
@ -173,7 +175,20 @@ public partial class PenumbraIpc
{ {
PluginLog.Error( $"Error registering IPC provider for {LabelProviderRedrawAll}:\n{e}" ); 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() private void DisposeRedrawProviders()
{ {
@ -181,6 +196,7 @@ public partial class PenumbraIpc
ProviderRedrawIndex?.UnregisterAction(); ProviderRedrawIndex?.UnregisterAction();
ProviderRedrawObject?.UnregisterAction(); ProviderRedrawObject?.UnregisterAction();
ProviderRedrawAll?.UnregisterAction(); ProviderRedrawAll?.UnregisterAction();
Api.GameObjectRedrawn -= OnGameObjectRedrawn;
} }
} }
@ -189,10 +205,12 @@ public partial class PenumbraIpc
public const string LabelProviderResolveDefault = "Penumbra.ResolveDefaultPath"; public const string LabelProviderResolveDefault = "Penumbra.ResolveDefaultPath";
public const string LabelProviderResolveCharacter = "Penumbra.ResolveCharacterPath"; public const string LabelProviderResolveCharacter = "Penumbra.ResolveCharacterPath";
public const string LabelProviderGetDrawObjectInfo = "Penumbra.GetDrawObjectInfo"; public const string LabelProviderGetDrawObjectInfo = "Penumbra.GetDrawObjectInfo";
public const string LabelProviderReverseResolvePath = "Penumbra.ReverseResolvePath";
internal ICallGateProvider< string, string >? ProviderResolveDefault; internal ICallGateProvider< string, string >? ProviderResolveDefault;
internal ICallGateProvider< string, string, string >? ProviderResolveCharacter; internal ICallGateProvider< string, string, string >? ProviderResolveCharacter;
internal ICallGateProvider< IntPtr, (IntPtr, string) >? ProviderGetDrawObjectInfo; internal ICallGateProvider< IntPtr, (IntPtr, string) >? ProviderGetDrawObjectInfo;
internal ICallGateProvider< string, string, IList< string > >? ProviderReverseResolvePath;
private void InitializeResolveProviders( DalamudPluginInterface pi ) private void InitializeResolveProviders( DalamudPluginInterface pi )
{ {
@ -225,6 +243,16 @@ public partial class PenumbraIpc
{ {
PluginLog.Error( $"Error registering IPC provider for {LabelProviderGetDrawObjectInfo}:\n{e}" ); 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() private void DisposeResolveProviders()
@ -232,6 +260,7 @@ public partial class PenumbraIpc
ProviderGetDrawObjectInfo?.UnregisterFunc(); ProviderGetDrawObjectInfo?.UnregisterFunc();
ProviderResolveDefault?.UnregisterFunc(); ProviderResolveDefault?.UnregisterFunc();
ProviderResolveCharacter?.UnregisterFunc(); ProviderResolveCharacter?.UnregisterFunc();
ProviderReverseResolvePath?.UnregisterFunc();
} }
} }

View file

@ -20,6 +20,9 @@ public partial class ModCollection
// The collection currently selected for changing settings. // The collection currently selected for changing settings.
public ModCollection Current { get; private set; } = Empty; 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. // The collection used for general file redirections and all characters not specifically named.
public ModCollection Default { get; private set; } = Empty; public ModCollection Default { get; private set; } = Empty;
@ -78,6 +81,8 @@ public partial class ModCollection
break; break;
} }
CurrentCollectionInUse = Characters.Values.Prepend( Default ).SelectMany( c => c.GetFlattenedInheritance() ).Contains( Current );
CollectionChanged.Invoke( type, this[ oldCollectionIdx ], newCollection, characterName ); CollectionChanged.Invoke( type, this[ oldCollectionIdx ], newCollection, characterName );
} }

View file

@ -44,6 +44,8 @@ public partial class ModCollection
PluginLog.Verbose( "Cleared cache of collection {Name:l}.", Name ); 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 ) public FullPath? ResolvePath( Utf8GamePath path )
=> _cache?.ResolvePath( path ); => _cache?.ResolvePath( path );

View file

@ -79,6 +79,28 @@ public partial class ModCollection
return candidate.Path; 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 _ ) private void OnModSettingChange( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool _ )
{ {
switch( type ) switch( type )

View file

@ -27,6 +27,7 @@ public partial class Configuration : IPluginConfiguration
public bool UseCharacterCollectionInTryOn { get; set; } = true; public bool UseCharacterCollectionInTryOn { get; set; } = true;
public bool UseOwnerNameForCharacterCollection { get; set; } = true; public bool UseOwnerNameForCharacterCollection { get; set; } = true;
public bool PreferNamedCollectionsOverOwners { get; set; } = true; public bool PreferNamedCollectionsOverOwners { get; set; } = true;
public bool UseDefaultCollectionForRetainers { get; set; } = false;
#if DEBUG #if DEBUG
public bool DebugMode { get; set; } = true; public bool DebugMode { get; set; } = true;

View file

@ -25,7 +25,6 @@ public unsafe partial class ObjectReloader
private static void EnableDraw( GameObject actor ) private static void EnableDraw( GameObject actor )
=> ( ( delegate* unmanaged< IntPtr, void >** )actor.Address )[ 0 ][ 16 ]( actor.Address ); => ( ( delegate* unmanaged< IntPtr, void >** )actor.Address )[ 0 ][ 16 ]( actor.Address );
// Check whether we currently are in GPose. // Check whether we currently are in GPose.
// Also clear the name list. // Also clear the name list.
private void SetGPose() private void SetGPose()
@ -106,6 +105,8 @@ public sealed unsafe partial class ObjectReloader : IDisposable
private readonly List< int > _afterGPoseQueue = new(GPoseSlots); private readonly List< int > _afterGPoseQueue = new(GPoseSlots);
private int _target = -1; private int _target = -1;
public event Action< IntPtr, int >? GameObjectRedrawn;
public ObjectReloader() public ObjectReloader()
=> Dalamud.Framework.Update += OnUpdateEvent; => 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 ) ) if( BadRedrawIndices( actor, out var tableIndex ) )
{ {
@ -141,11 +142,12 @@ public sealed unsafe partial class ObjectReloader : IDisposable
} }
*ActorDrawState( actor! ) &= ~DrawState.Invisibility; *ActorDrawState( actor! ) &= ~DrawState.Invisibility;
if( IsGPoseActor( tableIndex ) ) if( IsGPoseActor( tableIndex ) )
{ {
EnableDraw( actor! ); EnableDraw( actor! );
} }
GameObjectRedrawn?.Invoke( actor!.Address, tableIndex );
} }
private void ReloadActor( GameObject? actor ) private void ReloadActor( GameObject? actor )

View file

@ -271,6 +271,10 @@ public unsafe partial class PathResolver
return Penumbra.CollectionManager.Default; 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; string? actorName = null;
if( Penumbra.Config.PreferNamedCollectionsOverOwners ) if( Penumbra.Config.PreferNamedCollectionsOverOwners )
{ {

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

View file

@ -14,12 +14,6 @@ public unsafe partial class PathResolver
// [Signature( "48 8D 1D ?? ?? ?? ?? 48 C7 41", ScanType = ScanType.StaticAddress )] // [Signature( "48 8D 1D ?? ?? ?? ?? 48 C7 41", ScanType = ScanType.StaticAddress )]
// public IntPtr* DrawObjectVTable; // 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 ResolveRootIdx = 71;
public const int ResolveSklbIdx = 72; public const int ResolveSklbIdx = 72;

View file

@ -136,6 +136,43 @@ public unsafe partial class PathResolver
private IntPtr ResolveMonsterVfxDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4, ulong unk5 ) private IntPtr ResolveMonsterVfxDetour( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4, ulong unk5 )
=> ResolvePathDetour( drawObject, ResolveMonsterVfxPathHook!.Original( drawObject, path, unk3, unk4, 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 // Implementation
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )] [MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]

View file

@ -27,6 +27,7 @@ public partial class PathResolver : IDisposable
SetupHumanHooks(); SetupHumanHooks();
SetupWeaponHooks(); SetupWeaponHooks();
SetupMonsterHooks(); SetupMonsterHooks();
SetupDemiHooks();
SetupMetaHooks(); SetupMetaHooks();
} }
@ -105,6 +106,7 @@ public partial class PathResolver : IDisposable
EnableHumanHooks(); EnableHumanHooks();
EnableWeaponHooks(); EnableWeaponHooks();
EnableMonsterHooks(); EnableMonsterHooks();
EnableDemiHooks();
EnableMtrlHooks(); EnableMtrlHooks();
EnableDataHooks(); EnableDataHooks();
EnableMetaHooks(); EnableMetaHooks();
@ -124,6 +126,7 @@ public partial class PathResolver : IDisposable
DisableHumanHooks(); DisableHumanHooks();
DisableWeaponHooks(); DisableWeaponHooks();
DisableMonsterHooks(); DisableMonsterHooks();
DisableDemiHooks();
DisableMtrlHooks(); DisableMtrlHooks();
DisableDataHooks(); DisableDataHooks();
DisableMetaHooks(); DisableMetaHooks();
@ -141,6 +144,7 @@ public partial class PathResolver : IDisposable
DisposeHumanHooks(); DisposeHumanHooks();
DisposeWeaponHooks(); DisposeWeaponHooks();
DisposeMonsterHooks(); DisposeMonsterHooks();
DisposeDemiHooks();
DisposeMtrlHooks(); DisposeMtrlHooks();
DisposeDataHooks(); DisposeDataHooks();
DisposeMetaHooks(); DisposeMetaHooks();

View file

@ -152,6 +152,10 @@ public class Penumbra : IDisposable
{ {
PluginLog.Error( $"{ImcExceptions} IMC Exceptions thrown. Please repair your game files." ); 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 ) private void SetupInterface( out ConfigWindow cfg, out LaunchButton btn, out WindowSystem system )

View file

@ -41,7 +41,7 @@ public partial class ModEditWindow
private int _offsetX = 0; private int _offsetX = 0;
private int _offsetY = 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 ) private static bool DragFloat( string label, float width, ref float value )
{ {

View file

@ -19,7 +19,7 @@ namespace Penumbra.UI.Classes;
public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, ModFileSystemSelector.ModState > public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, ModFileSystemSelector.ModState >
{ {
private readonly FileDialogManager _fileManager = new(); private readonly FileDialogManager _fileManager = ConfigWindow.SetupFileManager();
private TexToolsImporter? _import; private TexToolsImporter? _import;
public ModSettings SelectedSettings { get; private set; } = ModSettings.Empty; public ModSettings SelectedSettings { get; private set; } = ModSettings.Empty;
public ModCollection SelectedSettingCollection { get; private set; } = ModCollection.Empty; public ModCollection SelectedSettingCollection { get; private set; } = ModCollection.Empty;

View file

@ -87,7 +87,7 @@ public partial class ConfigWindow
ImGui.TableSetupColumn( "mods", flags, varWidth - 100 * ImGuiHelpers.GlobalScale ); ImGui.TableSetupColumn( "mods", flags, varWidth - 100 * ImGuiHelpers.GlobalScale );
ImGui.TableSetupColumn( "id", flags, 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 var rest = _changedItemFilter.IsEmpty && _changedItemModFilter.IsEmpty
? ImGuiClip.ClippedDraw( items, skips, DrawChangedItemColumn, items.Count ) ? ImGuiClip.ClippedDraw( items, skips, DrawChangedItemColumn, items.Count )
: ImGuiClip.FilteredClippedDraw( items, skips, FilterChangedItem, DrawChangedItemColumn ); : ImGuiClip.FilteredClippedDraw( items, skips, FilterChangedItem, DrawChangedItemColumn );

View file

@ -50,7 +50,7 @@ public partial class ConfigWindow
ImGui.TableSetupColumn( string.Empty, ImGuiTableColumnFlags.WidthFixed, _effectiveArrowLength ); ImGui.TableSetupColumn( string.Empty, ImGuiTableColumnFlags.WidthFixed, _effectiveArrowLength );
ImGui.TableSetupColumn( "##file", ImGuiTableColumnFlags.WidthFixed, _effectiveRightTextLength ); ImGui.TableSetupColumn( "##file", ImGuiTableColumnFlags.WidthFixed, _effectiveRightTextLength );
DrawEffectiveRows( Penumbra.CollectionManager.Default, skips, height, DrawEffectiveRows( Penumbra.CollectionManager.Current, skips, height,
_effectiveFilePathFilter.Length > 0 || _effectiveGamePathFilter.Length > 0 ); _effectiveFilePathFilter.Length > 0 || _effectiveGamePathFilter.Length > 0 );
} }

View file

@ -1,6 +1,8 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Dalamud.Interface;
using Dalamud.Interface.ImGuiFileDialog;
using ImGuiNET; using ImGuiNET;
using Lumina.Data.Parsing; using Lumina.Data.Parsing;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
@ -11,6 +13,7 @@ using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
using Penumbra.Util;
namespace Penumbra.UI; namespace Penumbra.UI;
@ -102,11 +105,8 @@ public partial class ConfigWindow
}; };
using var combo = ImRaii.Combo( label, current.Name ); 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 ) ) foreach( var collection in Penumbra.CollectionManager.GetEnumeratorWithEmpty().Skip( withEmpty ? 0 : 1 ).OrderBy( c => c.Name ) )
{ {
using var id = ImRaii.PushId( collection.Index ); using var id = ImRaii.PushId( collection.Index );
@ -117,3 +117,32 @@ 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;
}
}

View file

@ -12,17 +12,12 @@ namespace Penumbra.UI;
public partial class ConfigWindow public partial class ConfigWindow
{ {
private partial class ModPanel : IDisposable private partial class ModPanel
{ {
// We use a big, nice game font for the title. // We use a big, nice game font for the title.
private readonly GameFontHandle _nameFont = private readonly GameFontHandle _nameFont =
Dalamud.PluginInterface.UiBuilder.GetGameFontHandle( new GameFontStyle( GameFontFamilyAndSize.Jupiter23 ) ); Dalamud.PluginInterface.UiBuilder.GetGameFontHandle( new GameFontStyle( GameFontFamilyAndSize.Jupiter23 ) );
public void Dispose()
{
_nameFont.Dispose();
}
// Header data. // Header data.
private string _modName = string.Empty; private string _modName = string.Empty;
private string _modAuthor = string.Empty; private string _modAuthor = string.Empty;

View file

@ -56,7 +56,7 @@ public partial class ConfigWindow
DrawChangedItemsTab(); DrawChangedItemsTab();
DrawConflictsTab(); DrawConflictsTab();
DrawEditModTab(); 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.ChangeMod( _mod );
_window.ModEditPopup.ChangeOption( -1, 0 ); _window.ModEditPopup.ChangeOption( -1, 0 );

View file

@ -63,6 +63,10 @@ public partial class ConfigWindow
DrawInheritedCollectionButton( 3 * buttonSize ); DrawInheritedCollectionButton( 3 * buttonSize );
ImGui.SameLine(); ImGui.SameLine();
DrawCollectionSelector( "##collectionSelector", 2 * buttonSize.X, ModCollection.Type.Current, false, null ); 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 ) private static void DrawDefaultCollectionButton( Vector2 width )
@ -112,7 +116,7 @@ public partial class ConfigWindow
// The basic setup for the mod panel. // The basic setup for the mod panel.
// Details are in other files. // Details are in other files.
private partial class ModPanel private partial class ModPanel : IDisposable
{ {
private readonly ConfigWindow _window; private readonly ConfigWindow _window;
@ -123,6 +127,11 @@ public partial class ConfigWindow
public ModPanel( ConfigWindow window ) public ModPanel( ConfigWindow window )
=> _window = window; => _window = window;
public void Dispose()
{
_nameFont.Dispose();
}
public void Draw( ModFileSystemSelector selector ) public void Draw( ModFileSystemSelector selector )
{ {
Init( selector ); Init( selector );

View file

@ -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" "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.", + "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 ); 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 ); ImGui.Dummy( _window._defaultSpace );
DrawFolderSortType(); DrawFolderSortType();
DrawAbsoluteSizeSelector(); DrawAbsoluteSizeSelector();

View file

@ -62,7 +62,7 @@ public partial class ConfigWindow
// Changing the base mod directory. // Changing the base mod directory.
private string? _newModDirectory; private string? _newModDirectory;
private readonly FileDialogManager _dialogManager = new(); private readonly FileDialogManager _dialogManager = SetupFileManager();
private bool _dialogOpen; // For toggling on/off. private bool _dialogOpen; // For toggling on/off.
// Do not change the directory without explicitly pressing enter or this button. // Do not change the directory without explicitly pressing enter or this button.

View file

@ -33,7 +33,6 @@ public sealed partial class ConfigWindow : Window, IDisposable
_effectiveTab = new EffectiveTab(); _effectiveTab = new EffectiveTab();
_debugTab = new DebugTab( this ); _debugTab = new DebugTab( this );
_resourceTab = new ResourceTab( this ); _resourceTab = new ResourceTab( this );
Flags |= ImGuiWindowFlags.NoDocking;
if( Penumbra.Config.FixMainWindow ) if( Penumbra.Config.FixMainWindow )
{ {
Flags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove; Flags |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove;