Make penumbra initialization before game code has run possible.

This commit is contained in:
Ottermandias 2022-06-29 11:42:55 +02:00
parent f13893cf77
commit 9ae843731d
19 changed files with 610 additions and 672 deletions

@ -1 +1 @@
Subproject commit 8053b24bb6b77a13853455c08a89df4894b3f2ee Subproject commit c5e54db0ae54b2eab943be9920e2888b0b4b1d13

View file

@ -456,7 +456,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
var collection = Penumbra.TempMods.Collections.TryGetValue( characterName, out var c ) var collection = Penumbra.TempMods.Collections.TryGetValue( characterName, out var c )
? c ? c
: Penumbra.CollectionManager.Character( characterName ); : 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 ); return Functions.ToCompressedBase64( set, MetaManipulation.CurrentVersion );
} }

View file

@ -110,8 +110,12 @@ public partial class ModCollection
{ {
case CollectionType.Default: case CollectionType.Default:
Default = newCollection; Default = newCollection;
if( Penumbra.CharacterUtility.Ready )
{
Penumbra.ResidentResources.Reload(); Penumbra.ResidentResources.Reload();
Default.SetFiles(); Default.SetFiles();
}
break; break;
case CollectionType.Current: case CollectionType.Current:
Current = newCollection; Current = newCollection;

View file

@ -116,68 +116,63 @@ public partial class ModCollection
} }
// Set Metadata files. // Set Metadata files.
[Conditional( "USE_EQP" )]
public void SetEqpFiles() public void SetEqpFiles()
{ {
if( _cache == null ) if( _cache == null )
{ {
MetaManager.MetaManagerEqp.ResetFiles(); MetaManager.ResetEqpFiles();
} }
else else
{ {
_cache.MetaManipulations.Eqp.SetFiles(); _cache.MetaManipulations.SetEqpFiles();
} }
} }
[Conditional( "USE_EQDP" )]
public void SetEqdpFiles() public void SetEqdpFiles()
{ {
if( _cache == null ) if( _cache == null )
{ {
MetaManager.MetaManagerEqdp.ResetFiles(); MetaManager.ResetEqdpFiles();
} }
else else
{ {
_cache.MetaManipulations.Eqdp.SetFiles(); _cache.MetaManipulations.SetEqdpFiles();
} }
} }
[Conditional( "USE_GMP" )]
public void SetGmpFiles() public void SetGmpFiles()
{ {
if( _cache == null ) if( _cache == null )
{ {
MetaManager.MetaManagerGmp.ResetFiles(); MetaManager.ResetGmpFiles();
} }
else else
{ {
_cache.MetaManipulations.Gmp.SetFiles(); _cache.MetaManipulations.SetGmpFiles();
} }
} }
[Conditional( "USE_EST" )]
public void SetEstFiles() public void SetEstFiles()
{ {
if( _cache == null ) if( _cache == null )
{ {
MetaManager.MetaManagerEst.ResetFiles(); MetaManager.ResetEstFiles();
} }
else else
{ {
_cache.MetaManipulations.Est.SetFiles(); _cache.MetaManipulations.SetEstFiles();
} }
} }
[Conditional( "USE_CMP" )]
public void SetCmpFiles() public void SetCmpFiles()
{ {
if( _cache == null ) if( _cache == null )
{ {
MetaManager.MetaManagerCmp.ResetFiles(); MetaManager.ResetCmpFiles();
} }
else else
{ {
_cache.MetaManipulations.Cmp.SetFiles(); _cache.MetaManipulations.SetCmpFiles();
} }
} }

View file

