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.