Add date sort methods.

This commit is contained in:
Ottermandias 2022-06-24 18:13:03 +02:00
parent c975336e65
commit ec91755065
9 changed files with 185 additions and 22 deletions

@ -1 +1 @@
Subproject commit 03934d3a19cb610898412045ad5ea7dad9766a59
Subproject commit 8053b24bb6b77a13853455c08a89df4894b3f2ee

View file

@ -27,6 +27,7 @@ public partial class Configuration
public Dictionary< string, string > ModSortOrder = new();
public bool InvertModListOrder;
public bool SortFoldersFirst;
public SortModeV3 SortMode = SortModeV3.FoldersFirst;
public static void Migrate( Configuration config )
{
@ -45,6 +46,31 @@ public partial class Configuration
m.Version0To1();
m.Version1To2();
m.Version2To3();
m.Version3To4();
}
// SortMode was changed from an enum to a type.
private void Version3To4()
{
if( _config.Version != 3 )
{
return;
}
SortMode = _data[ nameof( SortMode ) ]?.ToObject< SortModeV3 >() ?? SortMode;
_config.SortMode = SortMode switch
{
SortModeV3.FoldersFirst => ISortMode< Mod >.FoldersFirst,
SortModeV3.Lexicographical => ISortMode< Mod >.Lexicographical,
SortModeV3.InverseFoldersFirst => ISortMode< Mod >.InverseFoldersFirst,
SortModeV3.InverseLexicographical => ISortMode< Mod >.InverseLexicographical,
SortModeV3.FoldersLast => ISortMode< Mod >.FoldersLast,
SortModeV3.InverseFoldersLast => ISortMode< Mod >.InverseFoldersLast,
SortModeV3.InternalOrder => ISortMode< Mod >.InternalOrder,
SortModeV3.InternalOrderInverse => ISortMode< Mod >.InverseInternalOrder,
_ => ISortMode< Mod >.FoldersFirst,
};
_config.Version = 4;
}
// SortFoldersFirst was changed from a bool to the enum SortMode.
@ -56,7 +82,7 @@ public partial class Configuration
}
SortFoldersFirst = _data[ nameof( SortFoldersFirst ) ]?.ToObject< bool >() ?? false;
_config.SortMode = SortFoldersFirst ? SortMode.FoldersFirst : SortMode.Lexicographical;
SortMode = SortFoldersFirst ? SortModeV3.FoldersFirst : SortModeV3.Lexicographical;
_config.Version = 3;
}
@ -242,5 +268,17 @@ public partial class Configuration
PluginLog.Error( $"Could not create backup copy of config at {bakName}:\n{e}" );
}
}
public enum SortModeV3 : byte
{
FoldersFirst = 0x00,
Lexicographical = 0x01,
InverseFoldersFirst = 0x02,
InverseLexicographical = 0x03,
FoldersLast = 0x04,
InverseFoldersLast = 0x05,
InternalOrder = 0x06,
InternalOrderInverse = 0x07,
}
}
}

View file