@ -175,7 +175,7 @@ public partial class ModCollection
++ChangeCounter; ++ChangeCounter;
if( _collection == Penumbra.CollectionManager.Default ) if( _collection == Penumbra.CollectionManager.Default && Penumbra.CharacterUtility.Ready )
{ {
Penumbra.ResidentResources.Reload(); Penumbra.ResidentResources.Reload();
MetaManipulations.SetFiles(); MetaManipulations.SetFiles();
@ -237,7 +237,7 @@ public partial class ModCollection
if( addMetaChanges ) if( addMetaChanges )
{ {
++ChangeCounter; ++ChangeCounter;
if( _collection == Penumbra.CollectionManager.Default ) if( _collection == Penumbra.CollectionManager.Default && Penumbra.CharacterUtility.Ready )
{ {
Penumbra.ResidentResources.Reload(); Penumbra.ResidentResources.Reload();
MetaManipulations.SetFiles(); MetaManipulations.SetFiles();
@ -295,7 +295,7 @@ public partial class ModCollection
AddMetaFiles(); AddMetaFiles();
} }
if( _collection == Penumbra.CollectionManager.Default ) if( _collection == Penumbra.CollectionManager.Default && Penumbra.CharacterUtility.Ready )
{ {
Penumbra.ResidentResources.Reload(); Penumbra.ResidentResources.Reload();
MetaManipulations.SetFiles(); MetaManipulations.SetFiles();
@ -441,7 +441,7 @@ public partial class ModCollection
// Add all necessary meta file redirects. // Add all necessary meta file redirects.
private void AddMetaFiles() private void AddMetaFiles()
=> MetaManipulations.Imc.SetFiles(); => MetaManipulations.SetImcFiles();
// Identify and record all manipulated objects for this entire collection. // Identify and record all manipulated objects for this entire collection.
private void SetChangedItems() private void SetChangedItems()

View file

@ -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 )] [Signature( "48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? 00 48 8D 8E ?? ?? 00 00 E8 ?? ?? ?? 00 33 D2", ScanType = ScanType.StaticAddress )]
private readonly Structs.CharacterUtility** _characterUtilityAddress = null; private readonly Structs.CharacterUtility** _characterUtilityAddress = null;
// Only required for migration anymore. // Only required for migration anymore.
public delegate void LoadResources( Structs.CharacterUtility* address ); public delegate void LoadResources( Structs.CharacterUtility* address );
@ -62,12 +61,12 @@ public unsafe class CharacterUtility : IDisposable
public CharacterUtility() public CharacterUtility()
{ {
SignatureHelper.Initialise( this ); SignatureHelper.Initialise( this );
Dalamud.Framework.Update += LoadDefaultResources;
LoadingFinished += () => PluginLog.Debug( "Loading of CharacterUtility finished." ); LoadingFinished += () => PluginLog.Debug( "Loading of CharacterUtility finished." );
LoadDefaultResources( true );
} }
// We store the default data of the resources so we can always restore them. // 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; var missingCount = 0;
if( Address == null ) if( Address == null )
@ -96,12 +95,7 @@ public unsafe class CharacterUtility : IDisposable
{ {
Ready = true; Ready = true;
LoadingFinished.Invoke(); LoadingFinished.Invoke();
} Dalamud.Framework.Update -= LoadDefaultResources;
else if( repeat )
{
PluginLog.Debug( "Custom load of character resources triggered." );
LoadCharacterResources();
LoadDefaultResources( false );
} }
} }

View file

@ -28,9 +28,12 @@ public unsafe class ResidentResourceManager
// Reload certain player resources by force. // Reload certain player resources by force.
public void Reload() public void Reload()
{
if( Address != null && Address->NumResources > 0 )
{ {
PluginLog.Debug( "Reload of resident resources triggered." ); PluginLog.Debug( "Reload of resident resources triggered." );
UnloadPlayerResources.Invoke( Address ); UnloadPlayerResources.Invoke( Address );
LoadPlayerResources.Invoke( Address ); LoadPlayerResources.Invoke( Address );
} }
}
} }

View file

@ -1,73 +1,57 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using OtterGui.Filesystem;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.Meta.Files; using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
using Penumbra.Mods;
namespace Penumbra.Meta.Manager; namespace Penumbra.Meta.Manager;
public partial class MetaManager public partial class MetaManager
{ {
public struct MetaManagerCmp : IDisposable private CmpFile? _cmpFile = null;
{ private readonly List< RspManipulation > _cmpManipulations = new();
public CmpFile? File = null;
public readonly Dictionary< RspManipulation, IMod > Manipulations = new();
public MetaManagerCmp() public void SetCmpFiles()
{ } => SetFile( _cmpFile, CharacterUtility.HumanCmpIdx );
[Conditional( "USE_CMP" )] public static void ResetCmpFiles()
public void SetFiles()
=> SetFile( File, CharacterUtility.HumanCmpIdx );
[Conditional( "USE_CMP" )]
public static void ResetFiles()
=> SetFile( null, CharacterUtility.HumanCmpIdx ); => SetFile( null, CharacterUtility.HumanCmpIdx );
[Conditional( "USE_CMP" )] public void ResetCmp()
public void Reset()
{ {
if( File == null ) if( _cmpFile == null )
{ {
return; return;
} }
File.Reset( Manipulations.Keys.Select( m => ( m.SubRace, m.Attribute ) ) ); _cmpFile.Reset( _cmpManipulations.Select( m => ( m.SubRace, m.Attribute ) ) );
Manipulations.Clear(); _cmpManipulations.Clear();
} }
public bool ApplyMod( RspManipulation m, IMod mod ) public bool ApplyMod( RspManipulation manip )
{ {
#if USE_CMP _cmpManipulations.AddOrReplace( manip );
Manipulations[ m ] = mod; _cmpFile ??= new CmpFile();
File ??= new CmpFile(); return manip.Apply( _cmpFile );
return m.Apply( File );
#else
return false;
#endif
} }
public bool RevertMod( RspManipulation m ) public bool RevertMod( RspManipulation manip )
{ {
#if USE_CMP if( _cmpManipulations.Remove( manip ) )
if( Manipulations.Remove( m ) )
{ {
var def = CmpFile.GetDefault( m.SubRace, m.Attribute ); var def = CmpFile.GetDefault( manip.SubRace, manip.Attribute );
var manip = new RspManipulation( m.SubRace, m.Attribute, def ); manip = new RspManipulation( manip.SubRace, manip.Attribute, def );
return manip.Apply( File! ); return manip.Apply( _cmpFile! );
} }
#endif
return false; return false;
} }
public void Dispose() public void DisposeCmp()
{ {
File?.Dispose(); _cmpFile?.Dispose();
File = null; _cmpFile = null;
Manipulations.Clear(); _cmpManipulations.Clear();
}
} }
} }

