mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-01-03 14:23:43 +01:00
Use GamePath and RelPath where appropriate
This commit is contained in:
parent
ee280994ce
commit
44670198ab
11 changed files with 138 additions and 162 deletions
|
|
@ -114,16 +114,19 @@ namespace Penumbra.Hooks
|
|||
{
|
||||
if( GetResourceSyncHook == null )
|
||||
{
|
||||
PluginLog.Error("[GetResourceHandler] GetResourceSync is null." );
|
||||
PluginLog.Error( "[GetResourceHandler] GetResourceSync is null." );
|
||||
return null;
|
||||
}
|
||||
|
||||
return GetResourceSyncHook.OriginalFunction( pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown );
|
||||
}
|
||||
|
||||
if( GetResourceAsyncHook == null )
|
||||
{
|
||||
PluginLog.Error("[GetResourceHandler] GetResourceAsync is null." );
|
||||
PluginLog.Error( "[GetResourceHandler] GetResourceAsync is null." );
|
||||
return null;
|
||||
}
|
||||
|
||||
return GetResourceAsyncHook.OriginalFunction( pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown );
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +141,7 @@ namespace Penumbra.Hooks
|
|||
bool isUnknown
|
||||
)
|
||||
{
|
||||
var gameFsPath = Marshal.PtrToStringAnsi( new IntPtr( pPath ) );
|
||||
var gameFsPath = GamePath.GenerateUncheckedLower( Marshal.PtrToStringAnsi( new IntPtr( pPath ) )! );
|
||||
|
||||
if( LogAllFiles )
|
||||
{
|
||||
|
|
@ -152,16 +155,15 @@ namespace Penumbra.Hooks
|
|||
return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown );
|
||||
}
|
||||
|
||||
var replacementPath = modManager.ResolveSwappedOrReplacementFilePath( gameFsPath! );
|
||||
var replacementPath = modManager.ResolveSwappedOrReplacementFilePath( gameFsPath );
|
||||
|
||||
// path must be < 260 because statically defined array length :(
|
||||
if( replacementPath == null || replacementPath.Length >= 260 )
|
||||
if( replacementPath == null )
|
||||
{
|
||||
return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown );
|
||||
}
|
||||
|
||||
var cleanPath = replacementPath.Replace( '\\', '/' );
|
||||
var path = Encoding.ASCII.GetBytes( cleanPath );
|
||||
var path = Encoding.ASCII.GetBytes( replacementPath );
|
||||
|
||||
var bPath = stackalloc byte[path.Length + 1];
|
||||
Marshal.Copy( path, 0, new IntPtr( bPath ), path.Length );
|
||||
|
|
@ -185,7 +187,7 @@ namespace Penumbra.Hooks
|
|||
|
||||
var isRooted = Path.IsPathRooted( gameFsPath );
|
||||
|
||||
if( gameFsPath == null || gameFsPath.Length >= 260 || !isRooted || ReadFile == null)
|
||||
if( gameFsPath == null || gameFsPath.Length >= 260 || !isRooted || ReadFile == null )
|
||||
{
|
||||
return ReadSqpackHook?.OriginalFunction( pFileHandler, pFileDesc, priority, isSync ) ?? 0;
|
||||
}
|
||||
|
|
@ -216,9 +218,9 @@ namespace Penumbra.Hooks
|
|||
return;
|
||||
}
|
||||
|
||||
if( ReadSqpackHook == null || GetResourceSyncHook == null || GetResourceAsyncHook == null)
|
||||
if( ReadSqpackHook == null || GetResourceSyncHook == null || GetResourceAsyncHook == null )
|
||||
{
|
||||
PluginLog.Error("[GetResourceHandler] Could not activate hooks because at least one was not set." );
|
||||
PluginLog.Error( "[GetResourceHandler] Could not activate hooks because at least one was not set." );
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Lumina.Data;
|
|||
using Newtonsoft.Json;
|
||||
using Penumbra.Importer.Models;
|
||||
using Penumbra.Models;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Importer
|
||||
{
|
||||
|
|
@ -261,15 +262,14 @@ namespace Penumbra.Importer
|
|||
{
|
||||
OptionName = opt.Name!,
|
||||
OptionDesc = string.IsNullOrEmpty( opt.Description ) ? "" : opt.Description!,
|
||||
OptionFiles = new Dictionary< string, HashSet< string > >()
|
||||
OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >()
|
||||
};
|
||||
var optDir = new DirectoryInfo( Path.Combine( groupFolder.FullName, opt.Name!.ReplaceInvalidPathSymbols() ) );
|
||||
if( optDir.Exists )
|
||||
{
|
||||
foreach( var file in optDir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) )
|
||||
{
|
||||
option.AddFile( file.FullName.Substring( baseFolder.FullName.Length ).TrimStart( '\\' ),
|
||||
file.FullName.Substring( optDir.FullName.Length ).TrimStart( '\\' ).Replace( '\\', '/' ) );
|
||||
option.AddFile( new RelPath( file, baseFolder ), new GamePath( file, optDir ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using Dalamud.Plugin;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Models
|
||||
{
|
||||
public class Deduplicator
|
||||
{
|
||||
private readonly DirectoryInfo _baseDir;
|
||||
private readonly int _baseDirLength;
|
||||
private readonly ModMeta _mod;
|
||||
private SHA256? _hasher;
|
||||
|
||||
|
|
@ -24,10 +24,8 @@ namespace Penumbra.Models
|
|||
|
||||
public Deduplicator( DirectoryInfo baseDir, ModMeta mod )
|
||||
{
|
||||
_baseDir = baseDir;
|
||||
_baseDirLength = baseDir.FullName.Length;
|
||||
_mod = mod;
|
||||
|
||||
_baseDir = baseDir;
|
||||
_mod = mod;
|
||||
BuildDict();
|
||||
}
|
||||
|
||||
|
|
@ -89,8 +87,8 @@ namespace Penumbra.Models
|
|||
|
||||
private void ReplaceFile( FileInfo f1, FileInfo f2 )
|
||||
{
|
||||
var relName1 = f1.FullName.Substring( _baseDirLength ).TrimStart( '\\' );
|
||||
var relName2 = f2.FullName.Substring( _baseDirLength ).TrimStart( '\\' );
|
||||
RelPath relName1 = new( f1, _baseDir );
|
||||
RelPath relName2 = new( f2, _baseDir );
|
||||
|
||||
var inOption = false;
|
||||
foreach( var group in _mod.Groups.Select( g => g.Value.Options ) )
|
||||
|
|
@ -125,15 +123,15 @@ namespace Penumbra.Models
|
|||
{
|
||||
OptionName = "Required",
|
||||
OptionDesc = "",
|
||||
OptionFiles = new Dictionary< string, HashSet< string > >()
|
||||
OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >()
|
||||
}
|
||||
}
|
||||
};
|
||||
_mod.Groups.Add( duplicates, info );
|
||||
}
|
||||
|
||||
_mod.Groups[ duplicates ].Options[ 0 ].AddFile( relName1, relName2.Replace( '\\', '/' ) );
|
||||
_mod.Groups[ duplicates ].Options[ 0 ].AddFile( relName1, relName1.Replace( '\\', '/' ) );
|
||||
_mod.Groups[ duplicates ].Options[ 0 ].AddFile( relName1, new GamePath( relName2 ) );
|
||||
_mod.Groups[ duplicates ].Options[ 0 ].AddFile( relName1, new GamePath( relName1 ) );
|
||||
}
|
||||
|
||||
PluginLog.Information( $"File {relName1} and {relName2} are identical. Deleting the second." );
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Models
|
||||
{
|
||||
|
|
@ -14,17 +15,17 @@ namespace Penumbra.Models
|
|||
public string OptionName;
|
||||
public string OptionDesc;
|
||||
|
||||
[JsonProperty( ItemConverterType = typeof( Util.SingleOrArrayConverter< string > ) )]
|
||||
public Dictionary< string, HashSet< string > > OptionFiles;
|
||||
[JsonProperty( ItemConverterType = typeof( SingleOrArrayConverter< GamePath > ) )]
|
||||
public Dictionary< RelPath, HashSet< GamePath > > OptionFiles;
|
||||
|
||||
public bool AddFile( string filePath, string gamePath )
|
||||
public bool AddFile( RelPath filePath, GamePath gamePath )
|
||||
{
|
||||
if( OptionFiles.TryGetValue( filePath, out var set ) )
|
||||
{
|
||||
return set.Add( gamePath );
|
||||
}
|
||||
|
||||
OptionFiles[ filePath ] = new HashSet< string >() { gamePath };
|
||||
OptionFiles[ filePath ] = new HashSet< GamePath >() { gamePath };
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Models
|
||||
{
|
||||
|
|
@ -19,7 +20,7 @@ namespace Penumbra.Models
|
|||
|
||||
public List< string > ChangedItems { get; set; } = new();
|
||||
|
||||
public Dictionary< string, string > FileSwaps { get; } = new();
|
||||
public Dictionary< GamePath, GamePath > FileSwaps { get; } = new();
|
||||
|
||||
public Dictionary< string, OptionGroup > Groups { get; set; } = new();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,52 +4,37 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using Dalamud.Plugin;
|
||||
using Penumbra.Models;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods
|
||||
{
|
||||
public class ModManager : IDisposable
|
||||
{
|
||||
private readonly Plugin _plugin;
|
||||
public readonly Dictionary< string, FileInfo > ResolvedFiles = new();
|
||||
public readonly Dictionary< string, string > SwappedFiles = new();
|
||||
private readonly Plugin _plugin;
|
||||
public readonly Dictionary< GamePath, FileInfo > ResolvedFiles = new();
|
||||
public readonly Dictionary< GamePath, GamePath > SwappedFiles = new();
|
||||
|
||||
public ModCollection? Mods { get; set; }
|
||||
public ModCollection? Mods { get; set; }
|
||||
private DirectoryInfo? _basePath;
|
||||
|
||||
public ModManager( Plugin plugin )
|
||||
=> _plugin = plugin;
|
||||
|
||||
public void DiscoverMods()
|
||||
=> DiscoverMods( _basePath );
|
||||
|
||||
public void DiscoverMods( string? basePath )
|
||||
=> DiscoverMods( basePath == null ? null : new DirectoryInfo( basePath ) );
|
||||
|
||||
public void DiscoverMods( DirectoryInfo? basePath )
|
||||
{
|
||||
if( _basePath == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DiscoverMods( _basePath );
|
||||
}
|
||||
|
||||
|
||||
public void DiscoverMods( string basePath )
|
||||
{
|
||||
DiscoverMods( new DirectoryInfo( basePath ) );
|
||||
}
|
||||
|
||||
public void DiscoverMods( DirectoryInfo basePath )
|
||||
{
|
||||
if( basePath == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( !basePath.Exists )
|
||||
_basePath = basePath;
|
||||
if( basePath == null || !basePath.Exists )
|
||||
{
|
||||
Mods = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_basePath = basePath;
|
||||
|
||||
// FileSystemPasta();
|
||||
|
||||
Mods = new ModCollection( basePath );
|
||||
|
|
@ -69,7 +54,7 @@ namespace Penumbra.Mods
|
|||
return;
|
||||
}
|
||||
|
||||
var registeredFiles = new Dictionary< string, string >();
|
||||
var registeredFiles = new Dictionary< GamePath, string >();
|
||||
|
||||
foreach( var (mod, settings) in Mods.GetOrderedAndEnabledModListWithSettings( _plugin!.Configuration!.InvertModListOrder ) )
|
||||
{
|
||||
|
|
@ -82,15 +67,15 @@ namespace Penumbra.Mods
|
|||
_plugin!.GameUtils!.ReloadPlayerResources();
|
||||
}
|
||||
|
||||
private void ProcessSwappedFiles( Dictionary< string, string > registeredFiles, ResourceMod mod, ModInfo settings )
|
||||
private void ProcessSwappedFiles( Dictionary< GamePath, string > registeredFiles, ResourceMod mod, ModInfo settings )
|
||||
{
|
||||
foreach( var swap in mod.Meta.FileSwaps )
|
||||
{
|
||||
// just assume people put not fucked paths in here lol
|
||||
if( !SwappedFiles.ContainsKey( swap.Value ) )
|
||||
{
|
||||
SwappedFiles[ swap.Key.ToLowerInvariant() ] = swap.Value;
|
||||
registeredFiles[ swap.Key ] = mod.Meta.Name;
|
||||
SwappedFiles[ swap.Key ] = swap.Value;
|
||||
registeredFiles[ swap.Key ] = mod.Meta.Name;
|
||||
}
|
||||
else if( registeredFiles.TryGetValue( swap.Key, out var modName ) )
|
||||
{
|
||||
|
|
@ -99,18 +84,13 @@ namespace Penumbra.Mods
|
|||
}
|
||||
}
|
||||
|
||||
private void ProcessModFiles( Dictionary< string, string > registeredFiles, ResourceMod mod, ModInfo settings )
|
||||
private void ProcessModFiles( Dictionary< GamePath, string > registeredFiles, ResourceMod mod, ModInfo settings )
|
||||
{
|
||||
var baseDir = mod.ModBasePath.FullName;
|
||||
|
||||
foreach( var file in mod.ModFiles )
|
||||
{
|
||||
var relativeFilePath = file.FullName.Substring( baseDir.Length ).TrimStart( '\\' );
|
||||
|
||||
var doNotAdd = false;
|
||||
|
||||
HashSet< string > paths;
|
||||
foreach( var group in mod.Meta.Groups.Select( G => G.Value ) )
|
||||
RelPath relativeFilePath = new( file, mod.ModBasePath );
|
||||
var doNotAdd = false;
|
||||
foreach( var group in mod.Meta.Groups.Values )
|
||||
{
|
||||
if( !settings.Conf.TryGetValue( group.GroupName, out var setting )
|
||||
|| group.SelectionType == SelectType.Single
|
||||
|
|
@ -131,20 +111,18 @@ namespace Penumbra.Mods
|
|||
settings.Conf[ group.GroupName ] &= ( 1 << group.Options.Count ) - 1;
|
||||
}
|
||||
|
||||
HashSet< GamePath > paths;
|
||||
switch( group.SelectionType )
|
||||
{
|
||||
case SelectType.Single:
|
||||
if( group.Options[ setting ].OptionFiles.TryGetValue( relativeFilePath, out paths ) )
|
||||
{
|
||||
AddFiles( paths, out doNotAdd, file, registeredFiles, mod );
|
||||
doNotAdd |= AddFiles( paths, file, registeredFiles, mod );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( group.Options.Where( ( o, i ) => i != setting )
|
||||
.Any( option => option.OptionFiles.ContainsKey( relativeFilePath ) ) )
|
||||
{
|
||||
doNotAdd = true;
|
||||
}
|
||||
doNotAdd |= group.Options.Where( ( o, i ) => i != setting )
|
||||
.Any( option => option.OptionFiles.ContainsKey( relativeFilePath ) );
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -155,12 +133,12 @@ namespace Penumbra.Mods
|
|||
{
|
||||
if( group.Options[ i ].OptionFiles.TryGetValue( relativeFilePath, out paths ) )
|
||||
{
|
||||
AddFiles( paths, out doNotAdd, file, registeredFiles, mod );
|
||||
doNotAdd |= AddFiles( paths, file, registeredFiles, mod );
|
||||
}
|
||||
}
|
||||
else if( group.Options[ i ].OptionFiles.ContainsKey( relativeFilePath ) )
|
||||
else
|
||||
{
|
||||
doNotAdd = true;
|
||||
doNotAdd |= group.Options[ i ].OptionFiles.ContainsKey( relativeFilePath );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,27 +148,27 @@ namespace Penumbra.Mods
|
|||
|
||||
if( !doNotAdd )
|
||||
{
|
||||
AddFiles( new HashSet< string > { relativeFilePath.Replace( '\\', '/' ) }, out doNotAdd, file, registeredFiles, mod );
|
||||
AddFiles( new GamePath[] { new( relativeFilePath ) }, file, registeredFiles, mod );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddFiles( HashSet< string > gamePaths, out bool doNotAdd, FileInfo file, Dictionary< string, string > registeredFiles,
|
||||
private bool AddFiles( IEnumerable< GamePath > gamePaths, FileInfo file, Dictionary< GamePath, string > registeredFiles,
|
||||
ResourceMod mod )
|
||||
{
|
||||
doNotAdd = true;
|
||||
foreach( var gamePath in gamePaths )
|
||||
{
|
||||
if( !ResolvedFiles.ContainsKey( gamePath ) )
|
||||
{
|
||||
ResolvedFiles[ gamePath.ToLowerInvariant() ] = file;
|
||||
registeredFiles[ gamePath ] = mod.Meta.Name;
|
||||
ResolvedFiles[ gamePath ] = file;
|
||||
registeredFiles[ gamePath ] = mod.Meta.Name;
|
||||
}
|
||||
else if( registeredFiles.TryGetValue( gamePath, out var modName ) )
|
||||
{
|
||||
mod.AddConflict( modName, gamePath );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ChangeModPriority( ModInfo info, bool up = false )
|
||||
|
|
@ -216,7 +194,7 @@ namespace Penumbra.Mods
|
|||
DiscoverMods();
|
||||
}
|
||||
|
||||
public FileInfo? GetCandidateForGameFile( string gameResourcePath )
|
||||
public FileInfo? GetCandidateForGameFile( GamePath gameResourcePath )
|
||||
{
|
||||
var val = ResolvedFiles.TryGetValue( gameResourcePath, out var candidate );
|
||||
if( !val )
|
||||
|
|
@ -232,15 +210,11 @@ namespace Penumbra.Mods
|
|||
return candidate;
|
||||
}
|
||||
|
||||
public string? GetSwappedFilePath( string gameResourcePath )
|
||||
public GamePath? GetSwappedFilePath( GamePath gameResourcePath )
|
||||
=> SwappedFiles.TryGetValue( gameResourcePath, out var swappedPath ) ? swappedPath : null;
|
||||
|
||||
public string? ResolveSwappedOrReplacementFilePath( string gameResourcePath )
|
||||
{
|
||||
gameResourcePath = gameResourcePath.ToLowerInvariant();
|
||||
|
||||
return GetCandidateForGameFile( gameResourcePath )?.FullName ?? GetSwappedFilePath( gameResourcePath );
|
||||
}
|
||||
public string? ResolveSwappedOrReplacementFilePath( GamePath gameResourcePath )
|
||||
=> GetCandidateForGameFile( gameResourcePath )?.FullName ?? GetSwappedFilePath( gameResourcePath ) ?? null;
|
||||
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin;
|
||||
using Penumbra.Models;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods
|
||||
{
|
||||
|
|
@ -20,7 +20,7 @@ namespace Penumbra.Mods
|
|||
|
||||
public List< FileInfo > ModFiles { get; } = new();
|
||||
|
||||
public Dictionary< string, List< string > > FileConflicts { get; } = new();
|
||||
public Dictionary< string, List< GamePath > > FileConflicts { get; } = new();
|
||||
|
||||
public void RefreshModFiles()
|
||||
{
|
||||
|
|
@ -33,7 +33,7 @@ namespace Penumbra.Mods
|
|||
}
|
||||
}
|
||||
|
||||
public void AddConflict( string modName, string path )
|
||||
public void AddConflict( string modName, GamePath path )
|
||||
{
|
||||
if( FileConflicts.TryGetValue( modName, out var arr ) )
|
||||
{
|
||||
|
|
@ -45,10 +45,7 @@ namespace Penumbra.Mods
|
|||
return;
|
||||
}
|
||||
|
||||
FileConflicts[ modName ] = new List< string >
|
||||
{
|
||||
path
|
||||
};
|
||||
FileConflicts[ modName ] = new List< GamePath > { path };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using ImGuiNET;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.UI
|
||||
{
|
||||
|
|
@ -12,8 +14,7 @@ namespace Penumbra.UI
|
|||
private const float TextSizePadding = 5f;
|
||||
|
||||
private ModManager _mods => Service< ModManager >.Get();
|
||||
private (string, string)[]? _fileList;
|
||||
private float _maxGamePath;
|
||||
private float _maxGamePath;
|
||||
|
||||
public TabEffective( SettingsInterface ui )
|
||||
{
|
||||
|
|
@ -24,24 +25,24 @@ namespace Penumbra.UI
|
|||
{
|
||||
if( advanced )
|
||||
{
|
||||
_fileList = _mods.ResolvedFiles.Select( P => ( P.Value.FullName, P.Key ) ).ToArray();
|
||||
_maxGamePath = ( _fileList.Length > 0 ? _fileList.Max( P => ImGui.CalcTextSize( P.Item2 ).X ) : 0f ) + TextSizePadding;
|
||||
_maxGamePath = TextSizePadding + ( _mods.ResolvedFiles.Count > 0
|
||||
? _mods.ResolvedFiles.Keys.Max( f => ImGui.CalcTextSize( f ).X )
|
||||
: 0f );
|
||||
}
|
||||
else
|
||||
{
|
||||
_fileList = null;
|
||||
_maxGamePath = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawFileLine( (string, string) file )
|
||||
private void DrawFileLine( FileInfo file, GamePath path )
|
||||
{
|
||||
ImGui.Selectable( file.Item2 );
|
||||
ImGui.Selectable( path );
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosX( _maxGamePath );
|
||||
ImGui.TextUnformatted( " <-- " );
|
||||
ImGui.SameLine();
|
||||
ImGui.Selectable( file.Item1 );
|
||||
ImGui.Selectable( file.FullName );
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
|
|
@ -54,9 +55,9 @@ namespace Penumbra.UI
|
|||
|
||||
if( ImGui.ListBoxHeader( "##effective_files", AutoFillSize ) )
|
||||
{
|
||||
foreach( var file in _fileList ?? Enumerable.Empty<(string, string)>() )
|
||||
foreach( var file in _mods.ResolvedFiles )
|
||||
{
|
||||
DrawFileLine( file );
|
||||
DrawFileLine( file.Value, file.Key );
|
||||
}
|
||||
|
||||
ImGui.ListBoxFooter();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using ImGuiNET;
|
||||
using Penumbra.Models;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.UI
|
||||
{
|
||||
internal static class Extension
|
||||
internal static class ListRemoveExtension
|
||||
{
|
||||
// Remove the entry at idx from the list if the new string is empty, otherwise replace it.
|
||||
public static void RemoveOrChange( this List< string > list, string newString, int idx )
|
||||
|
|
@ -53,16 +55,16 @@ namespace Penumbra.UI
|
|||
private const uint ColorYellow = 0xFF00C8C8;
|
||||
private const uint ColorRed = 0xFF0000C8;
|
||||
|
||||
private bool _editMode = false;
|
||||
private int _selectedGroupIndex = 0;
|
||||
private OptionGroup? _selectedGroup = null;
|
||||
private int _selectedOptionIndex = 0;
|
||||
private Option? _selectedOption = null;
|
||||
private (string label, string name)[]? _changedItemsList = null;
|
||||
private float? _fileSwapOffset = null;
|
||||
private string _currentGamePaths = "";
|
||||
private bool _editMode;
|
||||
private int _selectedGroupIndex;
|
||||
private OptionGroup? _selectedGroup;
|
||||
private int _selectedOptionIndex;
|
||||
private Option? _selectedOption;
|
||||
private (string label, string name)[]? _changedItemsList;
|
||||
private float? _fileSwapOffset;
|
||||
private string _currentGamePaths = "";
|
||||
|
||||
private (string name, bool selected, uint color, string relName)[]? _fullFilenameList = null;
|
||||
private (FileInfo name, bool selected, uint color, RelPath relName)[]? _fullFilenameList;
|
||||
|
||||
private readonly Selector _selector;
|
||||
private readonly SettingsInterface _base;
|
||||
|
|
@ -72,7 +74,7 @@ namespace Penumbra.UI
|
|||
// Not using the properties here because we need it to be not null forgiving in this case.
|
||||
var numGroups = _selector.Mod()?.Mod.Meta.Groups.Count ?? 0;
|
||||
_selectedGroupIndex = idx;
|
||||
if( _selectedGroupIndex >= numGroups)
|
||||
if( _selectedGroupIndex >= numGroups )
|
||||
{
|
||||
_selectedGroupIndex = 0;
|
||||
}
|
||||
|
|
@ -87,7 +89,8 @@ namespace Penumbra.UI
|
|||
}
|
||||
}
|
||||
|
||||
private void SelectGroup() => SelectGroup( _selectedGroupIndex );
|
||||
private void SelectGroup()
|
||||
=> SelectGroup( _selectedGroupIndex );
|
||||
|
||||
private void SelectOption( int idx )
|
||||
{
|
||||
|
|
@ -107,7 +110,8 @@ namespace Penumbra.UI
|
|||
}
|
||||
}
|
||||
|
||||
private void SelectOption() => SelectOption( _selectedOptionIndex );
|
||||
private void SelectOption()
|
||||
=> SelectOption( _selectedOptionIndex );
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
|
|
@ -191,7 +195,8 @@ namespace Penumbra.UI
|
|||
ImGui.SetNextItemWidth( -1 );
|
||||
if( ImGui.ListBoxHeader( LabelChangedItemsHeader, AutoFillSize ) )
|
||||
{
|
||||
_changedItemsList ??= Meta.ChangedItems.Select( ( I, index ) => ( $"{LabelChangedItemIdx}{index}", I ) ).ToArray();
|
||||
_changedItemsList ??= Meta.ChangedItems
|
||||
.Select( ( I, index ) => ( $"{LabelChangedItemIdx}{index}", I ) ).ToArray();
|
||||
|
||||
for( var i = 0; i < Meta.ChangedItems.Count; ++i )
|
||||
{
|
||||
|
|
@ -207,13 +212,11 @@ namespace Penumbra.UI
|
|||
if( _editMode )
|
||||
{
|
||||
ImGui.SetNextItemWidth( -1 );
|
||||
if( ImGui.InputText( LabelChangedItemNew, ref newItem, 128, flags ) )
|
||||
if( ImGui.InputText( LabelChangedItemNew, ref newItem, 128, flags )
|
||||
&& newItem.Length > 0 )
|
||||
{
|
||||
if( newItem.Length > 0 )
|
||||
{
|
||||
Meta.ChangedItems.Add( newItem );
|
||||
_selector.SaveCurrentMod();
|
||||
}
|
||||
Meta.ChangedItems.Add( newItem );
|
||||
_selector.SaveCurrentMod();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -230,12 +233,7 @@ namespace Penumbra.UI
|
|||
|
||||
private void DrawConflictTab()
|
||||
{
|
||||
if( !Mod.Mod.FileConflicts.Any() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( !ImGui.BeginTabItem( LabelConflictsTab ) )
|
||||
if( !Mod.Mod.FileConflicts.Any() || !ImGui.BeginTabItem( LabelConflictsTab ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -275,7 +273,8 @@ namespace Penumbra.UI
|
|||
|
||||
if( ImGui.BeginTabItem( LabelFileSwapTab ) )
|
||||
{
|
||||
_fileSwapOffset ??= Meta.FileSwaps.Max( P => ImGui.CalcTextSize( P.Key ).X ) + TextSizePadding;
|
||||
_fileSwapOffset ??= Meta.FileSwaps
|
||||
.Max( P => ImGui.CalcTextSize( P.Key ).X ) + TextSizePadding;
|
||||
|
||||
ImGui.SetNextItemWidth( -1 );
|
||||
if( ImGui.ListBoxHeader( LabelFileSwapHeader, AutoFillSize ) )
|
||||
|
|
@ -308,7 +307,8 @@ namespace Penumbra.UI
|
|||
}
|
||||
|
||||
var len = Mod.Mod.ModBasePath.FullName.Length;
|
||||
_fullFilenameList = Mod.Mod.ModFiles.Select( F => ( F.FullName, false, ColorGreen, "" ) ).ToArray();
|
||||
_fullFilenameList = Mod.Mod.ModFiles
|
||||
.Select( F => ( F, false, ColorGreen, new RelPath( F, Mod.Mod.ModBasePath ) ) ).ToArray();
|
||||
|
||||
if( Meta.Groups.Count == 0 )
|
||||
{
|
||||
|
|
@ -317,12 +317,6 @@ namespace Penumbra.UI
|
|||
|
||||
for( var i = 0; i < Mod.Mod.ModFiles.Count; ++i )
|
||||
{
|
||||
_fullFilenameList[ i ].relName = _fullFilenameList[ i ].name.Substring( len ).TrimStart( '\\' );
|
||||
if( Meta.Groups == null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach( var group in Meta.Groups.Values )
|
||||
{
|
||||
var inAll = true;
|
||||
|
|
@ -365,7 +359,7 @@ namespace Penumbra.UI
|
|||
foreach( var file in _fullFilenameList! )
|
||||
{
|
||||
ImGui.PushStyleColor( ImGuiCol.Text, file.color );
|
||||
ImGui.Selectable( file.name );
|
||||
ImGui.Selectable( file.name.FullName );
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
|
|
@ -388,8 +382,8 @@ namespace Penumbra.UI
|
|||
|
||||
var option = ( Option )_selectedOption;
|
||||
|
||||
var gamePaths = _currentGamePaths.Split( ';' );
|
||||
if( gamePaths.Length == 0 || gamePaths[ 0 ].Length == 0 )
|
||||
var gamePaths = _currentGamePaths.Split( ';' ).Select( P => new GamePath( P ) ).ToArray();
|
||||
if( gamePaths.Length == 0 || ( ( string )gamePaths[ 0 ] ).Length == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -403,27 +397,28 @@ namespace Penumbra.UI
|
|||
continue;
|
||||
}
|
||||
|
||||
var fileName = _fullFilenameList[ i ].relName;
|
||||
var relName = _fullFilenameList[ i ].relName;
|
||||
if( defaultIndex >= 0 )
|
||||
{
|
||||
gamePaths[ ( int )defaultIndex ] = fileName.Replace( '\\', '/' );
|
||||
gamePaths[ defaultIndex ] = new GamePath( relName );
|
||||
}
|
||||
|
||||
if( remove && option.OptionFiles.TryGetValue( fileName, out var setPaths ) )
|
||||
if( remove && option.OptionFiles.TryGetValue( relName, out var setPaths ) )
|
||||
{
|
||||
if( setPaths.RemoveWhere( P => gamePaths.Contains( P ) ) > 0 )
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if( setPaths.Count == 0 && option.OptionFiles.Remove( fileName ) )
|
||||
if( setPaths.Count == 0 && option.OptionFiles.Remove( relName ) )
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
changed = gamePaths.Aggregate( changed, ( current, gamePath ) => current | option.AddFile( fileName, gamePath ) );
|
||||
changed = gamePaths
|
||||
.Aggregate( changed, ( current, gamePath ) => current | option.AddFile( relName, gamePath ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -503,7 +498,7 @@ namespace Penumbra.UI
|
|||
}
|
||||
|
||||
ImGui.PushStyleColor( ImGuiCol.Text, loc );
|
||||
ImGui.Selectable( _fullFilenameList[ idx ].name, ref _fullFilenameList[ idx ].selected );
|
||||
ImGui.Selectable( _fullFilenameList[ idx ].name.FullName, ref _fullFilenameList[ idx ].selected );
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
|
|
@ -514,22 +509,28 @@ namespace Penumbra.UI
|
|||
return;
|
||||
}
|
||||
|
||||
var fileName = _fullFilenameList![ idx ].relName;
|
||||
if( ( ( Option )_selectedOption ).OptionFiles.TryGetValue( fileName, out var gamePaths ) )
|
||||
var fileName = _fullFilenameList![ idx ].relName;
|
||||
var optionFiles = ( ( Option )_selectedOption ).OptionFiles;
|
||||
if( optionFiles.TryGetValue( fileName, out var gamePaths ) )
|
||||
{
|
||||
Selectable( 0, ColorGreen );
|
||||
|
||||
ImGui.Indent( indent );
|
||||
foreach( var gamePath in gamePaths )
|
||||
var tmpPaths = gamePaths.ToArray();
|
||||
foreach( var gamePath in tmpPaths )
|
||||
{
|
||||
var tmp = gamePath;
|
||||
string tmp = gamePath;
|
||||
if( ImGui.InputText( $"##{fileName}_{gamePath}", ref tmp, 128, ImGuiInputTextFlags.EnterReturnsTrue )
|
||||
&& tmp != gamePath )
|
||||
{
|
||||
gamePaths.Remove( gamePath );
|
||||
if( tmp.Length > 0 )
|
||||
{
|
||||
gamePaths.Add( tmp );
|
||||
gamePaths.Add( new GamePath( tmp ) );
|
||||
}
|
||||
else if( gamePaths.Count == 0 )
|
||||
{
|
||||
optionFiles.Remove( fileName );
|
||||
}
|
||||
|
||||
_selector.SaveCurrentMod();
|
||||
|
|
@ -547,7 +548,6 @@ namespace Penumbra.UI
|
|||
|
||||
private void DrawMultiSelectorCheckBox( OptionGroup group, int idx, int flag, string label )
|
||||
{
|
||||
var opt = group.Options[ idx ];
|
||||
var enabled = ( flag & ( 1 << idx ) ) != 0;
|
||||
var oldEnabled = enabled;
|
||||
if( ImGui.Checkbox( label, ref enabled ) && oldEnabled != enabled )
|
||||
|
|
@ -601,8 +601,6 @@ namespace Penumbra.UI
|
|||
{
|
||||
DrawMultiSelector( g );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void DrawConfigurationTab()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Linq;
|
|||
using System.Numerics;
|
||||
using ImGuiNET;
|
||||
using Penumbra.Models;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.UI
|
||||
{
|
||||
|
|
@ -134,7 +135,7 @@ namespace Penumbra.UI
|
|||
&& newOption.Length != 0 )
|
||||
{
|
||||
group.Options.Add( new Option()
|
||||
{ OptionName = newOption, OptionDesc = "", OptionFiles = new Dictionary< string, HashSet< string > >() } );
|
||||
{ OptionName = newOption, OptionDesc = "", OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >() } );
|
||||
_selector.SaveCurrentMod();
|
||||
}
|
||||
}
|
||||
|
|
@ -232,7 +233,7 @@ namespace Penumbra.UI
|
|||
modChanged = true;
|
||||
Mod.Conf[ group.GroupName ] = code;
|
||||
group.Options.Add( new Option()
|
||||
{ OptionName = newName, OptionDesc = "", OptionFiles = new Dictionary< string, HashSet< string > >() } );
|
||||
{ OptionName = newName, OptionDesc = "", OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >() } );
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -110,6 +110,9 @@ namespace Penumbra.Util
|
|||
public static GamePath GenerateUnchecked( string path )
|
||||
=> new( path, true );
|
||||
|
||||
public static GamePath GenerateUncheckedLower( string path )
|
||||
=> new( Lower(path), true );
|
||||
|
||||
public static implicit operator bool( GamePath gamePath )
|
||||
=> gamePath._path.Length > 0;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue