Use GamePath and RelPath where appropriate

This commit is contained in:
Ottermandias 2021-02-22 15:28:07 +01:00
parent ee280994ce
commit 44670198ab
11 changed files with 138 additions and 162 deletions

View file

@ -114,16 +114,19 @@ namespace Penumbra.Hooks
{ {
if( GetResourceSyncHook == null ) if( GetResourceSyncHook == null )
{ {
PluginLog.Error("[GetResourceHandler] GetResourceSync is null." ); PluginLog.Error( "[GetResourceHandler] GetResourceSync is null." );
return null; return null;
} }
return GetResourceSyncHook.OriginalFunction( pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown ); return GetResourceSyncHook.OriginalFunction( pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown );
} }
if( GetResourceAsyncHook == null ) if( GetResourceAsyncHook == null )
{ {
PluginLog.Error("[GetResourceHandler] GetResourceAsync is null." ); PluginLog.Error( "[GetResourceHandler] GetResourceAsync is null." );
return null; return null;
} }
return GetResourceAsyncHook.OriginalFunction( pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); return GetResourceAsyncHook.OriginalFunction( pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown );
} }
@ -138,7 +141,7 @@ namespace Penumbra.Hooks
bool isUnknown bool isUnknown
) )
{ {
var gameFsPath = Marshal.PtrToStringAnsi( new IntPtr( pPath ) ); var gameFsPath = GamePath.GenerateUncheckedLower( Marshal.PtrToStringAnsi( new IntPtr( pPath ) )! );
if( LogAllFiles ) if( LogAllFiles )
{ {
@ -152,16 +155,15 @@ namespace Penumbra.Hooks
return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); 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 :( // 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 ); return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown );
} }
var cleanPath = replacementPath.Replace( '\\', '/' ); var path = Encoding.ASCII.GetBytes( replacementPath );
var path = Encoding.ASCII.GetBytes( cleanPath );
var bPath = stackalloc byte[path.Length + 1]; var bPath = stackalloc byte[path.Length + 1];
Marshal.Copy( path, 0, new IntPtr( bPath ), path.Length ); Marshal.Copy( path, 0, new IntPtr( bPath ), path.Length );
@ -185,7 +187,7 @@ namespace Penumbra.Hooks
var isRooted = Path.IsPathRooted( gameFsPath ); 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; return ReadSqpackHook?.OriginalFunction( pFileHandler, pFileDesc, priority, isSync ) ?? 0;
} }
@ -216,9 +218,9 @@ namespace Penumbra.Hooks
return; 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; return;
} }

View file

@ -9,6 +9,7 @@ using Lumina.Data;
using Newtonsoft.Json; using Newtonsoft.Json;
using Penumbra.Importer.Models; using Penumbra.Importer.Models;
using Penumbra.Models; using Penumbra.Models;
using Penumbra.Util;
namespace Penumbra.Importer namespace Penumbra.Importer
{ {
@ -261,15 +262,14 @@ namespace Penumbra.Importer
{ {
OptionName = opt.Name!, OptionName = opt.Name!,
OptionDesc = string.IsNullOrEmpty( opt.Description ) ? "" : opt.Description!, 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() ) ); var optDir = new DirectoryInfo( Path.Combine( groupFolder.FullName, opt.Name!.ReplaceInvalidPathSymbols() ) );
if( optDir.Exists ) if( optDir.Exists )
{ {
foreach( var file in optDir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) ) foreach( var file in optDir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) )
{ {
option.AddFile( file.FullName.Substring( baseFolder.FullName.Length ).TrimStart( '\\' ), option.AddFile( new RelPath( file, baseFolder ), new GamePath( file, optDir ) );
file.FullName.Substring( optDir.FullName.Length ).TrimStart( '\\' ).Replace( '\\', '/' ) );
} }
} }

View file