View file

@ -1,38 +1,29 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using OtterGui.Filesystem;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.Meta.Files; using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
using Penumbra.Mods;
using Penumbra.Util;
namespace Penumbra.Meta.Manager; namespace Penumbra.Meta.Manager;
public partial class MetaManager public partial class MetaManager
{ {
public struct MetaManagerEqdp : IDisposable private readonly ExpandedEqdpFile?[] _eqdpFiles = new ExpandedEqdpFile?[CharacterUtility.NumEqdpFiles - 2]; // TODO: female Hrothgar
{
public readonly ExpandedEqdpFile?[] Files = new ExpandedEqdpFile?[CharacterUtility.NumEqdpFiles - 2]; // TODO: female Hrothgar
public readonly Dictionary< EqdpManipulation, IMod > Manipulations = new(); private readonly List< EqdpManipulation > _eqdpManipulations = new();
public MetaManagerEqdp() public void SetEqdpFiles()
{ }
[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 ] ); SetFile( _eqdpFiles[ i ], CharacterUtility.EqdpIndices[ i ] );
} }
} }
[Conditional( "USE_EQDP" )] public static void ResetEqdpFiles()
public static void ResetFiles()
{ {
foreach( var idx in CharacterUtility.EqdpIndices ) foreach( var idx in CharacterUtility.EqdpIndices )
{ {
@ -40,55 +31,49 @@ public partial class MetaManager
} }
} }
[Conditional( "USE_EQDP" )] public void ResetEqdp()
public void Reset()
{ {
foreach( var file in Files ) foreach( var file in _eqdpFiles )
{ {
file?.Reset( Manipulations.Keys.Where( m => m.FileIndex() == file.Index ).Select( m => ( int )m.SetId ) ); file?.Reset( _eqdpManipulations.Where( m => m.FileIndex() == file.Index ).Select( m => ( int )m.SetId ) );
} }
Manipulations.Clear(); _eqdpManipulations.Clear();
} }
public bool ApplyMod( EqdpManipulation m, IMod mod ) public bool ApplyMod( EqdpManipulation manip )
{ {
#if USE_EQDP _eqdpManipulations.AddOrReplace( manip );
Manipulations[ m ] = mod; var file = _eqdpFiles[ Array.IndexOf( CharacterUtility.EqdpIndices, manip.FileIndex() ) ] ??=
var file = Files[ Array.IndexOf( CharacterUtility.EqdpIndices, m.FileIndex() ) ] ??= new ExpandedEqdpFile( Names.CombinedRace( manip.Gender, manip.Race ), manip.Slot.IsAccessory() ); // TODO: female Hrothgar
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 ); return manip.Apply( file );
} }
#endif
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; return false;
} }
public ExpandedEqdpFile? File( GenderRace race, bool accessory ) public ExpandedEqdpFile? EqdpFile( GenderRace race, bool accessory )
=> Files[ Array.IndexOf( CharacterUtility.EqdpIndices, CharacterUtility.EqdpIdx( race, accessory ) ) ]; // TODO: female Hrothgar => _eqdpFiles
[ Array.IndexOf( CharacterUtility.EqdpIndices, CharacterUtility.EqdpIdx( race, accessory ) ) ]; // TODO: female Hrothgar
public void Dispose() public void DisposeEqdp()
{ {
for( var i = 0; i < Files.Length; ++i ) for( var i = 0; i < _eqdpFiles.Length; ++i )
{ {
Files[ i ]?.Dispose(); _eqdpFiles[ i ]?.Dispose();
Files[ i ] = null; _eqdpFiles[ i ] = null;
} }
Manipulations.Clear(); _eqdpManipulations.Clear();
}
} }
} }

View file

