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

@ -20,12 +20,14 @@ public class CollectionCacheManager : IDisposable
private readonly FrameworkManager _framework; private readonly FrameworkManager _framework;
private readonly ActiveCollections _active; private readonly ActiveCollections _active;
private readonly CommunicatorService _communicator; private readonly CommunicatorService _communicator;
private readonly CharacterUtility _characterUtility;
private readonly TempModManager _tempMods; private readonly TempModManager _tempMods;
private readonly ModStorage _modStorage; private readonly ModStorage _modStorage;
private readonly ModCacheManager _modCaches; private readonly ModCacheManager _modCaches;
private readonly Configuration _config; private readonly Configuration _config;
private readonly ResidentResourceManager _resources;
internal readonly ValidityChecker ValidityChecker;
internal readonly CharacterUtility CharacterUtility;
internal readonly ResidentResourceManager ResidentResources;
private readonly Dictionary<ModCollection, CollectionCache> _caches = new(); private readonly Dictionary<ModCollection, CollectionCache> _caches = new();
@ -37,17 +39,18 @@ public class CollectionCacheManager : IDisposable
public CollectionCacheManager(FrameworkManager framework, ActiveCollections active, CommunicatorService communicator, public CollectionCacheManager(FrameworkManager framework, ActiveCollections active, CommunicatorService communicator,
CharacterUtility characterUtility, TempModManager tempMods, ModStorage modStorage, Configuration config, CharacterUtility characterUtility, TempModManager tempMods, ModStorage modStorage, Configuration config,
ResidentResourceManager resources, ModCacheManager modCaches) ResidentResourceManager residentResources, ModCacheManager modCaches, ValidityChecker validityChecker)
{ {
_framework = framework; _framework = framework;
_active = active; _active = active;
_communicator = communicator; _communicator = communicator;
_characterUtility = characterUtility; CharacterUtility = characterUtility;
_tempMods = tempMods; _tempMods = tempMods;
_modStorage = modStorage; _modStorage = modStorage;
_config = config; _config = config;
_resources = resources; ResidentResources = residentResources;
_modCaches = modCaches; _modCaches = modCaches;
ValidityChecker = validityChecker;
_communicator.CollectionChange.Subscribe(OnCollectionChange); _communicator.CollectionChange.Subscribe(OnCollectionChange);
_communicator.ModPathChanged.Subscribe(OnModChangeAddition, -100); _communicator.ModPathChanged.Subscribe(OnModChangeAddition, -100);
@ -58,8 +61,8 @@ public class CollectionCacheManager : IDisposable
_communicator.CollectionInheritanceChanged.Subscribe(OnCollectionInheritanceChange); _communicator.CollectionInheritanceChanged.Subscribe(OnCollectionInheritanceChange);
CreateNecessaryCaches(); CreateNecessaryCaches();
if (!_characterUtility.Ready) if (!CharacterUtility.Ready)
_characterUtility.LoadingFinished += IncrementCounters; CharacterUtility.LoadingFinished += IncrementCounters;
} }
public void Dispose() public void Dispose()
@ -71,7 +74,7 @@ public class CollectionCacheManager : IDisposable
_communicator.ModOptionChanged.Unsubscribe(OnModOptionChange); _communicator.ModOptionChanged.Unsubscribe(OnModOptionChange);
_communicator.ModSettingChanged.Unsubscribe(OnModSettingChange); _communicator.ModSettingChanged.Unsubscribe(OnModSettingChange);
_communicator.CollectionInheritanceChanged.Unsubscribe(OnCollectionInheritanceChange); _communicator.CollectionInheritanceChanged.Unsubscribe(OnCollectionInheritanceChange);
_characterUtility.LoadingFinished -= IncrementCounters; CharacterUtility.LoadingFinished -= IncrementCounters;
} }
/// <summary> Only creates a new cache, does not update an existing one. </summary> /// <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) if (_caches.ContainsKey(collection) || collection.Index == ModCollection.Empty.Index)
return false; return false;
var cache = new CollectionCache(collection); var cache = new CollectionCache(this, collection);
_caches.Add(collection, cache); _caches.Add(collection, cache);
collection._cache = cache; collection._cache = cache;
Penumbra.Log.Verbose($"Created new cache for collection {collection.AnonymizedName}."); Penumbra.Log.Verbose($"Created new cache for collection {collection.AnonymizedName}.");
@ -95,6 +98,9 @@ public class CollectionCacheManager : IDisposable
=> _framework.RegisterImportant(nameof(CalculateEffectiveFileList) + collection.Name, => _framework.RegisterImportant(nameof(CalculateEffectiveFileList) + collection.Name,
() => CalculateEffectiveFileListInternal(collection)); () => CalculateEffectiveFileListInternal(collection));
public bool IsDefault(ModCollection collection)
=> _active.Default == collection;
private void CalculateEffectiveFileListInternal(ModCollection collection) private void CalculateEffectiveFileListInternal(ModCollection collection)
{ {
// Skip the empty collection. // Skip the empty collection.
@ -133,10 +139,10 @@ public class CollectionCacheManager : IDisposable
++collection.ChangeCounter; ++collection.ChangeCounter;
if (_active.Default != collection || !_characterUtility.Ready || !_config.EnableMods) if (_active.Default != collection || !CharacterUtility.Ready || !_config.EnableMods)
return; return;
_resources.Reload(); ResidentResources.Reload();
cache.MetaManipulations.SetFiles(); cache.MetaManipulations.SetFiles();
} }
@ -232,7 +238,7 @@ public class CollectionCacheManager : IDisposable
{ {
foreach (var (collection, _) in _caches) foreach (var (collection, _) in _caches)
++collection.ChangeCounter; ++collection.ChangeCounter;
_characterUtility.LoadingFinished -= IncrementCounters; CharacterUtility.LoadingFinished -= IncrementCounters;
} }
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx, bool _) 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,7 +9,7 @@ using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections.Cache; namespace Penumbra.Collections.Cache;
public partial class MetaCache public struct EstCache : IDisposable
{ {
private EstFile? _estFaceFile = null; private EstFile? _estFaceFile = null;
private EstFile? _estHairFile = null; private EstFile? _estHairFile = null;
@ -18,23 +18,18 @@ public partial class MetaCache
private readonly List< EstManipulation > _estManipulations = new(); private readonly List< EstManipulation > _estManipulations = new();
public void SetEstFiles() public EstCache()
{}
public void SetFiles(CollectionCacheManager manager)
{ {
SetFile( _estFaceFile, MetaIndex.FaceEst ); manager.SetFile( _estFaceFile, MetaIndex.FaceEst );
SetFile( _estHairFile, MetaIndex.HairEst ); manager.SetFile( _estHairFile, MetaIndex.HairEst );
SetFile( _estBodyFile, MetaIndex.BodyEst ); manager.SetFile( _estBodyFile, MetaIndex.BodyEst );
SetFile( _estHeadFile, MetaIndex.HeadEst ); manager.SetFile( _estHeadFile, MetaIndex.HeadEst );
} }
public static void ResetEstFiles() public CharacterUtility.MetaList.MetaReverter? TemporarilySetFiles(CollectionCacheManager manager, EstManipulation.EstType type)
{
SetFile( null, MetaIndex.FaceEst );
SetFile( null, MetaIndex.HairEst );
SetFile( null, MetaIndex.BodyEst );
SetFile( null, MetaIndex.HeadEst );
}
public CharacterUtility.MetaList.MetaReverter? TemporarilySetEstFile(EstManipulation.EstType type)
{ {
var (file, idx) = type switch var (file, idx) = type switch
{ {
@ -45,10 +40,10 @@ public partial class MetaCache
_ => ( null, 0 ), _ => ( 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(); _estFaceFile?.Reset();
_estHairFile?.Reset(); _estHairFile?.Reset();
@ -57,7 +52,7 @@ public partial class MetaCache
_estManipulations.Clear(); _estManipulations.Clear();
} }
public bool ApplyMod( EstManipulation m ) public bool ApplyMod( CollectionCacheManager manager, EstManipulation m )
{ {
_estManipulations.AddOrReplace( m ); _estManipulations.AddOrReplace( m );
var file = m.Slot switch var file = m.Slot switch
@ -71,10 +66,11 @@ public partial class MetaCache
return m.Apply( file ); return m.Apply( file );
} }
public bool RevertMod( EstManipulation m ) public bool RevertMod( CollectionCacheManager manager, EstManipulation m )
{
if( _estManipulations.Remove( m ) )
{ {
if (!_estManipulations.Remove(m))
return false;
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
@ -86,12 +82,10 @@ public partial class MetaCache
_ => throw new ArgumentOutOfRangeException(), _ => throw new ArgumentOutOfRangeException(),
}; };
return manip.Apply( file ); return manip.Apply( file );
} }
return false; public void Dispose()
}
public void DisposeEst()
{ {
_estFaceFile?.Dispose(); _estFaceFile?.Dispose();
_estHairFile?.Dispose(); _estHairFile?.Dispose();

View file

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

View file

@ -8,46 +8,32 @@ using Penumbra.String.Classes;
namespace Penumbra.Collections.Cache; namespace Penumbra.Collections.Cache;
public partial class MetaCache public readonly struct ImcCache : IDisposable
{ {
private readonly Dictionary< Utf8GamePath, ImcFile > _imcFiles = new(); private readonly Dictionary< Utf8GamePath, ImcFile > _imcFiles = new();
private readonly List< ImcManipulation > _imcManipulations = new(); private readonly List< ImcManipulation > _imcManipulations = new();
public void SetImcFiles() public ImcCache()
{ { }
if( !_collection.HasCache )
{
return;
}
public void SetFiles(CollectionCacheManager manager, ModCollection collection)
{
foreach( var path in _imcFiles.Keys ) foreach( var path in _imcFiles.Keys )
{ collection._cache!.ForceFile( path, CreateImcPath( collection, path ) );
_collection.ForceFile( path, CreateImcPath( 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 ); collection._cache!.RemoveFile( path );
file.Reset(); file.Reset();
} }
}
else
{
foreach( var (_, file) in _imcFiles )
{
file.Reset();
}
}
_imcManipulations.Clear(); _imcManipulations.Clear();
} }
public bool ApplyMod( ImcManipulation manip ) public bool ApplyMod( CollectionCacheManager manager, ModCollection collection, ImcManipulation manip )
{ {
if( !manip.Valid ) if( !manip.Valid )
{ {
@ -69,17 +55,14 @@ public partial class MetaCache
} }
_imcFiles[ path ] = file; _imcFiles[ path ] = file;
var fullPath = CreateImcPath( path ); var fullPath = CreateImcPath( collection, path );
if( _collection.HasCache ) collection._cache!.ForceFile( path, fullPath );
{
_collection.ForceFile( path, fullPath );
}
return true; return true;
} }
catch( ImcException e ) catch( ImcException e )
{ {
Penumbra.ValidityChecker.ImcExceptions.Add( e ); manager.ValidityChecker.ImcExceptions.Add( e );
Penumbra.Log.Error( e.ToString() ); Penumbra.Log.Error( e.ToString() );
} }
catch( Exception e ) catch( Exception e )
@ -90,7 +73,7 @@ public partial class MetaCache
return false; return false;
} }
public bool RevertMod( ImcManipulation m ) public bool RevertMod( CollectionCacheManager manager, ModCollection collection, ImcManipulation m )
{ {
if( !m.Valid || !_imcManipulations.Remove( 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 def = ImcFile.GetDefault( path, m.EquipSlot, m.Variant, out _ );
var manip = m.Copy( def ); var manip = m.Copy( def );
if( !manip.Apply( file ) ) if( !manip.Apply( file ) )
{
return false; return false;
}
var fullPath = CreateImcPath( path ); var fullPath = CreateImcPath( collection, path );
if( _collection.HasCache ) collection._cache!.ForceFile( path, fullPath );
{
_collection.ForceFile( path, fullPath );
}
return true; return true;
} }
public void DisposeImc() public void Dispose()
{ {
foreach( var file in _imcFiles.Values ) foreach( var file in _imcFiles.Values )
{
file.Dispose(); file.Dispose();
}
_imcFiles.Clear(); _imcFiles.Clear();
_imcManipulations.Clear(); _imcManipulations.Clear();
} }
private FullPath CreateImcPath( Utf8GamePath path ) private static FullPath CreateImcPath( ModCollection collection, Utf8GamePath path )
=> new($"|{_collection.Name}_{_collection.ChangeCounter}|{path}"); => new($"|{collection.Name}_{collection.ChangeCounter}|{path}");
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out ImcFile? file) public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out ImcFile? file)
=> _imcFiles.TryGetValue(path, out 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; 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 CollectionCacheManager _manager;
private readonly ModCollection _collection; 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 ) public bool TryGetValue(MetaManipulation manip, [NotNullWhen(true)] out IMod? mod)
=> _manipulations.TryGetValue( manip, out mod ); => _manipulations.TryGetValue(manip, out mod);
public int Count public int Count
=> _manipulations.Count; => _manipulations.Count;
public IReadOnlyCollection< MetaManipulation > Manipulations public IReadOnlyCollection<MetaManipulation> Manipulations
=> _manipulations.Keys; => _manipulations.Keys;
public IEnumerator< KeyValuePair< MetaManipulation, IMod > > GetEnumerator() public IEnumerator<KeyValuePair<MetaManipulation, IMod>> GetEnumerator()
=> _manipulations.GetEnumerator(); => _manipulations.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator(); => GetEnumerator();
public MetaCache( ModCollection collection ) public MetaCache(CollectionCacheManager manager, ModCollection collection)
{ {
_collection = collection; _manager = manager;
if( !Penumbra.CharacterUtility.Ready ) _imcCache = new ImcCache(collection);
{ if (!_manager.CharacterUtility.Ready)
Penumbra.CharacterUtility.LoadingFinished += ApplyStoredManipulations; _manager.CharacterUtility.LoadingFinished += ApplyStoredManipulations;
}
} }
public void SetFiles() public void SetFiles()
{ {
SetEqpFiles(); _eqpCache.SetFiles(_manager);
SetEqdpFiles(); _eqdpCache.SetFiles(_manager);
SetGmpFiles(); _estCache.SetFiles(_manager);
SetEstFiles(); _gmpCache.SetFiles(_manager);
SetCmpFiles(); _cmpCache.SetFiles(_manager);
SetImcFiles(); _imcCache.SetFiles(_manager, _collection);
} }
public void Reset() public void Reset()
{ {
ResetEqp(); _eqpCache.Reset(_manager);
ResetEqdp(); _eqdpCache.Reset(_manager);
ResetGmp(); _estCache.Reset(_manager);
ResetEst(); _gmpCache.Reset(_manager);
ResetCmp(); _cmpCache.Reset(_manager);
ResetImc(); _imcCache.Reset(_manager, _collection);
_manipulations.Clear(); _manipulations.Clear();
} }
public void Dispose() public void Dispose()
{ {
_manager.CharacterUtility.LoadingFinished -= ApplyStoredManipulations;
_eqpCache.Dispose();
_eqdpCache.Dispose();
_estCache.Dispose();
_gmpCache.Dispose();
_cmpCache.Dispose();
_imcCache.Dispose();
_manipulations.Clear(); _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 ) ) if (_manipulations.ContainsKey(manip))
{ _manipulations.Remove(manip);
_manipulations.Remove( manip );
}
_manipulations[ manip ] = mod; _manipulations[manip] = mod;
if( !Penumbra.CharacterUtility.Ready ) if (!_manager.CharacterUtility.Ready)
{
return true; return true;
}
// Imc manipulations do not require character utility, // Imc manipulations do not require character utility,
// but they do require the file space to be ready. // but they do require the file space to be ready.
return manip.ManipulationType switch return manip.ManipulationType switch
{ {
MetaManipulation.Type.Eqp => ApplyMod( manip.Eqp ), MetaManipulation.Type.Eqp => _eqpCache.ApplyMod(_manager, manip.Eqp),
MetaManipulation.Type.Gmp => ApplyMod( manip.Gmp ), MetaManipulation.Type.Eqdp => _eqdpCache.ApplyMod(_manager, manip.Eqdp),
MetaManipulation.Type.Eqdp => ApplyMod( manip.Eqdp ), MetaManipulation.Type.Est => _estCache.ApplyMod(_manager, manip.Est),
MetaManipulation.Type.Est => ApplyMod( manip.Est ), MetaManipulation.Type.Gmp => _gmpCache.ApplyMod(_manager, manip.Gmp),
MetaManipulation.Type.Rsp => ApplyMod( manip.Rsp ), MetaManipulation.Type.Rsp => _cmpCache.ApplyMod(_manager, manip.Rsp),
MetaManipulation.Type.Imc => ApplyMod( manip.Imc ), MetaManipulation.Type.Imc => _imcCache.ApplyMod(_manager, _collection, manip.Imc),
MetaManipulation.Type.Unknown => false, MetaManipulation.Type.Unknown => false,
_ => 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; return ret;
}
// Imc manipulations do not require character utility, // Imc manipulations do not require character utility,
// but they do require the file space to be ready. // but they do require the file space to be ready.
return manip.ManipulationType switch return manip.ManipulationType switch
{ {
MetaManipulation.Type.Eqp => RevertMod( (MetaManipulation)manip.Eqp ), MetaManipulation.Type.Eqp => _eqpCache.RevertMod(_manager, manip.Eqp),
MetaManipulation.Type.Gmp => RevertMod( (MetaManipulation)manip.Gmp ), MetaManipulation.Type.Eqdp => _eqdpCache.RevertMod(_manager, manip.Eqdp),
MetaManipulation.Type.Eqdp => RevertMod( (MetaManipulation)manip.Eqdp ), MetaManipulation.Type.Est => _estCache.RevertMod(_manager, manip.Est),
MetaManipulation.Type.Est => RevertMod( (MetaManipulation)manip.Est ), MetaManipulation.Type.Gmp => _gmpCache.RevertMod(_manager, manip.Gmp),
MetaManipulation.Type.Rsp => RevertMod( (MetaManipulation)manip.Rsp ), MetaManipulation.Type.Rsp => _cmpCache.RevertMod(_manager, manip.Rsp),
MetaManipulation.Type.Imc => RevertMod( (MetaManipulation)manip.Imc ), MetaManipulation.Type.Imc => _imcCache.RevertMod(_manager, _collection, manip.Imc),
MetaManipulation.Type.Unknown => false, MetaManipulation.Type.Unknown => false,
_ => false, _ => false,
}; };
@ -129,22 +129,20 @@ public partial class MetaCache : IDisposable, IEnumerable< KeyValuePair< MetaMan
// Use this when CharacterUtility becomes ready. // Use this when CharacterUtility becomes ready.
private void ApplyStoredManipulations() private void ApplyStoredManipulations()
{ {
if( !Penumbra.CharacterUtility.Ready ) if (!Penumbra.CharacterUtility.Ready)
{
return; return;
}
var loaded = 0; var loaded = 0;
foreach( var manip in Manipulations ) foreach (var manip in Manipulations)
{ {
loaded += manip.ManipulationType switch loaded += manip.ManipulationType switch
{ {
MetaManipulation.Type.Eqp => ApplyMod( manip.Eqp ), MetaManipulation.Type.Eqp => _eqpCache.ApplyMod(_manager, manip.Eqp),
MetaManipulation.Type.Gmp => ApplyMod( manip.Gmp ), MetaManipulation.Type.Eqdp => _eqdpCache.ApplyMod(_manager, manip.Eqdp),
MetaManipulation.Type.Eqdp => ApplyMod( manip.Eqdp ), MetaManipulation.Type.Est => _estCache.ApplyMod(_manager, manip.Est),
MetaManipulation.Type.Est => ApplyMod( manip.Est ), MetaManipulation.Type.Gmp => _gmpCache.ApplyMod(_manager, manip.Gmp),
MetaManipulation.Type.Rsp => ApplyMod( manip.Rsp ), MetaManipulation.Type.Rsp => _cmpCache.ApplyMod(_manager, manip.Rsp),
MetaManipulation.Type.Imc => ApplyMod( manip.Imc ), MetaManipulation.Type.Imc => _imcCache.ApplyMod(_manager, _collection, manip.Imc),
MetaManipulation.Type.Unknown => false, MetaManipulation.Type.Unknown => false,
_ => false, _ => false,
} }
@ -152,68 +150,28 @@ public partial class MetaCache : IDisposable, IEnumerable< KeyValuePair< MetaMan
: 0; : 0;
} }
if( Penumbra.CollectionManager.Active.Default == _collection ) if (_manager.IsDefault(_collection))
{ {
SetFiles(); SetFiles();
Penumbra.ResidentResources.Reload(); _manager.ResidentResources.Reload();
} }
Penumbra.CharacterUtility.LoadingFinished -= ApplyStoredManipulations; _manager.CharacterUtility.LoadingFinished -= ApplyStoredManipulations;
Penumbra.Log.Debug( $"{_collection.AnonymizedName}: Loaded {loaded} delayed meta manipulations." ); 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 ) if (file == null)
{ _manager.CharacterUtility.ResetResource(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 );
}
else else
{ _manager.CharacterUtility.SetResource(metaIndex, (IntPtr)file.Data, file.Length);
Penumbra.CharacterUtility.SetResource( metaIndex, ( IntPtr )file.Data, file.Length );
}
} }
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static unsafe CharacterUtility.MetaList.MetaReverter TemporarilySetFile( MetaBaseFile? file, MetaIndex metaIndex ) public unsafe CharacterUtility.MetaList.MetaReverter TemporarilySetFile(MetaBaseFile? file, MetaIndex metaIndex)
=> file == null => file == null
? Penumbra.CharacterUtility.TemporarilyResetResource( metaIndex ) ? _manager.CharacterUtility.TemporarilyResetResource(metaIndex)
: Penumbra.CharacterUtility.TemporarilySetResource( metaIndex, ( IntPtr )file.Data, file.Length ); : _manager.CharacterUtility.TemporarilySetResource(metaIndex, (IntPtr)file.Data, file.Length);
} }