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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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 )]
// 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;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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"
+ "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();

View file

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

View file

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