@ -1,73 +1,58 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using OtterGui.Filesystem;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.Meta.Files; using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
using Penumbra.Mods;
namespace Penumbra.Meta.Manager; namespace Penumbra.Meta.Manager;
public partial class MetaManager public partial class MetaManager
{ {
public struct MetaManagerEqp : IDisposable private ExpandedEqpFile? _eqpFile = null;
{ private readonly List< EqpManipulation > _eqpManipulations = new();
public ExpandedEqpFile? File = null;
public readonly Dictionary< EqpManipulation, IMod > Manipulations = new();
public MetaManagerEqp() public void SetEqpFiles()
{ } => SetFile( _eqpFile, CharacterUtility.EqpIdx );
[Conditional( "USE_EQP" )] public static void ResetEqpFiles()
public void SetFiles()
=> SetFile( File, CharacterUtility.EqpIdx );
[Conditional( "USE_EQP" )]
public static void ResetFiles()
=> SetFile( null, CharacterUtility.EqpIdx ); => SetFile( null, CharacterUtility.EqpIdx );
[Conditional( "USE_EQP" )] public void ResetEqp()
public void Reset()
{ {
if( File == null ) if( _eqpFile == null )
{ {
return; return;
} }
File.Reset( Manipulations.Keys.Select( m => ( int )m.SetId ) ); _eqpFile.Reset( _eqpManipulations.Select( m => ( int )m.SetId ) );
Manipulations.Clear(); _eqpManipulations.Clear();
} }
public bool ApplyMod( EqpManipulation m, IMod mod ) public bool ApplyMod( EqpManipulation manip )
{ {
#if USE_EQP _eqpManipulations.AddOrReplace( manip );
Manipulations[ m ] = mod; _eqpFile ??= new ExpandedEqpFile();
File ??= new ExpandedEqpFile(); return manip.Apply( _eqpFile );
return m.Apply( File );
#else
return false;
#endif
} }
public bool RevertMod( EqpManipulation m ) public bool RevertMod( EqpManipulation manip )
{ {
#if USE_EQP var idx = _eqpManipulations.FindIndex( manip.Equals );
if( Manipulations.Remove( m ) ) if( idx >= 0 )
{ {
var def = ExpandedEqpFile.GetDefault( m.SetId ); var def = ExpandedEqpFile.GetDefault( manip.SetId );
var manip = new EqpManipulation( def, m.Slot, m.SetId ); manip = new EqpManipulation( def, manip.Slot, manip.SetId );
return manip.Apply( File! ); return manip.Apply( _eqpFile! );
} }
#endif
return false; return false;
} }
public void Dispose() public void DisposeEqp()
{ {
File?.Dispose(); _eqpFile?.Dispose();
File = null; _eqpFile = null;
Manipulations.Clear(); _eqpManipulations.Clear();
}
} }
} }

View file

