Some cleanup.

This commit is contained in:
Ottermandias 2024-04-25 17:58:32 +02:00
parent cd76c31d8c
commit 72db023804
12 changed files with 163 additions and 154 deletions

View file

@ -51,7 +51,7 @@ public readonly struct ModSaveGroup : ISavable
if (_groupIdx >= 0) if (_groupIdx >= 0)
_group!.WriteJson(j, serializer); _group!.WriteJson(j, serializer);
else else
IModDataContainer.WriteModData(j, serializer, _defaultMod!, _basePath); SubModHelpers.WriteModContainer(j, serializer, _defaultMod!, _basePath);
j.WriteEndObject(); j.WriteEndObject();
} }
} }

View file

@ -54,7 +54,7 @@ public sealed class MultiModGroup(Mod mod) : IModGroup, ITexToolsGroup
return OptionData.Count - 1; return OptionData.Count - 1;
} }
public static MultiModGroup? Load(Mod mod, JObject json, int groupIdx) public static MultiModGroup? Load(Mod mod, JObject json)
{ {
var ret = new MultiModGroup(mod) var ret = new MultiModGroup(mod)
{ {
@ -140,10 +140,10 @@ public sealed class MultiModGroup(Mod mod) : IModGroup, ITexToolsGroup
jWriter.WriteStartArray(); jWriter.WriteStartArray();
foreach (var option in OptionData) foreach (var option in OptionData)
{ {
IModOption.WriteModOption(jWriter, option); SubModHelpers.WriteModOption(jWriter, option);
jWriter.WritePropertyName(nameof(option.Priority)); jWriter.WritePropertyName(nameof(option.Priority));
jWriter.WriteValue(option.Priority.Value); jWriter.WriteValue(option.Priority.Value);
IModDataContainer.WriteModData(jWriter, serializer, option, basePath ?? Mod.ModPath); SubModHelpers.WriteModContainer(jWriter, serializer, option, basePath ?? Mod.ModPath);
} }
jWriter.WriteEndArray(); jWriter.WriteEndArray();

View file

@ -52,7 +52,7 @@ public sealed class SingleModGroup(Mod mod) : IModGroup, ITexToolsGroup
public bool IsOption public bool IsOption
=> OptionData.Count > 1; => OptionData.Count > 1;
public static SingleModGroup? Load(Mod mod, JObject json, int groupIdx) public static SingleModGroup? Load(Mod mod, JObject json)
{ {
var options = json["Options"]; var options = json["Options"];
var ret = new SingleModGroup(mod) var ret = new SingleModGroup(mod)
@ -144,8 +144,8 @@ public sealed class SingleModGroup(Mod mod) : IModGroup, ITexToolsGroup
jWriter.WriteStartArray(); jWriter.WriteStartArray();
foreach (var option in OptionData) foreach (var option in OptionData)
{ {
IModOption.WriteModOption(jWriter, option); SubModHelpers.WriteModOption(jWriter, option);
IModDataContainer.WriteModData(jWriter, serializer, option, basePath ?? Mod.ModPath); SubModHelpers.WriteModContainer(jWriter, serializer, option, basePath ?? Mod.ModPath);
} }
jWriter.WriteEndArray(); jWriter.WriteEndArray();

View file

@ -251,7 +251,7 @@ public class ModOptionEditor(CommunicatorService communicator, SaveService saveS
Description = option.Description, Description = option.Description,
}; };
if (option is IModDataContainer data) if (option is IModDataContainer data)
IModDataContainer.Clone(data, newOption); SubModHelpers.Clone(data, newOption);
s.OptionData.Add(newOption); s.OptionData.Add(newOption);
break; break;
} }
@ -265,7 +265,7 @@ public class ModOptionEditor(CommunicatorService communicator, SaveService saveS
Priority = option is MultiSubMod s ? s.Priority : ModPriority.Default, Priority = option is MultiSubMod s ? s.Priority : ModPriority.Default,
}; };
if (option is IModDataContainer data) if (option is IModDataContainer data)
IModDataContainer.Clone(data, newOption); SubModHelpers.Clone(data, newOption);
m.OptionData.Add(newOption); m.OptionData.Add(newOption);
break; break;
} }

View file

@ -78,7 +78,7 @@ public sealed class Mod : IMod
group.AddData(config, dictRedirections, setManips); group.AddData(config, dictRedirections, setManips);
} }
Default.AddDataTo(dictRedirections, setManips); Default.AddTo(dictRedirections, setManips);
return new AppliedModData(dictRedirections, setManips); return new AppliedModData(dictRedirections, setManips);
} }

