fix a crash on non-ascii file paths, sqpack file swapping

This commit is contained in:
Adam 2020-12-26 11:37:50 +11:00
parent 21758a8c85
commit 76269ba64a
6 changed files with 71 additions and 15 deletions

View file

@ -1,3 +1,5 @@
using System.Collections.Generic;
namespace Penumbra.Models namespace Penumbra.Models
{ {
public class ModMeta public class ModMeta
@ -5,5 +7,7 @@ namespace Penumbra.Models
public string Name { get; set; } public string Name { get; set; }
public string Author { get; set; } public string Author { get; set; }
public string Description { get; set; } public string Description { get; set; }
public Dictionary< string, string > FileSwaps { get; } = new();
} }
} }

View file

@ -7,11 +7,10 @@ namespace Penumbra.Mods
public class ModManager public class ModManager
{ {
public readonly Dictionary< string, FileInfo > ResolvedFiles = new(); public readonly Dictionary< string, FileInfo > ResolvedFiles = new();
public readonly Dictionary< string, string > SwappedFiles = new();
public ModCollection Mods { get; set; } public ModCollection Mods { get; set; }
public ResourceMod[] AvailableMods => Mods?.EnabledMods;
private DirectoryInfo _basePath; private DirectoryInfo _basePath;
public void DiscoverMods() public void DiscoverMods()
@ -81,6 +80,20 @@ namespace Penumbra.Mods
mod.AddConflict( modName, path ); 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 ) 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;
} }
} }
} }

View file

@ -13,7 +13,7 @@ namespace Penumbra.Mods
public List< FileInfo > ModFiles { get; } = new(); 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() public void RefreshModFiles()
{ {

View file

@ -23,27 +23,27 @@
<ItemGroup> <ItemGroup>
<Reference Include="Dalamud"> <Reference Include="Dalamud">
<HintPath>$(DALAMUD_ROOT)\Dalamud.dll</HintPath>
<HintPath>..\libs\Dalamud.dll</HintPath> <HintPath>..\libs\Dalamud.dll</HintPath>
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Dalamud.dll</HintPath> <HintPath>$(AppData)\XIVLauncher\addon\Hooks\Dalamud.dll</HintPath>
<HintPath>$(DALAMUD_ROOT)\Dalamud.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="ImGui.NET"> <Reference Include="ImGui.NET">
<HintPath>$(DALAMUD_ROOT)\ImGui.NET.dll</HintPath>
<HintPath>..\libs\ImGui.NET.dll</HintPath> <HintPath>..\libs\ImGui.NET.dll</HintPath>
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\ImGui.NET.dll</HintPath> <HintPath>$(AppData)\XIVLauncher\addon\Hooks\ImGui.NET.dll</HintPath>
<HintPath>$(DALAMUD_ROOT)\ImGui.NET.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="ImGuiScene"> <Reference Include="ImGuiScene">
<HintPath>$(DALAMUD_ROOT)\ImGuiScene.dll</HintPath>
<HintPath>..\libs\ImGuiScene.dll</HintPath> <HintPath>..\libs\ImGuiScene.dll</HintPath>
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\ImGuiScene.dll</HintPath> <HintPath>$(AppData)\XIVLauncher\addon\Hooks\ImGuiScene.dll</HintPath>
<HintPath>$(DALAMUD_ROOT)\ImGuiScene.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="Lumina"> <Reference Include="Lumina">
<HintPath>$(DALAMUD_ROOT)\Lumina.dll</HintPath>
<HintPath>..\libs\Lumina.dll</HintPath> <HintPath>..\libs\Lumina.dll</HintPath>
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Lumina.dll</HintPath> <HintPath>$(AppData)\XIVLauncher\addon\Hooks\Lumina.dll</HintPath>
<HintPath>$(DALAMUD_ROOT)\Lumina.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>

View file

@ -158,23 +158,27 @@ namespace Penumbra
PluginLog.Log( "[GetResourceHandler] {0}", gameFsPath ); PluginLog.Log( "[GetResourceHandler] {0}", gameFsPath );
} }
var candidate = Plugin.ModManager.GetCandidateForGameFile( 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 :( // 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 ); return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown );
} }
var cleanPath = candidate.FullName.Replace( '\\', '/' ); var cleanPath = path.Replace( '\\', '/' );
var asciiPath = Encoding.ASCII.GetBytes( cleanPath ); var utfPath = Encoding.UTF8.GetBytes( cleanPath );
var bPath = stackalloc byte[asciiPath.Length + 1]; var bPath = stackalloc byte[utfPath.Length + 1];
Marshal.Copy( asciiPath, 0, new IntPtr( bPath ), asciiPath.Length ); Marshal.Copy( utfPath, 0, new IntPtr( bPath ), utfPath.Length );
pPath = ( char* )bPath; pPath = ( char* )bPath;
Crc32.Init(); Crc32.Init();
Crc32.Update( asciiPath ); Crc32.Update( utfPath );
*pResourceHash = Crc32.Checksum; *pResourceHash = Crc32.Checksum;
return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown );
@ -198,7 +202,7 @@ namespace Penumbra
pFileDesc->FileMode = FileMode.LoadUnpackedResource; 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 ); Marshal.Copy( utfPath, 0, new IntPtr( &pFileDesc->UtfFileName ), utfPath.Length );

View file

@ -414,6 +414,25 @@ namespace Penumbra.UI
ImGui.EndTabItem(); 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( _selectedMod.Mod.FileConflicts.Any() )
{ {
if( ImGui.BeginTabItem( "File Conflicts" ) ) if( ImGui.BeginTabItem( "File Conflicts" ) )