@ -4,13 +4,13 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using Dalamud.Plugin; using Dalamud.Plugin;
using Penumbra.Util;
namespace Penumbra.Models namespace Penumbra.Models
{ {
public class Deduplicator public class Deduplicator
{ {
private readonly DirectoryInfo _baseDir; private readonly DirectoryInfo _baseDir;
private readonly int _baseDirLength;
private readonly ModMeta _mod; private readonly ModMeta _mod;
private SHA256? _hasher; private SHA256? _hasher;
@ -24,10 +24,8 @@ namespace Penumbra.Models
public Deduplicator( DirectoryInfo baseDir, ModMeta mod ) public Deduplicator( DirectoryInfo baseDir, ModMeta mod )
{ {
_baseDir = baseDir; _baseDir = baseDir;
_baseDirLength = baseDir.FullName.Length; _mod = mod;
_mod = mod;
BuildDict(); BuildDict();
} }
@ -89,8 +87,8 @@ namespace Penumbra.Models
private void ReplaceFile( FileInfo f1, FileInfo f2 ) private void ReplaceFile( FileInfo f1, FileInfo f2 )
{ {
var relName1 = f1.FullName.Substring( _baseDirLength ).TrimStart( '\\' ); RelPath relName1 = new( f1, _baseDir );
var relName2 = f2.FullName.Substring( _baseDirLength ).TrimStart( '\\' ); RelPath relName2 = new( f2, _baseDir );
var inOption = false; var inOption = false;
foreach( var group in _mod.Groups.Select( g => g.Value.Options ) ) foreach( var group in _mod.Groups.Select( g => g.Value.Options ) )
@ -125,15 +123,15 @@ namespace Penumbra.Models
{ {
OptionName = "Required", OptionName = "Required",
OptionDesc = "", OptionDesc = "",
OptionFiles = new Dictionary< string, HashSet< string > >() OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >()
} }
} }
}; };
_mod.Groups.Add( duplicates, info ); _mod.Groups.Add( duplicates, info );
} }
_mod.Groups[ duplicates ].Options[ 0 ].AddFile( relName1, relName2.Replace( '\\', '/' ) ); _mod.Groups[ duplicates ].Options[ 0 ].AddFile( relName1, new GamePath( relName2 ) );
_mod.Groups[ duplicates ].Options[ 0 ].AddFile( relName1, relName1.Replace( '\\', '/' ) ); _mod.Groups[ duplicates ].Options[ 0 ].AddFile( relName1, new GamePath( relName1 ) );
} }
PluginLog.Information( $"File {relName1} and {relName2} are identical. Deleting the second." ); PluginLog.Information( $"File {relName1} and {relName2} are identical. Deleting the second." );

