mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-02-20 14:57:50 +01:00
145 lines
5.7 KiB
C#
145 lines
5.7 KiB
C#
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using Penumbra.GameData.Structs;
|
|
using Penumbra.Mods.Manager;
|
|
using Penumbra.Services;
|
|
|
|
namespace Penumbra.Mods;
|
|
|
|
public readonly struct ModMeta(Mod mod) : ISavable
|
|
{
|
|
public const uint FileVersion = 3;
|
|
|
|
public string ToFilePath(FilenameService fileNames)
|
|
=> fileNames.ModMetaPath(mod);
|
|
|
|
public void Save(StreamWriter writer)
|
|
{
|
|
var jObject = new JObject
|
|
{
|
|
{ nameof(FileVersion), JToken.FromObject(FileVersion) },
|
|
{ nameof(Mod.Name), JToken.FromObject(mod.Name) },
|
|
{ nameof(Mod.Author), JToken.FromObject(mod.Author) },
|
|
{ nameof(Mod.Description), JToken.FromObject(mod.Description) },
|
|
{ nameof(Mod.Image), JToken.FromObject(mod.Image) },
|
|
{ nameof(Mod.Version), JToken.FromObject(mod.Version) },
|
|
{ nameof(Mod.Website), JToken.FromObject(mod.Website) },
|
|
{ nameof(Mod.ModTags), JToken.FromObject(mod.ModTags) },
|
|
{ nameof(Mod.DefaultPreferredItems), JToken.FromObject(mod.DefaultPreferredItems) },
|
|
};
|
|
if (mod.RequiredFeatures is not FeatureFlags.None)
|
|
{
|
|
var features = mod.RequiredFeatures;
|
|
var array = new JArray();
|
|
foreach (var flag in Enum.GetValues<FeatureFlags>())
|
|
{
|
|
if ((features & flag) is not FeatureFlags.None)
|
|
array.Add(flag.ToString());
|
|
}
|
|
|
|
jObject[nameof(Mod.RequiredFeatures)] = array;
|
|
}
|
|
|
|
using var jWriter = new JsonTextWriter(writer);
|
|
jWriter.Formatting = Formatting.Indented;
|
|
jObject.WriteTo(jWriter);
|
|
}
|
|
|
|
public static ModDataChangeType Load(ModDataEditor editor, ModCreator creator, Mod mod)
|
|
{
|
|
var metaFile = editor.SaveService.FileNames.ModMetaPath(mod);
|
|
if (!File.Exists(metaFile))
|
|
{
|
|
Penumbra.Log.Debug($"No mod meta found for {mod.ModPath.Name}.");
|
|
return ModDataChangeType.Deletion;
|
|
}
|
|
|
|
try
|
|
{
|
|
var text = File.ReadAllText(metaFile);
|
|
var json = JObject.Parse(text);
|
|
|
|
var newFileVersion = json[nameof(FileVersion)]?.Value<uint>() ?? 0;
|
|
|
|
// Empty name gets checked after loading and is not allowed.
|
|
var newName = json[nameof(Mod.Name)]?.Value<string>() ?? string.Empty;
|
|
|
|
var newAuthor = json[nameof(Mod.Author)]?.Value<string>() ?? string.Empty;
|
|
var newDescription = json[nameof(Mod.Description)]?.Value<string>() ?? string.Empty;
|
|
var newImage = json[nameof(Mod.Image)]?.Value<string>() ?? string.Empty;
|
|
var newVersion = json[nameof(Mod.Version)]?.Value<string>() ?? string.Empty;
|
|
var newWebsite = json[nameof(Mod.Website)]?.Value<string>() ?? string.Empty;
|
|
var modTags = (json[nameof(Mod.ModTags)] as JArray)?.Values<string>().OfType<string>();
|
|
var defaultItems = (json[nameof(Mod.DefaultPreferredItems)] as JArray)?.Values<ulong>().Select(i => (CustomItemId)i).ToHashSet()
|
|
?? [];
|
|
var requiredFeatureArray = (json[nameof(Mod.RequiredFeatures)] as JArray)?.Values<string>() ?? [];
|
|
var requiredFeatures = FeatureChecker.ParseFlags(mod.ModPath.Name, newName.Length > 0 ? newName : mod.Name.Length > 0 ? mod.Name : "Unknown", requiredFeatureArray!);
|
|
|
|
ModDataChangeType changes = 0;
|
|
if (mod.Name != newName)
|
|
{
|
|
changes |= ModDataChangeType.Name;
|
|
mod.Name = newName;
|
|
}
|
|
|
|
if (mod.Author != newAuthor)
|
|
{
|
|
changes |= ModDataChangeType.Author;
|
|
mod.Author = newAuthor;
|
|
}
|
|
|
|
if (mod.Description != newDescription)
|
|
{
|
|
changes |= ModDataChangeType.Description;
|
|
mod.Description = newDescription;
|
|
}
|
|
|
|
if (mod.Image != newImage)
|
|
{
|
|
changes |= ModDataChangeType.Image;
|
|
mod.Image = newImage;
|
|
}
|
|
|
|
if (mod.Version != newVersion)
|
|
{
|
|
changes |= ModDataChangeType.Version;
|
|
mod.Version = newVersion;
|
|
}
|
|
|
|
if (mod.Website != newWebsite)
|
|
{
|
|
changes |= ModDataChangeType.Website;
|
|
mod.Website = newWebsite;
|
|
}
|
|
|
|
if (!mod.DefaultPreferredItems.SetEquals(defaultItems))
|
|
{
|
|
changes |= ModDataChangeType.DefaultChangedItems;
|
|
mod.DefaultPreferredItems = defaultItems;
|
|
}
|
|
|
|
if (newFileVersion != FileVersion)
|
|
if (ModMigration.Migrate(creator, editor.SaveService, mod, json, ref newFileVersion))
|
|
{
|
|
changes |= ModDataChangeType.Migration;
|
|
editor.SaveService.ImmediateSave(new ModMeta(mod));
|
|
}
|
|
|
|
// Required features get checked during parsing, in which case the new required features signal invalid.
|
|
if (requiredFeatures != mod.RequiredFeatures)
|
|
{
|
|
changes |= ModDataChangeType.RequiredFeatures;
|
|
mod.RequiredFeatures = requiredFeatures;
|
|
}
|
|
|
|
changes |= ModLocalData.UpdateTags(mod, modTags, null);
|
|
|
|
return changes;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Penumbra.Log.Error($"Could not load mod meta for {metaFile}:\n{e}");
|
|
return ModDataChangeType.Deletion;
|
|
}
|
|
}
|
|
}
|