From 44670198ab5bd3a17880cbf9ca049471ca4f2230 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 22 Feb 2021 15:28:07 +0100 Subject: [PATCH] Use GamePath and RelPath where appropriate --- Penumbra/Hooks/ResourceLoader.cs | 24 +++-- Penumbra/Importer/TexToolsImport.cs | 6 +- Penumbra/Models/Deduplicator.cs | 18 ++-- Penumbra/Models/GroupInformation.cs | 9 +- Penumbra/Models/ModMeta.cs | 3 +- Penumbra/Mods/ModManager.cs | 100 +++++++----------- Penumbra/Mods/ResourceMod.cs | 11 +- Penumbra/UI/MenuTabs/TabEffective.cs | 21 ++-- .../TabInstalled/TabInstalledDetails.cs | 100 +++++++++--------- .../TabInstalled/TabInstalledDetailsEdit.cs | 5 +- Penumbra/Util/PenumbraPath.cs | 3 + 11 files changed, 138 insertions(+), 162 deletions(-) diff --git a/Penumbra/Hooks/ResourceLoader.cs b/Penumbra/Hooks/ResourceLoader.cs index 86c36bc1..4fb7271b 100644 --- a/Penumbra/Hooks/ResourceLoader.cs +++ b/Penumbra/Hooks/ResourceLoader.cs @@ -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; } @@ -229,7 +231,7 @@ namespace Penumbra.Hooks ReadSqpackHook.Enable(); GetResourceSyncHook.Enable(); GetResourceAsyncHook.Enable(); - + IsEnabled = true; } diff --git a/Penumbra/Importer/TexToolsImport.cs b/Penumbra/Importer/TexToolsImport.cs index f0fff468..6e1745c6 100644 --- a/Penumbra/Importer/TexToolsImport.cs +++ b/Penumbra/Importer/TexToolsImport.cs @@ -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 ) ); } } diff --git a/Penumbra/Models/Deduplicator.cs b/Penumbra/Models/Deduplicator.cs index 1f10a471..deb3e411 100644 --- a/Penumbra/Models/Deduplicator.cs +++ b/Penumbra/Models/Deduplicator.cs @@ -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." ); diff --git a/Penumbra/Models/GroupInformation.cs b/Penumbra/Models/GroupInformation.cs index 200779e3..9982f52d 100644 --- a/Penumbra/Models/GroupInformation.cs +++ b/Penumbra/Models/GroupInformation.cs @@ -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; } } diff --git a/Penumbra/Models/ModMeta.cs b/Penumbra/Models/ModMeta.cs index 306b1ce5..1c17dbce 100644 --- a/Penumbra/Models/ModMeta.cs +++ b/Penumbra/Models/ModMeta.cs @@ -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(); diff --git a/Penumbra/Mods/ModManager.cs b/Penumbra/Mods/ModManager.cs index 648f5d16..e0a542b8 100644 --- a/Penumbra/Mods/ModManager.cs +++ b/Penumbra/Mods/ModManager.cs @@ -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() diff --git a/Penumbra/Mods/ResourceMod.cs b/Penumbra/Mods/ResourceMod.cs index 55ec3028..11e6f7dc 100644 --- a/Penumbra/Mods/ResourceMod.cs +++ b/Penumbra/Mods/ResourceMod.cs @@ -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 }; } } } \ No newline at end of file diff --git a/Penumbra/UI/MenuTabs/TabEffective.cs b/Penumbra/UI/MenuTabs/TabEffective.cs index bf8cd638..33a1d8b1 100644 --- a/Penumbra/UI/MenuTabs/TabEffective.cs +++ b/Penumbra/UI/MenuTabs/TabEffective.cs @@ -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(); diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs index 82f7a8cf..8ceb0b8f 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs @@ -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() diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs index af3aedbf..7b9d564e 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs @@ -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 diff --git a/Penumbra/Util/PenumbraPath.cs b/Penumbra/Util/PenumbraPath.cs index 2e00f3f2..a8e81cbc 100644 --- a/Penumbra/Util/PenumbraPath.cs +++ b/Penumbra/Util/PenumbraPath.cs @@ -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;