This commit is contained in:
Ottermandias 2023-04-15 20:40:00 +02:00
parent 9037166d92
commit 0186f176d0
11 changed files with 356 additions and 461 deletions

View file

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using OtterGui.Filesystem;
using Penumbra.Interop.Services;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections.Cache;
public struct CmpCache : IDisposable
{
private CmpFile? _cmpFile = null;
private readonly List< RspManipulation > _cmpManipulations = new();
public CmpCache()
{}
public void SetFiles(CollectionCacheManager manager)
=> manager.SetFile( _cmpFile, MetaIndex.HumanCmp );
public CharacterUtility.MetaList.MetaReverter TemporarilySetFiles(CollectionCacheManager manager)
=> manager.TemporarilySetFile( _cmpFile, MetaIndex.HumanCmp );
public bool ApplyMod( CollectionCacheManager manager, RspManipulation manip )
{
_cmpManipulations.AddOrReplace( manip );
_cmpFile ??= new CmpFile();
return manip.Apply( _cmpFile );
}
public bool RevertMod( CollectionCacheManager manager, RspManipulation manip )
{
if (!_cmpManipulations.Remove(manip))
return false;
var def = CmpFile.GetDefault( manip.SubRace, manip.Attribute );
manip = new RspManipulation( manip.SubRace, manip.Attribute, def );
return manip.Apply( _cmpFile! );
}
public void Dispose()
{
_cmpFile?.Dispose();
_cmpFile = null;
_cmpManipulations.Clear();
}
}

View file

@ -17,15 +17,17 @@ namespace Penumbra.Collections.Cache;
public class CollectionCacheManager : IDisposable
{
private readonly FrameworkManager _framework;
private readonly ActiveCollections _active;
private readonly CommunicatorService _communicator;
private readonly CharacterUtility _characterUtility;
private readonly TempModManager _tempMods;
private readonly ModStorage _modStorage;
private readonly ModCacheManager _modCaches;
private readonly Configuration _config;
private readonly ResidentResourceManager _resources;
private readonly FrameworkManager _framework;
private readonly ActiveCollections _active;
private readonly CommunicatorService _communicator;
private readonly TempModManager _tempMods;
private readonly ModStorage _modStorage;
private readonly ModCacheManager _modCaches;
private readonly Configuration _config;
internal readonly ValidityChecker ValidityChecker;
internal readonly CharacterUtility CharacterUtility;
internal readonly ResidentResourceManager ResidentResources;
private readonly Dictionary<ModCollection, CollectionCache> _caches = new();
@ -37,17 +39,18 @@ public class CollectionCacheManager : IDisposable
public CollectionCacheManager(FrameworkManager framework, ActiveCollections active, CommunicatorService communicator,
CharacterUtility characterUtility, TempModManager tempMods, ModStorage modStorage, Configuration config,
ResidentResourceManager resources, ModCacheManager modCaches)
ResidentResourceManager residentResources, ModCacheManager modCaches, ValidityChecker validityChecker)
{
_framework = framework;
_active = active;
_communicator = communicator;
_characterUtility = characterUtility;
CharacterUtility = characterUtility;
_tempMods = tempMods;
_modStorage = modStorage;
_config = config;
_resources = resources;
ResidentResources = residentResources;
_modCaches = modCaches;
ValidityChecker = validityChecker;
_communicator.CollectionChange.Subscribe(OnCollectionChange);
_communicator.ModPathChanged.Subscribe(OnModChangeAddition, -100);
@ -58,8 +61,8 @@ public class CollectionCacheManager : IDisposable
_communicator.CollectionInheritanceChanged.Subscribe(OnCollectionInheritanceChange);
CreateNecessaryCaches();
if (!_characterUtility.Ready)
_characterUtility.LoadingFinished += IncrementCounters;
if (!CharacterUtility.Ready)
CharacterUtility.LoadingFinished += IncrementCounters;
}
public void Dispose()
@ -71,7 +74,7 @@ public class CollectionCacheManager : IDisposable
_communicator.ModOptionChanged.Unsubscribe(OnModOptionChange);
_communicator.ModSettingChanged.Unsubscribe(OnModSettingChange);
_communicator.CollectionInheritanceChanged.Unsubscribe(OnCollectionInheritanceChange);
_characterUtility.LoadingFinished -= IncrementCounters;
CharacterUtility.LoadingFinished -= IncrementCounters;
}
/// <summary> Only creates a new cache, does not update an existing one. </summary>
@ -80,7 +83,7 @@ public class CollectionCacheManager : IDisposable
if (_caches.ContainsKey(collection) || collection.Index == ModCollection.Empty.Index)
return false;
var cache = new CollectionCache(collection);
var cache = new CollectionCache(this, collection);
_caches.Add(collection, cache);
collection._cache = cache;
Penumbra.Log.Verbose($"Created new cache for collection {collection.AnonymizedName}.");
@ -95,6 +98,9 @@ public class CollectionCacheManager : IDisposable
=> _framework.RegisterImportant(nameof(CalculateEffectiveFileList) + collection.Name,
() => CalculateEffectiveFileListInternal(collection));
public bool IsDefault(ModCollection collection)
=> _active.Default == collection;
private void CalculateEffectiveFileListInternal(ModCollection collection)
{
// Skip the empty collection.
@ -133,10 +139,10 @@ public class CollectionCacheManager : IDisposable
++collection.ChangeCounter;
if (_active.Default != collection || !_characterUtility.Ready || !_config.EnableMods)
if (_active.Default != collection || !CharacterUtility.Ready || !_config.EnableMods)
return;
_resources.Reload();
ResidentResources.Reload();
cache.MetaManipulations.SetFiles();
}
@ -232,7 +238,7 @@ public class CollectionCacheManager : IDisposable
{
foreach (var (collection, _) in _caches)
++collection.ChangeCounter;
_characterUtility.LoadingFinished -= IncrementCounters;
CharacterUtility.LoadingFinished -= IncrementCounters;
}
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx, bool _)