@ -1,12 +1,17 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Dalamud.Configuration;
using Dalamud.Logging;
using Newtonsoft.Json;
using OtterGui.Classes;
using OtterGui.Filesystem;
using Penumbra.Import;
using Penumbra.Mods;
using Penumbra.UI.Classes;
using Penumbra.Util;
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
namespace Penumbra;
@ -40,8 +45,10 @@ public partial class Configuration : IPluginConfiguration
public bool EnableResourceLogging { get; set; } = false;
public string ResourceLoggingFilter { get; set; } = string.Empty;
[JsonConverter( typeof( SortModeConverter ) )]
[JsonProperty( Order = int.MaxValue )]
public ISortMode< Mod > SortMode = ISortMode< Mod >.FoldersFirst;
public SortMode SortMode { get; set; } = SortMode.FoldersFirst;
public bool ScaleModSelector { get; set; } = false;
public float ModSelectorAbsoluteSize { get; set; } = Constants.DefaultAbsoluteSize;
public int ModSelectorScaledSize { get; set; } = Constants.DefaultScaledSize;
@ -65,9 +72,25 @@ public partial class Configuration : IPluginConfiguration
// Includes adding new colors and migrating from old versions.
public static Configuration Load()
{
var iConfiguration = Dalamud.PluginInterface.GetPluginConfig();
var configuration = iConfiguration as Configuration ?? new Configuration();
if( iConfiguration is { Version: Constants.CurrentVersion } )
void HandleDeserializationError( object? sender, ErrorEventArgs errorArgs )
{
PluginLog.Error(
$"Error parsing Configuration at {errorArgs.ErrorContext.Path}, using default or migrating:\n{errorArgs.ErrorContext.Error}" );
errorArgs.ErrorContext.Handled = true;
}
Configuration? configuration = null;
if( File.Exists( Dalamud.PluginInterface.ConfigFile.FullName ) )
{
var text = File.ReadAllText( Dalamud.PluginInterface.ConfigFile.FullName );
configuration = JsonConvert.DeserializeObject< Configuration >( text, new JsonSerializerSettings
{
Error = HandleDeserializationError,
} );
}
configuration ??= new Configuration();
if( configuration.Version == Constants.CurrentVersion )
{
configuration.AddColors( false );
return configuration;
@ -84,7 +107,8 @@ public partial class Configuration : IPluginConfiguration
{
try
{
Dalamud.PluginInterface.SavePluginConfig( this );
var text = JsonConvert.SerializeObject( this, Formatting.Indented );
File.WriteAllText( Dalamud.PluginInterface.ConfigFile.FullName, text );
}
catch( Exception e )
{
@ -113,12 +137,48 @@ public partial class Configuration : IPluginConfiguration
// Contains some default values or boundaries for config values.
public static class Constants
{
public const int CurrentVersion = 3;
public const int CurrentVersion = 4;
public const float MaxAbsoluteSize = 600;
public const int DefaultAbsoluteSize = 250;
public const float MinAbsoluteSize = 50;
public const int MaxScaledSize = 80;
public const int DefaultScaledSize = 20;
public const int MinScaledSize = 5;
public static readonly ISortMode< Mod >[] ValidSortModes =
{
ISortMode< Mod >.FoldersFirst,
ISortMode< Mod >.Lexicographical,
new ModFileSystem.ImportDate(),
new ModFileSystem.InverseImportDate(),
ISortMode< Mod >.InverseFoldersFirst,
ISortMode< Mod >.InverseLexicographical,
ISortMode< Mod >.FoldersLast,
ISortMode< Mod >.InverseFoldersLast,
ISortMode< Mod >.InternalOrder,
ISortMode< Mod >.InverseInternalOrder,
};
}
private class SortModeConverter : JsonConverter< ISortMode< Mod > >
{
public override void WriteJson( JsonWriter writer, ISortMode< Mod >? value, JsonSerializer serializer )
{
value ??= ISortMode< Mod >.FoldersFirst;
serializer.Serialize( writer, value.GetType().Name );
}
public override ISortMode< Mod > ReadJson( JsonReader reader, Type objectType, ISortMode< Mod >? existingValue,
bool hasExistingValue,
JsonSerializer serializer )
{
var name = serializer.Deserialize< string >( reader );
if( name == null || !Constants.ValidSortModes.FindFirst( s => s.GetType().Name == name, out var mode ) )
{
return existingValue ?? ISortMode< Mod >.FoldersFirst;
}
return mode;
}
}
}

View file

@ -1,9 +1,12 @@
using System;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Hooking;
using Dalamud.Logging;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Penumbra.Collections;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
namespace Penumbra.Interop.Resolver;
@ -21,7 +24,7 @@ public unsafe partial class PathResolver
private ulong LoadTimelineResourcesDetour( IntPtr timeline )
{
ulong ret;
var old = _animationLoadCollection;
var old = _animationLoadCollection;
try
{
var getGameObjectIdx = ( ( delegate* unmanaged < IntPtr, int>** )timeline )[ 0 ][ 28 ];
@ -92,6 +95,7 @@ public unsafe partial class PathResolver
_animationLoadCollection = IdentifyCollection( ( GameObject* )( Dalamud.Objects[ actorIdx ]?.Address ?? IntPtr.Zero ) );
}
}
LoadSomePapHook!.Original( a1, a2, a3, a4 );
_animationLoadCollection = last;
}
@ -107,4 +111,28 @@ public unsafe partial class PathResolver
SomeActionLoadHook!.Original( gameObject );
_animationLoadCollection = last;
}
[Signature( "E8 ?? ?? ?? ?? 44 84 BB", DetourName = nameof( SomeOtherAvfxDetour ) )]
public Hook< CharacterBaseDestructorDelegate >? SomeOtherAvfxHook;
private void SomeOtherAvfxDetour( IntPtr unk )
{
var last = _animationLoadCollection;
var gameObject = ( GameObject* )( unk - 0x8B0 );
_animationLoadCollection = IdentifyCollection( gameObject );
SomeOtherAvfxHook!.Original( unk );
_animationLoadCollection = last;
}
public delegate IntPtr SomeAtexDelegate( IntPtr a1, IntPtr a2, IntPtr a3, IntPtr a4, uint a5, IntPtr a6 );
[Signature( "E8 ?? ?? ?? ?? 84 C0 75 ?? 48 8B CE 41 B6" )]
public Hook< SomeAtexDelegate >? SomeAtexHook;
public IntPtr SomeAtexDetour( IntPtr a1, IntPtr a2, IntPtr a3, IntPtr a4, uint a5, IntPtr a6 )
{
var ret = SomeAtexHook!.Original( a1, a2, a3, a4, a5, a6 );
PluginLog.Information( $"{a1:X} {a2:X} {a3:X} {a4:X} {a5:X} {a6:X} {ret}" );
return ret;
}
}

