From e86899c9436ee32602fd555e3c642185dab9d976 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Wed, 12 Apr 2023 20:29:08 +0200 Subject: [PATCH] tmp --- .../Collections/Manager/CollectionEditor.cs | 44 +-- .../Collections/Manager/CollectionManager.cs | 2 + .../Collections/Manager/CollectionStorage.cs | 48 +-- .../Collections/Manager/InheritanceManager.cs | 14 +- .../Manager/TempCollectionManager.cs | 2 +- Penumbra/Collections/ModCollection.File.cs | 100 +++--- .../Collections/ModCollection.Migration.cs | 76 ++--- Penumbra/Collections/ModCollection.cs | 308 +++++++++--------- Penumbra/PenumbraNew.cs | 3 +- Penumbra/Services/ConfigMigrationService.cs | 112 +++---- Penumbra/UI/Tabs/CollectionsTab.cs | 6 +- 11 files changed, 362 insertions(+), 353 deletions(-) diff --git a/Penumbra/Collections/Manager/CollectionEditor.cs b/Penumbra/Collections/Manager/CollectionEditor.cs index 57a7b8f6..4000f504 100644 --- a/Penumbra/Collections/Manager/CollectionEditor.cs +++ b/Penumbra/Collections/Manager/CollectionEditor.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using OtterGui; using Penumbra.Api.Enums; using Penumbra.Mods; +using Penumbra.Mods.Manager; using Penumbra.Services; using Penumbra.Util; @@ -14,11 +15,13 @@ public class CollectionEditor { private readonly CommunicatorService _communicator; private readonly SaveService _saveService; + private readonly ModStorage _modStorage; - public CollectionEditor(SaveService saveService, CommunicatorService communicator) + public CollectionEditor(SaveService saveService, CommunicatorService communicator, ModStorage modStorage) { _saveService = saveService; _communicator = communicator; + _modStorage = modStorage; } /// Enable or disable the mod inheritance of mod idx. @@ -37,12 +40,12 @@ public class CollectionEditor /// public bool SetModState(ModCollection collection, Mod mod, bool newValue) { - var oldValue = collection._settings[mod.Index]?.Enabled ?? collection[mod.Index].Settings?.Enabled ?? false; + var oldValue = collection.Settings[mod.Index]?.Enabled ?? collection[mod.Index].Settings?.Enabled ?? false; if (newValue == oldValue) return false; var inheritance = FixInheritance(collection, mod, false); - collection._settings[mod.Index]!.Enabled = newValue; + ((List)collection.Settings)[mod.Index]!.Enabled = newValue; InvokeChange(collection, ModSettingChange.EnableState, mod, inheritance ? -1 : newValue ? 0 : 1, 0); return true; } @@ -65,13 +68,13 @@ public class CollectionEditor var changes = false; foreach (var mod in mods) { - var oldValue = collection._settings[mod.Index]?.Enabled; + var oldValue = collection.Settings[mod.Index]?.Enabled; if (newValue == oldValue) continue; FixInheritance(collection, mod, false); - collection._settings[mod.Index]!.Enabled = newValue; - changes = true; + ((List)collection.Settings)[mod.Index]!.Enabled = newValue; + changes = true; } if (!changes) @@ -86,12 +89,12 @@ public class CollectionEditor /// public bool SetModPriority(ModCollection collection, Mod mod, int newValue) { - var oldValue = collection._settings[mod.Index]?.Priority ?? collection[mod.Index].Settings?.Priority ?? 0; + var oldValue = collection.Settings[mod.Index]?.Priority ?? collection[mod.Index].Settings?.Priority ?? 0; if (newValue == oldValue) return false; var inheritance = FixInheritance(collection, mod, false); - collection._settings[mod.Index]!.Priority = newValue; + ((List)collection.Settings)[mod.Index]!.Priority = newValue; InvokeChange(collection, ModSettingChange.Priority, mod, inheritance ? -1 : oldValue, 0); return true; } @@ -102,15 +105,15 @@ public class CollectionEditor /// public bool SetModSetting(ModCollection collection, Mod mod, int groupIdx, uint newValue) { - var settings = collection._settings[mod.Index] != null - ? collection._settings[mod.Index]!.Settings + var settings = collection.Settings[mod.Index] != null + ? collection.Settings[mod.Index]!.Settings : collection[mod.Index].Settings?.Settings; var oldValue = settings?[groupIdx] ?? mod.Groups[groupIdx].DefaultSettings; if (oldValue == newValue) return false; var inheritance = FixInheritance(collection, mod, false); - collection._settings[mod.Index]!.SetValue(mod, groupIdx, newValue); + ((List)collection.Settings)[mod.Index]!.SetValue(mod, groupIdx, newValue); InvokeChange(collection, ModSettingChange.Setting, mod, inheritance ? -1 : (int)oldValue, groupIdx); return true; } @@ -125,10 +128,10 @@ public class CollectionEditor // If it does not exist, check unused settings. // If it does not exist and has no unused settings, also use null. ModSettings.SavedSettings? savedSettings = sourceMod != null - ? collection._settings[sourceMod.Index] != null - ? new ModSettings.SavedSettings(collection._settings[sourceMod.Index]!, sourceMod) + ? collection.Settings[sourceMod.Index] != null + ? new ModSettings.SavedSettings(collection.Settings[sourceMod.Index]!, sourceMod) : null - : collection._unusedSettings.TryGetValue(sourceName, out var s) + : collection.UnusedSettings.TryGetValue(sourceName, out var s) ? s : null; @@ -157,9 +160,9 @@ public class CollectionEditor // Either copy the unused source settings directly if they are not inheriting, // or remove any unused settings for the target if they are inheriting. if (savedSettings != null) - collection._unusedSettings[targetName] = savedSettings.Value; + ((Dictionary)collection.UnusedSettings)[targetName] = savedSettings.Value; else - collection._unusedSettings.Remove(targetName); + ((Dictionary)collection.UnusedSettings).Remove(targetName); } return true; @@ -189,11 +192,12 @@ public class CollectionEditor /// private static bool FixInheritance(ModCollection collection, Mod mod, bool inherit) { - var settings = collection._settings[mod.Index]; + var settings = collection.Settings[mod.Index]; if (inherit == (settings == null)) return false; - collection._settings[mod.Index] = inherit ? null : collection[mod.Index].Settings?.DeepCopy() ?? ModSettings.DefaultSettings(mod); + ((List)collection.Settings)[mod.Index] = + inherit ? null : collection[mod.Index].Settings?.DeepCopy() ?? ModSettings.DefaultSettings(mod); return true; } @@ -201,7 +205,7 @@ public class CollectionEditor [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private void InvokeChange(ModCollection changedCollection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx) { - _saveService.QueueSave(changedCollection); + _saveService.QueueSave(new ModCollectionSave(_modStorage, changedCollection)); _communicator.ModSettingChanged.Invoke(changedCollection, type, mod, oldValue, groupIdx, false); RecurseInheritors(changedCollection, type, mod, oldValue, groupIdx); } @@ -219,7 +223,7 @@ public class CollectionEditor _communicator.ModSettingChanged.Invoke(directInheritor, type, null, oldValue, groupIdx, true); break; default: - if (directInheritor._settings[mod!.Index] == null) + if (directInheritor.Settings[mod!.Index] == null) _communicator.ModSettingChanged.Invoke(directInheritor, type, mod, oldValue, groupIdx, true); break; } diff --git a/Penumbra/Collections/Manager/CollectionManager.cs b/Penumbra/Collections/Manager/CollectionManager.cs index 5e1c5781..45e10c6a 100644 --- a/Penumbra/Collections/Manager/CollectionManager.cs +++ b/Penumbra/Collections/Manager/CollectionManager.cs @@ -19,4 +19,6 @@ public class CollectionManager Temp = temp; Editor = editor; } + + } diff --git a/Penumbra/Collections/Manager/CollectionStorage.cs b/Penumbra/Collections/Manager/CollectionStorage.cs index e363c106..be8db099 100644 --- a/Penumbra/Collections/Manager/CollectionStorage.cs +++ b/Penumbra/Collections/Manager/CollectionStorage.cs @@ -18,6 +18,7 @@ public class CollectionStorage : IReadOnlyList, IDisposable { private readonly CommunicatorService _communicator; private readonly SaveService _saveService; + private readonly ModStorage _modStorage; /// The empty collection is always available at Index 0. private readonly List _collections = new() @@ -34,9 +35,6 @@ public class CollectionStorage : IReadOnlyList, IDisposable IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public IEnumerator GetEnumeratorWithEmpty() - => _collections.GetEnumerator(); - public int Count => _collections.Count; @@ -53,10 +51,11 @@ public class CollectionStorage : IReadOnlyList, IDisposable return true; } - public CollectionStorage(CommunicatorService communicator, SaveService saveService) + public CollectionStorage(CommunicatorService communicator, SaveService saveService, ModStorage modStorage) { _communicator = communicator; _saveService = saveService; + _modStorage = modStorage; _communicator.ModDiscoveryStarted.Subscribe(OnModDiscoveryStarted); _communicator.ModDiscoveryFinished.Subscribe(OnModDiscoveryFinished); _communicator.ModPathChanged.Subscribe(OnModPathChange, 10); @@ -114,21 +113,16 @@ public class CollectionStorage : IReadOnlyList, IDisposable return false; } - var newCollection = duplicate?.Duplicate(name) ?? ModCollection.CreateNewEmpty(name); - newCollection.Index = _collections.Count; + var newCollection = duplicate?.Duplicate(name, _collections.Count) ?? ModCollection.CreateEmpty(name, _collections.Count); _collections.Add(newCollection); - _saveService.ImmediateSave(newCollection); + _saveService.ImmediateSave(new ModCollectionSave(_modStorage, newCollection)); Penumbra.ChatService.NotificationMessage($"Created new collection {newCollection.AnonymizedName}.", "Success", NotificationType.Success); _communicator.CollectionChange.Invoke(CollectionType.Inactive, null, newCollection, string.Empty); return true; } - /// Whether the given collection can be deleted. - public bool CanRemoveCollection(ModCollection collection) - => collection.Index > ModCollection.Empty.Index && collection.Index < Count && collection.Index != DefaultNamed.Index; - /// /// Remove the given collection if it exists and is neither the empty nor the default-named collection. /// @@ -146,7 +140,7 @@ public class CollectionStorage : IReadOnlyList, IDisposable return false; } - _saveService.ImmediateDelete(collection); + _saveService.ImmediateDelete(new ModCollectionSave(_modStorage, collection)); _collections.RemoveAt(collection.Index); // Update indices. for (var i = collection.Index; i < Count; ++i) @@ -192,6 +186,15 @@ public class CollectionStorage : IReadOnlyList, IDisposable } } + /// Remove all settings for not currently-installed mods from the given collection. + public void CleanUnavailableSettings(ModCollection collection) + { + var any = collection.UnusedSettings.Count > 0; + ((Dictionary)collection.UnusedSettings).Clear(); + if (any) + _saveService.QueueSave(new ModCollectionSave(_modStorage, collection)); + } + /// /// Check if a name is valid to use for a collection. /// Does not check for uniqueness. @@ -211,24 +214,23 @@ public class CollectionStorage : IReadOnlyList, IDisposable foreach (var file in _saveService.FileNames.CollectionFiles) { - var collection = ModCollection.LoadFromFile(file, out var inheritance); - if (collection == null || collection.Name.Length == 0) + if (!ModCollectionSave.LoadFromFile(file, out var name, out var version, out var settings, out var inheritance) || !IsValidName(name)) continue; - if (ByName(collection.Name, out _)) + if (ByName(name, out _)) { - Penumbra.ChatService.NotificationMessage($"Duplicate collection found: {collection.Name} already exists. Import skipped.", + Penumbra.ChatService.NotificationMessage($"Duplicate collection found: {name} already exists. Import skipped.", "Warning", NotificationType.Warning); continue; } + var collection = ModCollection.CreateFromData(_saveService, _modStorage, name, version, Count, settings); var correctName = _saveService.FileNames.CollectionFile(collection); if (file.FullName != correctName) Penumbra.ChatService.NotificationMessage($"Collection {file.Name} does not correspond to {collection.Name}.", "Warning", NotificationType.Warning); _inheritancesByName?.Add(inheritance); - collection.Index = _collections.Count; _collections.Add(collection); } @@ -258,7 +260,7 @@ public class CollectionStorage : IReadOnlyList, IDisposable private void OnModDiscoveryStarted() { foreach (var collection in this) - collection.PrepareModDiscovery(); + collection.PrepareModDiscovery(_modStorage); } /// Restore all settings in all collections to mods. @@ -266,7 +268,7 @@ public class CollectionStorage : IReadOnlyList, IDisposable { // Re-apply all mod settings. foreach (var collection in this) - collection.ApplyModSettings(); + collection.ApplyModSettings(_saveService, _modStorage); } /// Add or remove a mod from all collections, or re-save all collections where the mod has settings. @@ -281,11 +283,11 @@ public class CollectionStorage : IReadOnlyList, IDisposable break; case ModPathChangeType.Deleted: foreach (var collection in this) - collection.RemoveMod(mod, mod.Index); + collection.RemoveMod(mod); break; case ModPathChangeType.Moved: foreach (var collection in this.Where(collection => collection.Settings[mod.Index] != null)) - _saveService.QueueSave(collection); + _saveService.QueueSave(new ModCollectionSave(_modStorage, collection)); break; } } @@ -299,8 +301,8 @@ public class CollectionStorage : IReadOnlyList, IDisposable foreach (var collection in this) { - if (collection._settings[mod.Index]?.HandleChanges(type, mod, groupIdx, optionIdx, movedToIdx) ?? false) - _saveService.QueueSave(collection); + if (collection.Settings[mod.Index]?.HandleChanges(type, mod, groupIdx, optionIdx, movedToIdx) ?? false) + _saveService.QueueSave(new ModCollectionSave(_modStorage, collection)); } } } \ No newline at end of file diff --git a/Penumbra/Collections/Manager/InheritanceManager.cs b/Penumbra/Collections/Manager/InheritanceManager.cs index e5034ece..1378ae56 100644 --- a/Penumbra/Collections/Manager/InheritanceManager.cs +++ b/Penumbra/Collections/Manager/InheritanceManager.cs @@ -4,6 +4,7 @@ using System.Linq; using Dalamud.Interface.Internal.Notifications; using OtterGui; using OtterGui.Filesystem; +using Penumbra.Mods.Manager; using Penumbra.Services; using Penumbra.Util; @@ -19,12 +20,16 @@ public class InheritanceManager : IDisposable public enum ValidInheritance { Valid, + /// Can not inherit from self Self, + /// Can not inherit from the empty collection Empty, + /// Already inherited from Contained, + /// Inheritance would lead to a circle. Circle, } @@ -32,12 +37,14 @@ public class InheritanceManager : IDisposable private readonly CollectionStorage _storage; private readonly CommunicatorService _communicator; private readonly SaveService _saveService; + private readonly ModStorage _modStorage; - public InheritanceManager(CollectionStorage storage, SaveService saveService, CommunicatorService communicator) + public InheritanceManager(CollectionStorage storage, SaveService saveService, CommunicatorService communicator, ModStorage modStorage) { _storage = storage; _saveService = saveService; _communicator = communicator; + _modStorage = modStorage; ApplyInheritances(); _communicator.CollectionChange.Subscribe(OnCollectionChange); @@ -80,6 +87,7 @@ public class InheritanceManager : IDisposable var parent = inheritor.DirectlyInheritsFrom[idx]; ((List)inheritor.DirectlyInheritsFrom).RemoveAt(idx); ((List)parent.DirectParentOf).Remove(inheritor); + _saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor)); _communicator.CollectionInheritanceChanged.Invoke(inheritor, false); RecurseInheritanceChanges(inheritor); Penumbra.Log.Debug($"Removed {parent.AnonymizedName} from {inheritor.AnonymizedName} inheritances."); @@ -91,6 +99,7 @@ public class InheritanceManager : IDisposable if (!((List)inheritor.DirectlyInheritsFrom).Move(from, to)) return; + _saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor)); _communicator.CollectionInheritanceChanged.Invoke(inheritor, false); RecurseInheritanceChanges(inheritor); Penumbra.Log.Debug($"Moved {inheritor.AnonymizedName}s inheritance {from} to {to}."); @@ -106,6 +115,7 @@ public class InheritanceManager : IDisposable ((List)parent.DirectParentOf).Add(inheritor); if (invokeEvent) { + _saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor)); _communicator.CollectionInheritanceChanged.Invoke(inheritor, false); RecurseInheritanceChanges(inheritor); } @@ -134,7 +144,7 @@ public class InheritanceManager : IDisposable } if (localChanges) - _saveService.ImmediateSave(collection); + _saveService.ImmediateSave(new ModCollectionSave(_modStorage, collection)); } } diff --git a/Penumbra/Collections/Manager/TempCollectionManager.cs b/Penumbra/Collections/Manager/TempCollectionManager.cs index 0eed53d6..42bbea19 100644 --- a/Penumbra/Collections/Manager/TempCollectionManager.cs +++ b/Penumbra/Collections/Manager/TempCollectionManager.cs @@ -52,7 +52,7 @@ public class TempCollectionManager : IDisposable if (GlobalChangeCounter == int.MaxValue) GlobalChangeCounter = 0; - var collection = ModCollection.CreateNewTemporary(name, GlobalChangeCounter++); + var collection = ModCollection.CreateTemporary(name, ~Count, GlobalChangeCounter++); if (_customCollections.TryAdd(collection.Name.ToLowerInvariant(), collection)) return collection.Name; diff --git a/Penumbra/Collections/ModCollection.File.cs b/Penumbra/Collections/ModCollection.File.cs index f688b5ca..6bb1a5af 100644 --- a/Penumbra/Collections/ModCollection.File.cs +++ b/Penumbra/Collections/ModCollection.File.cs @@ -6,49 +6,30 @@ using System.Collections.Generic; using System.IO; using System.Linq; using Newtonsoft.Json; +using Penumbra.Mods.Manager; using Penumbra.Util; namespace Penumbra.Collections; -// File operations like saving, loading and deleting for a collection. -public partial class ModCollection : ISavable +/// +/// Handle saving and loading a collection. +/// +internal readonly struct ModCollectionSave : ISavable { - // Since inheritances depend on other collections existing, - // we return them as a list to be applied after reading all collections. - internal static ModCollection? LoadFromFile(FileInfo file, out IReadOnlyList inheritance) + private readonly ModStorage _modStorage; + private readonly ModCollection _modCollection; + + public ModCollectionSave(ModStorage modStorage, ModCollection modCollection) { - inheritance = Array.Empty(); - if (!file.Exists) - { - Penumbra.Log.Error("Could not read collection because file does not exist."); - return null; - } - - try - { - var obj = JObject.Parse(File.ReadAllText(file.FullName)); - var name = obj[nameof(Name)]?.ToObject() ?? string.Empty; - var version = obj[nameof(Version)]?.ToObject() ?? 0; - // Custom deserialization that is converted with the constructor. - var settings = obj[nameof(Settings)]?.ToObject>() - ?? new Dictionary(); - inheritance = obj["Inheritance"]?.ToObject>() ?? (IReadOnlyList)Array.Empty(); - - return new ModCollection(name, version, settings); - } - catch (Exception e) - { - Penumbra.Log.Error($"Could not read collection information from file:\n{e}"); - } - - return null; + _modStorage = modStorage; + _modCollection = modCollection; } public string ToFilename(FilenameService fileNames) - => fileNames.CollectionFile(this); + => fileNames.CollectionFile(_modCollection); public string LogName(string _) - => AnonymizedName; + => _modCollection.AnonymizedName; public string TypeName => "Collection"; @@ -59,25 +40,25 @@ public partial class ModCollection : ISavable j.Formatting = Formatting.Indented; var x = JsonSerializer.Create(new JsonSerializerSettings { Formatting = Formatting.Indented }); j.WriteStartObject(); - j.WritePropertyName(nameof(Version)); - j.WriteValue(Version); - j.WritePropertyName(nameof(Name)); - j.WriteValue(Name); - j.WritePropertyName(nameof(Settings)); + j.WritePropertyName("Version"); + j.WriteValue(ModCollection.CurrentVersion); + j.WritePropertyName(nameof(ModCollection.Name)); + j.WriteValue(_modCollection.Name); + j.WritePropertyName(nameof(ModCollection.Settings)); // Write all used and unused settings by mod directory name. j.WriteStartObject(); - for (var i = 0; i < _settings.Count; ++i) + for (var i = 0; i < _modCollection.Settings.Count; ++i) { - var settings = _settings[i]; + var settings = _modCollection.Settings[i]; if (settings != null) { - j.WritePropertyName(Penumbra.ModManager[i].ModPath.Name); - x.Serialize(j, new ModSettings.SavedSettings(settings, Penumbra.ModManager[i])); + j.WritePropertyName(_modStorage[i].ModPath.Name); + x.Serialize(j, new ModSettings.SavedSettings(settings, _modStorage[i])); } } - foreach (var (modDir, settings) in _unusedSettings) + foreach (var (modDir, settings) in _modCollection.UnusedSettings) { j.WritePropertyName(modDir); x.Serialize(j, settings); @@ -87,7 +68,40 @@ public partial class ModCollection : ISavable // Inherit by collection name. j.WritePropertyName("Inheritance"); - x.Serialize(j, DirectlyInheritsFrom.Select(c => c.Name)); + x.Serialize(j, _modCollection.DirectlyInheritsFrom.Select(c => c.Name)); j.WriteEndObject(); } + + public static bool LoadFromFile(FileInfo file, out string name, out int version, out Dictionary settings, + out IReadOnlyList inheritance) + { + settings = new Dictionary(); + inheritance = Array.Empty(); + if (!file.Exists) + { + Penumbra.Log.Error("Could not read collection because file does not exist."); + name = string.Empty; + + version = 0; + return false; + } + + try + { + var obj = JObject.Parse(File.ReadAllText(file.FullName)); + name = obj[nameof(ModCollection.Name)]?.ToObject() ?? string.Empty; + version = obj["Version"]?.ToObject() ?? 0; + // Custom deserialization that is converted with the constructor. + settings = obj[nameof(ModCollection.Settings)]?.ToObject>() ?? settings; + inheritance = obj["Inheritance"]?.ToObject>() ?? inheritance; + return true; + } + catch (Exception e) + { + name = string.Empty; + version = 0; + Penumbra.Log.Error($"Could not read collection information from file:\n{e}"); + return false; + } + } } diff --git a/Penumbra/Collections/ModCollection.Migration.cs b/Penumbra/Collections/ModCollection.Migration.cs index e03268a4..02ecea47 100644 --- a/Penumbra/Collections/ModCollection.Migration.cs +++ b/Penumbra/Collections/ModCollection.Migration.cs @@ -1,58 +1,48 @@ using Penumbra.Mods; using System.Collections.Generic; using System.Linq; +using Penumbra.Mods.Manager; using Penumbra.Util; namespace Penumbra.Collections; -public sealed partial class ModCollection +/// Migration to convert ModCollections from older versions to newer. +internal static class ModCollectionMigration { - // Migration to convert ModCollections from older versions to newer. - private static class Migration + /// Migrate a mod collection to the current version. + public static void Migrate(SaveService saver, ModStorage mods, int version, ModCollection collection) + { + var changes = MigrateV0ToV1(collection, ref version); + if (changes) + saver.ImmediateSave(new ModCollectionSave(mods, collection)); + } + + /// Migrate a mod collection from Version 0 to Version 1, which introduced support for inheritance. + private static bool MigrateV0ToV1(ModCollection collection, ref int version) { - public static void Migrate(SaveService saver, ModCollection collection ) + if (version > 0) + return false; + + version = 1; + + // Remove all completely defaulted settings from active and inactive mods. + for (var i = 0; i < collection.Settings.Count; ++i) { - var changes = MigrateV0ToV1( collection ); - if( changes ) - { - saver.ImmediateSave(collection); - } + if (SettingIsDefaultV0(collection.Settings[i])) + ((List)collection.Settings)[i] = null; } - private static bool MigrateV0ToV1( ModCollection collection ) - { - if( collection.Version > 0 ) - { - return false; - } + foreach (var (key, _) in collection.UnusedSettings.Where(kvp => SettingIsDefaultV0(kvp.Value)).ToList()) + ((Dictionary)collection.UnusedSettings).Remove(key); - collection.Version = 1; - - // Remove all completely defaulted settings from active and inactive mods. - for( var i = 0; i < collection._settings.Count; ++i ) - { - if( SettingIsDefaultV0( collection._settings[ i ] ) ) - { - collection._settings[ i ] = null; - } - } - - foreach( var (key, _) in collection._unusedSettings.Where( kvp => SettingIsDefaultV0( kvp.Value ) ).ToList() ) - { - collection._unusedSettings.Remove( key ); - } - - return true; - } - - // We treat every completely defaulted setting as inheritance-ready. - private static bool SettingIsDefaultV0( ModSettings.SavedSettings setting ) - => setting is { Enabled: false, Priority: 0 } && setting.Settings.Values.All( s => s == 0 ); - - private static bool SettingIsDefaultV0( ModSettings? setting ) - => setting is { Enabled: false, Priority: 0 } && setting.Settings.All( s => s == 0 ); + return true; } - internal static ModCollection MigrateFromV0( string name, Dictionary< string, ModSettings.SavedSettings > allSettings ) - => new(name, 0, allSettings); -} \ No newline at end of file + /// We treat every completely defaulted setting as inheritance-ready. + private static bool SettingIsDefaultV0(ModSettings.SavedSettings setting) + => setting is { Enabled: false, Priority: 0 } && setting.Settings.Values.All(s => s == 0); + + /// + private static bool SettingIsDefaultV0(ModSettings? setting) + => setting is { Enabled: false, Priority: 0 } && setting.Settings.All(s => s == 0); +} diff --git a/Penumbra/Collections/ModCollection.cs b/Penumbra/Collections/ModCollection.cs index 363ee5e4..fc0ac1b7 100644 --- a/Penumbra/Collections/ModCollection.cs +++ b/Penumbra/Collections/ModCollection.cs @@ -1,168 +1,73 @@ -using OtterGui.Filesystem; using Penumbra.Mods; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; -using OtterGui; +using Penumbra.Mods.Manager; +using Penumbra.Util; namespace Penumbra.Collections; -// A ModCollection is a named set of ModSettings to all of the users' installed mods. -// Settings to mods that are not installed anymore are kept as long as no call to CleanUnavailableSettings is made. -// Invariants: -// - Index is the collections index in the ModCollection.Manager -// - Settings has the same size as ModManager.Mods. -// - any change in settings or inheritance of the collection causes a Save. +/// +/// A ModCollection is a named set of ModSettings to all of the users' installed mods. +/// Settings to mods that are not installed anymore are kept as long as no call to CleanUnavailableSettings is made. +/// Invariants: +/// - Index is the collections index in the ModCollection.Manager +/// - Settings has the same size as ModManager.Mods. +/// - any change in settings or inheritance of the collection causes a Save. +/// - the name can not contain invalid path characters and has to be unique when lower-cased. +/// public partial class ModCollection { public const int CurrentVersion = 1; public const string DefaultCollectionName = "Default"; - public const string EmptyCollection = "None"; + public const string EmptyCollectionName = "None"; - public static readonly ModCollection Empty = CreateEmpty(); + /// + /// Create the always available Empty Collection that will always sit at index 0, + /// can not be deleted and does never create a cache. + /// + public static readonly ModCollection Empty = CreateEmpty(EmptyCollectionName, 0); - // The collection name can contain invalid path characters, - // but after removing those and going to lower case it has to be unique. + /// The name of a collection can not contain characters invalid in a path. public string Name { get; internal init; } - // Get the first two letters of a collection name and its Index (or None if it is the empty collection). - public string AnonymizedName - => this == Empty ? Empty.Name : Name.Length > 2 ? $"{Name[..2]}... ({Index})" : $"{Name} ({Index})"; - - public int Version { get; internal set; } - public int Index { get; internal set; } = -1; - - // If a ModSetting is null, it can be inherited from other collections. - // If no collection provides a setting for the mod, it is just disabled. - internal readonly List _settings; - - public IReadOnlyList Settings - => _settings; - - // Returns whether there are settings not in use by any current mod. - public bool HasUnusedSettings - => _unusedSettings.Count > 0; - - public int NumUnusedSettings - => _unusedSettings.Count; - - // Evaluates the settings along the whole inheritance tree. - public IEnumerable ActualSettings - => Enumerable.Range(0, _settings.Count).Select(i => this[i].Settings); - - // Settings for deleted mods will be kept via directory name. - internal readonly Dictionary _unusedSettings; - - // Constructor for duplication. - private ModCollection(string name, ModCollection duplicate) - { - Name = name; - Version = duplicate.Version; - _settings = duplicate._settings.ConvertAll(s => s?.DeepCopy()); - _unusedSettings = duplicate._unusedSettings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.DeepCopy()); - DirectlyInheritsFrom = duplicate.DirectlyInheritsFrom.ToList(); - foreach (var c in DirectlyInheritsFrom) - ((List)c.DirectParentOf).Add(this); - } - - // Constructor for reading from files. - private ModCollection(string name, int version, Dictionary allSettings) - { - Name = name; - Version = version; - _unusedSettings = allSettings; - - _settings = new List(); - ApplyModSettings(); - - Migration.Migrate(Penumbra.SaveService, this); - } - - // Create a new, unique empty collection of a given name. - public static ModCollection CreateNewEmpty(string name) - => new(name, CurrentVersion, new Dictionary()); - - // Create a new temporary collection that does not save and has a negative index. - public static ModCollection CreateNewTemporary(string name, int changeCounter) - { - var collection = new ModCollection(name, Empty) - { - Index = ~Penumbra.TempCollections.Count, - ChangeCounter = changeCounter, - }; - collection.CreateCache(false); - return collection; - } - - // Duplicate the calling collection to a new, unique collection of a given name. - public ModCollection Duplicate(string name) - => new(name, this); - - // Remove all settings for not currently-installed mods. - public void CleanUnavailableSettings() - { - var any = _unusedSettings.Count > 0; - _unusedSettings.Clear(); - if (any) - Penumbra.SaveService.QueueSave(this); - } - - // Add settings for a new appended mod, by checking if the mod had settings from a previous deletion. - internal bool AddMod(Mod mod) - { - if (_unusedSettings.TryGetValue(mod.ModPath.Name, out var save)) - { - var ret = save.ToSettings(mod, out var settings); - _settings.Add(settings); - _unusedSettings.Remove(mod.ModPath.Name); - return ret; - } - - _settings.Add(null); - return false; - } - - // Move settings from the current mod list to the unused mod settings. - internal void RemoveMod(Mod mod, int idx) - { - var settings = _settings[idx]; - if (settings != null) - _unusedSettings[mod.ModPath.Name] = new ModSettings.SavedSettings(settings, mod); - - _settings.RemoveAt(idx); - } - - // Create the always available Empty Collection that will always sit at index 0, - // can not be deleted and does never create a cache. - private static ModCollection CreateEmpty() - { - var collection = CreateNewEmpty(EmptyCollection); - collection.Index = 0; - collection._settings.Clear(); - return collection; - } - - // Move all settings to unused settings for rediscovery. - internal void PrepareModDiscovery() - { - foreach (var (mod, setting) in Penumbra.ModManager.Zip(_settings).Where(s => s.Second != null)) - _unusedSettings[mod.ModPath.Name] = new ModSettings.SavedSettings(setting!, mod); - - _settings.Clear(); - } - - // Apply all mod settings from unused settings to the current set of mods. - // Also fixes invalid settings. - internal void ApplyModSettings() - { - _settings.Capacity = Math.Max(_settings.Capacity, Penumbra.ModManager.Count); - if (Penumbra.ModManager.Aggregate(false, (current, mod) => current | AddMod(mod))) - Penumbra.SaveService.ImmediateSave(this); - } - public override string ToString() => Name; + /// Get the first two letters of a collection name and its Index (or None if it is the empty collection). + public string AnonymizedName + => this == Empty ? Empty.Name : Name.Length > 2 ? $"{Name[..2]}... ({Index})" : $"{Name} ({Index})"; + + /// The index of the collection is set and kept up-to-date by the CollectionManager. + public int Index { get; internal set; } + + /// + /// If a ModSetting is null, it can be inherited from other collections. + /// If no collection provides a setting for the mod, it is just disabled. + /// + public readonly IReadOnlyList Settings; + + /// Settings for deleted mods will be kept via the mods identifier (directory name). + public readonly IReadOnlyDictionary UnusedSettings; + + /// Contains all direct parent collections this collection inherits settings from. + public readonly IReadOnlyList DirectlyInheritsFrom; + + /// Contains all direct child collections that inherit from this collection. + public readonly IReadOnlyList DirectParentOf = new List(); + + /// All inherited collections in application order without filtering for duplicates. + public static IEnumerable InheritedCollections(ModCollection collection) + => collection.DirectlyInheritsFrom.SelectMany(InheritedCollections).Prepend(collection); + + /// + /// Iterate over all collections inherited from in depth-first order. + /// Skip already visited collections to avoid circular dependencies. + /// + public IEnumerable GetFlattenedInheritance() + => InheritedCollections(this).Distinct(); + /// /// Obtain the actual settings for a given mod via index. /// Also returns the collection the settings are taken from. @@ -177,7 +82,7 @@ public partial class ModCollection foreach (var collection in GetFlattenedInheritance()) { - var settings = collection._settings[idx]; + var settings = collection.Settings[idx]; if (settings != null) return (settings, collection); } @@ -186,17 +91,104 @@ public partial class ModCollection } } - public readonly IReadOnlyList DirectlyInheritsFrom = new List(); - public readonly IReadOnlyList DirectParentOf = new List(); - - /// All inherited collections in application order without filtering for duplicates. - public static IEnumerable InheritedCollections(ModCollection collection) - => collection.DirectlyInheritsFrom.SelectMany(InheritedCollections).Prepend(collection); + /// Evaluates all settings along the whole inheritance tree. + public IEnumerable ActualSettings + => Enumerable.Range(0, Settings.Count).Select(i => this[i].Settings); /// - /// Iterate over all collections inherited from in depth-first order. - /// Skip already visited collections to avoid circular dependencies. + /// Constructor for duplication. Deep copies all settings and parent collections and adds the new collection to their children lists. /// - public IEnumerable GetFlattenedInheritance() - => InheritedCollections(this).Distinct(); + public ModCollection Duplicate(string name, int index) + { + Debug.Assert(index > 0, "Collection duplicated with non-positive index."); + return new ModCollection(name, index, 0, CurrentVersion, Settings.Select(s => s?.DeepCopy()).ToList(), + DirectlyInheritsFrom.ToList(), UnusedSettings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.DeepCopy())); + } + + /// Constructor for reading from files. + public static ModCollection CreateFromData(SaveService saver, ModStorage mods, string name, int version, int index, + Dictionary allSettings) + { + Debug.Assert(index > 0, "Collection read with non-positive index."); + var ret = new ModCollection(name, index, 0, version, new List(), new List(), allSettings); + ret.ApplyModSettings(saver, mods); + ModCollectionMigration.Migrate(saver, mods, version, ret); + return ret; + } + + /// Constructor for temporary collections. + public static ModCollection CreateTemporary(string name, int index, int changeCounter) + { + Debug.Assert(index < 0, "Temporary collection created with non-negative index."); + var ret = new ModCollection(name, index, changeCounter, CurrentVersion, new List(), new List(), + new Dictionary()); + ret.CreateCache(false); + return ret; + } + + /// Constructor for empty collections. + public static ModCollection CreateEmpty(string name, int index) + { + Debug.Assert(index >= 0, "Empty collection created with negative index."); + return new ModCollection(name, index, 0, CurrentVersion, new List(), new List(), + new Dictionary()); + } + + /// Add settings for a new appended mod, by checking if the mod had settings from a previous deletion. + internal bool AddMod(Mod mod) + { + if (UnusedSettings.TryGetValue(mod.ModPath.Name, out var save)) + { + var ret = save.ToSettings(mod, out var settings); + ((List)Settings).Add(settings); + ((Dictionary)UnusedSettings).Remove(mod.ModPath.Name); + return ret; + } + + ((List)Settings).Add(null); + return false; + } + + /// Move settings from the current mod list to the unused mod settings. + internal void RemoveMod(Mod mod) + { + var settings = Settings[mod.Index]; + if (settings != null) + ((Dictionary)UnusedSettings)[mod.ModPath.Name] = new ModSettings.SavedSettings(settings, mod); + + ((List)Settings).RemoveAt(mod.Index); + } + + /// Move all settings to unused settings for rediscovery. + internal void PrepareModDiscovery(ModStorage mods) + { + foreach (var (mod, setting) in mods.Zip(Settings).Where(s => s.Second != null)) + ((Dictionary)UnusedSettings)[mod.ModPath.Name] = new ModSettings.SavedSettings(setting!, mod); + + ((List)Settings).Clear(); + } + + /// + /// Apply all mod settings from unused settings to the current set of mods. + /// Also fixes invalid settings. + /// + internal void ApplyModSettings(SaveService saver, ModStorage mods) + { + ((List)Settings).Capacity = Math.Max(((List)Settings).Capacity, mods.Count); + if (mods.Aggregate(false, (current, mod) => current | AddMod(mod))) + saver.ImmediateSave(new ModCollectionSave(mods, this)); + } + + private ModCollection(string name, int index, int changeCounter, int version, List appliedSettings, + List inheritsFrom, Dictionary settings) + { + Name = name; + Index = index; + ChangeCounter = changeCounter; + Settings = appliedSettings; + UnusedSettings = settings; + DirectlyInheritsFrom = inheritsFrom; + foreach (var c in DirectlyInheritsFrom) + ((List)c.DirectParentOf).Add(this); + } } diff --git a/Penumbra/PenumbraNew.cs b/Penumbra/PenumbraNew.cs index ccc893d3..dc77ee37 100644 --- a/Penumbra/PenumbraNew.cs +++ b/Penumbra/PenumbraNew.cs @@ -102,7 +102,8 @@ public class PenumbraNew .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton(); + .AddSingleton() + .AddSingleton(s => (ModStorage) s.GetRequiredService()); // Add Resource services services.AddSingleton() diff --git a/Penumbra/Services/ConfigMigrationService.cs b/Penumbra/Services/ConfigMigrationService.cs index ddb6aba1..fde8ea62 100644 --- a/Penumbra/Services/ConfigMigrationService.cs +++ b/Penumbra/Services/ConfigMigrationService.cs @@ -9,7 +9,9 @@ using OtterGui.Filesystem; using Penumbra.Collections; using Penumbra.Collections.Manager; using Penumbra.Mods; +using Penumbra.Mods.Manager; using Penumbra.UI.Classes; +using Penumbra.Util; using SixLabors.ImageSharp; namespace Penumbra.Services; @@ -20,40 +22,32 @@ namespace Penumbra.Services; /// public class ConfigMigrationService { - private readonly FilenameService _fileNames; - private readonly DalamudPluginInterface _pluginInterface; + private readonly SaveService _saveService; private Configuration _config = null!; - private JObject _data = null!; + private JObject _data = null!; - public string CurrentCollection = ModCollection.DefaultCollectionName; - public string DefaultCollection = ModCollection.DefaultCollectionName; - public string ForcedCollection = string.Empty; + public string CurrentCollection = ModCollection.DefaultCollectionName; + public string DefaultCollection = ModCollection.DefaultCollectionName; + public string ForcedCollection = string.Empty; public Dictionary CharacterCollections = new(); - public Dictionary ModSortOrder = new(); - public bool InvertModListOrder; - public bool SortFoldersFirst; - public SortModeV3 SortMode = SortModeV3.FoldersFirst; + public Dictionary ModSortOrder = new(); + public bool InvertModListOrder; + public bool SortFoldersFirst; + public SortModeV3 SortMode = SortModeV3.FoldersFirst; - public ConfigMigrationService(FilenameService fileNames, DalamudPluginInterface pi) - { - _fileNames = fileNames; - _pluginInterface = pi; - } + public ConfigMigrationService(SaveService saveService) + => _saveService = saveService; /// Add missing colors to the dictionary if necessary. private static void AddColors(Configuration config, bool forceSave) { var save = false; foreach (var color in Enum.GetValues()) - { save |= config.Colors.TryAdd(color, color.Data().DefaultColor); - } if (save || forceSave) - { config.Save(); - } } public void Migrate(Configuration config) @@ -63,13 +57,13 @@ public class ConfigMigrationService // because it stayed alive for a bunch of people for some reason. DeleteMetaTmp(); - if (config.Version >= Configuration.Constants.CurrentVersion || !File.Exists(_fileNames.ConfigFile)) + if (config.Version >= Configuration.Constants.CurrentVersion || !File.Exists(_saveService.FileNames.ConfigFile)) { AddColors(config, false); return; } - _data = JObject.Parse(File.ReadAllText(_fileNames.ConfigFile)); + _data = JObject.Parse(File.ReadAllText(_saveService.FileNames.ConfigFile)); CreateBackup(); Version0To1(); @@ -88,7 +82,7 @@ public class ConfigMigrationService if (_config.Version != 6) return; - ActiveCollectionMigration.MigrateUngenderedCollections(_fileNames); + ActiveCollectionMigration.MigrateUngenderedCollections(_saveService.FileNames); _config.Version = 7; } @@ -115,7 +109,7 @@ public class ConfigMigrationService return; ModBackup.MigrateModBackups = true; - _config.Version = 5; + _config.Version = 5; } // SortMode was changed from an enum to a type. @@ -127,15 +121,15 @@ public class ConfigMigrationService SortMode = _data[nameof(SortMode)]?.ToObject() ?? SortMode; _config.SortMode = SortMode switch { - SortModeV3.FoldersFirst => ISortMode.FoldersFirst, - SortModeV3.Lexicographical => ISortMode.Lexicographical, - SortModeV3.InverseFoldersFirst => ISortMode.InverseFoldersFirst, + SortModeV3.FoldersFirst => ISortMode.FoldersFirst, + SortModeV3.Lexicographical => ISortMode.Lexicographical, + SortModeV3.InverseFoldersFirst => ISortMode.InverseFoldersFirst, SortModeV3.InverseLexicographical => ISortMode.InverseLexicographical, - SortModeV3.FoldersLast => ISortMode.FoldersLast, - SortModeV3.InverseFoldersLast => ISortMode.InverseFoldersLast, - SortModeV3.InternalOrder => ISortMode.InternalOrder, - SortModeV3.InternalOrderInverse => ISortMode.InverseInternalOrder, - _ => ISortMode.FoldersFirst, + SortModeV3.FoldersLast => ISortMode.FoldersLast, + SortModeV3.InverseFoldersLast => ISortMode.InverseFoldersLast, + SortModeV3.InternalOrder => ISortMode.InternalOrder, + SortModeV3.InternalOrderInverse => ISortMode.InverseInternalOrder, + _ => ISortMode.FoldersFirst, }; _config.Version = 4; } @@ -147,8 +141,8 @@ public class ConfigMigrationService return; SortFoldersFirst = _data[nameof(SortFoldersFirst)]?.ToObject() ?? false; - SortMode = SortFoldersFirst ? SortModeV3.FoldersFirst : SortModeV3.Lexicographical; - _config.Version = 3; + SortMode = SortFoldersFirst ? SortModeV3.FoldersFirst : SortModeV3.Lexicographical; + _config.Version = 3; } // The forced collection was removed due to general inheritance. @@ -192,7 +186,7 @@ public class ConfigMigrationService return; // Add the previous forced collection to all current collections except itself as an inheritance. - foreach (var collection in _fileNames.CollectionFiles) + foreach (var collection in _saveService.FileNames.CollectionFiles) { try { @@ -215,10 +209,10 @@ public class ConfigMigrationService private void ResettleSortOrder() { ModSortOrder = _data[nameof(ModSortOrder)]?.ToObject>() ?? ModSortOrder; - var file = _fileNames.FilesystemFile; + var file = _saveService.FileNames.FilesystemFile; using var stream = File.Open(file, File.Exists(file) ? FileMode.Truncate : FileMode.CreateNew); using var writer = new StreamWriter(stream); - using var j = new JsonTextWriter(writer); + using var j = new JsonTextWriter(writer); j.Formatting = Formatting.Indented; j.WriteStartObject(); j.WritePropertyName("Data"); @@ -239,10 +233,10 @@ public class ConfigMigrationService // Move the active collections to their own file. private void ResettleCollectionSettings() { - CurrentCollection = _data[nameof(CurrentCollection)]?.ToObject() ?? CurrentCollection; - DefaultCollection = _data[nameof(DefaultCollection)]?.ToObject() ?? DefaultCollection; + CurrentCollection = _data[nameof(CurrentCollection)]?.ToObject() ?? CurrentCollection; + DefaultCollection = _data[nameof(DefaultCollection)]?.ToObject() ?? DefaultCollection; CharacterCollections = _data[nameof(CharacterCollections)]?.ToObject>() ?? CharacterCollections; - SaveActiveCollectionsV0(DefaultCollection, CurrentCollection, DefaultCollection, + SaveActiveCollectionsV0(DefaultCollection, CurrentCollection, DefaultCollection, CharacterCollections.Select(kvp => (kvp.Key, kvp.Value)), Array.Empty<(CollectionType, string)>()); } @@ -250,12 +244,12 @@ public class ConfigMigrationService private void SaveActiveCollectionsV0(string def, string ui, string current, IEnumerable<(string, string)> characters, IEnumerable<(CollectionType, string)> special) { - var file = _fileNames.ActiveCollectionsFile; + var file = _saveService.FileNames.ActiveCollectionsFile; try { using var stream = File.Open(file, File.Exists(file) ? FileMode.Truncate : FileMode.CreateNew); using var writer = new StreamWriter(stream); - using var j = new JsonTextWriter(writer); + using var j = new JsonTextWriter(writer); j.Formatting = Formatting.Indented; j.WriteStartObject(); j.WritePropertyName(nameof(ActiveCollections.Default)); @@ -295,19 +289,18 @@ public class ConfigMigrationService return; _config.ModDirectory = _data[nameof(CurrentCollection)]?.ToObject() ?? string.Empty; - _config.Version = 1; + _config.Version = 1; ResettleCollectionJson(); } - // Move the previous mod configurations to a new default collection file. + /// Move the previous mod configurations to a new default collection file. private void ResettleCollectionJson() { var collectionJson = new FileInfo(Path.Combine(_config.ModDirectory, "collection.json")); if (!collectionJson.Exists) return; - var defaultCollection = ModCollection.CreateNewEmpty(ModCollection.DefaultCollectionName); - var defaultCollectionFile = new FileInfo(_fileNames.CollectionFile(defaultCollection)); + var defaultCollectionFile = new FileInfo(_saveService.FileNames.CollectionFile(ModCollection.DefaultCollectionName)); if (defaultCollectionFile.Exists) return; @@ -317,18 +310,18 @@ public class ConfigMigrationService var data = JArray.Parse(text); var maxPriority = 0; - var dict = new Dictionary(); + var dict = new Dictionary(); foreach (var setting in data.Cast()) { - var modName = (string)setting["FolderName"]!; - var enabled = (bool)setting["Enabled"]!; + var modName = (string)setting["FolderName"]!; + var enabled = (bool)setting["Enabled"]!; var priority = (int)setting["Priority"]!; var settings = setting["Settings"]!.ToObject>() ?? setting["Conf"]!.ToObject>(); dict[modName] = new ModSettings.SavedSettings() { - Enabled = enabled, + Enabled = enabled, Priority = priority, Settings = settings!, }; @@ -339,8 +332,9 @@ public class ConfigMigrationService if (!InvertModListOrder) dict = dict.ToDictionary(kvp => kvp.Key, kvp => kvp.Value with { Priority = maxPriority - kvp.Value.Priority }); - defaultCollection = ModCollection.MigrateFromV0(ModCollection.DefaultCollectionName, dict); - Penumbra.SaveService.ImmediateSave(defaultCollection); + var emptyStorage = new ModStorage(); + var collection = ModCollection.CreateFromData(_saveService, emptyStorage, ModCollection.DefaultCollectionName, 0, 1, dict); + _saveService.ImmediateSave(new ModCollectionSave(emptyStorage, collection)); } catch (Exception e) { @@ -352,7 +346,7 @@ public class ConfigMigrationService // Create a backup of the configuration file specifically. private void CreateBackup() { - var name = _fileNames.ConfigFile; + var name = _saveService.FileNames.ConfigFile; var bakName = name + ".bak"; try { @@ -366,13 +360,13 @@ public class ConfigMigrationService public enum SortModeV3 : byte { - FoldersFirst = 0x00, - Lexicographical = 0x01, - InverseFoldersFirst = 0x02, + FoldersFirst = 0x00, + Lexicographical = 0x01, + InverseFoldersFirst = 0x02, InverseLexicographical = 0x03, - FoldersLast = 0x04, - InverseFoldersLast = 0x05, - InternalOrder = 0x06, - InternalOrderInverse = 0x07, + FoldersLast = 0x04, + InverseFoldersLast = 0x05, + InternalOrder = 0x06, + InternalOrderInverse = 0x07, } } diff --git a/Penumbra/UI/Tabs/CollectionsTab.cs b/Penumbra/UI/Tabs/CollectionsTab.cs index 18dd3b95..0cf6474a 100644 --- a/Penumbra/UI/Tabs/CollectionsTab.cs +++ b/Penumbra/UI/Tabs/CollectionsTab.cs @@ -87,15 +87,15 @@ public class CollectionsTab : IDisposable, ITab /// Draw the Clean Unused Settings button if there are any. private void DrawCleanCollectionButton(Vector2 width) { - if (!_collectionManager.Active.Current.HasUnusedSettings) + if (_collectionManager.Active.Current.UnusedSettings.Count == 0) return; ImGui.SameLine(); if (ImGuiUtil.DrawDisabledButton( - $"Clean {_collectionManager.Active.Current.NumUnusedSettings} Unused Settings###CleanSettings", width + $"Clean {_collectionManager.Active.Current.UnusedSettings.Count} Unused Settings###CleanSettings", width , "Remove all stored settings for mods not currently available and fix invalid settings.\n\nUse at own risk." , false)) - _collectionManager.Active.Current.CleanUnavailableSettings(); + _collectionManager.Storage.CleanUnavailableSettings(_collectionManager.Active.Current); } /// Draw the new collection input as well as its buttons.