View file

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OtterGui;
using OtterGui.Filesystem;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Services;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections.Cache;
public readonly struct EqdpCache : IDisposable
{
private readonly ExpandedEqdpFile?[] _eqdpFiles = new ExpandedEqdpFile[CharacterUtilityData.EqdpIndices.Length]; // TODO: female Hrothgar
private readonly List<EqdpManipulation> _eqdpManipulations = new();
public EqdpCache()
{ }
public void SetFiles(CollectionCacheManager manager)
{
for (var i = 0; i < CharacterUtilityData.EqdpIndices.Length; ++i)
manager.SetFile(_eqdpFiles[i], CharacterUtilityData.EqdpIndices[i]);
}
public CharacterUtility.MetaList.MetaReverter? TemporarilySetEqdpFile(CollectionCacheManager manager, GenderRace genderRace, bool accessory)
{
var idx = CharacterUtilityData.EqdpIdx(genderRace, accessory);
if ((int)idx == -1)
return null;
var i = CharacterUtilityData.EqdpIndices.IndexOf(idx);
return i != -1 ? manager.TemporarilySetFile(_eqdpFiles[i], idx) : null;
}
public void Reset(CollectionCacheManager manager)
{
foreach (var file in _eqdpFiles.OfType<ExpandedEqdpFile>())
{
var relevant = CharacterUtility.RelevantIndices[file.Index.Value];
file.Reset(_eqdpManipulations.Where(m => m.FileIndex() == relevant).Select(m => (int)m.SetId));
}
_eqdpManipulations.Clear();
}
public bool ApplyMod(CollectionCacheManager manager, EqdpManipulation manip)
{
_eqdpManipulations.AddOrReplace(manip);
var file = _eqdpFiles[Array.IndexOf(CharacterUtilityData.EqdpIndices, manip.FileIndex())] ??=
new ExpandedEqdpFile(Names.CombinedRace(manip.Gender, manip.Race), manip.Slot.IsAccessory()); // TODO: female Hrothgar
return manip.Apply(file);
}
public bool RevertMod(CollectionCacheManager manager, EqdpManipulation manip)
{
if (!_eqdpManipulations.Remove(manip))
return false;
var def = ExpandedEqdpFile.GetDefault(Names.CombinedRace(manip.Gender, manip.Race), manip.Slot.IsAccessory(), manip.SetId);
var file = _eqdpFiles[Array.IndexOf(CharacterUtilityData.EqdpIndices, manip.FileIndex())]!;
manip = new EqdpManipulation(def, manip.Slot, manip.Gender, manip.Race, manip.SetId);
return manip.Apply(file);
}
public ExpandedEqdpFile? EqdpFile(GenderRace race, bool accessory)
=> _eqdpFiles
[Array.IndexOf(CharacterUtilityData.EqdpIndices, CharacterUtilityData.EqdpIdx(race, accessory))]; // TODO: female Hrothgar
public void Dispose()
{
for (var i = 0; i < _eqdpFiles.Length; ++i)
{
_eqdpFiles[i]?.Dispose();
_eqdpFiles[i] = null;
}
_eqdpManipulations.Clear();
}
}

