mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Let options keep visual ordering.
This commit is contained in:
parent
f24ec8ebe2
commit
81e93e0664
9 changed files with 105 additions and 28 deletions
|
|
@ -12,6 +12,7 @@
|
||||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
|
@ -43,6 +44,8 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3"/>
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3">
|
||||||
|
<Private>false</Private>
|
||||||
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,8 @@ public partial class TexToolsImporter
|
||||||
++_currentOptionIdx;
|
++_currentOptionIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mod.CreateOptionGroup( _currentModDirectory, group, groupPriority++, description.ToString(), options );
|
Mod.CreateOptionGroup( _currentModDirectory, group, groupPriority, groupPriority, description.ToString(), options );
|
||||||
|
++groupPriority;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ public sealed partial class Mod
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
group.DeleteFile( mod.BasePath );
|
group.DeleteFile( mod.BasePath, groupIdx );
|
||||||
|
|
||||||
var _ = group switch
|
var _ = group switch
|
||||||
{
|
{
|
||||||
|
|
@ -86,7 +86,7 @@ public sealed partial class Mod
|
||||||
{
|
{
|
||||||
var group = mod._groups[ groupIdx ];
|
var group = mod._groups[ groupIdx ];
|
||||||
mod._groups.RemoveAt( groupIdx );
|
mod._groups.RemoveAt( groupIdx );
|
||||||
group.DeleteFile( mod.BasePath );
|
group.DeleteFile( mod.BasePath, groupIdx );
|
||||||
ModOptionChanged.Invoke( ModOptionChangeType.GroupDeleted, mod, groupIdx, -1, -1 );
|
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 )
|
private static void OnModOptionChange( ModOptionChangeType type, Mod mod, int groupIdx, int _, int _2 )
|
||||||
{
|
{
|
||||||
// File deletion is handled in the actual function.
|
// 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.
|
// 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.GroupAdded => mod.SetCounts(),
|
||||||
ModOptionChangeType.GroupTypeChanged => mod.HasOptions = mod.Groups.Any( o => o.IsOption ),
|
ModOptionChangeType.GroupDeleted => mod.SetCounts(),
|
||||||
ModOptionChangeType.OptionAdded => mod.HasOptions |= mod._groups[ groupIdx ].IsOption,
|
ModOptionChangeType.GroupMoved => false,
|
||||||
ModOptionChangeType.OptionDeleted => mod.HasOptions = mod.Groups.Any( o => o.IsOption ),
|
ModOptionChangeType.GroupTypeChanged => mod.HasOptions = mod.Groups.Any( o => o.IsOption ),
|
||||||
_ => mod.HasOptions,
|
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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ public partial class Mod
|
||||||
|
|
||||||
// Create a file for an option group from given data.
|
// Create a file for an option group from given data.
|
||||||
internal static void CreateOptionGroup( DirectoryInfo baseFolder, ModGroup groupData,
|
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 )
|
switch( groupData.SelectionType )
|
||||||
{
|
{
|
||||||
|
|
@ -72,7 +72,7 @@ public partial class Mod
|
||||||
Priority = priority,
|
Priority = priority,
|
||||||
};
|
};
|
||||||
group.PrioritizedOptions.AddRange( subMods.OfType< SubMod >().Select( ( s, idx ) => ( s, idx ) ) );
|
group.PrioritizedOptions.AddRange( subMods.OfType< SubMod >().Select( ( s, idx ) => ( s, idx ) ) );
|
||||||
IModGroup.SaveModGroup( group, baseFolder );
|
IModGroup.SaveModGroup( group, baseFolder, index );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SelectType.Single:
|
case SelectType.Single:
|
||||||
|
|
@ -84,7 +84,7 @@ public partial class Mod
|
||||||
Priority = priority,
|
Priority = priority,
|
||||||
};
|
};
|
||||||
group.OptionData.AddRange( subMods.OfType< SubMod >() );
|
group.OptionData.AddRange( subMods.OfType< SubMod >() );
|
||||||
IModGroup.SaveModGroup( group, baseFolder );
|
IModGroup.SaveModGroup( group, baseFolder, index );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Penumbra.GameData.ByteString;
|
using Penumbra.GameData.ByteString;
|
||||||
using Penumbra.Meta.Manipulations;
|
using Penumbra.Meta.Manipulations;
|
||||||
|
using Penumbra.Util;
|
||||||
|
|
||||||
namespace Penumbra.Mods;
|
namespace Penumbra.Mods;
|
||||||
|
|
||||||
|
|
@ -25,7 +27,7 @@ public partial class Mod
|
||||||
public int TotalManipulations { get; private set; }
|
public int TotalManipulations { get; private set; }
|
||||||
public bool HasOptions { get; private set; }
|
public bool HasOptions { get; private set; }
|
||||||
|
|
||||||
private void SetCounts()
|
private bool SetCounts()
|
||||||
{
|
{
|
||||||
TotalFileCount = 0;
|
TotalFileCount = 0;
|
||||||
TotalSwapCount = 0;
|
TotalSwapCount = 0;
|
||||||
|
|
@ -40,6 +42,7 @@ public partial class Mod
|
||||||
HasOptions = _groups.Any( o
|
HasOptions = _groups.Any( o
|
||||||
=> o is MultiModGroup m && m.PrioritizedOptions.Count > 0
|
=> o is MultiModGroup m && m.PrioritizedOptions.Count > 0
|
||||||
|| o is SingleModGroup s && s.OptionData.Count > 1 );
|
|| o is SingleModGroup s && s.OptionData.Count > 1 );
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable< ISubMod > AllSubMods
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,10 +2,12 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Penumbra.GameData.ByteString;
|
using Penumbra.GameData.ByteString;
|
||||||
|
using Penumbra.Util;
|
||||||
|
|
||||||
namespace Penumbra.Mods;
|
namespace Penumbra.Mods;
|
||||||
|
|
||||||
|
|
@ -14,7 +16,36 @@ public sealed partial class Mod
|
||||||
private static class Migration
|
private static class Migration
|
||||||
{
|
{
|
||||||
public static bool Migrate( Mod mod, JObject json )
|
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 )
|
private static bool MigrateV0ToV1( Mod mod, JObject json )
|
||||||
{
|
{
|
||||||
|
|
@ -50,9 +81,9 @@ public sealed partial class Mod
|
||||||
}
|
}
|
||||||
|
|
||||||
mod._default.IncorporateMetaChanges( mod.BasePath, true );
|
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.
|
// Delete meta files.
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,12 @@ public interface IModGroup : IEnumerable< ISubMod >
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
public string FileName( DirectoryInfo basePath )
|
public string FileName( DirectoryInfo basePath, int groupIdx )
|
||||||
=> Path.Combine( basePath.FullName, $"group_{Name.RemoveInvalidPathSymbols().ToLowerInvariant()}.json" );
|
=> 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 ) )
|
if( !File.Exists( file ) )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
@ -48,7 +48,7 @@ public interface IModGroup : IEnumerable< ISubMod >
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.Delete( file );
|
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 )
|
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 s = File.Exists( file ) ? File.Open( file, FileMode.Truncate ) : File.Open( file, FileMode.CreateNew );
|
||||||
using var writer = new StreamWriter( s );
|
using var writer = new StreamWriter( s );
|
||||||
using var j = new JsonTextWriter( writer ) { Formatting = Formatting.Indented };
|
using var j = new JsonTextWriter( writer ) { Formatting = Formatting.Indented };
|
||||||
|
|
@ -82,7 +82,7 @@ public interface IModGroup : IEnumerable< ISubMod >
|
||||||
|
|
||||||
j.WriteEndArray();
|
j.WriteEndArray();
|
||||||
j.WriteEndObject();
|
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 );
|
public IModGroup Convert( SelectType type );
|
||||||
|
|
|
||||||
|
|
@ -259,7 +259,7 @@ public partial class ConfigWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var fileName = group.FileName( _mod.BasePath );
|
var fileName = group.FileName( _mod.BasePath, groupIdx );
|
||||||
var fileExists = File.Exists( fileName );
|
var fileExists = File.Exists( fileName );
|
||||||
tt = fileExists
|
tt = fileExists
|
||||||
? $"Open the {group.Name} json file in the text editor of your choice."
|
? $"Open the {group.Name} json file in the text editor of your choice."
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ public partial class ConfigWindow
|
||||||
DrawSingleGroup( _mod.Groups[ idx ], idx );
|
DrawSingleGroup( _mod.Groups[ idx ], idx );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.Dummy( _window._defaultSpace );
|
||||||
for( var idx = 0; idx < _mod.Groups.Count; ++idx )
|
for( var idx = 0; idx < _mod.Groups.Count; ++idx )
|
||||||
{
|
{
|
||||||
DrawMultiGroup( _mod.Groups[ idx ], idx );
|
DrawMultiGroup( _mod.Groups[ idx ], idx );
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue