mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
129 lines
No EOL
4.2 KiB
C#
129 lines
No EOL
4.2 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using Dalamud.Logging;
|
|
using OtterGui.Filesystem;
|
|
|
|
namespace Penumbra.Mods;
|
|
|
|
public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
|
|
{
|
|
public static string ModFileSystemFile
|
|
=> Path.Combine( Dalamud.PluginInterface.GetPluginConfigDirectory(), "sort_order.json" );
|
|
|
|
// Save the current sort order.
|
|
// Does not save or copy the backup in the current mod directory,
|
|
// as this is done on mod directory changes only.
|
|
private void Save()
|
|
{
|
|
SaveToFile( new FileInfo( ModFileSystemFile ), SaveMod, true );
|
|
PluginLog.Verbose( "Saved mod filesystem." );
|
|
}
|
|
|
|
// Create a new ModFileSystem from the currently loaded mods and the current sort order file.
|
|
public static ModFileSystem Load()
|
|
{
|
|
var ret = new ModFileSystem();
|
|
ret.Reload();
|
|
|
|
ret.Changed += ret.OnChange;
|
|
Penumbra.ModManager.ModDiscoveryFinished += ret.Reload;
|
|
Penumbra.ModManager.ModMetaChanged += ret.OnMetaChange;
|
|
Penumbra.ModManager.ModPathChanged += ret.OnModPathChange;
|
|
|
|
return ret;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Penumbra.ModManager.ModPathChanged -= OnModPathChange;
|
|
Penumbra.ModManager.ModDiscoveryFinished -= Reload;
|
|
Penumbra.ModManager.ModMetaChanged -= OnMetaChange;
|
|
}
|
|
|
|
// Reload the whole filesystem from currently loaded mods and the current sort order file.
|
|
// Used on construction and on mod rediscoveries.
|
|
private void Reload()
|
|
{
|
|
if( Load( new FileInfo( ModFileSystemFile ), Penumbra.ModManager, ModToIdentifier, ModToName ) )
|
|
{
|
|
Save();
|
|
}
|
|
PluginLog.Debug( "Reloaded mod filesystem." );
|
|
}
|
|
|
|
// Save the filesystem on every filesystem change except full reloading.
|
|
private void OnChange( FileSystemChangeType type, IPath _1, IPath? _2, IPath? _3 )
|
|
{
|
|
if( type != FileSystemChangeType.Reload )
|
|
{
|
|
Save();
|
|
}
|
|
}
|
|
|
|
// Update sort order when defaulted mod names change.
|
|
private void OnMetaChange( MetaChangeType type, Mod mod, string? oldName )
|
|
{
|
|
if( type.HasFlag( MetaChangeType.Name ) && oldName != null )
|
|
{
|
|
var old = oldName.FixName();
|
|
if( Find( old, out var child ) && child is not Folder)
|
|
{
|
|
Rename( child, mod.Name.Text );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the filesystem if a mod has been added or removed.
|
|
// Save it, if the mod directory has been moved, since this will change the save format.
|
|
private void OnModPathChange( ModPathChangeType type, Mod mod, DirectoryInfo? oldPath, DirectoryInfo? newPath )
|
|
{
|
|
switch( type )
|
|
{
|
|
case ModPathChangeType.Added:
|
|
var originalName = mod.Name.Text.FixName();
|
|
var name = originalName;
|
|
var counter = 1;
|
|
while( Find( name, out _ ) )
|
|
{
|
|
name = $"{originalName} ({++counter})";
|
|
}
|
|
|
|
CreateLeaf( Root, name, mod );
|
|
break;
|
|
case ModPathChangeType.Deleted:
|
|
var leaf = Root.GetAllDescendants( SortMode.Lexicographical ).OfType< Leaf >().FirstOrDefault( l => l.Value == mod );
|
|
if( leaf != null )
|
|
{
|
|
Delete( leaf );
|
|
}
|
|
break;
|
|
case ModPathChangeType.Moved:
|
|
Save();
|
|
break;
|
|
case ModPathChangeType.Reloaded:
|
|
// Nothing
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Used for saving and loading.
|
|
private static string ModToIdentifier( Mod mod )
|
|
=> mod.ModPath.Name;
|
|
|
|
private static string ModToName( Mod mod )
|
|
=> mod.Name.Text.FixName();
|
|
|
|
private static (string, bool) SaveMod( Mod mod, string fullPath )
|
|
{
|
|
var regex = new Regex( $@"^{Regex.Escape( ModToName( mod ) )}( \(\d+\))?$" );
|
|
// Only save pairs with non-default paths.
|
|
if( regex.IsMatch( fullPath ) )
|
|
{
|
|
return ( string.Empty, false );
|
|
}
|
|
|
|
return ( ModToIdentifier( mod ), true );
|
|
}
|
|
} |