View file

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using OtterGui.Filesystem;
using Penumbra.Interop.Services;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections.Cache;
public struct EqpCache : IDisposable
{
private ExpandedEqpFile? _eqpFile = null;
private readonly List< EqpManipulation > _eqpManipulations = new();
public EqpCache()
{}
public void SetFiles(CollectionCacheManager manager)
=> manager.SetFile( _eqpFile, MetaIndex.Eqp );
public static void ResetFiles(CollectionCacheManager manager)
=> manager.SetFile( null, MetaIndex.Eqp );
public CharacterUtility.MetaList.MetaReverter TemporarilySetFiles(CollectionCacheManager manager)
=> manager.TemporarilySetFile( _eqpFile, MetaIndex.Eqp );
public bool ApplyMod( CollectionCacheManager manager, EqpManipulation manip )
{
_eqpManipulations.AddOrReplace( manip );
_eqpFile ??= new ExpandedEqpFile();
return manip.Apply( _eqpFile );
}
public bool RevertMod( CollectionCacheManager manager, EqpManipulation manip )
{
var idx = _eqpManipulations.FindIndex( manip.Equals );
if (idx < 0)
return false;
var def = ExpandedEqpFile.GetDefault( manip.SetId );
manip = new EqpManipulation( def, manip.Slot, manip.SetId );
return manip.Apply( _eqpFile! );
}
public void Dispose()
{
_eqpFile?.Dispose();
_eqpFile = null;
_eqpManipulations.Clear();
}
}

View file

