diff --git a/Penumbra/Collections/CollectionManager.Active.cs b/Penumbra/Collections/CollectionManager.Active.cs index 7a8bb658..12b42e91 100644 --- a/Penumbra/Collections/CollectionManager.Active.cs +++ b/Penumbra/Collections/CollectionManager.Active.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; namespace Penumbra.Collections; @@ -215,7 +216,7 @@ public partial class ModCollection } // Load the interface collection. - var interfaceName = jObject[ nameof( Interface ) ]?.ToObject< string >() ?? (configChanged ? Empty.Name : Default.Name); + var interfaceName = jObject[ nameof( Interface ) ]?.ToObject< string >() ?? ( configChanged ? Empty.Name : Default.Name ); var interfaceIdx = GetIndexForCollectionName( interfaceName ); if( interfaceIdx < 0 ) { @@ -285,8 +286,6 @@ public partial class ModCollection { SaveActiveCollections(); } - - CreateNecessaryCaches(); } @@ -363,7 +362,6 @@ public partial class ModCollection return false; } - // Save if any of the active collections is changed. private void SaveOnChange( CollectionType collectionType, ModCollection? _1, ModCollection? _2, string? _3 ) { @@ -373,18 +371,20 @@ public partial class ModCollection } } - - // Cache handling. - private void CreateNecessaryCaches() + // Cache handling. Usually recreate caches on the next framework tick, + // but at launch create all of them at once. + public void CreateNecessaryCaches() { - Default.CreateCache(); - Interface.CreateCache(); - Current.CreateCache(); + var tasks = _specialCollections.OfType< ModCollection >() + .Concat( _characters.Values ) + .Prepend( Current ) + .Prepend( Default ) + .Prepend( Interface ) + .Distinct() + .Select( c => Task.Run( c.CalculateEffectiveFileListInternal ) ) + .ToArray(); - foreach( var collection in _specialCollections.OfType< ModCollection >().Concat( _characters.Values ) ) - { - collection.CreateCache(); - } + Task.WaitAll( tasks ); } private void RemoveCache( int idx ) diff --git a/Penumbra/Collections/ModCollection.Cache.Access.cs b/Penumbra/Collections/ModCollection.Cache.Access.cs index 351ae2fc..7f379dc5 100644 --- a/Penumbra/Collections/ModCollection.Cache.Access.cs +++ b/Penumbra/Collections/ModCollection.Cache.Access.cs @@ -133,67 +133,6 @@ public partial class ModCollection Penumbra.Log.Debug( $"[{Thread.CurrentThread.ManagedThreadId}] Recalculation of effective file list for {AnonymizedName} finished." ); } - // Set Metadata files. - public void SetEqpFiles() - { - if( _cache == null ) - { - MetaManager.ResetEqpFiles(); - } - else - { - _cache.MetaManipulations.SetEqpFiles(); - } - } - - public void SetEqdpFiles() - { - if( _cache == null ) - { - MetaManager.ResetEqdpFiles(); - } - else - { - _cache.MetaManipulations.SetEqdpFiles(); - } - } - - public void SetGmpFiles() - { - if( _cache == null ) - { - MetaManager.ResetGmpFiles(); - } - else - { - _cache.MetaManipulations.SetGmpFiles(); - } - } - - public void SetEstFiles() - { - if( _cache == null ) - { - MetaManager.ResetEstFiles(); - } - else - { - _cache.MetaManipulations.SetEstFiles(); - } - } - - public void SetCmpFiles() - { - if( _cache == null ) - { - MetaManager.ResetCmpFiles(); - } - else - { - _cache.MetaManipulations.SetCmpFiles(); - } - } - public void SetFiles() { if( _cache == null ) @@ -207,6 +146,18 @@ public partial class ModCollection } } + public void SetMetaFile( Interop.Structs.CharacterUtility.Index idx ) + { + if( _cache == null ) + { + Penumbra.CharacterUtility.ResetResource( idx ); + } + else + { + _cache.MetaManipulations.SetFile( idx ); + } + } + // Used for short periods of changed files. public CharacterUtility.List.MetaReverter? TemporarilySetEqdpFile( GenderRace genderRace, bool accessory ) => _cache?.MetaManipulations.TemporarilySetEqdpFile( genderRace, accessory ); @@ -222,5 +173,4 @@ public partial class ModCollection public CharacterUtility.List.MetaReverter? TemporarilySetEstFile( EstManipulation.EstType type ) => _cache?.MetaManipulations.TemporarilySetEstFile( type ); - -} +} \ No newline at end of file diff --git a/Penumbra/Interop/CharacterUtility.List.cs b/Penumbra/Interop/CharacterUtility.List.cs index 27146af6..e6f412ae 100644 --- a/Penumbra/Interop/CharacterUtility.List.cs +++ b/Penumbra/Interop/CharacterUtility.List.cs @@ -50,7 +50,8 @@ public unsafe partial class CharacterUtility public MetaReverter TemporarilyResetResource() { - Penumbra.Log.Verbose( $"Temporarily reset resource {GlobalIndex} to default at 0x{_defaultResourceData:X} ({_defaultResourceSize} bytes)." ); + Penumbra.Log.Verbose( + $"Temporarily reset resource {GlobalIndex} to default at 0x{_defaultResourceData:X} ({_defaultResourceSize} bytes)." ); var reverter = new MetaReverter( this ); _entries.AddFirst( reverter ); ResetResourceInternal(); @@ -84,6 +85,9 @@ public unsafe partial class CharacterUtility private void ResetResourceInternal() => SetResourceInternal( _defaultResourceData, _defaultResourceSize ); + private void SetResourceToDefaultCollection() + => Penumbra.CollectionManager.Default.SetMetaFile( GlobalIndex ); + public void Dispose() { if( _entries.Count > 0 ) @@ -127,14 +131,14 @@ public unsafe partial class CharacterUtility if( list.Count == 0 ) { - List.ResetResourceInternal(); + List.SetResourceToDefaultCollection(); } else { var next = list.First!.Value; if( next.Resetter ) { - List.ResetResourceInternal(); + List.SetResourceToDefaultCollection(); } else { diff --git a/Penumbra/Interop/CharacterUtility.cs b/Penumbra/Interop/CharacterUtility.cs index a5b155c1..52e842c9 100644 --- a/Penumbra/Interop/CharacterUtility.cs +++ b/Penumbra/Interop/CharacterUtility.cs @@ -78,9 +78,9 @@ public unsafe partial class CharacterUtility : IDisposable if( !anyMissing ) { - Ready = true; - LoadingFinished.Invoke(); + Ready = true; Dalamud.Framework.Update -= LoadDefaultResources; + LoadingFinished.Invoke(); } } diff --git a/Penumbra/Interop/Resolver/PathResolver.DrawObjectState.cs b/Penumbra/Interop/Resolver/PathResolver.DrawObjectState.cs index d6e6ec0e..9c71a121 100644 --- a/Penumbra/Interop/Resolver/PathResolver.DrawObjectState.cs +++ b/Penumbra/Interop/Resolver/PathResolver.DrawObjectState.cs @@ -56,7 +56,7 @@ public unsafe partial class PathResolver { if( type == ResourceType.Tex && LastCreatedCollection.Valid - && gamePath.Path.Substring( "chara/common/texture/".Length ).StartsWith( 'd', 'e', 'c', 'a', 'l', '_', 'f', 'a', 'c', 'e' ) ) + && gamePath.Path.Substring( "chara/common/texture/".Length ).StartsWith( 'd', 'e', 'c', 'a', 'l' ) ) { resolveData = LastCreatedCollection; return true; diff --git a/Penumbra/Interop/Resolver/PathResolver.Meta.cs b/Penumbra/Interop/Resolver/PathResolver.Meta.cs index d8f1db0a..23d7e21c 100644 --- a/Penumbra/Interop/Resolver/PathResolver.Meta.cs +++ b/Penumbra/Interop/Resolver/PathResolver.Meta.cs @@ -81,8 +81,8 @@ public unsafe partial class PathResolver var collection = GetResolveData( drawObject ); if( collection.Valid ) { - using var eqp = collection.ModCollection.TemporarilySetEqpFile(); - using var eqdp = ResolveEqdpData( collection.ModCollection, GetDrawObjectGenderRace( drawObject ), true, true ); + using var eqp = collection.ModCollection.TemporarilySetEqpFile(); + using var eqdp = ResolveEqdpData( collection.ModCollection, GetDrawObjectGenderRace( drawObject ), true, true ); _onModelLoadCompleteHook.Original.Invoke( drawObject ); } else @@ -211,61 +211,60 @@ public unsafe partial class PathResolver var equipmentEnumerable = equipment ? races.Select( r => collection.TemporarilySetEqdpFile( r, false ) ) - : Array.Empty().AsEnumerable(); + : Array.Empty< IDisposable? >().AsEnumerable(); var accessoryEnumerable = accessory ? races.Select( r => collection.TemporarilySetEqdpFile( r, true ) ) - : Array.Empty().AsEnumerable(); + : Array.Empty< IDisposable? >().AsEnumerable(); return new DisposableContainer( equipmentEnumerable.Concat( accessoryEnumerable ) ); } return race switch { - MidlanderMale => Convert( MidlanderMale ), + MidlanderMale => Convert( MidlanderMale ), HighlanderMale => Convert( MidlanderMale, HighlanderMale ), - ElezenMale => Convert( MidlanderMale, ElezenMale ), - MiqoteMale => Convert( MidlanderMale, MiqoteMale ), - RoegadynMale => Convert( MidlanderMale, RoegadynMale ), - LalafellMale => Convert( MidlanderMale, LalafellMale ), - AuRaMale => Convert( MidlanderMale, AuRaMale ), - HrothgarMale => Convert( MidlanderMale, RoegadynMale, HrothgarMale ), - VieraMale => Convert( MidlanderMale, VieraMale ), + ElezenMale => Convert( MidlanderMale, ElezenMale ), + MiqoteMale => Convert( MidlanderMale, MiqoteMale ), + RoegadynMale => Convert( MidlanderMale, RoegadynMale ), + LalafellMale => Convert( MidlanderMale, LalafellMale ), + AuRaMale => Convert( MidlanderMale, AuRaMale ), + HrothgarMale => Convert( MidlanderMale, RoegadynMale, HrothgarMale ), + VieraMale => Convert( MidlanderMale, VieraMale ), - MidlanderFemale => Convert( MidlanderMale, MidlanderFemale ), + MidlanderFemale => Convert( MidlanderMale, MidlanderFemale ), HighlanderFemale => Convert( MidlanderMale, MidlanderFemale, HighlanderFemale ), - ElezenFemale => Convert( MidlanderMale, MidlanderFemale, ElezenFemale ), - MiqoteFemale => Convert( MidlanderMale, MidlanderFemale, MiqoteFemale ), - RoegadynFemale => Convert( MidlanderMale, MidlanderFemale, RoegadynFemale ), - LalafellFemale => Convert( MidlanderMale, LalafellMale, LalafellFemale ), - AuRaFemale => Convert( MidlanderMale, MidlanderFemale, AuRaFemale ), - HrothgarFemale => Convert( MidlanderMale, MidlanderFemale, RoegadynFemale, HrothgarFemale ), - VieraFemale => Convert( MidlanderMale, MidlanderFemale, VieraFemale ), + ElezenFemale => Convert( MidlanderMale, MidlanderFemale, ElezenFemale ), + MiqoteFemale => Convert( MidlanderMale, MidlanderFemale, MiqoteFemale ), + RoegadynFemale => Convert( MidlanderMale, MidlanderFemale, RoegadynFemale ), + LalafellFemale => Convert( MidlanderMale, LalafellMale, LalafellFemale ), + AuRaFemale => Convert( MidlanderMale, MidlanderFemale, AuRaFemale ), + HrothgarFemale => Convert( MidlanderMale, MidlanderFemale, RoegadynFemale, HrothgarFemale ), + VieraFemale => Convert( MidlanderMale, MidlanderFemale, VieraFemale ), - MidlanderMaleNpc => Convert( MidlanderMale, MidlanderMaleNpc ), + MidlanderMaleNpc => Convert( MidlanderMale, MidlanderMaleNpc ), HighlanderMaleNpc => Convert( MidlanderMale, HighlanderMale, HighlanderMaleNpc ), - ElezenMaleNpc => Convert( MidlanderMale, ElezenMale, ElezenMaleNpc ), - MiqoteMaleNpc => Convert( MidlanderMale, MiqoteMale, MiqoteMaleNpc ), - RoegadynMaleNpc => Convert( MidlanderMale, RoegadynMale, RoegadynMaleNpc ), - LalafellMaleNpc => Convert( MidlanderMale, LalafellMale, LalafellMaleNpc ), - AuRaMaleNpc => Convert( MidlanderMale, AuRaMale, AuRaMaleNpc ), - HrothgarMaleNpc => Convert( MidlanderMale, RoegadynMale, HrothgarMale, HrothgarMaleNpc ), - VieraMaleNpc => Convert( MidlanderMale, VieraMale, VieraMaleNpc ), + ElezenMaleNpc => Convert( MidlanderMale, ElezenMale, ElezenMaleNpc ), + MiqoteMaleNpc => Convert( MidlanderMale, MiqoteMale, MiqoteMaleNpc ), + RoegadynMaleNpc => Convert( MidlanderMale, RoegadynMale, RoegadynMaleNpc ), + LalafellMaleNpc => Convert( MidlanderMale, LalafellMale, LalafellMaleNpc ), + AuRaMaleNpc => Convert( MidlanderMale, AuRaMale, AuRaMaleNpc ), + HrothgarMaleNpc => Convert( MidlanderMale, RoegadynMale, HrothgarMale, HrothgarMaleNpc ), + VieraMaleNpc => Convert( MidlanderMale, VieraMale, VieraMaleNpc ), - MidlanderFemaleNpc => Convert( MidlanderMale, MidlanderFemale, MidlanderFemaleNpc ), + MidlanderFemaleNpc => Convert( MidlanderMale, MidlanderFemale, MidlanderFemaleNpc ), HighlanderFemaleNpc => Convert( MidlanderMale, MidlanderFemale, HighlanderFemale, HighlanderFemaleNpc ), - ElezenFemaleNpc => Convert( MidlanderMale, MidlanderFemale, ElezenFemale, ElezenFemaleNpc ), - MiqoteFemaleNpc => Convert( MidlanderMale, MidlanderFemale, MiqoteFemale, MiqoteFemaleNpc ), - RoegadynFemaleNpc => Convert( MidlanderMale, MidlanderFemale, RoegadynFemale, RoegadynFemaleNpc ), - LalafellFemaleNpc => Convert( MidlanderMale, LalafellMale, LalafellFemale, LalafellFemaleNpc ), - AuRaFemaleNpc => Convert( MidlanderMale, MidlanderFemale, AuRaFemale, AuRaFemaleNpc ), - HrothgarFemaleNpc => Convert( MidlanderMale, MidlanderFemale, RoegadynFemale, HrothgarFemale, HrothgarFemaleNpc ), - VieraFemaleNpc => Convert( MidlanderMale, MidlanderFemale, VieraFemale, VieraFemaleNpc ), + ElezenFemaleNpc => Convert( MidlanderMale, MidlanderFemale, ElezenFemale, ElezenFemaleNpc ), + MiqoteFemaleNpc => Convert( MidlanderMale, MidlanderFemale, MiqoteFemale, MiqoteFemaleNpc ), + RoegadynFemaleNpc => Convert( MidlanderMale, MidlanderFemale, RoegadynFemale, RoegadynFemaleNpc ), + LalafellFemaleNpc => Convert( MidlanderMale, LalafellMale, LalafellFemale, LalafellFemaleNpc ), + AuRaFemaleNpc => Convert( MidlanderMale, MidlanderFemale, AuRaFemale, AuRaFemaleNpc ), + HrothgarFemaleNpc => Convert( MidlanderMale, MidlanderFemale, RoegadynFemale, HrothgarFemale, HrothgarFemaleNpc ), + VieraFemaleNpc => Convert( MidlanderMale, MidlanderFemale, VieraFemale, VieraFemaleNpc ), - UnknownMaleNpc => Convert( MidlanderMale, UnknownMaleNpc ), + UnknownMaleNpc => Convert( MidlanderMale, UnknownMaleNpc ), UnknownFemaleNpc => Convert( MidlanderMale, MidlanderFemale, UnknownFemaleNpc ), - _ => DisposableContainer.Empty, + _ => DisposableContainer.Empty, }; } - } } \ No newline at end of file diff --git a/Penumbra/Meta/Manager/MetaManager.cs b/Penumbra/Meta/Manager/MetaManager.cs index 701b77c0..d440e106 100644 --- a/Penumbra/Meta/Manager/MetaManager.cs +++ b/Penumbra/Meta/Manager/MetaManager.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; +using OtterGui; using Penumbra.Collections; using Penumbra.Interop.Structs; using Penumbra.Meta.Files; @@ -83,17 +84,14 @@ public partial class MetaManager : IDisposable, IEnumerable< KeyValuePair< MetaM } _manipulations[ manip ] = mod; - // Imc manipulations do not require character utility. - if( manip.ManipulationType == MetaManipulation.Type.Imc ) - { - return ApplyMod( manip.Imc ); - } if( !Penumbra.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 ), @@ -101,6 +99,7 @@ public partial class MetaManager : IDisposable, IEnumerable< KeyValuePair< MetaM 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, }; @@ -109,17 +108,13 @@ public partial class MetaManager : IDisposable, IEnumerable< KeyValuePair< MetaM public bool RevertMod( MetaManipulation manip ) { var ret = _manipulations.Remove( manip ); - // Imc manipulations do not require character utility. - if( manip.ManipulationType == MetaManipulation.Type.Imc ) - { - return RevertMod( manip.Imc ); - } - 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( manip.Eqp ), @@ -127,6 +122,7 @@ public partial class MetaManager : IDisposable, IEnumerable< KeyValuePair< MetaM 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, }; @@ -150,6 +146,7 @@ public partial class MetaManager : IDisposable, IEnumerable< KeyValuePair< MetaM 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, } @@ -167,6 +164,42 @@ public partial class MetaManager : IDisposable, IEnumerable< KeyValuePair< MetaM Penumbra.Log.Debug( $"{_collection.AnonymizedName}: Loaded {loaded} delayed meta manipulations." ); } + public void SetFile( CharacterUtility.Index index ) + { + switch( index ) + { + case CharacterUtility.Index.Eqp: + SetFile( _eqpFile, index ); + break; + case CharacterUtility.Index.Gmp: + SetFile( _gmpFile, index ); + break; + case CharacterUtility.Index.HumanCmp: + SetFile( _cmpFile, index ); + break; + case CharacterUtility.Index.FaceEst: + SetFile( _estFaceFile, index ); + break; + case CharacterUtility.Index.HairEst: + SetFile( _estHairFile, index ); + break; + case CharacterUtility.Index.HeadEst: + SetFile( _estHeadFile, index ); + break; + case CharacterUtility.Index.BodyEst: + SetFile( _estBodyFile, index ); + break; + default: + var i = CharacterUtility.EqdpIndices.IndexOf( index ); + if( i != -1 ) + { + SetFile( _eqdpFiles[ i ], index ); + } + + break; + } + } + [MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )] private static unsafe void SetFile( MetaBaseFile? file, CharacterUtility.Index index ) { diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index fa5b3fe7..b8ae544d 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -94,6 +94,7 @@ public class Penumbra : IDalamudPlugin ModManager = new Mod.Manager( Config.ModDirectory ); ModManager.DiscoverMods(); CollectionManager = new ModCollection.Manager( ModManager ); + CollectionManager.CreateNecessaryCaches(); ModFileSystem = ModFileSystem.Load(); ObjectReloader = new ObjectReloader(); PathResolver = new PathResolver( ResourceLoader );