View file

@ -115,7 +115,7 @@ public partial class ModCreator(
try try
{ {
var jObject = File.Exists(defaultFile) ? JObject.Parse(File.ReadAllText(defaultFile)) : new JObject(); var jObject = File.Exists(defaultFile) ? JObject.Parse(File.ReadAllText(defaultFile)) : new JObject();
IModDataContainer.Load(jObject, mod.Default, mod.ModPath); SubModHelpers.LoadDataContainer(jObject, mod.Default, mod.ModPath);
} }
catch (Exception e) catch (Exception e)
{ {
@ -162,7 +162,7 @@ public partial class ModCreator(
deleteList.AddRange(localDeleteList); deleteList.AddRange(localDeleteList);
} }
IModDataContainer.DeleteDeleteList(deleteList, delete); DeleteDeleteList(deleteList, delete);
if (!changes) if (!changes)
return; return;
@ -221,7 +221,7 @@ public partial class ModCreator(
} }
} }
IModDataContainer.DeleteDeleteList(deleteList, delete); DeleteDeleteList(deleteList, delete);
return (oldSize < option.Manipulations.Count, deleteList); return (oldSize < option.Manipulations.Count, deleteList);
} }
@ -392,10 +392,8 @@ public partial class ModCreator(
Penumbra.Log.Debug($"Writing the first {IModGroup.MaxMultiOptions} options to {Path.GetFileName(oldPath)} after split."); Penumbra.Log.Debug($"Writing the first {IModGroup.MaxMultiOptions} options to {Path.GetFileName(oldPath)} after split.");
using (var oldFile = File.CreateText(oldPath)) using (var oldFile = File.CreateText(oldPath))
{ {
using var j = new JsonTextWriter(oldFile) using var j = new JsonTextWriter(oldFile);
{ j.Formatting = Formatting.Indented;
Formatting = Formatting.Indented,
};
json.WriteTo(j); json.WriteTo(j);
} }
@ -403,10 +401,8 @@ public partial class ModCreator(
$"Writing the remaining {options.Count - IModGroup.MaxMultiOptions} options to {Path.GetFileName(newPath)} after split."); $"Writing the remaining {options.Count - IModGroup.MaxMultiOptions} options to {Path.GetFileName(newPath)} after split.");
using (var newFile = File.CreateText(newPath)) using (var newFile = File.CreateText(newPath))
{ {
using var j = new JsonTextWriter(newFile) using var j = new JsonTextWriter(newFile);
{ j.Formatting = Formatting.Indented;
Formatting = Formatting.Indented,
};
clone.WriteTo(j); clone.WriteTo(j);
} }
@ -436,8 +432,8 @@ public partial class ModCreator(
var json = JObject.Parse(File.ReadAllText(file.FullName)); var json = JObject.Parse(File.ReadAllText(file.FullName));
switch (json[nameof(Type)]?.ToObject<GroupType>() ?? GroupType.Single) switch (json[nameof(Type)]?.ToObject<GroupType>() ?? GroupType.Single)
{ {
case GroupType.Multi: return MultiModGroup.Load(mod, json, groupIdx); case GroupType.Multi: return MultiModGroup.Load(mod, json);
case GroupType.Single: return SingleModGroup.Load(mod, json, groupIdx); case GroupType.Single: return SingleModGroup.Load(mod, json);
} }
} }
catch (Exception e) catch (Exception e)
@ -446,5 +442,23 @@ public partial class ModCreator(
} }
return null; return null;
}
internal static void DeleteDeleteList(IEnumerable<string> deleteList, bool delete)
{
if (!delete)
return;
foreach (var file in deleteList)
{
try
{
File.Delete(file);
}
catch (Exception e)
{
Penumbra.Log.Error($"Could not delete incorporated meta file {file}:\n{e}");
}
}
} }
} }

View file

@ -22,9 +22,9 @@ public class DefaultSubMod(IMod mod) : IModDataContainer
IModGroup? IModDataContainer.Group IModGroup? IModDataContainer.Group
=> null; => null;
public void AddDataTo(Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations) public void AddTo(Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
=> IModDataContainer.AddDataTo(this, redirections, manipulations); => SubModHelpers.AddContainerTo(this, redirections, manipulations);
public (int GroupIndex, int DataIndex) GetDataIndices() public (int GroupIndex, int DataIndex) GetDataIndices()
=> (-1, 0); => (-1, 0);
} }

