using Newtonsoft.Json.Linq; using Penumbra.Services; using Newtonsoft.Json; using Penumbra.Mods.Manager; using Penumbra.Mods.Settings; namespace Penumbra.Collections; /// /// Handle saving and loading a collection. /// internal readonly struct ModCollectionSave(ModStorage modStorage, ModCollection modCollection) : ISavable { public string ToFilename(FilenameService fileNames) => fileNames.CollectionFile(modCollection); public string LogName(string _) => modCollection.AnonymizedName; public string TypeName => "Collection"; public void Save(StreamWriter writer) { using var j = new JsonTextWriter(writer); j.Formatting = Formatting.Indented; var x = JsonSerializer.Create(new JsonSerializerSettings { Formatting = Formatting.Indented }); j.WriteStartObject(); j.WritePropertyName("Version"); j.WriteValue(ModCollection.CurrentVersion); j.WritePropertyName(nameof(ModCollection.Id)); j.WriteValue(modCollection.Identifier); 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(); var list = new List<(string, ModSettings.SavedSettings)>(modCollection.Settings.Count + modCollection.UnusedSettings.Count); for (var i = 0; i < modCollection.Settings.Count; ++i) { var settings = modCollection.Settings[i]; if (settings != null) list.Add((modStorage[i].ModPath.Name, new ModSettings.SavedSettings(settings, modStorage[i]))); } list.AddRange(modCollection.UnusedSettings.Select(kvp => (kvp.Key, kvp.Value))); list.Sort((a, b) => string.Compare(a.Item1, b.Item1, StringComparison.OrdinalIgnoreCase)); foreach (var (modDir, settings) in list) { j.WritePropertyName(modDir); x.Serialize(j, settings); } j.WriteEndObject(); // Inherit by collection name. j.WritePropertyName("Inheritance"); x.Serialize(j, modCollection.InheritanceByName ?? modCollection.DirectlyInheritsFrom.Select(c => c.Identifier)); j.WriteEndObject(); } public static bool LoadFromFile(FileInfo file, out Guid id, out string name, out int version, out Dictionary settings, out IReadOnlyList inheritance) { settings = []; inheritance = []; if (!file.Exists) { Penumbra.Log.Error("Could not read collection because file does not exist."); name = string.Empty; id = Guid.Empty; version = 0; return false; } try { var obj = JObject.Parse(File.ReadAllText(file.FullName)); version = obj["Version"]?.ToObject() ?? 0; name = obj[nameof(ModCollection.Name)]?.ToObject() ?? string.Empty; id = obj[nameof(ModCollection.Id)]?.ToObject() ?? (version == 1 ? Guid.NewGuid() : Guid.Empty); // 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; id = Guid.Empty; Penumbra.Log.Error($"Could not read collection information from file:\n{e}"); return false; } } }