@ -1,39 +1,31 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using OtterGui.Filesystem;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.Meta.Files; using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
using Penumbra.Mods;
namespace Penumbra.Meta.Manager; namespace Penumbra.Meta.Manager;
public partial class MetaManager 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; SetFile( _estFaceFile, CharacterUtility.FaceEstIdx );
public EstFile? HairFile = null; SetFile( _estHairFile, CharacterUtility.HairEstIdx );
public EstFile? BodyFile = null; SetFile( _estBodyFile, CharacterUtility.BodyEstIdx );
public EstFile? HeadFile = null; SetFile( _estHeadFile, CharacterUtility.HeadEstIdx );
public readonly Dictionary< EstManipulation, IMod > Manipulations = new();
public MetaManagerEst()
{ }
[Conditional( "USE_EST" )]
public void SetFiles()
{
SetFile( FaceFile, CharacterUtility.FaceEstIdx );
SetFile( HairFile, CharacterUtility.HairEstIdx );
SetFile( BodyFile, CharacterUtility.BodyEstIdx );
SetFile( HeadFile, CharacterUtility.HeadEstIdx );
} }
[Conditional( "USE_EST" )] public static void ResetEstFiles()
public static void ResetFiles()
{ {
SetFile( null, CharacterUtility.FaceEstIdx ); SetFile( null, CharacterUtility.FaceEstIdx );
SetFile( null, CharacterUtility.HairEstIdx ); SetFile( null, CharacterUtility.HairEstIdx );
@ -41,66 +33,59 @@ public partial class MetaManager
SetFile( null, CharacterUtility.HeadEstIdx ); SetFile( null, CharacterUtility.HeadEstIdx );
} }
[Conditional( "USE_EST" )] public void ResetEst()
public void Reset()
{ {
FaceFile?.Reset(); _estFaceFile?.Reset();
HairFile?.Reset(); _estHairFile?.Reset();
BodyFile?.Reset(); _estBodyFile?.Reset();
HeadFile?.Reset(); _estHeadFile?.Reset();
Manipulations.Clear(); _estManipulations.Clear();
} }
public bool ApplyMod( EstManipulation m, IMod mod ) public bool ApplyMod( EstManipulation m )
{ {
#if USE_EST _estManipulations.AddOrReplace( m );
Manipulations[ m ] = mod;
var file = m.Slot switch var file = m.Slot switch
{ {
EstManipulation.EstType.Hair => HairFile ??= new EstFile( EstManipulation.EstType.Hair ), EstManipulation.EstType.Hair => _estHairFile ??= new EstFile( EstManipulation.EstType.Hair ),
EstManipulation.EstType.Face => FaceFile ??= new EstFile( EstManipulation.EstType.Face ), EstManipulation.EstType.Face => _estFaceFile ??= new EstFile( EstManipulation.EstType.Face ),
EstManipulation.EstType.Body => BodyFile ??= new EstFile( EstManipulation.EstType.Body ), EstManipulation.EstType.Body => _estBodyFile ??= new EstFile( EstManipulation.EstType.Body ),
EstManipulation.EstType.Head => HeadFile ??= new EstFile( EstManipulation.EstType.Head ), EstManipulation.EstType.Head => _estHeadFile ??= new EstFile( EstManipulation.EstType.Head ),
_ => throw new ArgumentOutOfRangeException(), _ => throw new ArgumentOutOfRangeException(),
}; };
return m.Apply( file ); return m.Apply( file );
#else
return false;
#endif
} }
public bool RevertMod( EstManipulation m ) public bool RevertMod( EstManipulation m )
{ {
#if USE_EST if( _estManipulations.Remove( m ) )
if( Manipulations.Remove( m ) )
{ {
var def = EstFile.GetDefault( m.Slot, Names.CombinedRace( m.Gender, m.Race ), m.SetId ); 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 manip = new EstManipulation( m.Gender, m.Race, m.Slot, m.SetId, def );
var file = m.Slot switch var file = m.Slot switch
{ {
EstManipulation.EstType.Hair => HairFile!, EstManipulation.EstType.Hair => _estHairFile!,
EstManipulation.EstType.Face => FaceFile!, EstManipulation.EstType.Face => _estFaceFile!,
EstManipulation.EstType.Body => BodyFile!, EstManipulation.EstType.Body => _estBodyFile!,
EstManipulation.EstType.Head => HeadFile!, EstManipulation.EstType.Head => _estHeadFile!,
_ => throw new ArgumentOutOfRangeException(), _ => throw new ArgumentOutOfRangeException(),
}; };
return manip.Apply( file ); return manip.Apply( file );
} }
#endif
return false; return false;
} }
public void Dispose() public void DisposeEst()
{ {
FaceFile?.Dispose(); _estFaceFile?.Dispose();
HairFile?.Dispose(); _estHairFile?.Dispose();
BodyFile?.Dispose(); _estBodyFile?.Dispose();
HeadFile?.Dispose(); _estHeadFile?.Dispose();
FaceFile = null; _estFaceFile = null;
HairFile = null; _estHairFile = null;
BodyFile = null; _estBodyFile = null;
HeadFile = null; _estHeadFile = null;
Manipulations.Clear(); _estManipulations.Clear();
}
} }
} }

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using OtterGui.Filesystem;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.Meta.Files; using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
@ -11,62 +12,49 @@ namespace Penumbra.Meta.Manager;
public partial class MetaManager public partial class MetaManager
{ {
public struct MetaManagerGmp : IDisposable private ExpandedGmpFile? _gmpFile = null;
{ private readonly List< GmpManipulation > _gmpManipulations = new();
public ExpandedGmpFile? File = null;
public readonly Dictionary< GmpManipulation, IMod > Manipulations = new();
public MetaManagerGmp() public void SetGmpFiles()
{ } => SetFile( _gmpFile, CharacterUtility.GmpIdx );
public static void ResetGmpFiles()
[Conditional( "USE_GMP" )]
public void SetFiles()
=> SetFile( File, CharacterUtility.GmpIdx );
[Conditional( "USE_GMP" )]
public static void ResetFiles()
=> SetFile( null, CharacterUtility.GmpIdx ); => SetFile( null, CharacterUtility.GmpIdx );
[Conditional( "USE_GMP" )] public void ResetGmp()
public void Reset()
{ {
if( File != null ) if( _gmpFile == null )
{ {
File.Reset( Manipulations.Keys.Select( m => ( int )m.SetId ) ); return;
Manipulations.Clear();
}
} }
public bool ApplyMod( GmpManipulation m, IMod mod ) _gmpFile.Reset( _gmpManipulations.Select( m => ( int )m.SetId ) );
{ _gmpManipulations.Clear();
#if USE_GMP
Manipulations[ m ] = mod;
File ??= new ExpandedGmpFile();
return m.Apply( File );
#else
return false;
#endif
} }
public bool RevertMod( GmpManipulation m ) public bool ApplyMod( GmpManipulation manip )
{ {
#if USE_GMP _gmpManipulations.AddOrReplace( manip );
if( Manipulations.Remove( m ) ) _gmpFile ??= new ExpandedGmpFile();
{ return manip.Apply( _gmpFile );
var def = ExpandedGmpFile.GetDefault( m.SetId );
var manip = new GmpManipulation( def, m.SetId );
return manip.Apply( File! );
} }
#endif
public bool RevertMod( GmpManipulation manip )
{
if( _gmpManipulations.Remove( manip ) )
{
var def = ExpandedGmpFile.GetDefault( manip.SetId );
manip = new GmpManipulation( def, manip.SetId );
return manip.Apply( _gmpFile! );
}
return false; return false;
} }
public void Dispose() public void DisposeGmp()
{ {
File?.Dispose(); _gmpFile?.Dispose();
File = null; _gmpFile = null;
Manipulations.Clear(); _gmpManipulations.Clear();
}
} }
} }

View file