@ -9,32 +9,27 @@ using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections.Cache;
public partial class MetaCache
public struct EstCache : IDisposable
{
private EstFile? _estFaceFile = null;
private EstFile? _estHairFile = null;
private EstFile? _estBodyFile = null;
private EstFile? _estHeadFile = null;
private readonly List< EstManipulation > _estManipulations = new();
private readonly List< EstManipulation > _estManipulations = new();
public EstCache()
{}
public void SetEstFiles()
public void SetFiles(CollectionCacheManager manager)
{
SetFile( _estFaceFile, MetaIndex.FaceEst );
SetFile( _estHairFile, MetaIndex.HairEst );
SetFile( _estBodyFile, MetaIndex.BodyEst );
SetFile( _estHeadFile, MetaIndex.HeadEst );
manager.SetFile( _estFaceFile, MetaIndex.FaceEst );
manager.SetFile( _estHairFile, MetaIndex.HairEst );
manager.SetFile( _estBodyFile, MetaIndex.BodyEst );
manager.SetFile( _estHeadFile, MetaIndex.HeadEst );
}
public static void ResetEstFiles()
{
SetFile( null, MetaIndex.FaceEst );
SetFile( null, MetaIndex.HairEst );
SetFile( null, MetaIndex.BodyEst );
SetFile( null, MetaIndex.HeadEst );
}
public CharacterUtility.MetaList.MetaReverter? TemporarilySetEstFile(EstManipulation.EstType type)
public CharacterUtility.MetaList.MetaReverter? TemporarilySetFiles(CollectionCacheManager manager, EstManipulation.EstType type)
{
var (file, idx) = type switch
{
@ -45,10 +40,10 @@ public partial class MetaCache
_ => ( null, 0 ),
};
return idx != 0 ? TemporarilySetFile( file, idx ) : null;
return idx != 0 ? manager.TemporarilySetFile( file, idx ) : null;
}
public void ResetEst()
public void Reset()
{
_estFaceFile?.Reset();
_estHairFile?.Reset();
@ -57,7 +52,7 @@ public partial class MetaCache
_estManipulations.Clear();
}
public bool ApplyMod( EstManipulation m )
public bool ApplyMod( CollectionCacheManager manager, EstManipulation m )
{
_estManipulations.AddOrReplace( m );
var file = m.Slot switch
@ -71,27 +66,26 @@ public partial class MetaCache
return m.Apply( file );
}
public bool RevertMod( EstManipulation m )
public bool RevertMod( CollectionCacheManager manager, EstManipulation m )
{
if( _estManipulations.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 => _estHairFile!,
EstManipulation.EstType.Face => _estFaceFile!,
EstManipulation.EstType.Body => _estBodyFile!,
EstManipulation.EstType.Head => _estHeadFile!,
_ => throw new ArgumentOutOfRangeException(),
};
return manip.Apply( file );
}
if (!_estManipulations.Remove(m))
return false;
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 => _estHairFile!,
EstManipulation.EstType.Face => _estFaceFile!,
EstManipulation.EstType.Body => _estBodyFile!,
EstManipulation.EstType.Head => _estHeadFile!,
_ => throw new ArgumentOutOfRangeException(),
};
return manip.Apply( file );
return false;
}
public void DisposeEst()
public void Dispose()
{
_estFaceFile?.Dispose();
_estHairFile?.Dispose();

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OtterGui.Filesystem;
@ -8,51 +9,47 @@ using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections.Cache;
public partial class MetaCache
public struct GmpCache : IDisposable
{
private ExpandedGmpFile? _gmpFile = null;
private readonly List< GmpManipulation > _gmpManipulations = new();
public GmpCache()
{}
public void SetGmpFiles()
=> SetFile( _gmpFile, MetaIndex.Gmp );
public void SetFiles(CollectionCacheManager manager)
=> manager.SetFile( _gmpFile, MetaIndex.Gmp );
public static void ResetGmpFiles()
=> SetFile( null, MetaIndex.Gmp );
public CharacterUtility.MetaList.MetaReverter TemporarilySetFiles(CollectionCacheManager manager)
=> manager.TemporarilySetFile( _gmpFile, MetaIndex.Gmp );
public CharacterUtility.MetaList.MetaReverter TemporarilySetGmpFile()
=> TemporarilySetFile( _gmpFile, MetaIndex.Gmp );
public void ResetGmp()
public void ResetGmp(CollectionCacheManager manager)
{
if( _gmpFile == null )
{
return;
}
_gmpFile.Reset( _gmpManipulations.Select( m => ( int )m.SetId ) );
_gmpManipulations.Clear();
}
public bool ApplyMod( GmpManipulation manip )
public bool ApplyMod( CollectionCacheManager manager, GmpManipulation manip )
{
_gmpManipulations.AddOrReplace( manip );
_gmpFile ??= new ExpandedGmpFile();
return manip.Apply( _gmpFile );
}
public bool RevertMod( GmpManipulation manip )
public bool RevertMod( CollectionCacheManager manager, GmpManipulation manip )
{
if( _gmpManipulations.Remove( manip ) )
{
var def = ExpandedGmpFile.GetDefault( manip.SetId );
manip = new GmpManipulation( def, manip.SetId );
return manip.Apply( _gmpFile! );
}
if (!_gmpManipulations.Remove(manip))
return false;
return false;
var def = ExpandedGmpFile.GetDefault( manip.SetId );
manip = new GmpManipulation( def, manip.SetId );
return manip.Apply( _gmpFile! );
}
public void DisposeGmp()
public void Dispose()
{
_gmpFile?.Dispose();
_gmpFile = null;

View file

@ -8,46 +8,32 @@ using Penumbra.String.Classes;
namespace Penumbra.Collections.Cache;
public partial class MetaCache
public readonly struct ImcCache : IDisposable
{
private readonly Dictionary< Utf8GamePath, ImcFile > _imcFiles = new();
private readonly List< ImcManipulation > _imcManipulations = new();
public void SetImcFiles()
{
if( !_collection.HasCache )
{
return;
}
public ImcCache()
{ }
public void SetFiles(CollectionCacheManager manager, ModCollection collection)
{
foreach( var path in _imcFiles.Keys )
{
_collection.ForceFile( path, CreateImcPath( path ) );
}
collection._cache!.ForceFile( path, CreateImcPath( collection, path ) );
}
public void ResetImc()
public void Reset(CollectionCacheManager manager, ModCollection collection)
{
if( _collection.HasCache )
foreach( var (path, file) in _imcFiles )
{
foreach( var (path, file) in _imcFiles )
{
_collection.RemoveFile( path );
file.Reset();
}
}
else
{
foreach( var (_, file) in _imcFiles )
{
file.Reset();
}
collection._cache!.RemoveFile( path );
file.Reset();
}
_imcManipulations.Clear();
}
public bool ApplyMod( ImcManipulation manip )
public bool ApplyMod( CollectionCacheManager manager, ModCollection collection, ImcManipulation manip )
{
if( !manip.Valid )
{
@ -69,17 +55,14 @@ public partial class MetaCache
}
_imcFiles[ path ] = file;
var fullPath = CreateImcPath( path );
if( _collection.HasCache )
{
_collection.ForceFile( path, fullPath );
}
var fullPath = CreateImcPath( collection, path );
collection._cache!.ForceFile( path, fullPath );
return true;
}
catch( ImcException e )
{
Penumbra.ValidityChecker.ImcExceptions.Add( e );
manager.ValidityChecker.ImcExceptions.Add( e );
Penumbra.Log.Error( e.ToString() );
}
catch( Exception e )
@ -90,7 +73,7 @@ public partial class MetaCache
return false;
}
public bool RevertMod( ImcManipulation m )
public bool RevertMod( CollectionCacheManager manager, ModCollection collection, ImcManipulation m )
{
if( !m.Valid || !_imcManipulations.Remove( m ) )
{
@ -106,32 +89,25 @@ public partial class MetaCache
var def = ImcFile.GetDefault( path, m.EquipSlot, m.Variant, out _ );
var manip = m.Copy( def );
if( !manip.Apply( file ) )
{
return false;
}
var fullPath = CreateImcPath( path );
if( _collection.HasCache )
{
_collection.ForceFile( path, fullPath );
}
var fullPath = CreateImcPath( collection, path );
collection._cache!.ForceFile( path, fullPath );
return true;
}
public void DisposeImc()
public void Dispose()
{
foreach( var file in _imcFiles.Values )
{
file.Dispose();
}
_imcFiles.Clear();
_imcManipulations.Clear();
}
private FullPath CreateImcPath( Utf8GamePath path )
=> new($"|{_collection.Name}_{_collection.ChangeCounter}|{path}");
private static FullPath CreateImcPath( ModCollection collection, Utf8GamePath path )
=> new($"|{collection.Name}_{collection.ChangeCounter}|{path}");
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out ImcFile? file)
=> _imcFiles.TryGetValue(path, out file);

View file

@ -1,61 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using OtterGui.Filesystem;
using Penumbra.Interop.Services;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections.Cache;
public partial class MetaCache
{
private CmpFile? _cmpFile = null;
private readonly List< RspManipulation > _cmpManipulations = new();
public void SetCmpFiles()
=> SetFile( _cmpFile, MetaIndex.HumanCmp );
public static void ResetCmpFiles()
=> SetFile( null, MetaIndex.HumanCmp );
public CharacterUtility.MetaList.MetaReverter TemporarilySetCmpFile()
=> TemporarilySetFile( _cmpFile, MetaIndex.HumanCmp );
public void ResetCmp()
{
if( _cmpFile == null )
{
return;
}
_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 ) )
{
var def = CmpFile.GetDefault( manip.SubRace, manip.Attribute );
manip = new RspManipulation( manip.SubRace, manip.Attribute, def );
return manip.Apply( _cmpFile! );
}
return false;
}
public void DisposeCmp()
{
_cmpFile?.Dispose();
_cmpFile = null;
_cmpManipulations.Clear();
}
}

View file

@ -1,97 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OtterGui;
using OtterGui.Filesystem;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Services;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections.Cache;
public partial class MetaCache
{
private readonly ExpandedEqdpFile?[] _eqdpFiles = new ExpandedEqdpFile[CharacterUtilityData.EqdpIndices.Length]; // TODO: female Hrothgar
private readonly List< EqdpManipulation > _eqdpManipulations = new();
public void SetEqdpFiles()
{
for( var i = 0; i < CharacterUtilityData.EqdpIndices.Length; ++i )
{
SetFile( _eqdpFiles[ i ], CharacterUtilityData.EqdpIndices[ i ] );
}
}
public CharacterUtility.MetaList.MetaReverter? TemporarilySetEqdpFile( GenderRace genderRace, bool accessory )
{
var idx = CharacterUtilityData.EqdpIdx( genderRace, accessory );
if( ( int )idx != -1 )
{
var i = CharacterUtilityData.EqdpIndices.IndexOf( idx );
if( i != -1 )
{
return TemporarilySetFile( _eqdpFiles[ i ], idx );
}
}
return null;
}
public static void ResetEqdpFiles()
{
foreach( var idx in CharacterUtilityData.EqdpIndices )
{
SetFile( null, idx );
}
}
public void ResetEqdp()
{
foreach( var file in _eqdpFiles.OfType< ExpandedEqdpFile >() )
{
var relevant = CharacterUtility.RelevantIndices[ file.Index.Value ];
file.Reset( _eqdpManipulations.Where( m => m.FileIndex() == relevant ).Select( m => ( int )m.SetId ) );
}
_eqdpManipulations.Clear();
}
public bool ApplyMod( EqdpManipulation manip )
{
_eqdpManipulations.AddOrReplace( manip );
var file = _eqdpFiles[ Array.IndexOf( CharacterUtilityData.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( CharacterUtilityData.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( CharacterUtilityData.EqdpIndices, CharacterUtilityData.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();
}
}

View file

@ -1,62 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using OtterGui.Filesystem;
using Penumbra.Interop.Services;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections.Cache;
public partial class MetaCache
{
private ExpandedEqpFile? _eqpFile = null;
private readonly List< EqpManipulation > _eqpManipulations = new();
public void SetEqpFiles()
=> SetFile( _eqpFile, MetaIndex.Eqp );
public static void ResetEqpFiles()
=> SetFile( null, MetaIndex.Eqp );
public CharacterUtility.MetaList.MetaReverter TemporarilySetEqpFile()
=> TemporarilySetFile( _eqpFile, MetaIndex.Eqp );
public void ResetEqp()
{
if( _eqpFile == null )
{
return;
}
_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 )
{
var def = ExpandedEqpFile.GetDefault( manip.SetId );
manip = new EqpManipulation( def, manip.Slot, manip.SetId );
return manip.Apply( _eqpFile! );
}
return false;
}
public void DisposeEqp()
{
_eqpFile?.Dispose();
_eqpFile = null;
_eqpManipulations.Clear();
}
}

View file

@ -12,115 +12,115 @@ using Penumbra.Mods;
namespace Penumbra.Collections.Cache;
public partial class MetaCache : IDisposable, IEnumerable< KeyValuePair< MetaManipulation, IMod > >
public struct MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation, IMod>>
{
private readonly Dictionary< MetaManipulation, IMod > _manipulations = new();
private readonly ModCollection _collection;
private readonly CollectionCacheManager _manager;
private readonly ModCollection _collection;
private readonly Dictionary<MetaManipulation, IMod> _manipulations = new();
private EqpCache _eqpCache = new();
private readonly EqdpCache _eqdpCache = new();
private EstCache _estCache = new();
private GmpCache _gmpCache = new();
private CmpCache _cmpCache = new();
private readonly ImcCache _imcCache;
public bool TryGetValue( MetaManipulation manip, [NotNullWhen( true )] out IMod? mod )
=> _manipulations.TryGetValue( manip, out mod );
public bool TryGetValue(MetaManipulation manip, [NotNullWhen(true)] out IMod? mod)
=> _manipulations.TryGetValue(manip, out mod);
public int Count
=> _manipulations.Count;
public IReadOnlyCollection< MetaManipulation > Manipulations
public IReadOnlyCollection<MetaManipulation> Manipulations
=> _manipulations.Keys;
public IEnumerator< KeyValuePair< MetaManipulation, IMod > > GetEnumerator()
public IEnumerator<KeyValuePair<MetaManipulation, IMod>> GetEnumerator()
=> _manipulations.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
public MetaCache( ModCollection collection )
public MetaCache(CollectionCacheManager manager, ModCollection collection)
{
_collection = collection;
if( !Penumbra.CharacterUtility.Ready )
{
Penumbra.CharacterUtility.LoadingFinished += ApplyStoredManipulations;
}
_manager = manager;
_imcCache = new ImcCache(collection);
if (!_manager.CharacterUtility.Ready)
_manager.CharacterUtility.LoadingFinished += ApplyStoredManipulations;
}
public void SetFiles()
{
SetEqpFiles();
SetEqdpFiles();
SetGmpFiles();
SetEstFiles();
SetCmpFiles();
SetImcFiles();
_eqpCache.SetFiles(_manager);
_eqdpCache.SetFiles(_manager);
_estCache.SetFiles(_manager);
_gmpCache.SetFiles(_manager);
_cmpCache.SetFiles(_manager);
_imcCache.SetFiles(_manager, _collection);
}
public void Reset()
{
ResetEqp();
ResetEqdp();
ResetGmp();
ResetEst();
ResetCmp();
ResetImc();
_eqpCache.Reset(_manager);
_eqdpCache.Reset(_manager);
_estCache.Reset(_manager);
_gmpCache.Reset(_manager);
_cmpCache.Reset(_manager);
_imcCache.Reset(_manager, _collection);
_manipulations.Clear();
}
public void Dispose()
{
_manager.CharacterUtility.LoadingFinished -= ApplyStoredManipulations;
_eqpCache.Dispose();
_eqdpCache.Dispose();
_estCache.Dispose();
_gmpCache.Dispose();
_cmpCache.Dispose();
_imcCache.Dispose();
_manipulations.Clear();
Penumbra.CharacterUtility.LoadingFinished -= ApplyStoredManipulations;
DisposeEqp();
DisposeEqdp();
DisposeCmp();
DisposeGmp();
DisposeEst();
DisposeImc();
}
public bool ApplyMod( MetaManipulation manip, IMod mod )
public bool ApplyMod(MetaManipulation manip, IMod mod)
{
if( _manipulations.ContainsKey( manip ) )
{
_manipulations.Remove( manip );
}
if (_manipulations.ContainsKey(manip))
_manipulations.Remove(manip);
_manipulations[ manip ] = mod;
_manipulations[manip] = mod;
if( !Penumbra.CharacterUtility.Ready )
{
if (!_manager.CharacterUtility.Ready)
return true;
}
// Imc manipulations do not require character utility,
// but they do require the file space to be ready.
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.Eqp => _eqpCache.ApplyMod(_manager, manip.Eqp),
MetaManipulation.Type.Eqdp => _eqdpCache.ApplyMod(_manager, manip.Eqdp),
MetaManipulation.Type.Est => _estCache.ApplyMod(_manager, manip.Est),
MetaManipulation.Type.Gmp => _gmpCache.ApplyMod(_manager, manip.Gmp),
MetaManipulation.Type.Rsp => _cmpCache.ApplyMod(_manager, manip.Rsp),
MetaManipulation.Type.Imc => _imcCache.ApplyMod(_manager, _collection, manip.Imc),
MetaManipulation.Type.Unknown => false,
_ => false,
};
}
public bool RevertMod( MetaManipulation manip )
public bool RevertMod(MetaManipulation manip)
{
var ret = _manipulations.Remove( manip );
if( !Penumbra.CharacterUtility.Ready )
{
var ret = _manipulations.Remove(manip);
if (!Penumbra.CharacterUtility.Ready)
return ret;
}
// Imc manipulations do not require character utility,
// but they do require the file space to be ready.
return manip.ManipulationType switch
{
MetaManipulation.Type.Eqp => RevertMod( (MetaManipulation)manip.Eqp ),
MetaManipulation.Type.Gmp => RevertMod( (MetaManipulation)manip.Gmp ),
MetaManipulation.Type.Eqdp => RevertMod( (MetaManipulation)manip.Eqdp ),
MetaManipulation.Type.Est => RevertMod( (MetaManipulation)manip.Est ),
MetaManipulation.Type.Rsp => RevertMod( (MetaManipulation)manip.Rsp ),
MetaManipulation.Type.Imc => RevertMod( (MetaManipulation)manip.Imc ),
MetaManipulation.Type.Eqp => _eqpCache.RevertMod(_manager, manip.Eqp),
MetaManipulation.Type.Eqdp => _eqdpCache.RevertMod(_manager, manip.Eqdp),
MetaManipulation.Type.Est => _estCache.RevertMod(_manager, manip.Est),
MetaManipulation.Type.Gmp => _gmpCache.RevertMod(_manager, manip.Gmp),
MetaManipulation.Type.Rsp => _cmpCache.RevertMod(_manager, manip.Rsp),
MetaManipulation.Type.Imc => _imcCache.RevertMod(_manager, _collection, manip.Imc),
MetaManipulation.Type.Unknown => false,
_ => false,
};
@ -129,22 +129,20 @@ public partial class MetaCache : IDisposable, IEnumerable< KeyValuePair< MetaMan
// Use this when CharacterUtility becomes ready.
private void ApplyStoredManipulations()
{
if( !Penumbra.CharacterUtility.Ready )
{
if (!Penumbra.CharacterUtility.Ready)
return;
}
var loaded = 0;
foreach( var manip in Manipulations )
foreach (var manip in Manipulations)
{
loaded += 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.Eqp => _eqpCache.ApplyMod(_manager, manip.Eqp),
MetaManipulation.Type.Eqdp => _eqdpCache.ApplyMod(_manager, manip.Eqdp),
MetaManipulation.Type.Est => _estCache.ApplyMod(_manager, manip.Est),
MetaManipulation.Type.Gmp => _gmpCache.ApplyMod(_manager, manip.Gmp),
MetaManipulation.Type.Rsp => _cmpCache.ApplyMod(_manager, manip.Rsp),
MetaManipulation.Type.Imc => _imcCache.ApplyMod(_manager, _collection, manip.Imc),
MetaManipulation.Type.Unknown => false,
_ => false,
}
@ -152,68 +150,28 @@ public partial class MetaCache : IDisposable, IEnumerable< KeyValuePair< MetaMan
: 0;
}
if( Penumbra.CollectionManager.Active.Default == _collection )
if (_manager.IsDefault(_collection))
{
SetFiles();
Penumbra.ResidentResources.Reload();
_manager.ResidentResources.Reload();
}
Penumbra.CharacterUtility.LoadingFinished -= ApplyStoredManipulations;
Penumbra.Log.Debug( $"{_collection.AnonymizedName}: Loaded {loaded} delayed meta manipulations." );
_manager.CharacterUtility.LoadingFinished -= ApplyStoredManipulations;
Penumbra.Log.Debug($"{_collection.AnonymizedName}: Loaded {loaded} delayed meta manipulations.");
}
public void SetFile( MetaIndex metaIndex )
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public unsafe void SetFile(MetaBaseFile? file, MetaIndex metaIndex)
{
switch( metaIndex )
{
case MetaIndex.Eqp:
SetFile( _eqpFile, metaIndex );
break;
case MetaIndex.Gmp:
SetFile( _gmpFile, metaIndex );
break;
case MetaIndex.HumanCmp:
SetFile( _cmpFile, metaIndex );
break;
case MetaIndex.FaceEst:
SetFile( _estFaceFile, metaIndex );
break;
case MetaIndex.HairEst:
SetFile( _estHairFile, metaIndex );
break;
case MetaIndex.HeadEst:
SetFile( _estHeadFile, metaIndex );
break;
case MetaIndex.BodyEst:
SetFile( _estBodyFile, metaIndex );
break;
default:
var i = CharacterUtilityData.EqdpIndices.IndexOf( metaIndex );
if( i != -1 )
{
SetFile( _eqdpFiles[ i ], metaIndex );
}
break;
}
}
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
private static unsafe void SetFile( MetaBaseFile? file, MetaIndex metaIndex )
{
if( file == null )
{
Penumbra.CharacterUtility.ResetResource( metaIndex );
}
if (file == null)
_manager.CharacterUtility.ResetResource(metaIndex);
else
{
Penumbra.CharacterUtility.SetResource( metaIndex, ( IntPtr )file.Data, file.Length );
}
_manager.CharacterUtility.SetResource(metaIndex, (IntPtr)file.Data, file.Length);
}
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
private static unsafe CharacterUtility.MetaList.MetaReverter TemporarilySetFile( MetaBaseFile? file, MetaIndex metaIndex )
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public unsafe CharacterUtility.MetaList.MetaReverter TemporarilySetFile(MetaBaseFile? file, MetaIndex metaIndex)
=> file == null
? Penumbra.CharacterUtility.TemporarilyResetResource( metaIndex )
: Penumbra.CharacterUtility.TemporarilySetResource( metaIndex, ( IntPtr )file.Data, file.Length );
? _manager.CharacterUtility.TemporarilyResetResource(metaIndex)
: _manager.CharacterUtility.TemporarilySetResource(metaIndex, (IntPtr)file.Data, file.Length);
}