View file

@ -97,6 +97,8 @@ public unsafe partial class PathResolver
LoadSomeAvfxHook?.Enable();
LoadSomePapHook?.Enable();
SomeActionLoadHook?.Enable();
SomeOtherAvfxHook?.Enable();
SomeAtexHook?.Enable();
}
private void DisableDataHooks()
@ -111,6 +113,8 @@ public unsafe partial class PathResolver
LoadSomeAvfxHook?.Disable();
LoadSomePapHook?.Disable();
SomeActionLoadHook?.Disable();
SomeOtherAvfxHook?.Disable();
SomeAtexHook?.Disable();
}
private void DisposeDataHooks()
@ -124,6 +128,8 @@ public unsafe partial class PathResolver
LoadSomeAvfxHook?.Dispose();
LoadSomePapHook?.Dispose();
SomeActionLoadHook?.Dispose();
SomeOtherAvfxHook?.Dispose();
SomeAtexHook?.Dispose();
}
// This map links DrawObjects directly to Actors (by ObjectTable index) and their collections.

View file

@ -76,6 +76,13 @@ public partial class PathResolver : IDisposable
private bool HandleAnimationFile( ResourceType type, Utf8GamePath _, [NotNullWhen(true)] out ModCollection? collection )
{
if( type == ResourceType.Atex )
if (_animationLoadCollection == null)
PluginLog.Information( $"ATEX {_} Default" );
else
{
PluginLog.Information( $"ATEX {_} {_animationLoadCollection?.Name}" );
}
if( _animationLoadCollection != null )
{
switch( type )

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
@ -45,6 +46,30 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
Penumbra.ModManager.ModMetaChanged -= OnMetaChange;
}
public struct ImportDate : ISortMode< Mod >
{
public string Name
=> "Import Date (Older First)";
public string Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their import date.";
public IEnumerable< IPath > GetChildren( Folder f )
=> f.GetSubFolders().Cast< IPath >().Concat( f.GetLeaves().OrderBy( l => l.Value.ImportDate ) );
}
public struct InverseImportDate : ISortMode< Mod >
{
public string Name
=> "Import Date (Newer First)";
public string Description
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse import date.";
public IEnumerable< IPath > GetChildren( Folder f )
=> f.GetSubFolders().Cast< IPath >().Concat( f.GetLeaves().OrderByDescending( l => l.Value.ImportDate ) );
}
// Reload the whole filesystem from currently loaded mods and the current sort order file.
// Used on construction and on mod rediscoveries.
private void Reload()
@ -72,7 +97,7 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
if( type.HasFlag( MetaChangeType.Name ) && oldName != null )
{
var old = oldName.FixName();
if( Find( old, out var child ) && child is not Folder)
if( Find( old, out var child ) && child is not Folder )
{
Rename( child, mod.Name.Text );
}
@ -97,7 +122,7 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
CreateLeaf( Root, name, mod );
break;
case ModPathChangeType.Deleted:
var leaf = Root.GetAllDescendants( SortMode.Lexicographical ).OfType< Leaf >().FirstOrDefault( l => l.Value == mod );
var leaf = Root.GetAllDescendants( ISortMode< Mod >.Lexicographical ).OfType< Leaf >().FirstOrDefault( l => l.Value == mod );
if( leaf != null )
{
Delete( leaf );

View file

@ -14,7 +14,7 @@ using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Numerics;
using OtterGui.Classes;
using Penumbra.Util;
namespace Penumbra.UI.Classes;
@ -67,7 +67,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
=> base.SelectedLeaf;
// Customization points.
public override SortMode SortMode
public override ISortMode< Mod > SortMode
=> Penumbra.Config.SortMode;
protected override uint ExpandedFolderColor
@ -315,7 +315,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
// Helpers.
private static void SetDescendants( ModFileSystem.Folder folder, bool enabled, bool inherit = false )
{
var mods = folder.GetAllDescendants( SortMode.Lexicographical ).OfType< ModFileSystem.Leaf >().Select( l => l.Value );
var mods = folder.GetAllDescendants( ISortMode< Mod >.Lexicographical ).OfType< ModFileSystem.Leaf >().Select( l => l.Value );
if( inherit )
{
Penumbra.CollectionManager.Current.SetMultipleModInheritances( mods, enabled );
@ -404,7 +404,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
{
if( _lastSelectedDirectory.Length > 0 )
{
base.SelectedLeaf = ( ModFileSystem.Leaf? )FileSystem.Root.GetAllDescendants( SortMode.Lexicographical )
base.SelectedLeaf = ( ModFileSystem.Leaf? )FileSystem.Root.GetAllDescendants( ISortMode< Mod >.Lexicographical )
.FirstOrDefault( l => l is ModFileSystem.Leaf m && m.Value.ModPath.FullName == _lastSelectedDirectory );
OnSelectionChange( null, base.SelectedLeaf?.Value, default );
_lastSelectedDirectory = string.Empty;
@ -422,7 +422,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
try
{
var leaf = FileSystem.Root.GetChildren( SortMode.Lexicographical )
var leaf = FileSystem.Root.GetChildren( ISortMode< Mod >.Lexicographical )
.FirstOrDefault( f => f is FileSystem< Mod >.Leaf l && l.Value == mod );
if( leaf == null )
{

View file

@ -4,9 +4,9 @@ using System.Numerics;
using Dalamud.Interface;
using ImGuiNET;
using OtterGui;
using OtterGui.Filesystem;
using OtterGui.Raii;
using OtterGui.Widgets;
using Penumbra.Util;
namespace Penumbra.UI;
@ -119,20 +119,19 @@ public partial class ConfigWindow
{
var sortMode = Penumbra.Config.SortMode;
ImGui.SetNextItemWidth( _window._inputTextWidth.X );
using var combo = ImRaii.Combo( "##sortMode", sortMode.Data().Name );
using var combo = ImRaii.Combo( "##sortMode", sortMode.Name );
if( combo )
{
foreach( var val in Enum.GetValues< SortMode >() )
foreach( var val in Configuration.Constants.ValidSortModes )
{
var (name, desc) = val.Data();
if( ImGui.Selectable( name, val == sortMode ) && val != sortMode )
if( ImGui.Selectable( val.Name, val.GetType() == sortMode.GetType() ) && val.GetType() != sortMode.GetType() )
{
Penumbra.Config.SortMode = val;
_window._selector.SetFilterDirty();
Penumbra.Config.Save();
}
ImGuiUtil.HoverTooltip( desc );
ImGuiUtil.HoverTooltip( val.Description );
}
}