Add design migration for inverted gloss and specular, and a backup before doing that.

This commit is contained in:
Ottermandias 2024-08-09 16:01:44 +02:00
parent d7074c5791
commit 5cd224b164
5 changed files with 91 additions and 14 deletions

View file

@ -143,10 +143,10 @@ public class Configuration : IPluginConfiguration, ISavable
public static class Constants public static class Constants
{ {
public const int CurrentVersion = 6; public const int CurrentVersion = 7;
public static readonly ISortMode<Design>[] ValidSortModes = public static readonly ISortMode<Design>[] ValidSortModes =
{ [
ISortMode<Design>.FoldersFirst, ISortMode<Design>.FoldersFirst,
ISortMode<Design>.Lexicographical, ISortMode<Design>.Lexicographical,
new DesignFileSystem.CreationDate(), new DesignFileSystem.CreationDate(),
@ -159,7 +159,7 @@ public class Configuration : IPluginConfiguration, ISavable
ISortMode<Design>.InverseFoldersLast, ISortMode<Design>.InverseFoldersLast,
ISortMode<Design>.InternalOrder, ISortMode<Design>.InternalOrder,
ISortMode<Design>.InverseInternalOrder, ISortMode<Design>.InverseInternalOrder,
}; ];
} }
/// <summary> Convert SortMode Types to their name. </summary> /// <summary> Convert SortMode Types to their name. </summary>

View file

@ -9,6 +9,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Classes; using OtterGui.Classes;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Notification = OtterGui.Classes.Notification;
namespace Glamourer.Designs; namespace Glamourer.Designs;
@ -34,7 +35,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
} }
// Metadata // Metadata
public new const int FileVersion = 1; public new const int FileVersion = 2;
public Guid Identifier { get; internal init; } public Guid Identifier { get; internal init; }
public DateTimeOffset CreationDate { get; internal init; } public DateTimeOffset CreationDate { get; internal init; }
@ -143,17 +144,81 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
#region Deserialization #region Deserialization
public static Design LoadDesign(CustomizeService customizations, ItemManager items, DesignLinkLoader linkLoader, JObject json) public static Design LoadDesign(SaveService saveService, CustomizeService customizations, ItemManager items, DesignLinkLoader linkLoader, JObject json)
{ {
var version = json["FileVersion"]?.ToObject<int>() ?? 0; var version = json["FileVersion"]?.ToObject<int>() ?? 0;
return version switch return version switch
{ {
FileVersion => LoadDesignV1(customizations, items, linkLoader, json), 1 => LoadDesignV1(saveService, customizations, items, linkLoader, json),
FileVersion => LoadDesignV2(customizations, items, linkLoader, json),
_ => throw new Exception("The design to be loaded has no valid Version."), _ => throw new Exception("The design to be loaded has no valid Version."),
}; };
} }
private static Design LoadDesignV1(CustomizeService customizations, ItemManager items, DesignLinkLoader linkLoader, JObject json) /// <summary> The values for gloss and specular strength were switched. Swap them for all appropriate designs. </summary>
private static Design LoadDesignV1(SaveService saveService, CustomizeService customizations, ItemManager items, DesignLinkLoader linkLoader, JObject json)
{
var design = LoadDesignV2(customizations, items, linkLoader, json);
var materialDesignData = design.GetMaterialDataRef();
if (materialDesignData.Values.Count == 0)
return design;
var materialData = materialDesignData.Clone();
// Guesstimate whether to migrate material rows:
// Update 1.3.0.10 released at that time, so any design last updated before that can be migrated.
if (design.LastEdit <= new DateTime(2024, 8, 7, 16, 0, 0, DateTimeKind.Utc))
{
Migrate("because it was saved the wrong way around before 1.3.0.10, and this design was not changed since that release.");
}
else
{
var hasNegativeGloss = false;
var hasNonPositiveGloss = false;
var specularLarger = 0;
foreach (var (key, value) in materialData.GetValues(MaterialValueIndex.Min(), MaterialValueIndex.Max()))
{
hasNegativeGloss |= value.Value.GlossStrength < 0;
hasNonPositiveGloss |= value.Value.GlossStrength <= 0;
if (value.Value.SpecularStrength > value.Value.GlossStrength)
++specularLarger;
}
// If there is any negative gloss, this is wrong and can be migrated.
if (hasNegativeGloss)
Migrate("because it had a negative Gloss value, which is not supported and thus probably outdated.");
// If there is any non-positive Gloss and some specular values that are larger, it is probably wrong and can be migrated.
else if (hasNonPositiveGloss && specularLarger > 0)
Migrate("because it had a zero Gloss value, and at least one Specular Strength larger than the Gloss, which is unusual.");
// If most of the specular strengths are larger, it is probably wrong and can be migrated.
else if (specularLarger > materialData.Values.Count / 2)
Migrate("because most of its Specular Strength values were larger than the Gloss values, which is unusual.");
}
return design;
void Migrate(string reason)
{
materialDesignData.Clear();
foreach (var (key, value) in materialData.GetValues(MaterialValueIndex.Min(), MaterialValueIndex.Max()))
{
var gloss = Math.Clamp(value.Value.SpecularStrength, 0, (float)Half.MaxValue);
var specularStrength = Math.Clamp(value.Value.GlossStrength, 0, (float)Half.MaxValue);
var colorRow = value.Value with
{
GlossStrength = gloss,
SpecularStrength = specularStrength,
};
materialDesignData.AddOrUpdateValue(MaterialValueIndex.FromKey(key), value with { Value = colorRow });
}
Glamourer.Messager.AddMessage(new Notification(
$"Swapped Gloss and Specular Strength in {materialDesignData.Values.Count} Rows in design {design.Incognito} {reason}",
NotificationType.Info));
saveService.Save(SaveType.ImmediateSync,design);
}
}
private static Design LoadDesignV2(CustomizeService customizations, ItemManager items, DesignLinkLoader linkLoader, JObject json)
{ {
var creationDate = json["CreationDate"]?.ToObject<DateTimeOffset>() ?? throw new ArgumentNullException("CreationDate"); var creationDate = json["CreationDate"]?.ToObject<DateTimeOffset>() ?? throw new ArgumentNullException("CreationDate");
@ -183,7 +248,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
static string[] ParseTags(JObject json) static string[] ParseTags(JObject json)
{ {
var tags = json["Tags"]?.ToObject<string[]>() ?? Array.Empty<string>(); var tags = json["Tags"]?.ToObject<string[]>() ?? [];
return tags.OrderBy(t => t).Distinct().ToArray(); return tags.OrderBy(t => t).Distinct().ToArray();
} }
} }