@ -1,54 +1,41 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using Dalamud.Logging; using Dalamud.Logging;
using FFXIVClientStructs.FFXIV.Client.System.Resource; using FFXIVClientStructs.FFXIV.Client.System.Resource;
using OtterGui.Filesystem;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.GameData.ByteString; using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.Meta.Files; using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
using Penumbra.Mods;
namespace Penumbra.Meta.Manager; namespace Penumbra.Meta.Manager;
public partial class MetaManager public partial class MetaManager
{ {
public readonly struct MetaManagerImc : IDisposable private readonly Dictionary< Utf8GamePath, ImcFile > _imcFiles = new();
{ private readonly List< ImcManipulation > _imcManipulations = new();
public readonly Dictionary< Utf8GamePath, ImcFile > Files = new();
public readonly Dictionary< ImcManipulation, IMod > Manipulations = new();
private readonly ModCollection _collection;
private static int _imcManagerCount; private static int _imcManagerCount;
public MetaManagerImc( ModCollection collection ) public void SetImcFiles()
{
_collection = collection;
SetupDelegate();
}
[Conditional( "USE_IMC" )]
public void SetFiles()
{ {
if( !_collection.HasCache ) if( !_collection.HasCache )
{ {
return; return;
} }
foreach( var path in Files.Keys ) foreach( var path in _imcFiles.Keys )
{ {
_collection.ForceFile( path, CreateImcPath( path ) ); _collection.ForceFile( path, CreateImcPath( path ) );
} }
} }
[Conditional( "USE_IMC" )] public void ResetImc()
public void Reset()
{ {
if( _collection.HasCache ) if( _collection.HasCache )
{ {
foreach( var (path, file) in Files ) foreach( var (path, file) in _imcFiles )
{ {
_collection.RemoveFile( path ); _collection.RemoveFile( path );
file.Reset(); file.Reset();
@ -56,33 +43,32 @@ public partial class MetaManager
} }
else else
{ {
foreach( var (_, file) in Files ) foreach( var (_, file) in _imcFiles )
{ {
file.Reset(); file.Reset();
} }
} }
Manipulations.Clear(); _imcManipulations.Clear();
} }
public bool ApplyMod( ImcManipulation m, IMod mod ) public bool ApplyMod( ImcManipulation manip )
{ {
#if USE_IMC _imcManipulations.AddOrReplace( manip );
Manipulations[ m ] = mod; var path = manip.GamePath();
var path = m.GamePath();
try try
{ {
if( !Files.TryGetValue( path, out var file ) ) if( !_imcFiles.TryGetValue( path, out var file ) )
{ {
file = new ImcFile( path ); file = new ImcFile( path );
} }
if( !m.Apply( file ) ) if( !manip.Apply( file ) )
{ {
return false; return false;
} }
Files[ path ] = file; _imcFiles[ path ] = file;
var fullPath = CreateImcPath( path ); var fullPath = CreateImcPath( path );
if( _collection.HasCache ) if( _collection.HasCache )
{ {
@ -97,18 +83,15 @@ public partial class MetaManager
PluginLog.Error( $"Could not apply IMC Manipulation:\n{e}" ); PluginLog.Error( $"Could not apply IMC Manipulation:\n{e}" );
return false; return false;
} }
#else
return false;
#endif
} }
public bool RevertMod( ImcManipulation m ) public bool RevertMod( ImcManipulation m )
{ {
#if USE_IMC #if USE_IMC
if( Manipulations.Remove( m ) ) if( _imcManipulations.Remove( m ) )
{ {
var path = m.GamePath(); var path = m.GamePath();
if( !Files.TryGetValue( path, out var file ) ) if( !_imcFiles.TryGetValue( path, out var file ) )
{ {
return false; return false;
} }
@ -132,20 +115,19 @@ public partial class MetaManager
return false; return false;
} }
public void Dispose() public void DisposeImc()
{ {
foreach( var file in Files.Values ) foreach( var file in _imcFiles.Values )
{ {
file.Dispose(); file.Dispose();
} }
Files.Clear(); _imcFiles.Clear();
Manipulations.Clear(); _imcManipulations.Clear();
RestoreDelegate(); RestoreImcDelegate();
} }
[Conditional( "USE_IMC" )] private static unsafe void SetupImcDelegate()
private static unsafe void SetupDelegate()
{ {
if( _imcManagerCount++ == 0 ) if( _imcManagerCount++ == 0 )
{ {
@ -154,8 +136,7 @@ public partial class MetaManager
} }
} }
[Conditional( "USE_IMC" )] private static unsafe void RestoreImcDelegate()
private static unsafe void RestoreDelegate()
{ {
if( --_imcManagerCount == 0 ) if( --_imcManagerCount == 0 )
{ {
@ -167,6 +148,7 @@ public partial class MetaManager
private FullPath CreateImcPath( Utf8GamePath path ) private FullPath CreateImcPath( Utf8GamePath path )
=> new($"|{_collection.Name}_{_collection.RecomputeCounter}|{path}"); => new($"|{_collection.Name}_{_collection.RecomputeCounter}|{path}");
private static unsafe bool ImcLoadHandler( Utf8String split, Utf8String path, ResourceManager* resourceManager, private static unsafe bool ImcLoadHandler( Utf8String split, Utf8String path, ResourceManager* resourceManager,
SeFileDescriptor* fileDescriptor, int priority, bool isSync, out byte ret ) SeFileDescriptor* fileDescriptor, int priority, bool isSync, out byte ret )
{ {
@ -183,8 +165,7 @@ public partial class MetaManager
var name = lastUnderscore == -1 ? split.ToString() : split.Substring( 0, lastUnderscore ).ToString(); var name = lastUnderscore == -1 ? split.ToString() : split.Substring( 0, lastUnderscore ).ToString();
if( Penumbra.CollectionManager.ByName( name, out var collection ) if( Penumbra.CollectionManager.ByName( name, out var collection )
&& collection.HasCache && collection.HasCache
&& collection.MetaCache!.Imc.Files.TryGetValue( && collection.MetaCache!._imcFiles.TryGetValue( Utf8GamePath.FromSpan( path.Span, out var p ) ? p : Utf8GamePath.Empty, out var file ) )
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, PluginLog.Debug( "Loaded {GamePath:l} from file and replaced with IMC from collection {Collection:l}.", path,
collection.Name ); collection.Name );
@ -200,7 +181,7 @@ public partial class MetaManager
// Only check imcs. // Only check imcs.
if( resource->FileType != ResourceType.Imc if( resource->FileType != ResourceType.Imc
|| resolveData is not ModCollection { HasCache: true } collection || resolveData is not ModCollection { HasCache: true } collection
|| !collection.MetaCache!.Imc.Files.TryGetValue( gamePath, out var file ) || !collection.MetaCache!._imcFiles.TryGetValue( gamePath, out var file )
|| !file.ChangesSinceLoad ) || !file.ChangesSinceLoad )
{ {
return; return;
@ -211,5 +192,4 @@ public partial class MetaManager
file.Replace( resource, false ); file.Replace( resource, false );
file.ChangesSinceLoad = false; file.ChangesSinceLoad = false;
} }
}
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.Meta.Files; using Penumbra.Meta.Files;
@ -9,17 +10,135 @@ using Penumbra.Mods;
namespace Penumbra.Meta.Manager; namespace Penumbra.Meta.Manager;
public partial class MetaManager : IDisposable public partial class MetaManager : IDisposable, IEnumerable<KeyValuePair<MetaManipulation, IMod>>
{ {
public MetaManagerEqp Eqp = new(); private readonly Dictionary< MetaManipulation, IMod > _manipulations = new();
public MetaManagerEqdp Eqdp = new(); private readonly ModCollection _collection;
public MetaManagerGmp Gmp = new();
public MetaManagerEst Est = new(); public bool TryGetValue( MetaManipulation manip, [NotNullWhen( true )] out IMod? mod )
public MetaManagerCmp Cmp = new(); => _manipulations.TryGetValue( manip, out mod );
public MetaManagerImc Imc;
public int Count
=> _manipulations.Count;
public IReadOnlyCollection< MetaManipulation > Manipulations
=> _manipulations.Keys;
public IEnumerator<KeyValuePair<MetaManipulation, IMod>> 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 )] [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 ) if( file == null )
{ {
@ -30,99 +149,4 @@ public partial class MetaManager : IDisposable
Penumbra.CharacterUtility.SetResource( index, ( IntPtr )file.Data, file.Length ); 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,
};
}
} }

View file

@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
@ -220,4 +219,16 @@ public readonly struct MetaManipulation : IEquatable< MetaManipulation >, ICompa
return Functions.MemCmpUnchecked( lhs, &other, sizeof( MetaManipulation ) ); 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(),
};
} }

View file

@ -77,13 +77,13 @@ public class Penumbra : IDalamudPlugin
Backup.CreateBackup( PenumbraBackupFiles() ); Backup.CreateBackup( PenumbraBackupFiles() );
Config = Configuration.Load(); Config = Configuration.Load();
ResidentResources = new ResidentResourceManager();
TempMods = new TempModManager(); TempMods = new TempModManager();
MetaFileManager = new MetaFileManager(); MetaFileManager = new MetaFileManager();
ResourceLoader = new ResourceLoader( this ); ResourceLoader = new ResourceLoader( this );
ResourceLoader.EnableHooks(); ResourceLoader.EnableHooks();
ResourceLogger = new ResourceLogger( ResourceLoader ); ResourceLogger = new ResourceLogger( ResourceLoader );
CharacterUtility = new CharacterUtility(); CharacterUtility = new CharacterUtility();
ResidentResources = new ResidentResourceManager();
ModManager = new Mod.Manager( Config.ModDirectory ); ModManager = new Mod.Manager( Config.ModDirectory );
ModManager.DiscoverMods(); ModManager.DiscoverMods();
CollectionManager = new ModCollection.Manager( ModManager ); CollectionManager = new ModCollection.Manager( ModManager );
@ -96,8 +96,6 @@ public class Penumbra : IDalamudPlugin
HelpMessage = "/penumbra - toggle ui\n/penumbra reload - reload mod file lists & discover any new mods", HelpMessage = "/penumbra - toggle ui\n/penumbra reload - reload mod file lists & discover any new mods",
} ); } );
ResidentResources.Reload();
SetupInterface( out _configWindow, out _launchButton, out _windowSystem ); SetupInterface( out _configWindow, out _launchButton, out _windowSystem );
if( Config.EnableMods ) if( Config.EnableMods )
@ -122,7 +120,10 @@ public class Penumbra : IDalamudPlugin
ResourceLoader.EnableFullLogging(); ResourceLoader.EnableFullLogging();
} }
if( CharacterUtility.Ready )
{
ResidentResources.Reload(); ResidentResources.Reload();
}
Api = new PenumbraApi( this ); Api = new PenumbraApi( this );
Ipc = new PenumbraIpc( Dalamud.PluginInterface, Api ); Ipc = new PenumbraIpc( Dalamud.PluginInterface, Api );
@ -165,12 +166,15 @@ public class Penumbra : IDalamudPlugin
Config.EnableMods = true; Config.EnableMods = true;
ResourceLoader.EnableReplacements(); ResourceLoader.EnableReplacements();
PathResolver.Enable();
Config.Save();
if( CharacterUtility.Ready )
{
CollectionManager.Default.SetFiles(); CollectionManager.Default.SetFiles();
ResidentResources.Reload(); ResidentResources.Reload();
PathResolver.Enable();
Config.Save();
ObjectReloader.RedrawAll( RedrawType.Redraw ); ObjectReloader.RedrawAll( RedrawType.Redraw );
}
return true; return true;
} }
@ -183,12 +187,15 @@ public class Penumbra : IDalamudPlugin
Config.EnableMods = false; Config.EnableMods = false;
ResourceLoader.DisableReplacements(); ResourceLoader.DisableReplacements();
PathResolver.Disable();
Config.Save();
if( CharacterUtility.Ready )
{
CharacterUtility.ResetAll(); CharacterUtility.ResetAll();
ResidentResources.Reload(); ResidentResources.Reload();
PathResolver.Disable();
Config.Save();
ObjectReloader.RedrawAll( RedrawType.Redraw ); ObjectReloader.RedrawAll( RedrawType.Redraw );
}
return true; return true;
} }

