From 33ada1d9949b6b68315e0e6f96f6714973250128 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Tue, 8 Apr 2025 16:56:23 +0200 Subject: [PATCH] Remove meta-default-value checking from TT imports, move it entirely to mod loads, and keep default-valued entries if other options actually edit the same entry. --- .editorconfig | 12 ++ OtterGui | 2 +- .../Import/TexToolsMeta.Deserialization.cs | 31 +--- Penumbra/Import/TexToolsMeta.Rgsp.cs | 7 +- Penumbra/Import/TexToolsMeta.cs | 9 +- Penumbra/Mods/Editor/ModMetaEditor.cs | 162 ++++++++++++++++-- Penumbra/Mods/Manager/ModMigration.cs | 10 +- Penumbra/Mods/ModCreator.cs | 36 ++-- 8 files changed, 194 insertions(+), 75 deletions(-) diff --git a/.editorconfig b/.editorconfig index c645b573..f0328fd7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3576,6 +3576,18 @@ resharper_xaml_xaml_xamarin_forms_data_type_and_binding_context_type_mismatched_ resharper_xaml_x_key_attribute_disallowed_highlighting=error resharper_xml_doc_comment_syntax_problem_highlighting=warning resharper_xunit_xunit_test_with_console_output_highlighting=warning +csharp_style_prefer_implicitly_typed_lambda_expression = true:suggestion +csharp_style_expression_bodied_methods = true:silent +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_unbound_generic_type_in_nameof = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_expression_bodied_constructors = true:silent +csharp_style_expression_bodied_operators = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_style_expression_bodied_properties = true:silent [*.{cshtml,htm,html,proto,razor}] indent_style=tab diff --git a/OtterGui b/OtterGui index f53fd227..21ddfccb 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit f53fd227a242435ce44a9fe9c5e847d0ca788869 +Subproject commit 21ddfccb91ba3fa56e1c191e706ff91bffaa9515 diff --git a/Penumbra/Import/TexToolsMeta.Deserialization.cs b/Penumbra/Import/TexToolsMeta.Deserialization.cs index 1f970dfe..7861a95b 100644 --- a/Penumbra/Import/TexToolsMeta.Deserialization.cs +++ b/Penumbra/Import/TexToolsMeta.Deserialization.cs @@ -2,7 +2,6 @@ using Lumina.Extensions; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Penumbra.Import.Structs; -using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; namespace Penumbra.Import; @@ -19,9 +18,7 @@ public partial class TexToolsMeta var identifier = new EqpIdentifier(metaFileInfo.PrimaryId, metaFileInfo.EquipSlot); var value = Eqp.FromSlotAndBytes(metaFileInfo.EquipSlot, data) & mask; - var def = ExpandedEqpFile.GetDefault(_metaFileManager, metaFileInfo.PrimaryId) & mask; - if (_keepDefault || def != value) - MetaManipulations.TryAdd(identifier, value); + MetaManipulations.TryAdd(identifier, value); } // Deserialize and check Eqdp Entries and add them to the list if they are non-default. @@ -41,11 +38,9 @@ public partial class TexToolsMeta continue; var identifier = new EqdpIdentifier(metaFileInfo.PrimaryId, metaFileInfo.EquipSlot, gr); - var mask = Eqdp.Mask(metaFileInfo.EquipSlot); - var value = Eqdp.FromSlotAndBits(metaFileInfo.EquipSlot, (byteValue & 1) == 1, (byteValue & 2) == 2) & mask; - var def = ExpandedEqdpFile.GetDefault(_metaFileManager, gr, metaFileInfo.EquipSlot.IsAccessory(), metaFileInfo.PrimaryId) & mask; - if (_keepDefault || def != value) - MetaManipulations.TryAdd(identifier, value); + var mask = Eqdp.Mask(metaFileInfo.EquipSlot); + var value = Eqdp.FromSlotAndBits(metaFileInfo.EquipSlot, (byteValue & 1) == 1, (byteValue & 2) == 2) & mask; + MetaManipulations.TryAdd(identifier, value); } } @@ -55,10 +50,9 @@ public partial class TexToolsMeta if (data == null) return; - var value = GmpEntry.FromTexToolsMeta(data.AsSpan(0, 5)); - var def = ExpandedGmpFile.GetDefault(_metaFileManager, metaFileInfo.PrimaryId); - if (_keepDefault || value != def) - MetaManipulations.TryAdd(new GmpIdentifier(metaFileInfo.PrimaryId), value); + var value = GmpEntry.FromTexToolsMeta(data.AsSpan(0, 5)); + var identifier = new GmpIdentifier(metaFileInfo.PrimaryId); + MetaManipulations.TryAdd(identifier, value); } // Deserialize and check Est Entries and add them to the list if they are non-default. @@ -86,9 +80,7 @@ public partial class TexToolsMeta continue; var identifier = new EstIdentifier(id, type, gr); - var def = EstFile.GetDefault(_metaFileManager, type, gr, id); - if (_keepDefault || def != value) - MetaManipulations.TryAdd(identifier, value); + MetaManipulations.TryAdd(identifier, value); } } @@ -108,15 +100,10 @@ public partial class TexToolsMeta { var identifier = new ImcIdentifier(metaFileInfo.PrimaryId, 0, metaFileInfo.PrimaryType, metaFileInfo.SecondaryId, metaFileInfo.EquipSlot, metaFileInfo.SecondaryType); - var file = new ImcFile(_metaFileManager, identifier); - var partIdx = ImcFile.PartIndex(identifier.EquipSlot); // Gets turned to unknown for things without equip, and unknown turns to 0. foreach (var value in values) { identifier = identifier with { Variant = (Variant)i }; - var def = file.GetEntry(partIdx, (Variant)i); - if (_keepDefault || def != value && identifier.Validate()) - MetaManipulations.TryAdd(identifier, value); - + MetaManipulations.TryAdd(identifier, value); ++i; } } diff --git a/Penumbra/Import/TexToolsMeta.Rgsp.cs b/Penumbra/Import/TexToolsMeta.Rgsp.cs index 7b0bb5a8..77c70e6c 100644 --- a/Penumbra/Import/TexToolsMeta.Rgsp.cs +++ b/Penumbra/Import/TexToolsMeta.Rgsp.cs @@ -1,6 +1,5 @@ using Penumbra.GameData.Enums; using Penumbra.Meta; -using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; namespace Penumbra.Import; @@ -8,7 +7,7 @@ namespace Penumbra.Import; public partial class TexToolsMeta { // Parse a single rgsp file. - public static TexToolsMeta FromRgspFile(MetaFileManager manager, string filePath, byte[] data, bool keepDefault) + public static TexToolsMeta FromRgspFile(MetaFileManager manager, string filePath, byte[] data) { if (data.Length != 45 && data.Length != 42) { @@ -70,9 +69,7 @@ public partial class TexToolsMeta void Add(RspAttribute attribute, float value) { var identifier = new RspIdentifier(subRace, attribute); - var def = CmpFile.GetDefault(manager, subRace, attribute); - if (keepDefault || value != def.Value) - ret.MetaManipulations.TryAdd(identifier, new RspEntry(value)); + ret.MetaManipulations.TryAdd(identifier, new RspEntry(value)); } } } diff --git a/Penumbra/Import/TexToolsMeta.cs b/Penumbra/Import/TexToolsMeta.cs index c4a8e81f..f98eddbe 100644 --- a/Penumbra/Import/TexToolsMeta.cs +++ b/Penumbra/Import/TexToolsMeta.cs @@ -23,15 +23,11 @@ public partial class TexToolsMeta // The info class determines the files or table locations the changes need to apply to from the filename. public readonly uint Version; public readonly string FilePath; - public readonly MetaDictionary MetaManipulations = new(); - private readonly bool _keepDefault; + public readonly MetaDictionary MetaManipulations = new(); - private readonly MetaFileManager _metaFileManager; - public TexToolsMeta(MetaFileManager metaFileManager, GamePathParser parser, byte[] data, bool keepDefault) + public TexToolsMeta(GamePathParser parser, byte[] data) { - _metaFileManager = metaFileManager; - _keepDefault = keepDefault; try { using var reader = new BinaryReader(new MemoryStream(data)); @@ -79,7 +75,6 @@ public partial class TexToolsMeta private TexToolsMeta(MetaFileManager metaFileManager, string filePath, uint version) { - _metaFileManager = metaFileManager; FilePath = filePath; Version = version; } diff --git a/Penumbra/Mods/Editor/ModMetaEditor.cs b/Penumbra/Mods/Editor/ModMetaEditor.cs index c5c8fb8b..050dab51 100644 --- a/Penumbra/Mods/Editor/ModMetaEditor.cs +++ b/Penumbra/Mods/Editor/ModMetaEditor.cs @@ -1,11 +1,15 @@ using System.Collections.Frozen; +using OtterGui.Classes; using OtterGui.Services; using Penumbra.Collections.Cache; using Penumbra.Meta; using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; +using Penumbra.Mods.Groups; using Penumbra.Mods.Manager.OptionEditor; using Penumbra.Mods.SubMods; +using Penumbra.Services; +using static Penumbra.GameData.Files.ShpkFile; namespace Penumbra.Mods.Editor; @@ -68,13 +72,157 @@ public class ModMetaEditor( Changes = false; } - public static bool DeleteDefaultValues(MetaFileManager metaFileManager, MetaDictionary dict) + public static bool DeleteDefaultValues(Mod mod, MetaFileManager metaFileManager, SaveService? saveService, bool deleteAll = false) + { + if (deleteAll) + { + var changes = false; + foreach (var container in mod.AllDataContainers) + { + if (!DeleteDefaultValues(metaFileManager, container.Manipulations)) + continue; + + saveService?.ImmediateSaveSync(new ModSaveGroup(container, metaFileManager.Config.ReplaceNonAsciiOnImport)); + changes = true; + } + + return changes; + } + + var defaultEntries = new MultiDictionary(); + var actualEntries = new HashSet(); + if (!FilterDefaultValues(mod.AllDataContainers, metaFileManager, defaultEntries, actualEntries)) + return false; + + var groups = new HashSet(); + DefaultSubMod? defaultMod = null; + foreach (var (defaultIdentifier, containers) in defaultEntries.Grouped) + { + if (!deleteAll && actualEntries.Contains(defaultIdentifier)) + continue; + + foreach (var container in containers) + { + if (!container.Manipulations.Remove(defaultIdentifier)) + continue; + + Penumbra.Log.Verbose($"Deleted default-valued meta-entry {defaultIdentifier}."); + if (container.Group is { } group) + groups.Add(group); + else if (container is DefaultSubMod d) + defaultMod = d; + } + } + + if (saveService is not null) + { + if (defaultMod is not null) + saveService.ImmediateSaveSync(new ModSaveGroup(defaultMod, metaFileManager.Config.ReplaceNonAsciiOnImport)); + foreach (var group in groups) + saveService.ImmediateSaveSync(new ModSaveGroup(group, metaFileManager.Config.ReplaceNonAsciiOnImport)); + } + + return defaultMod is not null || groups.Count > 0; + } + + public void DeleteDefaultValues() + => Changes = DeleteDefaultValues(metaFileManager, this); + + public void Apply(IModDataContainer container) + { + if (!Changes) + return; + + groupEditor.SetManipulations(container, this); + Changes = false; + } + + private static bool FilterDefaultValues(IEnumerable containers, MetaFileManager metaFileManager, + MultiDictionary defaultEntries, HashSet actualEntries) + { + if (!metaFileManager.CharacterUtility.Ready) + { + Penumbra.Log.Warning("Trying to filter default meta values before CharacterUtility was ready, skipped."); + return false; + } + + foreach (var container in containers) + { + foreach (var (key, value) in container.Manipulations.Imc) + { + var defaultEntry = ImcChecker.GetDefaultEntry(key, false); + if (defaultEntry.Entry.Equals(value)) + defaultEntries.TryAdd(key, container); + else + actualEntries.Add(key); + } + + foreach (var (key, value) in container.Manipulations.Eqp) + { + var defaultEntry = new EqpEntryInternal(ExpandedEqpFile.GetDefault(metaFileManager, key.SetId), key.Slot); + if (defaultEntry.Equals(value)) + defaultEntries.TryAdd(key, container); + else + actualEntries.Add(key); + } + + foreach (var (key, value) in container.Manipulations.Eqdp) + { + var defaultEntry = new EqdpEntryInternal(ExpandedEqdpFile.GetDefault(metaFileManager, key), key.Slot); + if (defaultEntry.Equals(value)) + defaultEntries.TryAdd(key, container); + else + actualEntries.Add(key); + } + + foreach (var (key, value) in container.Manipulations.Est) + { + var defaultEntry = EstFile.GetDefault(metaFileManager, key); + if (defaultEntry.Equals(value)) + defaultEntries.TryAdd(key, container); + else + actualEntries.Add(key); + } + + foreach (var (key, value) in container.Manipulations.Gmp) + { + var defaultEntry = ExpandedGmpFile.GetDefault(metaFileManager, key); + if (defaultEntry.Equals(value)) + defaultEntries.TryAdd(key, container); + else + actualEntries.Add(key); + } + + foreach (var (key, value) in container.Manipulations.Rsp) + { + var defaultEntry = CmpFile.GetDefault(metaFileManager, key.SubRace, key.Attribute); + if (defaultEntry.Equals(value)) + defaultEntries.TryAdd(key, container); + else + actualEntries.Add(key); + } + + foreach (var (key, value) in container.Manipulations.Atch) + { + var defaultEntry = AtchCache.GetDefault(metaFileManager, key); + if (defaultEntry.Equals(value)) + defaultEntries.TryAdd(key, container); + else + actualEntries.Add(key); + } + } + + return true; + } + + private static bool DeleteDefaultValues(MetaFileManager metaFileManager, MetaDictionary dict) { if (!metaFileManager.CharacterUtility.Ready) { Penumbra.Log.Warning("Trying to delete default meta values before CharacterUtility was ready, skipped."); return false; } + var clone = dict.Clone(); dict.ClearForDefault(); @@ -189,16 +337,4 @@ public class ModMetaEditor( Penumbra.Log.Debug($"Deleted {count} default-valued meta-entries from a mod option."); return true; } - - public void DeleteDefaultValues() - => Changes = DeleteDefaultValues(metaFileManager, this); - - public void Apply(IModDataContainer container) - { - if (!Changes) - return; - - groupEditor.SetManipulations(container, this); - Changes = false; - } } diff --git a/Penumbra/Mods/Manager/ModMigration.cs b/Penumbra/Mods/Manager/ModMigration.cs index 3e58c515..8b5b80d0 100644 --- a/Penumbra/Mods/Manager/ModMigration.cs +++ b/Penumbra/Mods/Manager/ModMigration.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OtterGui; using Penumbra.Api.Enums; +using Penumbra.Mods.Editor; using Penumbra.Mods.Groups; using Penumbra.Mods.Settings; using Penumbra.Mods.SubMods; @@ -82,9 +83,8 @@ public static partial class ModMigration foreach (var (gamePath, swapPath) in swaps) mod.Default.FileSwaps.Add(gamePath, swapPath); - creator.IncorporateMetaChanges(mod.Default, mod.ModPath, true, true); - foreach (var group in mod.Groups) - saveService.ImmediateSave(new ModSaveGroup(group, creator.Config.ReplaceNonAsciiOnImport)); + creator.IncorporateAllMetaChanges(mod, true, true); + saveService.SaveAllOptionGroups(mod, false, creator.Config.ReplaceNonAsciiOnImport); // Delete meta files. foreach (var file in seenMetaFiles.Where(f => f.Exists)) @@ -182,7 +182,7 @@ public static partial class ModMigration Description = option.OptionDesc, }; AddFilesToSubMod(subMod, mod.ModPath, option, seenMetaFiles); - creator.IncorporateMetaChanges(subMod, mod.ModPath, false, true); + creator.IncorporateMetaChanges(subMod, mod.ModPath, false); return subMod; } @@ -196,7 +196,7 @@ public static partial class ModMigration Priority = priority, }; AddFilesToSubMod(subMod, mod.ModPath, option, seenMetaFiles); - creator.IncorporateMetaChanges(subMod, mod.ModPath, false, true); + creator.IncorporateMetaChanges(subMod, mod.ModPath, false); return subMod; } diff --git a/Penumbra/Mods/ModCreator.cs b/Penumbra/Mods/ModCreator.cs index 0db83ef9..f4f182eb 100644 --- a/Penumbra/Mods/ModCreator.cs +++ b/Penumbra/Mods/ModCreator.cs @@ -80,14 +80,10 @@ public partial class ModCreator( LoadDefaultOption(mod); LoadAllGroups(mod); if (incorporateMetaChanges) - IncorporateAllMetaChanges(mod, true); - if (deleteDefaultMetaChanges && !Config.KeepDefaultMetaChanges) - foreach (var container in mod.AllDataContainers) - { - if (ModMetaEditor.DeleteDefaultValues(metaFileManager, container.Manipulations)) - saveService.ImmediateSaveSync(new ModSaveGroup(container, Config.ReplaceNonAsciiOnImport)); - } - + IncorporateAllMetaChanges(mod, true, deleteDefaultMetaChanges); + else if (deleteDefaultMetaChanges) + ModMetaEditor.DeleteDefaultValues(mod, metaFileManager, saveService, false); + return true; } @@ -158,19 +154,21 @@ public partial class ModCreator( /// Convert all .meta and .rgsp files to their respective meta changes and add them to their options. /// Deletes the source files if delete is true. /// - public void IncorporateAllMetaChanges(Mod mod, bool delete) + public void IncorporateAllMetaChanges(Mod mod, bool delete, bool removeDefaultValues) { var changes = false; - List deleteList = new(); + List deleteList = []; foreach (var subMod in mod.AllDataContainers) { - var (localChanges, localDeleteList) = IncorporateMetaChanges(subMod, mod.ModPath, false, true); + var (localChanges, localDeleteList) = IncorporateMetaChanges(subMod, mod.ModPath, false); changes |= localChanges; if (delete) deleteList.AddRange(localDeleteList); } DeleteDeleteList(deleteList, delete); + if (removeDefaultValues && !Config.KeepDefaultMetaChanges) + changes |= ModMetaEditor.DeleteDefaultValues(mod, metaFileManager, null, false); if (!changes) return; @@ -184,8 +182,7 @@ public partial class ModCreator( /// If .meta or .rgsp files are encountered, parse them and incorporate their meta changes into the mod. /// If delete is true, the files are deleted afterwards. /// - public (bool Changes, List DeleteList) IncorporateMetaChanges(IModDataContainer option, DirectoryInfo basePath, bool delete, - bool deleteDefault) + public (bool Changes, List DeleteList) IncorporateMetaChanges(IModDataContainer option, DirectoryInfo basePath, bool delete) { var deleteList = new List(); var oldSize = option.Manipulations.Count; @@ -202,8 +199,7 @@ public partial class ModCreator( if (!file.Exists) continue; - var meta = new TexToolsMeta(metaFileManager, gamePathParser, File.ReadAllBytes(file.FullName), - Config.KeepDefaultMetaChanges); + var meta = new TexToolsMeta(gamePathParser, File.ReadAllBytes(file.FullName)); Penumbra.Log.Verbose( $"Incorporating {file} as Metadata file of {meta.MetaManipulations.Count} manipulations {deleteString}"); deleteList.Add(file.FullName); @@ -215,8 +211,7 @@ public partial class ModCreator( if (!file.Exists) continue; - var rgsp = TexToolsMeta.FromRgspFile(metaFileManager, file.FullName, File.ReadAllBytes(file.FullName), - Config.KeepDefaultMetaChanges); + var rgsp = TexToolsMeta.FromRgspFile(metaFileManager, file.FullName, File.ReadAllBytes(file.FullName)); Penumbra.Log.Verbose( $"Incorporating {file} as racial scaling file of {rgsp.MetaManipulations.Count} manipulations {deleteString}"); deleteList.Add(file.FullName); @@ -232,9 +227,6 @@ public partial class ModCreator( DeleteDeleteList(deleteList, delete); var changes = oldSize < option.Manipulations.Count; - if (deleteDefault && !Config.KeepDefaultMetaChanges) - changes |= ModMetaEditor.DeleteDefaultValues(metaFileManager, option.Manipulations); - return (changes, deleteList); } @@ -289,7 +281,7 @@ public partial class ModCreator( foreach (var (_, gamePath, file) in list) mod.Files.TryAdd(gamePath, file); - IncorporateMetaChanges(mod, baseFolder, true, true); + IncorporateMetaChanges(mod, baseFolder, true); return mod; } @@ -308,7 +300,7 @@ public partial class ModCreator( mod.Default.Files.TryAdd(gamePath, file); } - IncorporateMetaChanges(mod.Default, directory, true, true); + IncorporateMetaChanges(mod.Default, directory, true); saveService.ImmediateSaveSync(new ModSaveGroup(mod.ModPath, mod.Default, Config.ReplaceNonAsciiOnImport)); }