diff --git a/OtterGui b/OtterGui index 8053b24b..c5e54db0 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit 8053b24bb6b77a13853455c08a89df4894b3f2ee +Subproject commit c5e54db0ae54b2eab943be9920e2888b0b4b1d13 diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index f4403e50..f84cbc58 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -456,7 +456,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi var collection = Penumbra.TempMods.Collections.TryGetValue( characterName, out var c ) ? c : Penumbra.CollectionManager.Character( characterName ); - var set = collection.MetaCache?.Manipulations ?? Array.Empty< MetaManipulation >(); + var set = collection.MetaCache?.Manipulations.ToArray() ?? Array.Empty< MetaManipulation >(); return Functions.ToCompressedBase64( set, MetaManipulation.CurrentVersion ); } diff --git a/Penumbra/Collections/CollectionManager.Active.cs b/Penumbra/Collections/CollectionManager.Active.cs index 5e950228..ceca52be 100644 --- a/Penumbra/Collections/CollectionManager.Active.cs +++ b/Penumbra/Collections/CollectionManager.Active.cs @@ -110,8 +110,12 @@ public partial class ModCollection { case CollectionType.Default: Default = newCollection; - Penumbra.ResidentResources.Reload(); - Default.SetFiles(); + if( Penumbra.CharacterUtility.Ready ) + { + Penumbra.ResidentResources.Reload(); + Default.SetFiles(); + } + break; case CollectionType.Current: Current = newCollection; diff --git a/Penumbra/Collections/ModCollection.Cache.Access.cs b/Penumbra/Collections/ModCollection.Cache.Access.cs index a6fa0ffe..b5ea41f1 100644 --- a/Penumbra/Collections/ModCollection.Cache.Access.cs +++ b/Penumbra/Collections/ModCollection.Cache.Access.cs @@ -116,68 +116,63 @@ public partial class ModCollection } // Set Metadata files. - [Conditional( "USE_EQP" )] public void SetEqpFiles() { if( _cache == null ) { - MetaManager.MetaManagerEqp.ResetFiles(); + MetaManager.ResetEqpFiles(); } else { - _cache.MetaManipulations.Eqp.SetFiles(); + _cache.MetaManipulations.SetEqpFiles(); } } - [Conditional( "USE_EQDP" )] public void SetEqdpFiles() { if( _cache == null ) { - MetaManager.MetaManagerEqdp.ResetFiles(); + MetaManager.ResetEqdpFiles(); } else { - _cache.MetaManipulations.Eqdp.SetFiles(); + _cache.MetaManipulations.SetEqdpFiles(); } } - [Conditional( "USE_GMP" )] public void SetGmpFiles() { if( _cache == null ) { - MetaManager.MetaManagerGmp.ResetFiles(); + MetaManager.ResetGmpFiles(); } else { - _cache.MetaManipulations.Gmp.SetFiles(); + _cache.MetaManipulations.SetGmpFiles(); } } - [Conditional( "USE_EST" )] public void SetEstFiles() { if( _cache == null ) { - MetaManager.MetaManagerEst.ResetFiles(); + MetaManager.ResetEstFiles(); } else { - _cache.MetaManipulations.Est.SetFiles(); + _cache.MetaManipulations.SetEstFiles(); } } - [Conditional( "USE_CMP" )] public void SetCmpFiles() { if( _cache == null ) { - MetaManager.MetaManagerCmp.ResetFiles(); + MetaManager.ResetCmpFiles(); } else { - _cache.MetaManipulations.Cmp.SetFiles(); + _cache.MetaManipulations.SetCmpFiles(); } } diff --git a/Penumbra/Collections/ModCollection.Cache.cs b/Penumbra/Collections/ModCollection.Cache.cs index e8f72e32..34ef8924 100644 --- a/Penumbra/Collections/ModCollection.Cache.cs +++ b/Penumbra/Collections/ModCollection.Cache.cs @@ -175,7 +175,7 @@ public partial class ModCollection ++ChangeCounter; - if( _collection == Penumbra.CollectionManager.Default ) + if( _collection == Penumbra.CollectionManager.Default && Penumbra.CharacterUtility.Ready ) { Penumbra.ResidentResources.Reload(); MetaManipulations.SetFiles(); @@ -237,7 +237,7 @@ public partial class ModCollection if( addMetaChanges ) { ++ChangeCounter; - if( _collection == Penumbra.CollectionManager.Default ) + if( _collection == Penumbra.CollectionManager.Default && Penumbra.CharacterUtility.Ready ) { Penumbra.ResidentResources.Reload(); MetaManipulations.SetFiles(); @@ -295,7 +295,7 @@ public partial class ModCollection AddMetaFiles(); } - if( _collection == Penumbra.CollectionManager.Default ) + if( _collection == Penumbra.CollectionManager.Default && Penumbra.CharacterUtility.Ready ) { Penumbra.ResidentResources.Reload(); MetaManipulations.SetFiles(); @@ -441,7 +441,7 @@ public partial class ModCollection // Add all necessary meta file redirects. private void AddMetaFiles() - => MetaManipulations.Imc.SetFiles(); + => MetaManipulations.SetImcFiles(); // Identify and record all manipulated objects for this entire collection. private void SetChangedItems() diff --git a/Penumbra/Interop/CharacterUtility.cs b/Penumbra/Interop/CharacterUtility.cs index 0db41cd7..d96f948e 100644 --- a/Penumbra/Interop/CharacterUtility.cs +++ b/Penumbra/Interop/CharacterUtility.cs @@ -11,7 +11,6 @@ public unsafe class CharacterUtility : IDisposable [Signature( "48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? 00 48 8D 8E ?? ?? 00 00 E8 ?? ?? ?? 00 33 D2", ScanType = ScanType.StaticAddress )] private readonly Structs.CharacterUtility** _characterUtilityAddress = null; - // Only required for migration anymore. public delegate void LoadResources( Structs.CharacterUtility* address ); @@ -62,12 +61,12 @@ public unsafe class CharacterUtility : IDisposable public CharacterUtility() { SignatureHelper.Initialise( this ); - LoadingFinished += () => PluginLog.Debug( "Loading of CharacterUtility finished." ); - LoadDefaultResources( true ); + Dalamud.Framework.Update += LoadDefaultResources; + LoadingFinished += () => PluginLog.Debug( "Loading of CharacterUtility finished." ); } // We store the default data of the resources so we can always restore them. - private void LoadDefaultResources( bool repeat ) + private void LoadDefaultResources( object _ ) { var missingCount = 0; if( Address == null ) @@ -96,12 +95,7 @@ public unsafe class CharacterUtility : IDisposable { Ready = true; LoadingFinished.Invoke(); - } - else if( repeat ) - { - PluginLog.Debug( "Custom load of character resources triggered." ); - LoadCharacterResources(); - LoadDefaultResources( false ); + Dalamud.Framework.Update -= LoadDefaultResources; } } diff --git a/Penumbra/Interop/ResidentResourceManager.cs b/Penumbra/Interop/ResidentResourceManager.cs index 8e75dcde..f533a7ba 100644 --- a/Penumbra/Interop/ResidentResourceManager.cs +++ b/Penumbra/Interop/ResidentResourceManager.cs @@ -29,8 +29,11 @@ public unsafe class ResidentResourceManager // Reload certain player resources by force. public void Reload() { - PluginLog.Debug( "Reload of resident resources triggered." ); - UnloadPlayerResources.Invoke( Address ); - LoadPlayerResources.Invoke( Address ); + if( Address != null && Address->NumResources > 0 ) + { + PluginLog.Debug( "Reload of resident resources triggered." ); + UnloadPlayerResources.Invoke( Address ); + LoadPlayerResources.Invoke( Address ); + } } } \ No newline at end of file diff --git a/Penumbra/Meta/Manager/MetaManager.Cmp.cs b/Penumbra/Meta/Manager/MetaManager.Cmp.cs index d9371c41..2a916c5b 100644 --- a/Penumbra/Meta/Manager/MetaManager.Cmp.cs +++ b/Penumbra/Meta/Manager/MetaManager.Cmp.cs @@ -1,73 +1,57 @@ -using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; +using OtterGui.Filesystem; using Penumbra.Interop.Structs; using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; -using Penumbra.Mods; namespace Penumbra.Meta.Manager; public partial class MetaManager { - public struct MetaManagerCmp : IDisposable + private CmpFile? _cmpFile = null; + private readonly List< RspManipulation > _cmpManipulations = new(); + + public void SetCmpFiles() + => SetFile( _cmpFile, CharacterUtility.HumanCmpIdx ); + + public static void ResetCmpFiles() + => SetFile( null, CharacterUtility.HumanCmpIdx ); + + public void ResetCmp() { - public CmpFile? File = null; - public readonly Dictionary< RspManipulation, IMod > Manipulations = new(); - - public MetaManagerCmp() - { } - - [Conditional( "USE_CMP" )] - public void SetFiles() - => SetFile( File, CharacterUtility.HumanCmpIdx ); - - [Conditional( "USE_CMP" )] - public static void ResetFiles() - => SetFile( null, CharacterUtility.HumanCmpIdx ); - - [Conditional( "USE_CMP" )] - public void Reset() + if( _cmpFile == null ) { - if( File == null ) - { - return; - } - - File.Reset( Manipulations.Keys.Select( m => ( m.SubRace, m.Attribute ) ) ); - Manipulations.Clear(); + return; } - public bool ApplyMod( RspManipulation m, IMod mod ) + _cmpFile.Reset( _cmpManipulations.Select( m => ( m.SubRace, m.Attribute ) ) ); + _cmpManipulations.Clear(); + } + + public bool ApplyMod( RspManipulation manip ) + { + _cmpManipulations.AddOrReplace( manip ); + _cmpFile ??= new CmpFile(); + return manip.Apply( _cmpFile ); + } + + public bool RevertMod( RspManipulation manip ) + { + if( _cmpManipulations.Remove( manip ) ) { -#if USE_CMP - Manipulations[ m ] = mod; - File ??= new CmpFile(); - return m.Apply( File ); -#else - return false; -#endif + var def = CmpFile.GetDefault( manip.SubRace, manip.Attribute ); + manip = new RspManipulation( manip.SubRace, manip.Attribute, def ); + return manip.Apply( _cmpFile! ); } - public bool RevertMod( RspManipulation m ) - { -#if USE_CMP - if( Manipulations.Remove( m ) ) - { - var def = CmpFile.GetDefault( m.SubRace, m.Attribute ); - var manip = new RspManipulation( m.SubRace, m.Attribute, def ); - return manip.Apply( File! ); - } -#endif - return false; - } + return false; + } - public void Dispose() - { - File?.Dispose(); - File = null; - Manipulations.Clear(); - } + public void DisposeCmp() + { + _cmpFile?.Dispose(); + _cmpFile = null; + _cmpManipulations.Clear(); } } \ No newline at end of file diff --git a/Penumbra/Meta/Manager/MetaManager.Eqdp.cs b/Penumbra/Meta/Manager/MetaManager.Eqdp.cs index 92a60470..e89bd67e 100644 --- a/Penumbra/Meta/Manager/MetaManager.Eqdp.cs +++ b/Penumbra/Meta/Manager/MetaManager.Eqdp.cs @@ -1,94 +1,79 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; +using OtterGui.Filesystem; using Penumbra.GameData.Enums; using Penumbra.Interop.Structs; using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; -using Penumbra.Mods; -using Penumbra.Util; namespace Penumbra.Meta.Manager; public partial class MetaManager { - public struct MetaManagerEqdp : IDisposable + private readonly ExpandedEqdpFile?[] _eqdpFiles = new ExpandedEqdpFile?[CharacterUtility.NumEqdpFiles - 2]; // TODO: female Hrothgar + + private readonly List< EqdpManipulation > _eqdpManipulations = new(); + + public void SetEqdpFiles() { - public readonly ExpandedEqdpFile?[] Files = new ExpandedEqdpFile?[CharacterUtility.NumEqdpFiles - 2]; // TODO: female Hrothgar - - public readonly Dictionary< EqdpManipulation, IMod > Manipulations = new(); - - public MetaManagerEqdp() - { } - - [Conditional( "USE_EQDP" )] - public void SetFiles() + for( var i = 0; i < CharacterUtility.EqdpIndices.Length; ++i ) { - for( var i = 0; i < CharacterUtility.EqdpIndices.Length; ++i ) - { - SetFile( Files[ i ], CharacterUtility.EqdpIndices[ i ] ); - } - } - - [Conditional( "USE_EQDP" )] - public static void ResetFiles() - { - foreach( var idx in CharacterUtility.EqdpIndices ) - { - SetFile( null, idx ); - } - } - - [Conditional( "USE_EQDP" )] - public void Reset() - { - foreach( var file in Files ) - { - file?.Reset( Manipulations.Keys.Where( m => m.FileIndex() == file.Index ).Select( m => ( int )m.SetId ) ); - } - - Manipulations.Clear(); - } - - public bool ApplyMod( EqdpManipulation m, IMod mod ) - { -#if USE_EQDP - Manipulations[ m ] = mod; - var file = Files[ Array.IndexOf( CharacterUtility.EqdpIndices, m.FileIndex() ) ] ??= - new ExpandedEqdpFile( Names.CombinedRace( m.Gender, m.Race ), m.Slot.IsAccessory() ); // TODO: female Hrothgar - return m.Apply( file ); -#else - return false; -#endif - } - - public bool RevertMod( EqdpManipulation m ) - { -#if USE_EQDP - if( Manipulations.Remove( m ) ) - { - var def = ExpandedEqdpFile.GetDefault( Names.CombinedRace( m.Gender, m.Race ), m.Slot.IsAccessory(), m.SetId ); - var file = Files[ Array.IndexOf( CharacterUtility.EqdpIndices, m.FileIndex() ) ]!; - var manip = new EqdpManipulation( def, m.Slot, m.Gender, m.Race, m.SetId ); - return manip.Apply( file ); - } -#endif - return false; - } - - public ExpandedEqdpFile? File( GenderRace race, bool accessory ) - => Files[ Array.IndexOf( CharacterUtility.EqdpIndices, CharacterUtility.EqdpIdx( race, accessory ) ) ]; // TODO: female Hrothgar - - public void Dispose() - { - for( var i = 0; i < Files.Length; ++i ) - { - Files[ i ]?.Dispose(); - Files[ i ] = null; - } - - Manipulations.Clear(); + SetFile( _eqdpFiles[ i ], CharacterUtility.EqdpIndices[ i ] ); } } + + public static void ResetEqdpFiles() + { + foreach( var idx in CharacterUtility.EqdpIndices ) + { + SetFile( null, idx ); + } + } + + public void ResetEqdp() + { + foreach( var file in _eqdpFiles ) + { + file?.Reset( _eqdpManipulations.Where( m => m.FileIndex() == file.Index ).Select( m => ( int )m.SetId ) ); + } + + _eqdpManipulations.Clear(); + } + + public bool ApplyMod( EqdpManipulation manip ) + { + _eqdpManipulations.AddOrReplace( manip ); + var file = _eqdpFiles[ Array.IndexOf( CharacterUtility.EqdpIndices, manip.FileIndex() ) ] ??= + new ExpandedEqdpFile( Names.CombinedRace( manip.Gender, manip.Race ), manip.Slot.IsAccessory() ); // TODO: female Hrothgar + return manip.Apply( file ); + } + + public bool RevertMod( EqdpManipulation manip ) + { + if( _eqdpManipulations.Remove( manip ) ) + { + var def = ExpandedEqdpFile.GetDefault( Names.CombinedRace( manip.Gender, manip.Race ), manip.Slot.IsAccessory(), manip.SetId ); + var file = _eqdpFiles[ Array.IndexOf( CharacterUtility.EqdpIndices, manip.FileIndex() ) ]!; + manip = new EqdpManipulation( def, manip.Slot, manip.Gender, manip.Race, manip.SetId ); + return manip.Apply( file ); + } + + return false; + } + + public ExpandedEqdpFile? EqdpFile( GenderRace race, bool accessory ) + => _eqdpFiles + [ Array.IndexOf( CharacterUtility.EqdpIndices, CharacterUtility.EqdpIdx( race, accessory ) ) ]; // TODO: female Hrothgar + + public void DisposeEqdp() + { + for( var i = 0; i < _eqdpFiles.Length; ++i ) + { + _eqdpFiles[ i ]?.Dispose(); + _eqdpFiles[ i ] = null; + } + + _eqdpManipulations.Clear(); + } } \ No newline at end of file diff --git a/Penumbra/Meta/Manager/MetaManager.Eqp.cs b/Penumbra/Meta/Manager/MetaManager.Eqp.cs index 831e26d9..4c75615f 100644 --- a/Penumbra/Meta/Manager/MetaManager.Eqp.cs +++ b/Penumbra/Meta/Manager/MetaManager.Eqp.cs @@ -1,73 +1,58 @@ -using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; +using OtterGui.Filesystem; using Penumbra.Interop.Structs; using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; -using Penumbra.Mods; namespace Penumbra.Meta.Manager; public partial class MetaManager { - public struct MetaManagerEqp : IDisposable + private ExpandedEqpFile? _eqpFile = null; + private readonly List< EqpManipulation > _eqpManipulations = new(); + + public void SetEqpFiles() + => SetFile( _eqpFile, CharacterUtility.EqpIdx ); + + public static void ResetEqpFiles() + => SetFile( null, CharacterUtility.EqpIdx ); + + public void ResetEqp() { - public ExpandedEqpFile? File = null; - public readonly Dictionary< EqpManipulation, IMod > Manipulations = new(); - - public MetaManagerEqp() - { } - - [Conditional( "USE_EQP" )] - public void SetFiles() - => SetFile( File, CharacterUtility.EqpIdx ); - - [Conditional( "USE_EQP" )] - public static void ResetFiles() - => SetFile( null, CharacterUtility.EqpIdx ); - - [Conditional( "USE_EQP" )] - public void Reset() + if( _eqpFile == null ) { - if( File == null ) - { - return; - } - - File.Reset( Manipulations.Keys.Select( m => ( int )m.SetId ) ); - Manipulations.Clear(); + return; } - public bool ApplyMod( EqpManipulation m, IMod mod ) + _eqpFile.Reset( _eqpManipulations.Select( m => ( int )m.SetId ) ); + _eqpManipulations.Clear(); + } + + public bool ApplyMod( EqpManipulation manip ) + { + _eqpManipulations.AddOrReplace( manip ); + _eqpFile ??= new ExpandedEqpFile(); + return manip.Apply( _eqpFile ); + } + + public bool RevertMod( EqpManipulation manip ) + { + var idx = _eqpManipulations.FindIndex( manip.Equals ); + if( idx >= 0 ) { -#if USE_EQP - Manipulations[ m ] = mod; - File ??= new ExpandedEqpFile(); - return m.Apply( File ); -#else - return false; -#endif + var def = ExpandedEqpFile.GetDefault( manip.SetId ); + manip = new EqpManipulation( def, manip.Slot, manip.SetId ); + return manip.Apply( _eqpFile! ); } - public bool RevertMod( EqpManipulation m ) - { -#if USE_EQP - if( Manipulations.Remove( m ) ) - { - var def = ExpandedEqpFile.GetDefault( m.SetId ); - var manip = new EqpManipulation( def, m.Slot, m.SetId ); - return manip.Apply( File! ); - } -#endif - return false; - } + return false; + } - public void Dispose() - { - File?.Dispose(); - File = null; - Manipulations.Clear(); - } + public void DisposeEqp() + { + _eqpFile?.Dispose(); + _eqpFile = null; + _eqpManipulations.Clear(); } } \ No newline at end of file diff --git a/Penumbra/Meta/Manager/MetaManager.Est.cs b/Penumbra/Meta/Manager/MetaManager.Est.cs index 7af2609c..702b53b6 100644 --- a/Penumbra/Meta/Manager/MetaManager.Est.cs +++ b/Penumbra/Meta/Manager/MetaManager.Est.cs @@ -1,106 +1,91 @@ using System; using System.Collections.Generic; -using System.Diagnostics; +using OtterGui.Filesystem; using Penumbra.GameData.Enums; using Penumbra.Interop.Structs; using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; -using Penumbra.Mods; namespace Penumbra.Meta.Manager; public partial class MetaManager { - public struct MetaManagerEst : IDisposable + private EstFile? _estFaceFile = null; + private EstFile? _estHairFile = null; + private EstFile? _estBodyFile = null; + private EstFile? _estHeadFile = null; + + private readonly List< EstManipulation > _estManipulations = new(); + + public void SetEstFiles() { - public EstFile? FaceFile = null; - public EstFile? HairFile = null; - public EstFile? BodyFile = null; - public EstFile? HeadFile = null; + SetFile( _estFaceFile, CharacterUtility.FaceEstIdx ); + SetFile( _estHairFile, CharacterUtility.HairEstIdx ); + SetFile( _estBodyFile, CharacterUtility.BodyEstIdx ); + SetFile( _estHeadFile, CharacterUtility.HeadEstIdx ); + } - public readonly Dictionary< EstManipulation, IMod > Manipulations = new(); + public static void ResetEstFiles() + { + SetFile( null, CharacterUtility.FaceEstIdx ); + SetFile( null, CharacterUtility.HairEstIdx ); + SetFile( null, CharacterUtility.BodyEstIdx ); + SetFile( null, CharacterUtility.HeadEstIdx ); + } - public MetaManagerEst() - { } + public void ResetEst() + { + _estFaceFile?.Reset(); + _estHairFile?.Reset(); + _estBodyFile?.Reset(); + _estHeadFile?.Reset(); + _estManipulations.Clear(); + } - [Conditional( "USE_EST" )] - public void SetFiles() + public bool ApplyMod( EstManipulation m ) + { + _estManipulations.AddOrReplace( m ); + var file = m.Slot switch { - SetFile( FaceFile, CharacterUtility.FaceEstIdx ); - SetFile( HairFile, CharacterUtility.HairEstIdx ); - SetFile( BodyFile, CharacterUtility.BodyEstIdx ); - SetFile( HeadFile, CharacterUtility.HeadEstIdx ); - } + EstManipulation.EstType.Hair => _estHairFile ??= new EstFile( EstManipulation.EstType.Hair ), + EstManipulation.EstType.Face => _estFaceFile ??= new EstFile( EstManipulation.EstType.Face ), + EstManipulation.EstType.Body => _estBodyFile ??= new EstFile( EstManipulation.EstType.Body ), + EstManipulation.EstType.Head => _estHeadFile ??= new EstFile( EstManipulation.EstType.Head ), + _ => throw new ArgumentOutOfRangeException(), + }; + return m.Apply( file ); + } - [Conditional( "USE_EST" )] - public static void ResetFiles() + public bool RevertMod( EstManipulation m ) + { + if( _estManipulations.Remove( m ) ) { - SetFile( null, CharacterUtility.FaceEstIdx ); - SetFile( null, CharacterUtility.HairEstIdx ); - SetFile( null, CharacterUtility.BodyEstIdx ); - SetFile( null, CharacterUtility.HeadEstIdx ); - } - - [Conditional( "USE_EST" )] - public void Reset() - { - FaceFile?.Reset(); - HairFile?.Reset(); - BodyFile?.Reset(); - HeadFile?.Reset(); - Manipulations.Clear(); - } - - public bool ApplyMod( EstManipulation m, IMod mod ) - { -#if USE_EST - Manipulations[ m ] = mod; + var def = EstFile.GetDefault( m.Slot, Names.CombinedRace( m.Gender, m.Race ), m.SetId ); + var manip = new EstManipulation( m.Gender, m.Race, m.Slot, m.SetId, def ); var file = m.Slot switch { - EstManipulation.EstType.Hair => HairFile ??= new EstFile( EstManipulation.EstType.Hair ), - EstManipulation.EstType.Face => FaceFile ??= new EstFile( EstManipulation.EstType.Face ), - EstManipulation.EstType.Body => BodyFile ??= new EstFile( EstManipulation.EstType.Body ), - EstManipulation.EstType.Head => HeadFile ??= new EstFile( EstManipulation.EstType.Head ), + EstManipulation.EstType.Hair => _estHairFile!, + EstManipulation.EstType.Face => _estFaceFile!, + EstManipulation.EstType.Body => _estBodyFile!, + EstManipulation.EstType.Head => _estHeadFile!, _ => throw new ArgumentOutOfRangeException(), }; - return m.Apply( file ); -#else - return false; -#endif + return manip.Apply( file ); } - public bool RevertMod( EstManipulation m ) - { -#if USE_EST - if( Manipulations.Remove( m ) ) - { - var def = EstFile.GetDefault( m.Slot, Names.CombinedRace( m.Gender, m.Race ), m.SetId ); - var manip = new EstManipulation( m.Gender, m.Race, m.Slot, m.SetId, def ); - var file = m.Slot switch - { - EstManipulation.EstType.Hair => HairFile!, - EstManipulation.EstType.Face => FaceFile!, - EstManipulation.EstType.Body => BodyFile!, - EstManipulation.EstType.Head => HeadFile!, - _ => throw new ArgumentOutOfRangeException(), - }; - return manip.Apply( file ); - } -#endif - return false; - } + return false; + } - public void Dispose() - { - FaceFile?.Dispose(); - HairFile?.Dispose(); - BodyFile?.Dispose(); - HeadFile?.Dispose(); - FaceFile = null; - HairFile = null; - BodyFile = null; - HeadFile = null; - Manipulations.Clear(); - } + public void DisposeEst() + { + _estFaceFile?.Dispose(); + _estHairFile?.Dispose(); + _estBodyFile?.Dispose(); + _estHeadFile?.Dispose(); + _estFaceFile = null; + _estHairFile = null; + _estBodyFile = null; + _estHeadFile = null; + _estManipulations.Clear(); } } \ No newline at end of file diff --git a/Penumbra/Meta/Manager/MetaManager.Gmp.cs b/Penumbra/Meta/Manager/MetaManager.Gmp.cs index 82833017..1aaba52f 100644 --- a/Penumbra/Meta/Manager/MetaManager.Gmp.cs +++ b/Penumbra/Meta/Manager/MetaManager.Gmp.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using OtterGui.Filesystem; using Penumbra.Interop.Structs; using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; @@ -11,62 +12,49 @@ namespace Penumbra.Meta.Manager; public partial class MetaManager { - public struct MetaManagerGmp : IDisposable + private ExpandedGmpFile? _gmpFile = null; + private readonly List< GmpManipulation > _gmpManipulations = new(); + + public void SetGmpFiles() + => SetFile( _gmpFile, CharacterUtility.GmpIdx ); + + public static void ResetGmpFiles() + => SetFile( null, CharacterUtility.GmpIdx ); + + public void ResetGmp() { - public ExpandedGmpFile? File = null; - public readonly Dictionary< GmpManipulation, IMod > Manipulations = new(); - - public MetaManagerGmp() - { } - - - [Conditional( "USE_GMP" )] - public void SetFiles() - => SetFile( File, CharacterUtility.GmpIdx ); - - [Conditional( "USE_GMP" )] - public static void ResetFiles() - => SetFile( null, CharacterUtility.GmpIdx ); - - [Conditional( "USE_GMP" )] - public void Reset() + if( _gmpFile == null ) { - if( File != null ) - { - File.Reset( Manipulations.Keys.Select( m => ( int )m.SetId ) ); - Manipulations.Clear(); - } + return; } - public bool ApplyMod( GmpManipulation m, IMod mod ) + _gmpFile.Reset( _gmpManipulations.Select( m => ( int )m.SetId ) ); + _gmpManipulations.Clear(); + } + + public bool ApplyMod( GmpManipulation manip ) + { + _gmpManipulations.AddOrReplace( manip ); + _gmpFile ??= new ExpandedGmpFile(); + return manip.Apply( _gmpFile ); + } + + public bool RevertMod( GmpManipulation manip ) + { + if( _gmpManipulations.Remove( manip ) ) { -#if USE_GMP - Manipulations[ m ] = mod; - File ??= new ExpandedGmpFile(); - return m.Apply( File ); -#else - return false; -#endif + var def = ExpandedGmpFile.GetDefault( manip.SetId ); + manip = new GmpManipulation( def, manip.SetId ); + return manip.Apply( _gmpFile! ); } - public bool RevertMod( GmpManipulation m ) - { -#if USE_GMP - if( Manipulations.Remove( m ) ) - { - var def = ExpandedGmpFile.GetDefault( m.SetId ); - var manip = new GmpManipulation( def, m.SetId ); - return manip.Apply( File! ); - } -#endif - return false; - } + return false; + } - public void Dispose() - { - File?.Dispose(); - File = null; - Manipulations.Clear(); - } + public void DisposeGmp() + { + _gmpFile?.Dispose(); + _gmpFile = null; + _gmpManipulations.Clear(); } } \ No newline at end of file diff --git a/Penumbra/Meta/Manager/MetaManager.Imc.cs b/Penumbra/Meta/Manager/MetaManager.Imc.cs index 35e7434f..1621090d 100644 --- a/Penumbra/Meta/Manager/MetaManager.Imc.cs +++ b/Penumbra/Meta/Manager/MetaManager.Imc.cs @@ -1,215 +1,195 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using Dalamud.Logging; using FFXIVClientStructs.FFXIV.Client.System.Resource; +using OtterGui.Filesystem; using Penumbra.Collections; using Penumbra.GameData.ByteString; using Penumbra.GameData.Enums; using Penumbra.Interop.Structs; using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; -using Penumbra.Mods; namespace Penumbra.Meta.Manager; public partial class MetaManager { - public readonly struct MetaManagerImc : IDisposable + private readonly Dictionary< Utf8GamePath, ImcFile > _imcFiles = new(); + private readonly List< ImcManipulation > _imcManipulations = new(); + private static int _imcManagerCount; + + public void SetImcFiles() { - public readonly Dictionary< Utf8GamePath, ImcFile > Files = new(); - public readonly Dictionary< ImcManipulation, IMod > Manipulations = new(); - - private readonly ModCollection _collection; - private static int _imcManagerCount; - - public MetaManagerImc( ModCollection collection ) + if( !_collection.HasCache ) { - _collection = collection; - SetupDelegate(); + return; } - [Conditional( "USE_IMC" )] - public void SetFiles() + foreach( var path in _imcFiles.Keys ) { - if( !_collection.HasCache ) - { - return; - } + _collection.ForceFile( path, CreateImcPath( path ) ); + } + } - foreach( var path in Files.Keys ) + public void ResetImc() + { + if( _collection.HasCache ) + { + foreach( var (path, file) in _imcFiles ) { - _collection.ForceFile( path, CreateImcPath( path ) ); + _collection.RemoveFile( path ); + file.Reset(); + } + } + else + { + foreach( var (_, file) in _imcFiles ) + { + file.Reset(); } } - [Conditional( "USE_IMC" )] - public void Reset() + _imcManipulations.Clear(); + } + + public bool ApplyMod( ImcManipulation manip ) + { + _imcManipulations.AddOrReplace( manip ); + var path = manip.GamePath(); + try { + if( !_imcFiles.TryGetValue( path, out var file ) ) + { + file = new ImcFile( path ); + } + + if( !manip.Apply( file ) ) + { + return false; + } + + _imcFiles[ path ] = file; + var fullPath = CreateImcPath( path ); if( _collection.HasCache ) { - foreach( var (path, file) in Files ) - { - _collection.RemoveFile( path ); - file.Reset(); - } - } - else - { - foreach( var (_, file) in Files ) - { - file.Reset(); - } - } - - Manipulations.Clear(); - } - - public bool ApplyMod( ImcManipulation m, IMod mod ) - { -#if USE_IMC - Manipulations[ m ] = mod; - var path = m.GamePath(); - try - { - if( !Files.TryGetValue( path, out var file ) ) - { - file = new ImcFile( path ); - } - - if( !m.Apply( file ) ) - { - return false; - } - - Files[ path ] = file; - var fullPath = CreateImcPath( path ); - if( _collection.HasCache ) - { - _collection.ForceFile( path, fullPath ); - } - - return true; - } - catch( Exception e ) - { - ++Penumbra.ImcExceptions; - PluginLog.Error( $"Could not apply IMC Manipulation:\n{e}" ); - return false; - } -#else - return false; -#endif - } - - public bool RevertMod( ImcManipulation m ) - { -#if USE_IMC - if( Manipulations.Remove( m ) ) - { - var path = m.GamePath(); - if( !Files.TryGetValue( path, out var file ) ) - { - return false; - } - - var def = ImcFile.GetDefault( path, m.EquipSlot, m.Variant, out _ ); - var manip = m with { Entry = def }; - if( !manip.Apply( file ) ) - { - return false; - } - - var fullPath = CreateImcPath( path ); - if( _collection.HasCache ) - { - _collection.ForceFile( path, fullPath ); - } - - return true; - } -#endif - return false; - } - - public void Dispose() - { - foreach( var file in Files.Values ) - { - file.Dispose(); - } - - Files.Clear(); - Manipulations.Clear(); - RestoreDelegate(); - } - - [Conditional( "USE_IMC" )] - private static unsafe void SetupDelegate() - { - if( _imcManagerCount++ == 0 ) - { - Penumbra.ResourceLoader.ResourceLoadCustomization += ImcLoadHandler; - Penumbra.ResourceLoader.ResourceLoaded += ImcResourceHandler; - } - } - - [Conditional( "USE_IMC" )] - private static unsafe void RestoreDelegate() - { - if( --_imcManagerCount == 0 ) - { - Penumbra.ResourceLoader.ResourceLoadCustomization -= ImcLoadHandler; - Penumbra.ResourceLoader.ResourceLoaded -= ImcResourceHandler; - } - } - - private FullPath CreateImcPath( Utf8GamePath path ) - => new($"|{_collection.Name}_{_collection.RecomputeCounter}|{path}"); - - private static unsafe bool ImcLoadHandler( Utf8String split, Utf8String path, ResourceManager* resourceManager, - SeFileDescriptor* fileDescriptor, int priority, bool isSync, out byte ret ) - { - ret = 0; - if( fileDescriptor->ResourceHandle->FileType != ResourceType.Imc ) - { - return false; - } - - PluginLog.Verbose( "Using ImcLoadHandler for path {$Path:l}.", path ); - ret = Penumbra.ResourceLoader.ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync ); - - var lastUnderscore = split.LastIndexOf( ( byte )'_' ); - var name = lastUnderscore == -1 ? split.ToString() : split.Substring( 0, lastUnderscore ).ToString(); - if( Penumbra.CollectionManager.ByName( name, out var collection ) - && collection.HasCache - && collection.MetaCache!.Imc.Files.TryGetValue( - Utf8GamePath.FromSpan( path.Span, out var p ) ? p : Utf8GamePath.Empty, out var file ) ) - { - PluginLog.Debug( "Loaded {GamePath:l} from file and replaced with IMC from collection {Collection:l}.", path, - collection.Name ); - file.Replace( fileDescriptor->ResourceHandle, true ); - file.ChangesSinceLoad = false; + _collection.ForceFile( path, fullPath ); } return true; } - - private static unsafe void ImcResourceHandler( ResourceHandle* resource, Utf8GamePath gamePath, FullPath? _2, object? resolveData ) + catch( Exception e ) { - // Only check imcs. - if( resource->FileType != ResourceType.Imc - || resolveData is not ModCollection { HasCache: true } collection - || !collection.MetaCache!.Imc.Files.TryGetValue( gamePath, out var file ) - || !file.ChangesSinceLoad ) - { - return; - } - - PluginLog.Debug( "File {GamePath:l} was already loaded but IMC in collection {Collection:l} was changed, so reloaded.", gamePath, - collection.Name ); - file.Replace( resource, false ); - file.ChangesSinceLoad = false; + ++Penumbra.ImcExceptions; + PluginLog.Error( $"Could not apply IMC Manipulation:\n{e}" ); + return false; } } + + public bool RevertMod( ImcManipulation m ) + { +#if USE_IMC + if( _imcManipulations.Remove( m ) ) + { + var path = m.GamePath(); + if( !_imcFiles.TryGetValue( path, out var file ) ) + { + return false; + } + + var def = ImcFile.GetDefault( path, m.EquipSlot, m.Variant, out _ ); + var manip = m with { Entry = def }; + if( !manip.Apply( file ) ) + { + return false; + } + + var fullPath = CreateImcPath( path ); + if( _collection.HasCache ) + { + _collection.ForceFile( path, fullPath ); + } + + return true; + } +#endif + return false; + } + + public void DisposeImc() + { + foreach( var file in _imcFiles.Values ) + { + file.Dispose(); + } + + _imcFiles.Clear(); + _imcManipulations.Clear(); + RestoreImcDelegate(); + } + + private static unsafe void SetupImcDelegate() + { + if( _imcManagerCount++ == 0 ) + { + Penumbra.ResourceLoader.ResourceLoadCustomization += ImcLoadHandler; + Penumbra.ResourceLoader.ResourceLoaded += ImcResourceHandler; + } + } + + private static unsafe void RestoreImcDelegate() + { + if( --_imcManagerCount == 0 ) + { + Penumbra.ResourceLoader.ResourceLoadCustomization -= ImcLoadHandler; + Penumbra.ResourceLoader.ResourceLoaded -= ImcResourceHandler; + } + } + + private FullPath CreateImcPath( Utf8GamePath path ) + => new($"|{_collection.Name}_{_collection.RecomputeCounter}|{path}"); + + + private static unsafe bool ImcLoadHandler( Utf8String split, Utf8String path, ResourceManager* resourceManager, + SeFileDescriptor* fileDescriptor, int priority, bool isSync, out byte ret ) + { + ret = 0; + if( fileDescriptor->ResourceHandle->FileType != ResourceType.Imc ) + { + return false; + } + + PluginLog.Verbose( "Using ImcLoadHandler for path {$Path:l}.", path ); + ret = Penumbra.ResourceLoader.ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync ); + + var lastUnderscore = split.LastIndexOf( ( byte )'_' ); + var name = lastUnderscore == -1 ? split.ToString() : split.Substring( 0, lastUnderscore ).ToString(); + if( Penumbra.CollectionManager.ByName( name, out var collection ) + && collection.HasCache + && collection.MetaCache!._imcFiles.TryGetValue( Utf8GamePath.FromSpan( path.Span, out var p ) ? p : Utf8GamePath.Empty, out var file ) ) + { + PluginLog.Debug( "Loaded {GamePath:l} from file and replaced with IMC from collection {Collection:l}.", path, + collection.Name ); + file.Replace( fileDescriptor->ResourceHandle, true ); + file.ChangesSinceLoad = false; + } + + return true; + } + + private static unsafe void ImcResourceHandler( ResourceHandle* resource, Utf8GamePath gamePath, FullPath? _2, object? resolveData ) + { + // Only check imcs. + if( resource->FileType != ResourceType.Imc + || resolveData is not ModCollection { HasCache: true } collection + || !collection.MetaCache!._imcFiles.TryGetValue( gamePath, out var file ) + || !file.ChangesSinceLoad ) + { + return; + } + + PluginLog.Debug( "File {GamePath:l} was already loaded but IMC in collection {Collection:l} was changed, so reloaded.", gamePath, + collection.Name ); + file.Replace( resource, false ); + file.ChangesSinceLoad = false; + } } \ No newline at end of file diff --git a/Penumbra/Meta/Manager/MetaManager.cs b/Penumbra/Meta/Manager/MetaManager.cs index 50404234..699273dd 100644 --- a/Penumbra/Meta/Manager/MetaManager.cs +++ b/Penumbra/Meta/Manager/MetaManager.cs @@ -1,6 +1,7 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Runtime.CompilerServices; using Penumbra.Collections; using Penumbra.Meta.Files; @@ -9,17 +10,135 @@ using Penumbra.Mods; namespace Penumbra.Meta.Manager; -public partial class MetaManager : IDisposable +public partial class MetaManager : IDisposable, IEnumerable> { - public MetaManagerEqp Eqp = new(); - public MetaManagerEqdp Eqdp = new(); - public MetaManagerGmp Gmp = new(); - public MetaManagerEst Est = new(); - public MetaManagerCmp Cmp = new(); - public MetaManagerImc Imc; + private readonly Dictionary< MetaManipulation, IMod > _manipulations = new(); + private readonly ModCollection _collection; + + public bool TryGetValue( MetaManipulation manip, [NotNullWhen( true )] out IMod? mod ) + => _manipulations.TryGetValue( manip, out mod ); + + public int Count + => _manipulations.Count; + + public IReadOnlyCollection< MetaManipulation > Manipulations + => _manipulations.Keys; + + + public IEnumerator> GetEnumerator() + => _manipulations.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + public MetaManager( ModCollection collection ) + { + _collection = collection; + SetupImcDelegate(); + Penumbra.CharacterUtility.LoadingFinished += ApplyStoredManipulations; + } + + public void SetFiles() + { + SetEqpFiles(); + SetEqdpFiles(); + SetGmpFiles(); + SetEstFiles(); + SetCmpFiles(); + SetImcFiles(); + } + + public void Reset() + { + ResetEqp(); + ResetEqdp(); + ResetGmp(); + ResetEst(); + ResetCmp(); + ResetImc(); + } + + public void Dispose() + { + Penumbra.CharacterUtility.LoadingFinished -= ApplyStoredManipulations; + DisposeEqp(); + DisposeEqdp(); + DisposeCmp(); + DisposeGmp(); + DisposeEst(); + DisposeImc(); + } + + public bool ApplyMod( MetaManipulation manip, IMod mod ) + { + _manipulations[ manip ] = mod; + if( !Penumbra.CharacterUtility.Ready ) + { + return true; + } + + return manip.ManipulationType switch + { + MetaManipulation.Type.Eqp => ApplyMod( manip.Eqp ), + MetaManipulation.Type.Gmp => ApplyMod( manip.Gmp ), + MetaManipulation.Type.Eqdp => ApplyMod( manip.Eqdp ), + MetaManipulation.Type.Est => ApplyMod( manip.Est ), + MetaManipulation.Type.Rsp => ApplyMod( manip.Rsp ), + MetaManipulation.Type.Imc => ApplyMod( manip.Imc ), + MetaManipulation.Type.Unknown => false, + _ => false, + }; + } + + public bool RevertMod( MetaManipulation manip ) + { + var ret = _manipulations.Remove( manip ); + if( !Penumbra.CharacterUtility.Ready ) + { + return ret; + } + + return manip.ManipulationType switch + { + MetaManipulation.Type.Eqp => RevertMod( manip.Eqp ), + MetaManipulation.Type.Gmp => RevertMod( manip.Gmp ), + MetaManipulation.Type.Eqdp => RevertMod( manip.Eqdp ), + MetaManipulation.Type.Est => RevertMod( manip.Est ), + MetaManipulation.Type.Rsp => RevertMod( manip.Rsp ), + MetaManipulation.Type.Imc => RevertMod( manip.Imc ), + MetaManipulation.Type.Unknown => false, + _ => false, + }; + } + + // Use this when CharacterUtility becomes ready. + private void ApplyStoredManipulations() + { + if( !Penumbra.CharacterUtility.Ready ) + { + return; + } + + foreach( var manip in Manipulations ) + { + var _ = manip.ManipulationType switch + { + MetaManipulation.Type.Eqp => ApplyMod( manip.Eqp ), + MetaManipulation.Type.Gmp => ApplyMod( manip.Gmp ), + MetaManipulation.Type.Eqdp => ApplyMod( manip.Eqdp ), + MetaManipulation.Type.Est => ApplyMod( manip.Est ), + MetaManipulation.Type.Rsp => ApplyMod( manip.Rsp ), + MetaManipulation.Type.Imc => ApplyMod( manip.Imc ), + MetaManipulation.Type.Unknown => false, + _ => false, + }; + } + + Penumbra.CharacterUtility.LoadingFinished -= ApplyStoredManipulations; + } [MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )] - public static unsafe void SetFile( MetaBaseFile? file, int index ) + private static unsafe void SetFile( MetaBaseFile? file, int index ) { if( file == null ) { @@ -30,99 +149,4 @@ public partial class MetaManager : IDisposable Penumbra.CharacterUtility.SetResource( index, ( IntPtr )file.Data, file.Length ); } } - - public bool TryGetValue( MetaManipulation manip, [NotNullWhen( true )] out IMod? mod ) - { - mod = manip.ManipulationType switch - { - MetaManipulation.Type.Eqp => Eqp.Manipulations.TryGetValue( manip.Eqp, out var m ) ? m : null, - MetaManipulation.Type.Gmp => Gmp.Manipulations.TryGetValue( manip.Gmp, out var m ) ? m : null, - MetaManipulation.Type.Eqdp => Eqdp.Manipulations.TryGetValue( manip.Eqdp, out var m ) ? m : null, - MetaManipulation.Type.Est => Est.Manipulations.TryGetValue( manip.Est, out var m ) ? m : null, - MetaManipulation.Type.Rsp => Cmp.Manipulations.TryGetValue( manip.Rsp, out var m ) ? m : null, - MetaManipulation.Type.Imc => Imc.Manipulations.TryGetValue( manip.Imc, out var m ) ? m : null, - _ => throw new ArgumentOutOfRangeException(), - }; - return mod != null; - } - - public int Count - => Imc.Manipulations.Count - + Eqdp.Manipulations.Count - + Cmp.Manipulations.Count - + Gmp.Manipulations.Count - + Est.Manipulations.Count - + Eqp.Manipulations.Count; - - public MetaManipulation[] Manipulations - => Imc.Manipulations.Keys.Select( m => ( MetaManipulation )m ) - .Concat( Eqdp.Manipulations.Keys.Select( m => ( MetaManipulation )m ) ) - .Concat( Cmp.Manipulations.Keys.Select( m => ( MetaManipulation )m ) ) - .Concat( Gmp.Manipulations.Keys.Select( m => ( MetaManipulation )m ) ) - .Concat( Est.Manipulations.Keys.Select( m => ( MetaManipulation )m ) ) - .Concat( Eqp.Manipulations.Keys.Select( m => ( MetaManipulation )m ) ) - .ToArray(); - - public MetaManager( ModCollection collection ) - => Imc = new MetaManagerImc( collection ); - - public void SetFiles() - { - Eqp.SetFiles(); - Eqdp.SetFiles(); - Gmp.SetFiles(); - Est.SetFiles(); - Cmp.SetFiles(); - Imc.SetFiles(); - } - - public void Reset() - { - Eqp.Reset(); - Eqdp.Reset(); - Gmp.Reset(); - Est.Reset(); - Cmp.Reset(); - Imc.Reset(); - } - - public void Dispose() - { - Eqp.Dispose(); - Eqdp.Dispose(); - Gmp.Dispose(); - Est.Dispose(); - Cmp.Dispose(); - Imc.Dispose(); - } - - public bool ApplyMod( MetaManipulation m, IMod mod ) - { - return m.ManipulationType switch - { - MetaManipulation.Type.Eqp => Eqp.ApplyMod( m.Eqp, mod ), - MetaManipulation.Type.Gmp => Gmp.ApplyMod( m.Gmp, mod ), - MetaManipulation.Type.Eqdp => Eqdp.ApplyMod( m.Eqdp, mod ), - MetaManipulation.Type.Est => Est.ApplyMod( m.Est, mod ), - MetaManipulation.Type.Rsp => Cmp.ApplyMod( m.Rsp, mod ), - MetaManipulation.Type.Imc => Imc.ApplyMod( m.Imc, mod ), - MetaManipulation.Type.Unknown => false, - _ => false, - }; - } - - public bool RevertMod( MetaManipulation m ) - { - return m.ManipulationType switch - { - MetaManipulation.Type.Eqp => Eqp.RevertMod( m.Eqp ), - MetaManipulation.Type.Gmp => Gmp.RevertMod( m.Gmp ), - MetaManipulation.Type.Eqdp => Eqdp.RevertMod( m.Eqdp ), - MetaManipulation.Type.Est => Est.RevertMod( m.Est ), - MetaManipulation.Type.Rsp => Cmp.RevertMod( m.Rsp ), - MetaManipulation.Type.Imc => Imc.RevertMod( m.Imc ), - MetaManipulation.Type.Unknown => false, - _ => false, - }; - } } \ No newline at end of file diff --git a/Penumbra/Meta/Manipulations/MetaManipulation.cs b/Penumbra/Meta/Manipulations/MetaManipulation.cs index 5732522d..bd0c132c 100644 --- a/Penumbra/Meta/Manipulations/MetaManipulation.cs +++ b/Penumbra/Meta/Manipulations/MetaManipulation.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Runtime.InteropServices; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -220,4 +219,16 @@ public readonly struct MetaManipulation : IEquatable< MetaManipulation >, ICompa return Functions.MemCmpUnchecked( lhs, &other, sizeof( MetaManipulation ) ); } } + + public override string ToString() + => ManipulationType switch + { + Type.Eqp => Eqp.ToString(), + Type.Gmp => Gmp.ToString(), + Type.Eqdp => Eqdp.ToString(), + Type.Est => Est.ToString(), + Type.Rsp => Rsp.ToString(), + Type.Imc => Imc.ToString(), + _ => throw new ArgumentOutOfRangeException(), + }; } \ No newline at end of file diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index 696ab08b..f61805f8 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -77,14 +77,14 @@ public class Penumbra : IDalamudPlugin Backup.CreateBackup( PenumbraBackupFiles() ); Config = Configuration.Load(); - ResidentResources = new ResidentResourceManager(); TempMods = new TempModManager(); MetaFileManager = new MetaFileManager(); ResourceLoader = new ResourceLoader( this ); ResourceLoader.EnableHooks(); - ResourceLogger = new ResourceLogger( ResourceLoader ); - CharacterUtility = new CharacterUtility(); - ModManager = new Mod.Manager( Config.ModDirectory ); + ResourceLogger = new ResourceLogger( ResourceLoader ); + CharacterUtility = new CharacterUtility(); + ResidentResources = new ResidentResourceManager(); + ModManager = new Mod.Manager( Config.ModDirectory ); ModManager.DiscoverMods(); CollectionManager = new ModCollection.Manager( ModManager ); ModFileSystem = ModFileSystem.Load(); @@ -96,8 +96,6 @@ public class Penumbra : IDalamudPlugin HelpMessage = "/penumbra - toggle ui\n/penumbra reload - reload mod file lists & discover any new mods", } ); - ResidentResources.Reload(); - SetupInterface( out _configWindow, out _launchButton, out _windowSystem ); if( Config.EnableMods ) @@ -122,7 +120,10 @@ public class Penumbra : IDalamudPlugin ResourceLoader.EnableFullLogging(); } - ResidentResources.Reload(); + if( CharacterUtility.Ready ) + { + ResidentResources.Reload(); + } Api = new PenumbraApi( this ); Ipc = new PenumbraIpc( Dalamud.PluginInterface, Api ); @@ -165,12 +166,15 @@ public class Penumbra : IDalamudPlugin Config.EnableMods = true; ResourceLoader.EnableReplacements(); - CollectionManager.Default.SetFiles(); - ResidentResources.Reload(); PathResolver.Enable(); - Config.Save(); - ObjectReloader.RedrawAll( RedrawType.Redraw ); + if( CharacterUtility.Ready ) + { + CollectionManager.Default.SetFiles(); + ResidentResources.Reload(); + ObjectReloader.RedrawAll( RedrawType.Redraw ); + } + return true; } @@ -183,12 +187,15 @@ public class Penumbra : IDalamudPlugin Config.EnableMods = false; ResourceLoader.DisableReplacements(); - CharacterUtility.ResetAll(); - ResidentResources.Reload(); PathResolver.Disable(); - Config.Save(); - ObjectReloader.RedrawAll( RedrawType.Redraw ); + if( CharacterUtility.Ready ) + { + CharacterUtility.ResetAll(); + ResidentResources.Reload(); + ObjectReloader.RedrawAll( RedrawType.Redraw ); + } + return true; } diff --git a/Penumbra/UI/ConfigWindow.EffectiveTab.cs b/Penumbra/UI/ConfigWindow.EffectiveTab.cs index b9a2213f..c45668ca 100644 --- a/Penumbra/UI/ConfigWindow.EffectiveTab.cs +++ b/Penumbra/UI/ConfigWindow.EffectiveTab.cs @@ -8,6 +8,7 @@ using OtterGui.Classes; using OtterGui.Raii; using Penumbra.Collections; using Penumbra.GameData.ByteString; +using Penumbra.Meta.Manipulations; using Penumbra.Mods; namespace Penumbra.UI; @@ -110,22 +111,10 @@ public partial class ConfigWindow // If no meta manipulations are active, we can just draw the end dummy. if( m is { Count: > 0 } ) { - // We can treat all meta manipulations the same, - // we are only really interested in their ToString function here. - static (object, IMod) Convert< T >( KeyValuePair< T, IMod > kvp ) - => ( kvp.Key!, kvp.Value ); - - var it = m.Cmp.Manipulations.Select( Convert ) - .Concat( m.Eqp.Manipulations.Select( Convert ) ) - .Concat( m.Eqdp.Manipulations.Select( Convert ) ) - .Concat( m.Gmp.Manipulations.Select( Convert ) ) - .Concat( m.Est.Manipulations.Select( Convert ) ) - .Concat( m.Imc.Manipulations.Select( Convert ) ); - // Filters mean we can not use the known counts. if( hasFilters ) { - var it2 = it.Select( p => ( p.Item1.ToString() ?? string.Empty, p.Item2.Name ) ); + var it2 = m.Select( p => ( p.Key.ToString(), p.Value.Name ) ); if( stop >= 0 ) { ImGuiClip.DrawEndDummy( stop + it2.Count( CheckFilters ), height ); @@ -144,7 +133,7 @@ public partial class ConfigWindow } else { - stop = ImGuiClip.ClippedDraw( it, skips, DrawLine, m.Count, ~stop ); + stop = ImGuiClip.ClippedDraw( m, skips, DrawLine, m.Count, ~stop ); ImGuiClip.DrawEndDummy( stop, height ); } } @@ -183,7 +172,7 @@ public partial class ConfigWindow } // Draw a line for a unfiltered/unconverted manipulation and mod-index pair. - private static void DrawLine( (object, IMod) pair ) + private static void DrawLine( KeyValuePair< MetaManipulation, IMod > pair ) { var (manipulation, mod) = pair; ImGui.TableNextColumn(); diff --git a/Penumbra/UI/ConfigWindow.SettingsTab.Advanced.cs b/Penumbra/UI/ConfigWindow.SettingsTab.Advanced.cs index 8b52adfb..1166bd58 100644 --- a/Penumbra/UI/ConfigWindow.SettingsTab.Advanced.cs +++ b/Penumbra/UI/ConfigWindow.SettingsTab.Advanced.cs @@ -135,7 +135,7 @@ public partial class ConfigWindow private static void DrawReloadResourceButton() { - if( ImGui.Button( "Reload Resident Resources" ) ) + if( ImGui.Button( "Reload Resident Resources" ) && Penumbra.CharacterUtility.Ready ) { Penumbra.ResidentResources.Reload(); } diff --git a/Penumbra/UI/LaunchButton.cs b/Penumbra/UI/LaunchButton.cs index 1bcdda4d..84d89e3e 100644 --- a/Penumbra/UI/LaunchButton.cs +++ b/Penumbra/UI/LaunchButton.cs @@ -9,20 +9,24 @@ namespace Penumbra.UI; // using the Dalamud-provided collapsible submenu. public class LaunchButton : IDisposable { - private readonly ConfigWindow _configWindow; - private readonly TextureWrap? _icon; - private readonly TitleScreenMenu.TitleScreenMenuEntry? _entry; + private readonly ConfigWindow _configWindow; + private TextureWrap? _icon; + private TitleScreenMenu.TitleScreenMenuEntry? _entry; public LaunchButton( ConfigWindow ui ) { _configWindow = ui; - - _icon = Dalamud.PluginInterface.UiBuilder.LoadImage( Path.Combine( Dalamud.PluginInterface.AssemblyLocation.DirectoryName!, - "tsmLogo.png" ) ); - if( _icon != null ) - { - _entry = Dalamud.TitleScreenMenu.AddEntry( "Manage Penumbra", _icon, OnTriggered ); - } + _icon = null; + _entry = null; + //Dalamud.Framework.RunOnTick( () => + //{ + // _icon = Dalamud.PluginInterface.UiBuilder.LoadImage( Path.Combine( Dalamud.PluginInterface.AssemblyLocation.DirectoryName!, + // "tsmLogo.png" ) ); + // if( _icon != null ) + // { + // _entry = Dalamud.TitleScreenMenu.AddEntry( "Manage Penumbra", _icon, OnTriggered ); + // } + //} ); } private void OnTriggered()