View file

@ -8,6 +8,7 @@ using OtterGui.Classes;
using OtterGui.Raii; using OtterGui.Raii;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.GameData.ByteString; using Penumbra.GameData.ByteString;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods; using Penumbra.Mods;
namespace Penumbra.UI; 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 no meta manipulations are active, we can just draw the end dummy.
if( m is { Count: > 0 } ) 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. // Filters mean we can not use the known counts.
if( hasFilters ) 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 ) if( stop >= 0 )
{ {
ImGuiClip.DrawEndDummy( stop + it2.Count( CheckFilters ), height ); ImGuiClip.DrawEndDummy( stop + it2.Count( CheckFilters ), height );
@ -144,7 +133,7 @@ public partial class ConfigWindow
} }
else else
{ {
stop = ImGuiClip.ClippedDraw( it, skips, DrawLine, m.Count, ~stop ); stop = ImGuiClip.ClippedDraw( m, skips, DrawLine, m.Count, ~stop );
ImGuiClip.DrawEndDummy( stop, height ); ImGuiClip.DrawEndDummy( stop, height );
} }
} }
@ -183,7 +172,7 @@ public partial class ConfigWindow
} }
// Draw a line for a unfiltered/unconverted manipulation and mod-index pair. // 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; var (manipulation, mod) = pair;
ImGui.TableNextColumn(); ImGui.TableNextColumn();