View file

@ -1,5 +1,3 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor; using Penumbra.Mods.Editor;
using Penumbra.Mods.Groups; using Penumbra.Mods.Groups;
@ -7,6 +5,7 @@ using Penumbra.String.Classes;
namespace Penumbra.Mods.SubMods; namespace Penumbra.Mods.SubMods;
public interface IModDataContainer public interface IModDataContainer
{ {
public IMod Mod { get; } public IMod Mod { get; }
@ -16,116 +15,24 @@ public interface IModDataContainer
public Dictionary<Utf8GamePath, FullPath> FileSwaps { get; set; } public Dictionary<Utf8GamePath, FullPath> FileSwaps { get; set; }
public HashSet<MetaManipulation> Manipulations { get; set; } public HashSet<MetaManipulation> Manipulations { get; set; }
public static void AddDataTo(IModDataContainer container, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
{
foreach (var (path, file) in container.Files)
redirections.TryAdd(path, file);
foreach (var (path, file) in container.FileSwaps)
redirections.TryAdd(path, file);
manipulations.UnionWith(container.Manipulations);
}
public string GetName() public string GetName()
=> this switch => this switch
{ {
IModOption o => o.FullName, IModOption o => o.FullName,
DefaultSubMod => DefaultSubMod.FullName, DefaultSubMod => DefaultSubMod.FullName,
_ => $"Container {GetDataIndices().DataIndex + 1}", _ => $"Container {GetDataIndices().DataIndex + 1}",
}; };
public string GetFullName() public string GetFullName()
=> this switch => this switch
{ {
IModOption o => o.FullName, IModOption o => o.FullName,
DefaultSubMod => DefaultSubMod.FullName, DefaultSubMod => DefaultSubMod.FullName,
_ when Group != null => $"{Group.Name}: Container {GetDataIndices().DataIndex + 1}", _ when Group != null => $"{Group.Name}: Container {GetDataIndices().DataIndex + 1}",
_ => $"Container {GetDataIndices().DataIndex + 1}", _ => $"Container {GetDataIndices().DataIndex + 1}",
}; };
public static void Clone(IModDataContainer from, IModDataContainer to)
{
to.Files = new Dictionary<Utf8GamePath, FullPath>(from.Files);
to.FileSwaps = new Dictionary<Utf8GamePath, FullPath>(from.FileSwaps);
to.Manipulations = [.. from.Manipulations];
}
public (int GroupIndex, int DataIndex) GetDataIndices(); public (int GroupIndex, int DataIndex) GetDataIndices();
public static void Load(JToken json, IModDataContainer data, DirectoryInfo basePath)
{
data.Files.Clear();
data.FileSwaps.Clear();
data.Manipulations.Clear();
var files = (JObject?)json[nameof(Files)];
if (files != null)
foreach (var property in files.Properties())
{
if (Utf8GamePath.FromString(property.Name, out var p, true))
data.Files.TryAdd(p, new FullPath(basePath, property.Value.ToObject<Utf8RelPath>()));
}
var swaps = (JObject?)json[nameof(FileSwaps)];
if (swaps != null)
foreach (var property in swaps.Properties())
{
if (Utf8GamePath.FromString(property.Name, out var p, true))
data.FileSwaps.TryAdd(p, new FullPath(property.Value.ToObject<string>()!));
}
var manips = json[nameof(Manipulations)];
if (manips != null)
foreach (var s in manips.Children().Select(c => c.ToObject<MetaManipulation>())
.Where(m => m.Validate()))
data.Manipulations.Add(s);
}
public static void WriteModData(JsonWriter j, JsonSerializer serializer, IModDataContainer data, DirectoryInfo basePath)
{
j.WritePropertyName(nameof(data.Files));
j.WriteStartObject();
foreach (var (gamePath, file) in data.Files)
{
if (file.ToRelPath(basePath, out var relPath))
{
j.WritePropertyName(gamePath.ToString());
j.WriteValue(relPath.ToString());
}
}
j.WriteEndObject();
j.WritePropertyName(nameof(data.FileSwaps));
j.WriteStartObject();
foreach (var (gamePath, file) in data.FileSwaps)
{
j.WritePropertyName(gamePath.ToString());
j.WriteValue(file.ToString());
}
j.WriteEndObject();
j.WritePropertyName(nameof(data.Manipulations));
serializer.Serialize(j, data.Manipulations);
j.WriteEndObject();
}
internal static void DeleteDeleteList(IEnumerable<string> deleteList, bool delete)
{
if (!delete)
return;
foreach (var file in deleteList)
{
try
{
File.Delete(file);
}
catch (Exception e)
{
Penumbra.Log.Error($"Could not delete incorporated meta file {file}:\n{e}");
}
}
}
} }
public interface IModDataOption : IModOption, IModDataContainer; public interface IModDataOption : IModOption, IModDataContainer;

View file

@ -1,27 +1,10 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Penumbra.Mods.SubMods; namespace Penumbra.Mods.SubMods;
public interface IModOption public interface IModOption
{ {
public string Name { get; set; } public string Name { get; set; }
public string FullName { get; } public string FullName { get; }
public string Description { get; set; } public string Description { get; set; }
public static void Load(JToken json, IModOption option)
{
option.Name = json[nameof(Name)]?.ToObject<string>() ?? string.Empty;
option.Description = json[nameof(Description)]?.ToObject<string>() ?? string.Empty;
}
public (int GroupIndex, int OptionIndex) GetOptionIndices(); public (int GroupIndex, int OptionIndex) GetOptionIndices();
public static void WriteModOption(JsonWriter j, IModOption option)
{
j.WritePropertyName(nameof(Name));
j.WriteValue(option.Name);
j.WritePropertyName(nameof(Description));
j.WriteValue(option.Description);
}
} }

View file

@ -35,8 +35,8 @@ public class MultiSubMod(Mod mod, MultiModGroup group) : IModDataOption
public MultiSubMod(Mod mod, MultiModGroup group, JToken json) public MultiSubMod(Mod mod, MultiModGroup group, JToken json)
: this(mod, group) : this(mod, group)
{ {
IModOption.Load(json, this); SubModHelpers.LoadOptionData(json, this);
IModDataContainer.Load(json, this, mod.ModPath); SubModHelpers.LoadDataContainer(json, this, mod.ModPath);
Priority = json[nameof(IModGroup.Priority)]?.ToObject<ModPriority>() ?? ModPriority.Default; Priority = json[nameof(IModGroup.Priority)]?.ToObject<ModPriority>() ?? ModPriority.Default;
} }
@ -48,7 +48,7 @@ public class MultiSubMod(Mod mod, MultiModGroup group) : IModDataOption
Description = Description, Description = Description,
Priority = Priority, Priority = Priority,
}; };
IModDataContainer.Clone(this, ret); SubModHelpers.Clone(this, ret);
return ret; return ret;
} }
@ -60,12 +60,12 @@ public class MultiSubMod(Mod mod, MultiModGroup group) : IModDataOption
Name = Name, Name = Name,
Description = Description, Description = Description,
}; };
IModDataContainer.Clone(this, ret); SubModHelpers.Clone(this, ret);
return ret; return ret;
} }
public void AddDataTo(Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations) public void AddDataTo(Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
=> IModDataContainer.AddDataTo(this, redirections, manipulations); => SubModHelpers.AddContainerTo(this, redirections, manipulations);
public static MultiSubMod CreateForSaving(string name, string description, ModPriority priority) public static MultiSubMod CreateForSaving(string name, string description, ModPriority priority)
=> new(null!, null!) => new(null!, null!)

View file

@ -33,8 +33,8 @@ public class SingleSubMod(Mod mod, SingleModGroup group) : IModDataOption
public SingleSubMod(Mod mod, SingleModGroup group, JToken json) public SingleSubMod(Mod mod, SingleModGroup group, JToken json)
: this(mod, group) : this(mod, group)
{ {
IModOption.Load(json, this); SubModHelpers.LoadOptionData(json, this);
IModDataContainer.Load(json, this, mod.ModPath); SubModHelpers.LoadDataContainer(json, this, mod.ModPath);
} }
public SingleSubMod Clone(Mod mod, SingleModGroup group) public SingleSubMod Clone(Mod mod, SingleModGroup group)
@ -44,7 +44,7 @@ public class SingleSubMod(Mod mod, SingleModGroup group) : IModDataOption
Name = Name, Name = Name,
Description = Description, Description = Description,
}; };
IModDataContainer.Clone(this, ret); SubModHelpers.Clone(this, ret);
return ret; return ret;
} }
@ -57,13 +57,13 @@ public class SingleSubMod(Mod mod, SingleModGroup group) : IModDataOption
Description = Description, Description = Description,
Priority = priority, Priority = priority,
}; };
IModDataContainer.Clone(this, ret); SubModHelpers.Clone(this, ret);
return ret; return ret;
} }
public void AddDataTo(Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations) public void AddDataTo(Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
=> IModDataContainer.AddDataTo(this, redirections, manipulations); => SubModHelpers.AddContainerTo(this, redirections, manipulations);
public (int GroupIndex, int DataIndex) GetDataIndices() public (int GroupIndex, int DataIndex) GetDataIndices()
=> (Group.GetIndex(), GetDataIndex()); => (Group.GetIndex(), GetDataIndex());

View file

@ -0,0 +1,105 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.ItemSwap;
using Penumbra.String.Classes;
namespace Penumbra.Mods.SubMods;
public static class SubModHelpers
{
/// <summary> Add all unique meta manipulations, file redirections and then file swaps from a ModDataContainer to the given sets. Skip any keys that are already contained. </summary>
public static void AddContainerTo(IModDataContainer container, Dictionary<Utf8GamePath, FullPath> redirections,
HashSet<MetaManipulation> manipulations)
{
foreach (var (path, file) in container.Files)
redirections.TryAdd(path, file);
foreach (var (path, file) in container.FileSwaps)
redirections.TryAdd(path, file);
manipulations.UnionWith(container.Manipulations);
}
/// <summary> Replace all data of <paramref name="to"/> with the data of <paramref name="from"/>. </summary>
public static void Clone(IModDataContainer from, IModDataContainer to)
{
to.Files = new Dictionary<Utf8GamePath, FullPath>(from.Files);
to.FileSwaps = new Dictionary<Utf8GamePath, FullPath>(from.FileSwaps);
to.Manipulations = [.. from.Manipulations];
}
/// <summary> Load all file redirections, file swaps and meta manipulations from a JToken of that option into a data container. </summary>
public static void LoadDataContainer(JToken json, IModDataContainer data, DirectoryInfo basePath)
{
data.Files.Clear();
data.FileSwaps.Clear();
data.Manipulations.Clear();
var files = (JObject?)json[nameof(data.Files)];
if (files != null)
foreach (var property in files.Properties())
{
if (Utf8GamePath.FromString(property.Name, out var p, true))
data.Files.TryAdd(p, new FullPath(basePath, property.Value.ToObject<Utf8RelPath>()));
}
var swaps = (JObject?)json[nameof(data.FileSwaps)];
if (swaps != null)
foreach (var property in swaps.Properties())
{
if (Utf8GamePath.FromString(property.Name, out var p, true))
data.FileSwaps.TryAdd(p, new FullPath(property.Value.ToObject<string>()!));
}
var manips = json[nameof(data.Manipulations)];
if (manips != null)
foreach (var s in manips.Children().Select(c => c.ToObject<MetaManipulation>())
.Where(m => m.Validate()))
data.Manipulations.Add(s);
}
/// <summary> Load the relevant data for a selectable option from a JToken of that option. </summary>
public static void LoadOptionData(JToken json, IModOption option)
{
option.Name = json[nameof(option.Name)]?.ToObject<string>() ?? string.Empty;
option.Description = json[nameof(option.Description)]?.ToObject<string>() ?? string.Empty;
}
/// <summary> Write file redirections, file swaps and meta manipulations from a data container on a JsonWriter. </summary>
public static void WriteModContainer(JsonWriter j, JsonSerializer serializer, IModDataContainer data, DirectoryInfo basePath)
{
j.WritePropertyName(nameof(data.Files));
j.WriteStartObject();
foreach (var (gamePath, file) in data.Files)
{
if (file.ToRelPath(basePath, out var relPath))
{
j.WritePropertyName(gamePath.ToString());
j.WriteValue(relPath.ToString());
}
}
j.WriteEndObject();
j.WritePropertyName(nameof(data.FileSwaps));
j.WriteStartObject();
foreach (var (gamePath, file) in data.FileSwaps)
{
j.WritePropertyName(gamePath.ToString());
j.WriteValue(file.ToString());
}
j.WriteEndObject();
j.WritePropertyName(nameof(data.Manipulations));
serializer.Serialize(j, data.Manipulations);
j.WriteEndObject();
}
/// <summary> Write the data for a selectable mod option on a JsonWriter. </summary>
public static void WriteModOption(JsonWriter j, IModOption option)
{
j.WritePropertyName(nameof(option.Name));
j.WriteValue(option.Name);
j.WritePropertyName(nameof(option.Description));
j.WriteValue(option.Description);
}
}