View file

@ -13,6 +13,7 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Designs; namespace Glamourer.Designs;
public class DesignConverter( public class DesignConverter(
SaveService saveService,
ItemManager _items, ItemManager _items,
DesignManager _designs, DesignManager _designs,
CustomizeService _customize, CustomizeService _customize,
@ -69,7 +70,7 @@ public class DesignConverter(
try try
{ {
var ret = jObject["Identifier"] != null var ret = jObject["Identifier"] != null
? Design.LoadDesign(_customize, _items, _linkLoader, jObject) ? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObject)
: DesignBase.LoadDesignBase(_customize, _items, jObject); : DesignBase.LoadDesignBase(_customize, _items, jObject);
if (!customize) if (!customize)
@ -100,7 +101,7 @@ public class DesignConverter(
case (byte)'{': case (byte)'{':
var jObj1 = JObject.Parse(Encoding.UTF8.GetString(bytes)); var jObj1 = JObject.Parse(Encoding.UTF8.GetString(bytes));
ret = jObj1["Identifier"] != null ret = jObj1["Identifier"] != null
? Design.LoadDesign(_customize, _items, _linkLoader, jObj1) ? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj1)
: DesignBase.LoadDesignBase(_customize, _items, jObj1); : DesignBase.LoadDesignBase(_customize, _items, jObj1);
break; break;
case 1: case 1:
@ -115,7 +116,7 @@ public class DesignConverter(
var jObj2 = JObject.Parse(decompressed); var jObj2 = JObject.Parse(decompressed);
Debug.Assert(version == 3); Debug.Assert(version == 3);
ret = jObj2["Identifier"] != null ret = jObj2["Identifier"] != null
? Design.LoadDesign(_customize, _items, _linkLoader, jObj2) ? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj2)
: DesignBase.LoadDesignBase(_customize, _items, jObj2); : DesignBase.LoadDesignBase(_customize, _items, jObj2);
break; break;
} }
@ -126,7 +127,7 @@ public class DesignConverter(
var jObj2 = JObject.Parse(decompressed); var jObj2 = JObject.Parse(decompressed);
Debug.Assert(version == 5); Debug.Assert(version == 5);
ret = jObj2["Identifier"] != null ret = jObj2["Identifier"] != null
? Design.LoadDesign(_customize, _items, _linkLoader, jObj2) ? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj2)
: DesignBase.LoadDesignBase(_customize, _items, jObj2); : DesignBase.LoadDesignBase(_customize, _items, jObj2);
break; break;
} }
@ -136,7 +137,7 @@ public class DesignConverter(
var jObj2 = JObject.Parse(decompressed); var jObj2 = JObject.Parse(decompressed);
Debug.Assert(version == 6); Debug.Assert(version == 6);
ret = jObj2["Identifier"] != null ret = jObj2["Identifier"] != null
? Design.LoadDesign(_customize, _items, _linkLoader, jObj2) ? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj2)
: DesignBase.LoadDesignBase(_customize, _items, jObj2); : DesignBase.LoadDesignBase(_customize, _items, jObj2);
break; break;
} }

View file

@ -53,7 +53,7 @@ public sealed class DesignManager : DesignEditor
{ {
var text = File.ReadAllText(f.FullName); var text = File.ReadAllText(f.FullName);
var data = JObject.Parse(text); var data = JObject.Parse(text);
var design = Design.LoadDesign(Customizations, Items, linkLoader, data); var design = Design.LoadDesign(SaveService, Customizations, Items, linkLoader, data);
designs.Value!.Add((design, f.FullName)); designs.Value!.Add((design, f.FullName));
} }
catch (Exception ex) catch (Exception ex)

View file

@ -23,9 +23,20 @@ public class ConfigMigrationService(SaveService saveService, FixedDesignMigrator
MigrateV2To4(); MigrateV2To4();
MigrateV4To5(); MigrateV4To5();
MigrateV5To6(); MigrateV5To6();
MigrateV6To7();
AddColors(config, true); AddColors(config, true);
} }
private void MigrateV6To7()
{
if (_config.Version > 6)
return;
// Do not actually change anything in the config, just create a backup before designs are migrated.
backupService.CreateMigrationBackup("pre_gloss_specular_migration");
_config.Version = 7;
}
private void MigrateV5To6() private void MigrateV5To6()
{ {
if (_config.Version > 5) if (_config.Version > 5)