View file

@ -135,7 +135,7 @@ public partial class ConfigWindow
private static void DrawReloadResourceButton() private static void DrawReloadResourceButton()
{ {
if( ImGui.Button( "Reload Resident Resources" ) ) if( ImGui.Button( "Reload Resident Resources" ) && Penumbra.CharacterUtility.Ready )
{ {
Penumbra.ResidentResources.Reload(); Penumbra.ResidentResources.Reload();
} }

View file

@ -10,19 +10,23 @@ namespace Penumbra.UI;
public class LaunchButton : IDisposable public class LaunchButton : IDisposable
{ {
private readonly ConfigWindow _configWindow; private readonly ConfigWindow _configWindow;
private readonly TextureWrap? _icon; private TextureWrap? _icon;
private readonly TitleScreenMenu.TitleScreenMenuEntry? _entry; private TitleScreenMenu.TitleScreenMenuEntry? _entry;
public LaunchButton( ConfigWindow ui ) public LaunchButton( ConfigWindow ui )
{ {
_configWindow = ui; _configWindow = ui;
_icon = null;
_icon = Dalamud.PluginInterface.UiBuilder.LoadImage( Path.Combine( Dalamud.PluginInterface.AssemblyLocation.DirectoryName!, _entry = null;
"tsmLogo.png" ) ); //Dalamud.Framework.RunOnTick( () =>
if( _icon != null ) //{
{ // _icon = Dalamud.PluginInterface.UiBuilder.LoadImage( Path.Combine( Dalamud.PluginInterface.AssemblyLocation.DirectoryName!,
_entry = Dalamud.TitleScreenMenu.AddEntry( "Manage Penumbra", _icon, OnTriggered ); // "tsmLogo.png" ) );
} // if( _icon != null )
// {
// _entry = Dalamud.TitleScreenMenu.AddEntry( "Manage Penumbra", _icon, OnTriggered );
// }
//} );
} }
private void OnTriggered() private void OnTriggered()