View file

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
using Penumbra.Util;
namespace Penumbra.Models namespace Penumbra.Models
{ {
@ -14,17 +15,17 @@ namespace Penumbra.Models
public string OptionName; public string OptionName;
public string OptionDesc; public string OptionDesc;
[JsonProperty( ItemConverterType = typeof( Util.SingleOrArrayConverter< string > ) )] [JsonProperty( ItemConverterType = typeof( SingleOrArrayConverter< GamePath > ) )]
public Dictionary< string, HashSet< string > > OptionFiles; 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 ) ) if( OptionFiles.TryGetValue( filePath, out var set ) )
{ {
return set.Add( gamePath ); return set.Add( gamePath );
} }
OptionFiles[ filePath ] = new HashSet< string >() { gamePath }; OptionFiles[ filePath ] = new HashSet< GamePath >() { gamePath };
return true; return true;
} }
} }

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Penumbra.Util;
namespace Penumbra.Models namespace Penumbra.Models
{ {
@ -19,7 +20,7 @@ namespace Penumbra.Models
public List< string > ChangedItems { get; set; } = new(); 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(); public Dictionary< string, OptionGroup > Groups { get; set; } = new();

View file

@ -4,52 +4,37 @@ using System.IO;
using System.Linq; using System.Linq;
using Dalamud.Plugin; using Dalamud.Plugin;
using Penumbra.Models; using Penumbra.Models;
using Penumbra.Util;
namespace Penumbra.Mods namespace Penumbra.Mods
{ {
public class ModManager : IDisposable public class ModManager : IDisposable
{ {
private readonly Plugin _plugin; private readonly Plugin _plugin;
public readonly Dictionary< string, FileInfo > ResolvedFiles = new(); public readonly Dictionary< GamePath, FileInfo > ResolvedFiles = new();
public readonly Dictionary< string, string > SwappedFiles = new(); public readonly Dictionary< GamePath, GamePath > SwappedFiles = new();
public ModCollection? Mods { get; set; } public ModCollection? Mods { get; set; }
private DirectoryInfo? _basePath; private DirectoryInfo? _basePath;
public ModManager( Plugin plugin ) public ModManager( Plugin plugin )
=> _plugin = plugin; => _plugin = plugin;
public void DiscoverMods() 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 ) _basePath = basePath;
{ if( basePath == null || !basePath.Exists )
return;
}
DiscoverMods( _basePath );
}
public void DiscoverMods( string basePath )
{
DiscoverMods( new DirectoryInfo( basePath ) );
}
public void DiscoverMods( DirectoryInfo basePath )
{
if( basePath == null )
{
return;
}
if( !basePath.Exists )
{ {
Mods = null; Mods = null;
return; return;
} }
_basePath = basePath;
// FileSystemPasta(); // FileSystemPasta();
Mods = new ModCollection( basePath ); Mods = new ModCollection( basePath );
@ -69,7 +54,7 @@ namespace Penumbra.Mods
return; return;
} }
var registeredFiles = new Dictionary< string, string >(); var registeredFiles = new Dictionary< GamePath, string >();
foreach( var (mod, settings) in Mods.GetOrderedAndEnabledModListWithSettings( _plugin!.Configuration!.InvertModListOrder ) ) foreach( var (mod, settings) in Mods.GetOrderedAndEnabledModListWithSettings( _plugin!.Configuration!.InvertModListOrder ) )
{ {
@ -82,15 +67,15 @@ namespace Penumbra.Mods
_plugin!.GameUtils!.ReloadPlayerResources(); _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 ) foreach( var swap in mod.Meta.FileSwaps )
{ {
// just assume people put not fucked paths in here lol // just assume people put not fucked paths in here lol
if( !SwappedFiles.ContainsKey( swap.Value ) ) if( !SwappedFiles.ContainsKey( swap.Value ) )
{ {
SwappedFiles[ swap.Key.ToLowerInvariant() ] = swap.Value; SwappedFiles[ swap.Key ] = swap.Value;
registeredFiles[ swap.Key ] = mod.Meta.Name; registeredFiles[ swap.Key ] = mod.Meta.Name;
} }
else if( registeredFiles.TryGetValue( swap.Key, out var modName ) ) 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 ) foreach( var file in mod.ModFiles )
{ {
var relativeFilePath = file.FullName.Substring( baseDir.Length ).TrimStart( '\\' ); RelPath relativeFilePath = new( file, mod.ModBasePath );
var doNotAdd = false;
var doNotAdd = false; foreach( var group in mod.Meta.Groups.Values )
HashSet< string > paths;
foreach( var group in mod.Meta.Groups.Select( G => G.Value ) )
{ {
if( !settings.Conf.TryGetValue( group.GroupName, out var setting ) if( !settings.Conf.TryGetValue( group.GroupName, out var setting )
|| group.SelectionType == SelectType.Single || group.SelectionType == SelectType.Single
@ -131,20 +111,18 @@ namespace Penumbra.Mods
settings.Conf[ group.GroupName ] &= ( 1 << group.Options.Count ) - 1; settings.Conf[ group.GroupName ] &= ( 1 << group.Options.Count ) - 1;
} }
HashSet< GamePath > paths;
switch( group.SelectionType ) switch( group.SelectionType )
{ {
case SelectType.Single: case SelectType.Single:
if( group.Options[ setting ].OptionFiles.TryGetValue( relativeFilePath, out paths ) ) if( group.Options[ setting ].OptionFiles.TryGetValue( relativeFilePath, out paths ) )
{ {
AddFiles( paths, out doNotAdd, file, registeredFiles, mod ); doNotAdd |= AddFiles( paths, file, registeredFiles, mod );
} }
else else
{ {
if( group.Options.Where( ( o, i ) => i != setting ) doNotAdd |= group.Options.Where( ( o, i ) => i != setting )
.Any( option => option.OptionFiles.ContainsKey( relativeFilePath ) ) ) .Any( option => option.OptionFiles.ContainsKey( relativeFilePath ) );
{
doNotAdd = true;
}
} }
break; break;
@ -155,12 +133,12 @@ namespace Penumbra.Mods
{ {
if( group.Options[ i ].OptionFiles.TryGetValue( relativeFilePath, out paths ) ) 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 ) 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 ) ResourceMod mod )
{ {
doNotAdd = true;
foreach( var gamePath in gamePaths ) foreach( var gamePath in gamePaths )
{ {
if( !ResolvedFiles.ContainsKey( gamePath ) ) if( !ResolvedFiles.ContainsKey( gamePath ) )
{ {
ResolvedFiles[ gamePath.ToLowerInvariant() ] = file; ResolvedFiles[ gamePath ] = file;
registeredFiles[ gamePath ] = mod.Meta.Name; registeredFiles[ gamePath ] = mod.Meta.Name;
} }
else if( registeredFiles.TryGetValue( gamePath, out var modName ) ) else if( registeredFiles.TryGetValue( gamePath, out var modName ) )
{ {
mod.AddConflict( modName, gamePath ); mod.AddConflict( modName, gamePath );
} }
} }
return true;
} }
public void ChangeModPriority( ModInfo info, bool up = false ) public void ChangeModPriority( ModInfo info, bool up = false )
@ -216,7 +194,7 @@ namespace Penumbra.Mods
DiscoverMods(); DiscoverMods();
} }
public FileInfo? GetCandidateForGameFile( string gameResourcePath ) public FileInfo? GetCandidateForGameFile( GamePath gameResourcePath )
{ {
var val = ResolvedFiles.TryGetValue( gameResourcePath, out var candidate ); var val = ResolvedFiles.TryGetValue( gameResourcePath, out var candidate );
if( !val ) if( !val )
@ -232,15 +210,11 @@ namespace Penumbra.Mods
return candidate; return candidate;
} }
public string? GetSwappedFilePath( string gameResourcePath ) public GamePath? GetSwappedFilePath( GamePath gameResourcePath )
=> SwappedFiles.TryGetValue( gameResourcePath, out var swappedPath ) ? swappedPath : null; => SwappedFiles.TryGetValue( gameResourcePath, out var swappedPath ) ? swappedPath : null;
public string? ResolveSwappedOrReplacementFilePath( string gameResourcePath ) public string? ResolveSwappedOrReplacementFilePath( GamePath gameResourcePath )
{ => GetCandidateForGameFile( gameResourcePath )?.FullName ?? GetSwappedFilePath( gameResourcePath ) ?? null;
gameResourcePath = gameResourcePath.ToLowerInvariant();
return GetCandidateForGameFile( gameResourcePath )?.FullName ?? GetSwappedFilePath( gameResourcePath );
}
public void Dispose() public void Dispose()

View file

@ -1,8 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Dalamud.Plugin;
using Penumbra.Models; using Penumbra.Models;
using Penumbra.Util;
namespace Penumbra.Mods namespace Penumbra.Mods
{ {
@ -20,7 +20,7 @@ namespace Penumbra.Mods
public List< FileInfo > ModFiles { get; } = new(); 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() 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 ) ) if( FileConflicts.TryGetValue( modName, out var arr ) )
{ {
@ -45,10 +45,7 @@ namespace Penumbra.Mods
return; return;
} }
FileConflicts[ modName ] = new List< string > FileConflicts[ modName ] = new List< GamePath > { path };
{
path
};
} }
} }
} }

View file

@ -1,6 +1,8 @@
using System.IO;
using System.Linq; using System.Linq;
using ImGuiNET; using ImGuiNET;
using Penumbra.Mods; using Penumbra.Mods;
using Penumbra.Util;
namespace Penumbra.UI namespace Penumbra.UI
{ {
@ -12,8 +14,7 @@ namespace Penumbra.UI
private const float TextSizePadding = 5f; private const float TextSizePadding = 5f;
private ModManager _mods => Service< ModManager >.Get(); private ModManager _mods => Service< ModManager >.Get();
private (string, string)[]? _fileList; private float _maxGamePath;
private float _maxGamePath;
public TabEffective( SettingsInterface ui ) public TabEffective( SettingsInterface ui )
{ {
@ -24,24 +25,24 @@ namespace Penumbra.UI
{ {
if( advanced ) if( advanced )
{ {
_fileList = _mods.ResolvedFiles.Select( P => ( P.Value.FullName, P.Key ) ).ToArray(); _maxGamePath = TextSizePadding + ( _mods.ResolvedFiles.Count > 0
_maxGamePath = ( _fileList.Length > 0 ? _fileList.Max( P => ImGui.CalcTextSize( P.Item2 ).X ) : 0f ) + TextSizePadding; ? _mods.ResolvedFiles.Keys.Max( f => ImGui.CalcTextSize( f ).X )
: 0f );
} }
else else
{ {
_fileList = null;
_maxGamePath = 0f; _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.SameLine();
ImGui.SetCursorPosX( _maxGamePath ); ImGui.SetCursorPosX( _maxGamePath );
ImGui.TextUnformatted( " <-- " ); ImGui.TextUnformatted( " <-- " );
ImGui.SameLine(); ImGui.SameLine();
ImGui.Selectable( file.Item1 ); ImGui.Selectable( file.FullName );
} }
public void Draw() public void Draw()
@ -54,9 +55,9 @@ namespace Penumbra.UI
if( ImGui.ListBoxHeader( "##effective_files", AutoFillSize ) ) 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(); ImGui.ListBoxFooter();

View file

@ -1,12 +1,14 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.IO;
using ImGuiNET; using ImGuiNET;
using Penumbra.Models; using Penumbra.Models;
using Penumbra.Mods; using Penumbra.Mods;
using Penumbra.Util;
namespace Penumbra.UI 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. // 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 ) 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 ColorYellow = 0xFF00C8C8;
private const uint ColorRed = 0xFF0000C8; private const uint ColorRed = 0xFF0000C8;
private bool _editMode = false; private bool _editMode;
private int _selectedGroupIndex = 0; private int _selectedGroupIndex;
private OptionGroup? _selectedGroup = null; private OptionGroup? _selectedGroup;
private int _selectedOptionIndex = 0; private int _selectedOptionIndex;
private Option? _selectedOption = null; private Option? _selectedOption;
private (string label, string name)[]? _changedItemsList = null; private (string label, string name)[]? _changedItemsList;
private float? _fileSwapOffset = null; private float? _fileSwapOffset;
private string _currentGamePaths = ""; 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 Selector _selector;
private readonly SettingsInterface _base; 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. // 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; var numGroups = _selector.Mod()?.Mod.Meta.Groups.Count ?? 0;
_selectedGroupIndex = idx; _selectedGroupIndex = idx;
if( _selectedGroupIndex >= numGroups) if( _selectedGroupIndex >= numGroups )
{ {
_selectedGroupIndex = 0; _selectedGroupIndex = 0;
} }
@ -87,7 +89,8 @@ namespace Penumbra.UI
} }
} }
private void SelectGroup() => SelectGroup( _selectedGroupIndex ); private void SelectGroup()
=> SelectGroup( _selectedGroupIndex );
private void SelectOption( int idx ) 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() public void ResetState()
{ {
@ -191,7 +195,8 @@ namespace Penumbra.UI
ImGui.SetNextItemWidth( -1 ); ImGui.SetNextItemWidth( -1 );
if( ImGui.ListBoxHeader( LabelChangedItemsHeader, AutoFillSize ) ) 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 ) for( var i = 0; i < Meta.ChangedItems.Count; ++i )
{ {
@ -207,13 +212,11 @@ namespace Penumbra.UI
if( _editMode ) if( _editMode )
{ {
ImGui.SetNextItemWidth( -1 ); 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() private void DrawConflictTab()
{ {
if( !Mod.Mod.FileConflicts.Any() ) if( !Mod.Mod.FileConflicts.Any() || !ImGui.BeginTabItem( LabelConflictsTab ) )
{
return;
}
if( !ImGui.BeginTabItem( LabelConflictsTab ) )
{ {
return; return;
} }
@ -275,7 +273,8 @@ namespace Penumbra.UI
if( ImGui.BeginTabItem( LabelFileSwapTab ) ) 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 ); ImGui.SetNextItemWidth( -1 );
if( ImGui.ListBoxHeader( LabelFileSwapHeader, AutoFillSize ) ) if( ImGui.ListBoxHeader( LabelFileSwapHeader, AutoFillSize ) )
@ -308,7 +307,8 @@ namespace Penumbra.UI
} }
var len = Mod.Mod.ModBasePath.FullName.Length; 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 ) if( Meta.Groups.Count == 0 )
{ {
@ -317,12 +317,6 @@ namespace Penumbra.UI
for( var i = 0; i < Mod.Mod.ModFiles.Count; ++i ) 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 ) foreach( var group in Meta.Groups.Values )
{ {
var inAll = true; var inAll = true;
@ -365,7 +359,7 @@ namespace Penumbra.UI
foreach( var file in _fullFilenameList! ) foreach( var file in _fullFilenameList! )
{ {
ImGui.PushStyleColor( ImGuiCol.Text, file.color ); ImGui.PushStyleColor( ImGuiCol.Text, file.color );
ImGui.Selectable( file.name ); ImGui.Selectable( file.name.FullName );
ImGui.PopStyleColor(); ImGui.PopStyleColor();
} }
@ -388,8 +382,8 @@ namespace Penumbra.UI
var option = ( Option )_selectedOption; var option = ( Option )_selectedOption;
var gamePaths = _currentGamePaths.Split( ';' ); var gamePaths = _currentGamePaths.Split( ';' ).Select( P => new GamePath( P ) ).ToArray();
if( gamePaths.Length == 0 || gamePaths[ 0 ].Length == 0 ) if( gamePaths.Length == 0 || ( ( string )gamePaths[ 0 ] ).Length == 0 )
{ {
return; return;
} }
@ -403,27 +397,28 @@ namespace Penumbra.UI
continue; continue;
} }
var fileName = _fullFilenameList[ i ].relName; var relName = _fullFilenameList[ i ].relName;
if( defaultIndex >= 0 ) 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 ) if( setPaths.RemoveWhere( P => gamePaths.Contains( P ) ) > 0 )
{ {
changed = true; changed = true;
} }
if( setPaths.Count == 0 && option.OptionFiles.Remove( fileName ) ) if( setPaths.Count == 0 && option.OptionFiles.Remove( relName ) )
{ {
changed = true; changed = true;
} }
} }
else 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.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(); ImGui.PopStyleColor();
} }
@ -514,22 +509,28 @@ namespace Penumbra.UI
return; return;
} }
var fileName = _fullFilenameList![ idx ].relName; var fileName = _fullFilenameList![ idx ].relName;
if( ( ( Option )_selectedOption ).OptionFiles.TryGetValue( fileName, out var gamePaths ) ) var optionFiles = ( ( Option )_selectedOption ).OptionFiles;
if( optionFiles.TryGetValue( fileName, out var gamePaths ) )
{ {
Selectable( 0, ColorGreen ); Selectable( 0, ColorGreen );
ImGui.Indent( indent ); 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 ) if( ImGui.InputText( $"##{fileName}_{gamePath}", ref tmp, 128, ImGuiInputTextFlags.EnterReturnsTrue )
&& tmp != gamePath ) && tmp != gamePath )
{ {
gamePaths.Remove( gamePath ); gamePaths.Remove( gamePath );
if( tmp.Length > 0 ) if( tmp.Length > 0 )
{ {
gamePaths.Add( tmp ); gamePaths.Add( new GamePath( tmp ) );
}
else if( gamePaths.Count == 0 )
{
optionFiles.Remove( fileName );
} }
_selector.SaveCurrentMod(); _selector.SaveCurrentMod();
@ -547,7 +548,6 @@ namespace Penumbra.UI
private void DrawMultiSelectorCheckBox( OptionGroup group, int idx, int flag, string label ) private void DrawMultiSelectorCheckBox( OptionGroup group, int idx, int flag, string label )
{ {
var opt = group.Options[ idx ];
var enabled = ( flag & ( 1 << idx ) ) != 0; var enabled = ( flag & ( 1 << idx ) ) != 0;
var oldEnabled = enabled; var oldEnabled = enabled;
if( ImGui.Checkbox( label, ref enabled ) && oldEnabled != enabled ) if( ImGui.Checkbox( label, ref enabled ) && oldEnabled != enabled )
@ -601,8 +601,6 @@ namespace Penumbra.UI
{ {
DrawMultiSelector( g ); DrawMultiSelector( g );
} }
return;
} }
private void DrawConfigurationTab() private void DrawConfigurationTab()

View file

@ -3,6 +3,7 @@ using System.Linq;
using System.Numerics; using System.Numerics;
using ImGuiNET; using ImGuiNET;
using Penumbra.Models; using Penumbra.Models;
using Penumbra.Util;
namespace Penumbra.UI namespace Penumbra.UI
{ {
@ -134,7 +135,7 @@ namespace Penumbra.UI
&& newOption.Length != 0 ) && newOption.Length != 0 )
{ {
group.Options.Add( new Option() 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(); _selector.SaveCurrentMod();
} }
} }
@ -232,7 +233,7 @@ namespace Penumbra.UI
modChanged = true; modChanged = true;
Mod.Conf[ group.GroupName ] = code; Mod.Conf[ group.GroupName ] = code;
group.Options.Add( new Option() group.Options.Add( new Option()
{ OptionName = newName, OptionDesc = "", OptionFiles = new Dictionary< string, HashSet< string > >() } ); { OptionName = newName, OptionDesc = "", OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >() } );
} }
} }
else else

View file

@ -110,6 +110,9 @@ namespace Penumbra.Util
public static GamePath GenerateUnchecked( string path ) public static GamePath GenerateUnchecked( string path )
=> new( path, true ); => new( path, true );
public static GamePath GenerateUncheckedLower( string path )
=> new( Lower(path), true );
public static implicit operator bool( GamePath gamePath ) public static implicit operator bool( GamePath gamePath )
=> gamePath._path.Length > 0; => gamePath._path.Length > 0;