Glamourer/Glamourer/Designs/Design.cs
2023-07-31 18:17:41 +02:00

185 lines
6.6 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Dalamud.Interface.Internal.Notifications;
using Glamourer.Interop.Penumbra;
using Glamourer.Services;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
namespace Glamourer.Designs;
public sealed class Design : DesignBase, ISavable
{
#region Data
internal Design(ItemManager items)
: base(items)
{ }
internal Design(DesignBase other)
: base(other)
{ }
internal Design(Design other)
: base(other)
{
Tags = Tags.ToArray();
Description = Description;
AssociatedMods = new SortedList<Mod, ModSettings>(other.AssociatedMods);
}
// Metadata
public new const int FileVersion = 1;
public Guid Identifier { get; internal init; }
public DateTimeOffset CreationDate { get; internal init; }
public DateTimeOffset LastEdit { get; internal set; }
public LowerString Name { get; internal set; } = LowerString.Empty;
public string Description { get; internal set; } = string.Empty;
public string[] Tags { get; internal set; } = Array.Empty<string>();
public int Index { get; internal set; }
public SortedList<Mod, ModSettings> AssociatedMods { get; private set; } = new();
public string Incognito
=> Identifier.ToString()[..8];
#endregion
#region Serialization
public new JObject JsonSerialize()
{
var ret = new JObject()
{
["FileVersion"] = FileVersion,
["Identifier"] = Identifier,
["CreationDate"] = CreationDate,
["LastEdit"] = LastEdit,
["Name"] = Name.Text,
["Description"] = Description,
["Tags"] = JArray.FromObject(Tags),
["WriteProtected"] = WriteProtected(),
["Equipment"] = SerializeEquipment(),
["Customize"] = SerializeCustomize(),
["Mods"] = SerializeMods(),
}
;
return ret;
}
private JArray SerializeMods()
{
var ret = new JArray();
foreach (var (mod, settings) in AssociatedMods)
{
var obj = new JObject()
{
["Name"] = mod.Name,
["Directory"] = mod.DirectoryName,
["Enabled"] = settings.Enabled,
};
if (settings.Enabled)
{
obj["Priority"] = settings.Priority;
obj["Settings"] = JObject.FromObject(settings.Settings);
}
ret.Add(obj);
}
return ret;
}
#endregion
#region Deserialization
public static Design LoadDesign(CustomizationService customizations, ItemManager items, JObject json)
{
var version = json["FileVersion"]?.ToObject<int>() ?? 0;
return version switch
{
FileVersion => LoadDesignV1(customizations, items, json),
_ => throw new Exception("The design to be loaded has no valid Version."),
};
}
private static Design LoadDesignV1(CustomizationService customizations, ItemManager items, JObject json)
{
static string[] ParseTags(JObject json)
{
var tags = json["Tags"]?.ToObject<string[]>() ?? Array.Empty<string>();
return tags.OrderBy(t => t).Distinct().ToArray();
}
var creationDate = json["CreationDate"]?.ToObject<DateTimeOffset>() ?? throw new ArgumentNullException("CreationDate");
var design = new Design(items)
{
CreationDate = creationDate,
Identifier = json["Identifier"]?.ToObject<Guid>() ?? throw new ArgumentNullException("Identifier"),
Name = new LowerString(json["Name"]?.ToObject<string>() ?? throw new ArgumentNullException("Name")),
Description = json["Description"]?.ToObject<string>() ?? string.Empty,
Tags = ParseTags(json),
LastEdit = json["LastEdit"]?.ToObject<DateTimeOffset>() ?? creationDate,
};
if (design.LastEdit < creationDate)
design.LastEdit = creationDate;
design.SetWriteProtected(json["WriteProtected"]?.ToObject<bool>() ?? false);
LoadCustomize(customizations, json["Customize"], design, design.Name, true, false);
LoadEquip(items, json["Equipment"], design, design.Name, true);
LoadMods(json["Mods"], design);
return design;
}
private static void LoadMods(JToken? mods, Design design)
{
if (mods is not JArray array)
return;
foreach (var tok in array)
{
var name = tok["Name"]?.ToObject<string>();
var directory = tok["Directory"]?.ToObject<string>();
var enabled = tok["Enabled"]?.ToObject<bool>();
if (name == null || directory == null || enabled == null)
{
Glamourer.Chat.NotificationMessage("The loaded design contains an invalid mod, skipped.", "Warning", NotificationType.Warning);
continue;
}
var settingsDict = tok["Settings"]?.ToObject<Dictionary<string, string[]>>() ?? new Dictionary<string, string[]>();
var settings = new SortedList<string, IList<string>>(settingsDict.Count);
foreach (var (key, value) in settingsDict)
settings.Add(key, value);
var priority = tok["Priority"]?.ToObject<int>() ?? 0;
if (!design.AssociatedMods.TryAdd(new Mod(name, directory), new ModSettings(settings, priority, enabled.Value)))
Glamourer.Chat.NotificationMessage("The loaded design contains a mod more than once, skipped.", "Warning",
NotificationType.Warning);
}
}
#endregion
#region ISavable
public string ToFilename(FilenameService fileNames)
=> fileNames.DesignFile(this);
public void Save(StreamWriter writer)
{
using var j = new JsonTextWriter(writer)
{
Formatting = Formatting.Indented,
};
var obj = JsonSerialize();
obj.WriteTo(j);
}
public string LogName(string fileName)
=> Path.GetFileNameWithoutExtension(fileName);
#endregion
}