From 76269ba64a35f2db935c5b527a4d860a815e6c6d Mon Sep 17 00:00:00 2001 From: Adam <893184+NotAdam@users.noreply.github.com> Date: Sat, 26 Dec 2020 11:37:50 +1100 Subject: [PATCH] fix a crash on non-ascii file paths, sqpack file swapping --- Penumbra/Models/ModMeta.cs | 4 ++++ Penumbra/Mods/ModManager.cs | 35 +++++++++++++++++++++++++++++--- Penumbra/Mods/ResourceMod.cs | 2 +- Penumbra/Penumbra.csproj | 8 ++++---- Penumbra/ResourceLoader.cs | 18 +++++++++------- Penumbra/UI/SettingsInterface.cs | 19 +++++++++++++++++ 6 files changed, 71 insertions(+), 15 deletions(-) diff --git a/Penumbra/Models/ModMeta.cs b/Penumbra/Models/ModMeta.cs index 6a7a5f4d..79c84c67 100644 --- a/Penumbra/Models/ModMeta.cs +++ b/Penumbra/Models/ModMeta.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace Penumbra.Models { public class ModMeta @@ -5,5 +7,7 @@ namespace Penumbra.Models public string Name { get; set; } public string Author { get; set; } public string Description { get; set; } + + public Dictionary< string, string > FileSwaps { get; } = new(); } } \ No newline at end of file diff --git a/Penumbra/Mods/ModManager.cs b/Penumbra/Mods/ModManager.cs index fd5c8222..85260ad4 100644 --- a/Penumbra/Mods/ModManager.cs +++ b/Penumbra/Mods/ModManager.cs @@ -7,11 +7,10 @@ namespace Penumbra.Mods public class ModManager { public readonly Dictionary< string, FileInfo > ResolvedFiles = new(); + public readonly Dictionary< string, string > SwappedFiles = new(); public ModCollection Mods { get; set; } - public ResourceMod[] AvailableMods => Mods?.EnabledMods; - private DirectoryInfo _basePath; public void DiscoverMods() @@ -81,6 +80,20 @@ namespace Penumbra.Mods mod.AddConflict( modName, path ); } } + + 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 ] = swap.Value; + registeredFiles[ swap.Key ] = mod.Meta.Name; + } + else if( registeredFiles.TryGetValue( swap.Key, out var modName ) ) + { + mod.AddConflict( modName, swap.Key ); + } + } } } @@ -99,7 +112,23 @@ namespace Penumbra.Mods public FileInfo GetCandidateForGameFile( string resourcePath ) { - return ResolvedFiles.TryGetValue( resourcePath.ToLowerInvariant(), out var fileInfo ) ? fileInfo : null; + var val = ResolvedFiles.TryGetValue( resourcePath.ToLowerInvariant(), out var candidate ); + if( !val ) + { + return null; + } + + if( candidate.FullName.Length >= 260 || !candidate.Exists ) + { + return null; + } + + return candidate; + } + + public string GetSwappedFilePath( string originalPath ) + { + return SwappedFiles.TryGetValue( originalPath, out var swappedPath ) ? swappedPath : null; } } } \ No newline at end of file diff --git a/Penumbra/Mods/ResourceMod.cs b/Penumbra/Mods/ResourceMod.cs index e6272904..a150928e 100644 --- a/Penumbra/Mods/ResourceMod.cs +++ b/Penumbra/Mods/ResourceMod.cs @@ -13,7 +13,7 @@ namespace Penumbra.Mods public List< FileInfo > ModFiles { get; } = new(); - public Dictionary< string, List< string > > FileConflicts { get; set; } = new(); + public Dictionary< string, List< string > > FileConflicts { get; } = new(); public void RefreshModFiles() { diff --git a/Penumbra/Penumbra.csproj b/Penumbra/Penumbra.csproj index 9be3f8c8..80962626 100644 --- a/Penumbra/Penumbra.csproj +++ b/Penumbra/Penumbra.csproj @@ -23,27 +23,27 @@ - $(DALAMUD_ROOT)\Dalamud.dll ..\libs\Dalamud.dll $(AppData)\XIVLauncher\addon\Hooks\Dalamud.dll + $(DALAMUD_ROOT)\Dalamud.dll False - $(DALAMUD_ROOT)\ImGui.NET.dll ..\libs\ImGui.NET.dll $(AppData)\XIVLauncher\addon\Hooks\ImGui.NET.dll + $(DALAMUD_ROOT)\ImGui.NET.dll False - $(DALAMUD_ROOT)\ImGuiScene.dll ..\libs\ImGuiScene.dll $(AppData)\XIVLauncher\addon\Hooks\ImGuiScene.dll + $(DALAMUD_ROOT)\ImGuiScene.dll False - $(DALAMUD_ROOT)\Lumina.dll ..\libs\Lumina.dll $(AppData)\XIVLauncher\addon\Hooks\Lumina.dll + $(DALAMUD_ROOT)\Lumina.dll False diff --git a/Penumbra/ResourceLoader.cs b/Penumbra/ResourceLoader.cs index 4e01fe75..0fa13057 100644 --- a/Penumbra/ResourceLoader.cs +++ b/Penumbra/ResourceLoader.cs @@ -158,23 +158,27 @@ namespace Penumbra PluginLog.Log( "[GetResourceHandler] {0}", gameFsPath ); } + var candidate = Plugin.ModManager.GetCandidateForGameFile( gameFsPath ); + var swappedFilePath = Plugin.ModManager.GetSwappedFilePath( gameFsPath ); + + var path = candidate?.FullName ?? swappedFilePath; // path must be < 260 because statically defined array length :( - if( candidate == null || candidate.FullName.Length >= 260 || !candidate.Exists ) + if( path == null || path.Length < 260 ) { return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); } - var cleanPath = candidate.FullName.Replace( '\\', '/' ); - var asciiPath = Encoding.ASCII.GetBytes( cleanPath ); + var cleanPath = path.Replace( '\\', '/' ); + var utfPath = Encoding.UTF8.GetBytes( cleanPath ); - var bPath = stackalloc byte[asciiPath.Length + 1]; - Marshal.Copy( asciiPath, 0, new IntPtr( bPath ), asciiPath.Length ); + var bPath = stackalloc byte[utfPath.Length + 1]; + Marshal.Copy( utfPath, 0, new IntPtr( bPath ), utfPath.Length ); pPath = ( char* )bPath; Crc32.Init(); - Crc32.Update( asciiPath ); + Crc32.Update( utfPath ); *pResourceHash = Crc32.Checksum; return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); @@ -198,7 +202,7 @@ namespace Penumbra pFileDesc->FileMode = FileMode.LoadUnpackedResource; - var utfPath = Encoding.Unicode.GetBytes( gameFsPath ); + var utfPath = Encoding.UTF8.GetBytes( gameFsPath ); Marshal.Copy( utfPath, 0, new IntPtr( &pFileDesc->UtfFileName ), utfPath.Length ); diff --git a/Penumbra/UI/SettingsInterface.cs b/Penumbra/UI/SettingsInterface.cs index 8a8e4da0..1231ee19 100644 --- a/Penumbra/UI/SettingsInterface.cs +++ b/Penumbra/UI/SettingsInterface.cs @@ -414,6 +414,25 @@ namespace Penumbra.UI ImGui.EndTabItem(); } + if( _selectedMod.Mod.Meta.FileSwaps.Any() ) + { + if( ImGui.BeginTabItem( "File Swaps" ) ) + { + ImGui.SetNextItemWidth( -1 ); + if( ImGui.ListBoxHeader( "##", AutoFillSize ) ) + { + foreach( var file in _selectedMod.Mod.Meta.FileSwaps ) + { + // todo: fucking gross alloc every frame * items + ImGui.Selectable( $"{file.Key} -> {file.Value}" ); + } + } + + ImGui.ListBoxFooter(); + ImGui.EndTabItem(); + } + } + if( _selectedMod.Mod.FileConflicts.Any() ) { if( ImGui.BeginTabItem( "File Conflicts" ) )