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 Dictionary< string, string > ModSortOrder = new();
public bool InvertModListOrder; public bool InvertModListOrder;
public bool SortFoldersFirst; public bool SortFoldersFirst;
public SortModeV3 SortMode = SortModeV3.FoldersFirst;
public static void Migrate( Configuration config ) public static void Migrate( Configuration config )
{ {
@ -45,6 +46,31 @@ public partial class Configuration
m.Version0To1(); m.Version0To1();
m.Version1To2(); m.Version1To2();
m.Version2To3(); 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. // 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; SortFoldersFirst = _data[ nameof( SortFoldersFirst ) ]?.ToObject< bool >() ?? false;
_config.SortMode = SortFoldersFirst ? SortMode.FoldersFirst : SortMode.Lexicographical; SortMode = SortFoldersFirst ? SortModeV3.FoldersFirst : SortModeV3.Lexicographical;
_config.Version = 3; _config.Version = 3;
} }
@ -242,5 +268,17 @@ public partial class Configuration
PluginLog.Error( $"Could not create backup copy of config at {bakName}:\n{e}" ); 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using Dalamud.Configuration; using Dalamud.Configuration;
using Dalamud.Logging; using Dalamud.Logging;
using Newtonsoft.Json;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using Penumbra.Import; using Penumbra.Import;
using Penumbra.Mods;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
using Penumbra.Util;
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
namespace Penumbra; namespace Penumbra;
@ -40,8 +45,10 @@ public partial class Configuration : IPluginConfiguration
public bool EnableResourceLogging { get; set; } = false; public bool EnableResourceLogging { get; set; } = false;
public string ResourceLoggingFilter { get; set; } = string.Empty; 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 bool ScaleModSelector { get; set; } = false;
public float ModSelectorAbsoluteSize { get; set; } = Constants.DefaultAbsoluteSize; public float ModSelectorAbsoluteSize { get; set; } = Constants.DefaultAbsoluteSize;
public int ModSelectorScaledSize { get; set; } = Constants.DefaultScaledSize; 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. // Includes adding new colors and migrating from old versions.
public static Configuration Load() public static Configuration Load()
{ {
var iConfiguration = Dalamud.PluginInterface.GetPluginConfig(); void HandleDeserializationError( object? sender, ErrorEventArgs errorArgs )
var configuration = iConfiguration as Configuration ?? new Configuration(); {
if( iConfiguration is { Version: Constants.CurrentVersion } ) 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 ); configuration.AddColors( false );
return configuration; return configuration;
@ -84,7 +107,8 @@ public partial class Configuration : IPluginConfiguration
{ {
try try
{ {
Dalamud.PluginInterface.SavePluginConfig( this ); var text = JsonConvert.SerializeObject( this, Formatting.Indented );
File.WriteAllText( Dalamud.PluginInterface.ConfigFile.FullName, text );
} }
catch( Exception e ) catch( Exception e )
{ {
@ -113,12 +137,48 @@ public partial class Configuration : IPluginConfiguration
// Contains some default values or boundaries for config values. // Contains some default values or boundaries for config values.
public static class Constants public static class Constants
{ {
public const int CurrentVersion = 3; public const int CurrentVersion = 4;
public const float MaxAbsoluteSize = 600; public const float MaxAbsoluteSize = 600;
public const int DefaultAbsoluteSize = 250; public const int DefaultAbsoluteSize = 250;
public const float MinAbsoluteSize = 50; public const float MinAbsoluteSize = 50;
public const int MaxScaledSize = 80; public const int MaxScaledSize = 80;
public const int DefaultScaledSize = 20; public const int DefaultScaledSize = 20;
public const int MinScaledSize = 5; 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 System;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Hooking; using Dalamud.Hooking;
using Dalamud.Logging; using Dalamud.Logging;
using Dalamud.Utility.Signatures; using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Penumbra.Collections; using Penumbra.Collections;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
namespace Penumbra.Interop.Resolver; namespace Penumbra.Interop.Resolver;
@ -21,7 +24,7 @@ public unsafe partial class PathResolver
private ulong LoadTimelineResourcesDetour( IntPtr timeline ) private ulong LoadTimelineResourcesDetour( IntPtr timeline )
{ {
ulong ret; ulong ret;
var old = _animationLoadCollection; var old = _animationLoadCollection;
try try
{ {
var getGameObjectIdx = ( ( delegate* unmanaged < IntPtr, int>** )timeline )[ 0 ][ 28 ]; 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 ) ); _animationLoadCollection = IdentifyCollection( ( GameObject* )( Dalamud.Objects[ actorIdx ]?.Address ?? IntPtr.Zero ) );
} }
} }
LoadSomePapHook!.Original( a1, a2, a3, a4 ); LoadSomePapHook!.Original( a1, a2, a3, a4 );
_animationLoadCollection = last; _animationLoadCollection = last;
} }
@ -107,4 +111,28 @@ public unsafe partial class PathResolver
SomeActionLoadHook!.Original( gameObject ); SomeActionLoadHook!.Original( gameObject );
_animationLoadCollection = last; _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(); LoadSomeAvfxHook?.Enable();
LoadSomePapHook?.Enable(); LoadSomePapHook?.Enable();
SomeActionLoadHook?.Enable(); SomeActionLoadHook?.Enable();
SomeOtherAvfxHook?.Enable();
SomeAtexHook?.Enable();
} }
private void DisableDataHooks() private void DisableDataHooks()
@ -111,6 +113,8 @@ public unsafe partial class PathResolver
LoadSomeAvfxHook?.Disable(); LoadSomeAvfxHook?.Disable();
LoadSomePapHook?.Disable(); LoadSomePapHook?.Disable();
SomeActionLoadHook?.Disable(); SomeActionLoadHook?.Disable();
SomeOtherAvfxHook?.Disable();
SomeAtexHook?.Disable();
} }
private void DisposeDataHooks() private void DisposeDataHooks()
@ -124,6 +128,8 @@ public unsafe partial class PathResolver
LoadSomeAvfxHook?.Dispose(); LoadSomeAvfxHook?.Dispose();
LoadSomePapHook?.Dispose(); LoadSomePapHook?.Dispose();
SomeActionLoadHook?.Dispose(); SomeActionLoadHook?.Dispose();
SomeOtherAvfxHook?.Dispose();
SomeAtexHook?.Dispose();
} }
// This map links DrawObjects directly to Actors (by ObjectTable index) and their collections. // 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 ) 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 ) if( _animationLoadCollection != null )
{ {
switch( type ) switch( type )

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -45,6 +46,30 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
Penumbra.ModManager.ModMetaChanged -= OnMetaChange; 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. // Reload the whole filesystem from currently loaded mods and the current sort order file.
// Used on construction and on mod rediscoveries. // Used on construction and on mod rediscoveries.
private void Reload() private void Reload()
@ -72,7 +97,7 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
if( type.HasFlag( MetaChangeType.Name ) && oldName != null ) if( type.HasFlag( MetaChangeType.Name ) && oldName != null )
{ {
var old = oldName.FixName(); 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 ); Rename( child, mod.Name.Text );
} }
@ -97,7 +122,7 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
CreateLeaf( Root, name, mod ); CreateLeaf( Root, name, mod );
break; break;
case ModPathChangeType.Deleted: 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 ) if( leaf != null )
{ {
Delete( leaf ); Delete( leaf );

View file

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

View file

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