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 );