From 81e93e0664b69de2213c9d297b2976f4eba611ff Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 30 Apr 2022 23:04:39 +0200 Subject: [PATCH] Let options keep visual ordering. --- Penumbra.GameData/Penumbra.GameData.csproj | 5 ++- Penumbra/Import/TexToolsImporter.ModPack.cs | 3 +- Penumbra/Mods/Manager/Mod.Manager.Options.cs | 33 ++++++++++++----- Penumbra/Mods/Mod.Creation.cs | 6 +-- Penumbra/Mods/Mod.Files.cs | 30 ++++++++++++++- Penumbra/Mods/Mod.Meta.Migration.cs | 37 +++++++++++++++++-- Penumbra/Mods/Subclasses/IModGroup.cs | 16 ++++---- Penumbra/UI/ConfigWindow.ModPanel.Edit.cs | 2 +- Penumbra/UI/ConfigWindow.ModPanel.Settings.cs | 1 + 9 files changed, 105 insertions(+), 28 deletions(-) diff --git a/Penumbra.GameData/Penumbra.GameData.csproj b/Penumbra.GameData/Penumbra.GameData.csproj index c7dbc908..d2470349 100644 --- a/Penumbra.GameData/Penumbra.GameData.csproj +++ b/Penumbra.GameData/Penumbra.GameData.csproj @@ -12,6 +12,7 @@ bin\$(Configuration)\ true enable + true @@ -43,6 +44,8 @@ - + + false + diff --git a/Penumbra/Import/TexToolsImporter.ModPack.cs b/Penumbra/Import/TexToolsImporter.ModPack.cs index dadfe4ac..5533b75e 100644 --- a/Penumbra/Import/TexToolsImporter.ModPack.cs +++ b/Penumbra/Import/TexToolsImporter.ModPack.cs @@ -173,7 +173,8 @@ public partial class TexToolsImporter ++_currentOptionIdx; } - Mod.CreateOptionGroup( _currentModDirectory, group, groupPriority++, description.ToString(), options ); + Mod.CreateOptionGroup( _currentModDirectory, group, groupPriority, groupPriority, description.ToString(), options ); + ++groupPriority; } } diff --git a/Penumbra/Mods/Manager/Mod.Manager.Options.cs b/Penumbra/Mods/Manager/Mod.Manager.Options.cs index 98267ae7..ec1a4beb 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.Options.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.Options.cs @@ -55,7 +55,7 @@ public sealed partial class Mod return; } - group.DeleteFile( mod.BasePath ); + group.DeleteFile( mod.BasePath, groupIdx ); var _ = group switch { @@ -86,7 +86,7 @@ public sealed partial class Mod { var group = mod._groups[ groupIdx ]; mod._groups.RemoveAt( groupIdx ); - group.DeleteFile( mod.BasePath ); + group.DeleteFile( mod.BasePath, groupIdx ); ModOptionChanged.Invoke( ModOptionChangeType.GroupDeleted, mod, groupIdx, -1, -1 ); } @@ -400,19 +400,32 @@ public sealed partial class Mod private static void OnModOptionChange( ModOptionChangeType type, Mod mod, int groupIdx, int _, int _2 ) { // File deletion is handled in the actual function. - if( type != ModOptionChangeType.GroupDeleted ) + if( type is ModOptionChangeType.GroupDeleted or ModOptionChangeType.GroupMoved ) { - IModGroup.SaveModGroup( mod._groups[ groupIdx ], mod.BasePath ); + mod.SaveAllGroups(); + } + else + { + IModGroup.SaveModGroup( mod._groups[ groupIdx ], mod.BasePath, groupIdx ); } // State can not change on adding groups, as they have no immediate options. - mod.HasOptions = type switch + var unused = type switch { - ModOptionChangeType.GroupDeleted => mod.HasOptions = mod.Groups.Any( o => o.IsOption ), - ModOptionChangeType.GroupTypeChanged => mod.HasOptions = mod.Groups.Any( o => o.IsOption ), - ModOptionChangeType.OptionAdded => mod.HasOptions |= mod._groups[ groupIdx ].IsOption, - ModOptionChangeType.OptionDeleted => mod.HasOptions = mod.Groups.Any( o => o.IsOption ), - _ => mod.HasOptions, + ModOptionChangeType.GroupAdded => mod.SetCounts(), + ModOptionChangeType.GroupDeleted => mod.SetCounts(), + ModOptionChangeType.GroupMoved => false, + ModOptionChangeType.GroupTypeChanged => mod.HasOptions = mod.Groups.Any( o => o.IsOption ), + ModOptionChangeType.PriorityChanged => false, + ModOptionChangeType.OptionAdded => mod.SetCounts(), + ModOptionChangeType.OptionDeleted => mod.SetCounts(), + ModOptionChangeType.OptionMoved => false, + ModOptionChangeType.OptionFilesChanged => 0 < ( mod.TotalFileCount = mod.AllSubMods.Sum( s => s.Files.Count ) ), + ModOptionChangeType.OptionSwapsChanged => 0 < ( mod.TotalSwapCount = mod.AllSubMods.Sum( s => s.FileSwaps.Count ) ), + ModOptionChangeType.OptionMetaChanged => 0 < ( mod.TotalManipulations = mod.AllSubMods.Sum( s => s.Manipulations.Count ) ), + ModOptionChangeType.OptionUpdated => mod.SetCounts(), + ModOptionChangeType.DisplayChange => false, + _ => false, }; } } diff --git a/Penumbra/Mods/Mod.Creation.cs b/Penumbra/Mods/Mod.Creation.cs index 79056ba2..211d2574 100644 --- a/Penumbra/Mods/Mod.Creation.cs +++ b/Penumbra/Mods/Mod.Creation.cs @@ -59,7 +59,7 @@ public partial class Mod // Create a file for an option group from given data. internal static void CreateOptionGroup( DirectoryInfo baseFolder, ModGroup groupData, - int priority, string desc, IEnumerable< ISubMod > subMods ) + int priority, int index, string desc, IEnumerable< ISubMod > subMods ) { switch( groupData.SelectionType ) { @@ -72,7 +72,7 @@ public partial class Mod Priority = priority, }; group.PrioritizedOptions.AddRange( subMods.OfType< SubMod >().Select( ( s, idx ) => ( s, idx ) ) ); - IModGroup.SaveModGroup( group, baseFolder ); + IModGroup.SaveModGroup( group, baseFolder, index ); break; } case SelectType.Single: @@ -84,7 +84,7 @@ public partial class Mod Priority = priority, }; group.OptionData.AddRange( subMods.OfType< SubMod >() ); - IModGroup.SaveModGroup( group, baseFolder ); + IModGroup.SaveModGroup( group, baseFolder, index ); break; } } diff --git a/Penumbra/Mods/Mod.Files.cs b/Penumbra/Mods/Mod.Files.cs index 0eee20b0..8d14f2ea 100644 --- a/Penumbra/Mods/Mod.Files.cs +++ b/Penumbra/Mods/Mod.Files.cs @@ -2,10 +2,12 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using Dalamud.Logging; using Newtonsoft.Json.Linq; using Penumbra.GameData.ByteString; using Penumbra.Meta.Manipulations; +using Penumbra.Util; namespace Penumbra.Mods; @@ -25,7 +27,7 @@ public partial class Mod public int TotalManipulations { get; private set; } public bool HasOptions { get; private set; } - private void SetCounts() + private bool SetCounts() { TotalFileCount = 0; TotalSwapCount = 0; @@ -40,6 +42,7 @@ public partial class Mod HasOptions = _groups.Any( o => o is MultiModGroup m && m.PrioritizedOptions.Count > 0 || o is SingleModGroup s && s.OptionData.Count > 1 ); + return true; } public IEnumerable< ISubMod > AllSubMods @@ -114,4 +117,29 @@ public partial class Mod } } } + + // Delete all existing group files and save them anew. + // Used when indices change in complex ways. + private void SaveAllGroups() + { + foreach( var file in GroupFiles ) + { + try + { + if( file.Exists ) + { + file.Delete(); + } + } + catch( Exception e ) + { + PluginLog.Error( $"Could not delete outdated group file {file}:\n{e}" ); + } + } + + foreach( var (group, index) in _groups.WithIndex() ) + { + IModGroup.SaveModGroup( group, BasePath, index ); + } + } } \ No newline at end of file diff --git a/Penumbra/Mods/Mod.Meta.Migration.cs b/Penumbra/Mods/Mod.Meta.Migration.cs index 59a67cab..2eae72d1 100644 --- a/Penumbra/Mods/Mod.Meta.Migration.cs +++ b/Penumbra/Mods/Mod.Meta.Migration.cs @@ -2,10 +2,12 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using Dalamud.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Penumbra.GameData.ByteString; +using Penumbra.Util; namespace Penumbra.Mods; @@ -14,7 +16,36 @@ public sealed partial class Mod private static class Migration { public static bool Migrate( Mod mod, JObject json ) - => MigrateV0ToV1( mod, json ); + => MigrateV0ToV1( mod, json ) || MigrateV1ToV2( mod ); + + private static bool MigrateV1ToV2( Mod mod ) + { + if( mod.FileVersion > 1 ) + { + return false; + } + + foreach( var (group, index) in mod.GroupFiles.WithIndex().ToArray() ) + { + var newName = Regex.Replace( group.Name, "^group_", $"group_{index + 1:D3}_", RegexOptions.Compiled ); + try + { + if( newName != group.Name ) + { + group.MoveTo( Path.Combine( group.DirectoryName ?? string.Empty, newName ), false ); + } + } + catch( Exception e ) + { + PluginLog.Error( $"Could not rename group file {group.Name} to {newName} during migration:\n{e}" ); + } + } + + mod.FileVersion = 2; + mod.SaveMeta(); + + return true; + } private static bool MigrateV0ToV1( Mod mod, JObject json ) { @@ -50,9 +81,9 @@ public sealed partial class Mod } mod._default.IncorporateMetaChanges( mod.BasePath, true ); - foreach( var group in mod.Groups ) + foreach( var (group, index) in mod.Groups.WithIndex() ) { - IModGroup.SaveModGroup( group, mod.BasePath ); + IModGroup.SaveModGroup( group, mod.BasePath, index ); } // Delete meta files. diff --git a/Penumbra/Mods/Subclasses/IModGroup.cs b/Penumbra/Mods/Subclasses/IModGroup.cs index 5dae5481..bea449ee 100644 --- a/Penumbra/Mods/Subclasses/IModGroup.cs +++ b/Penumbra/Mods/Subclasses/IModGroup.cs @@ -34,12 +34,12 @@ public interface IModGroup : IEnumerable< ISubMod > _ => false, }; - public string FileName( DirectoryInfo basePath ) - => Path.Combine( basePath.FullName, $"group_{Name.RemoveInvalidPathSymbols().ToLowerInvariant()}.json" ); + public string FileName( DirectoryInfo basePath, int groupIdx ) + => Path.Combine( basePath.FullName, $"group_{groupIdx + 1:D3}_{Name.RemoveInvalidPathSymbols().ToLowerInvariant()}.json" ); - public void DeleteFile( DirectoryInfo basePath ) + public void DeleteFile( DirectoryInfo basePath, int groupIdx ) { - var file = FileName( basePath ); + var file = FileName( basePath, groupIdx ); if( !File.Exists( file ) ) { return; @@ -48,7 +48,7 @@ public interface IModGroup : IEnumerable< ISubMod > try { File.Delete( file ); - PluginLog.Debug( "Deleted group file {File:l} for {GroupName:l}.", file, Name ); + PluginLog.Debug( "Deleted group file {File:l} for group {GroupIdx}: {GroupName:l}.", file, groupIdx + 1, Name ); } catch( Exception e ) { @@ -57,9 +57,9 @@ public interface IModGroup : IEnumerable< ISubMod > } } - public static void SaveModGroup( IModGroup group, DirectoryInfo basePath ) + public static void SaveModGroup( IModGroup group, DirectoryInfo basePath, int groupIdx ) { - var file = group.FileName( basePath ); + var file = group.FileName( basePath, groupIdx ); using var s = File.Exists( file ) ? File.Open( file, FileMode.Truncate ) : File.Open( file, FileMode.CreateNew ); using var writer = new StreamWriter( s ); using var j = new JsonTextWriter( writer ) { Formatting = Formatting.Indented }; @@ -82,7 +82,7 @@ public interface IModGroup : IEnumerable< ISubMod > j.WriteEndArray(); j.WriteEndObject(); - PluginLog.Debug( "Saved group file {File:l} for {GroupName:l}.", file, group.Name ); + PluginLog.Debug( "Saved group file {File:l} for group {GroupIdx}: {GroupName:l}.", file, groupIdx + 1, group.Name ); } public IModGroup Convert( SelectType type ); diff --git a/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs b/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs index f2c9ad8d..eb64503a 100644 --- a/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs +++ b/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs @@ -259,7 +259,7 @@ public partial class ConfigWindow } ImGui.SameLine(); - var fileName = group.FileName( _mod.BasePath ); + var fileName = group.FileName( _mod.BasePath, groupIdx ); var fileExists = File.Exists( fileName ); tt = fileExists ? $"Open the {group.Name} json file in the text editor of your choice." diff --git a/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs b/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs index 197a846d..e3574c99 100644 --- a/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs +++ b/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs @@ -59,6 +59,7 @@ public partial class ConfigWindow DrawSingleGroup( _mod.Groups[ idx ], idx ); } + ImGui.Dummy( _window._defaultSpace ); for( var idx = 0; idx < _mod.Groups.Count; ++idx ) { DrawMultiGroup( _mod.Groups[ idx ], idx );