From 1d254e58562bc1cf5df496ddb8e3028464261f0f Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 10 Dec 2021 10:23:51 +0100 Subject: [PATCH] Update for 6.0 --- .../Structs/CharacterEquipment.cs | 6 +- Penumbra.PlayerWatch/CharacterFactory.cs | 2 +- Penumbra/Extensions/FuckedExtensions.cs | 81 --- Penumbra/Interop/ResidentResources.cs | 2 +- Penumbra/Interop/ResourceLoader.cs | 487 ++++++++++-------- Penumbra/Penumbra.json | 2 +- Penumbra/UI/MenuTabs/TabResourceManager.cs | 2 +- 7 files changed, 280 insertions(+), 302 deletions(-) delete mode 100644 Penumbra/Extensions/FuckedExtensions.cs diff --git a/Penumbra.GameData/Structs/CharacterEquipment.cs b/Penumbra.GameData/Structs/CharacterEquipment.cs index 84d244ae..11be31ed 100644 --- a/Penumbra.GameData/Structs/CharacterEquipment.cs +++ b/Penumbra.GameData/Structs/CharacterEquipment.cs @@ -9,9 +9,9 @@ namespace Penumbra.GameData.Structs [StructLayout( LayoutKind.Sequential, Pack = 1 )] public class CharacterEquipment { - public const int MainWeaponOffset = 0x0F08; - public const int OffWeaponOffset = 0x0F70; - public const int EquipmentOffset = 0x1040; + public const int MainWeaponOffset = 0x0C78; + public const int OffWeaponOffset = 0x0CE0; + public const int EquipmentOffset = 0xDB0; public const int EquipmentSlots = 10; public const int WeaponSlots = 2; diff --git a/Penumbra.PlayerWatch/CharacterFactory.cs b/Penumbra.PlayerWatch/CharacterFactory.cs index 9c18c29e..b88cb28f 100644 --- a/Penumbra.PlayerWatch/CharacterFactory.cs +++ b/Penumbra.PlayerWatch/CharacterFactory.cs @@ -8,7 +8,7 @@ namespace Penumbra.PlayerWatch { public static class CharacterFactory { - private static ConstructorInfo? _characterConstructor = null; + private static ConstructorInfo? _characterConstructor; private static void Initialize() { diff --git a/Penumbra/Extensions/FuckedExtensions.cs b/Penumbra/Extensions/FuckedExtensions.cs deleted file mode 100644 index f9f41413..00000000 --- a/Penumbra/Extensions/FuckedExtensions.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Reflection; -using System.Reflection.Emit; - -namespace Penumbra.Extensions -{ - public static class FuckedExtensions - { - private delegate ref TFieldType RefGet< in TObject, TFieldType >( TObject obj ); - - /// - /// Create a delegate which will return a zero-copy reference to a given field in a manner that's fucked tiers of quick and - /// fucked tiers of stupid, but hey, why not? - /// - /// - /// The only thing that this can't do is inline, this always ends up as a call instruction because we're generating code at - /// runtime and need to jump to it. That said, this is still super quick and provides a convenient and type safe shim around - /// a primitive type - /// - /// You can use the resultant to access a ref to a field on an object without invoking any - /// unsafe code too. - /// - /// The name of the field to grab a reference to - /// The object that holds the field - /// The type of the underlying field - /// A delegate that will return a reference to a particular field - zero copy - /// - private static RefGet< TObject, TField > CreateRefGetter< TObject, TField >( string fieldName ) - where TField : unmanaged - { - const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; - - var fieldInfo = typeof( TObject ).GetField( fieldName, flags ); - if( fieldInfo == null ) - { - throw new MissingFieldException( typeof( TObject ).Name, fieldName ); - } - - var dm = new DynamicMethod( - $"__refget_{typeof( TObject ).Name}_{fieldInfo.Name}", - typeof( TField ).MakeByRefType(), - new[] { typeof( TObject ) }, - typeof( TObject ), - true - ); - - var il = dm.GetILGenerator(); - - il.Emit( OpCodes.Ldarg_0 ); - il.Emit( OpCodes.Ldflda, fieldInfo ); - il.Emit( OpCodes.Ret ); - - return ( RefGet< TObject, TField > )dm.CreateDelegate( typeof( RefGet< TObject, TField > ) ); - } - - private static readonly RefGet< string, byte > StringRefGet = CreateRefGetter< string, byte >( "_firstChar" ); - - public static unsafe IntPtr UnsafePtr( this string str ) - { - // nb: you can do it without __makeref but the code becomes way shittier because the way of getting the ptr - // is more fucked up so it's easier to just abuse __makeref - // but you can just use the StringRefGet func to get a `ref byte` too, though you'll probs want a better delegate so it's - // actually usable, lol - var fieldRef = __makeref( StringRefGet( str ) ); - - return *( IntPtr* )&fieldRef; - } - - public static unsafe int UnsafeLength( this string str ) - { - var fieldRef = __makeref( StringRefGet( str ) ); - - // c# strings are utf16 so we just multiply len by 2 to get the total byte count + 2 for null terminator (:D) - // very simple and intuitive - - // this also maps to a defined structure, so you can just move the pointer backwards to read from the native string struct - // see: https://github.com/dotnet/coreclr/blob/master/src/vm/object.h#L897-L909 - return *( int* )( *( IntPtr* )&fieldRef - 4 ) * 2 + 2; - } - } -} \ No newline at end of file diff --git a/Penumbra/Interop/ResidentResources.cs b/Penumbra/Interop/ResidentResources.cs index 4a2cc2b9..9bf6d93a 100644 --- a/Penumbra/Interop/ResidentResources.cs +++ b/Penumbra/Interop/ResidentResources.cs @@ -45,7 +45,7 @@ namespace Penumbra.Interop var module = Dalamud.SigScanner.Module.BaseAddress.ToInt64(); var loadPlayerResourcesAddress = Dalamud.SigScanner.ScanText( - "E8 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? BA ?? ?? ?? ?? 41 B8 ?? ?? ?? ?? 48 8B 48 30 48 8B 01 FF 50 10 48 85 C0 74 0A " ); + "E8 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? BA ?? ?? ?? ?? 41 B8 ?? ?? ?? ?? 48 8B 48 30 48 8B 01 FF 50 10 48 85 C0 74 0A" ); GeneralUtil.PrintDebugAddress( "LoadPlayerResources", loadPlayerResourcesAddress ); var unloadPlayerResourcesAddress = diff --git a/Penumbra/Interop/ResourceLoader.cs b/Penumbra/Interop/ResourceLoader.cs index 8d4c5937..0ce2be54 100644 --- a/Penumbra/Interop/ResourceLoader.cs +++ b/Penumbra/Interop/ResourceLoader.cs @@ -11,262 +11,321 @@ using Penumbra.Structs; using Penumbra.Util; using FileMode = Penumbra.Structs.FileMode; -namespace Penumbra.Interop +namespace Penumbra.Interop; + +public class ResourceLoader : IDisposable { - public class ResourceLoader : IDisposable + public Penumbra Penumbra { get; set; } + + public bool IsEnabled { get; set; } + + public Crc32 Crc32 { get; } + + + // Delegate prototypes + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public unsafe delegate byte ReadFilePrototype( IntPtr pFileHandler, SeFileDescriptor* pFileDesc, int priority, bool isSync ); + + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public unsafe delegate byte ReadSqpackPrototype( IntPtr pFileHandler, SeFileDescriptor* pFileDesc, int priority, bool isSync ); + + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public unsafe delegate void* GetResourceSyncPrototype( IntPtr pFileManager, uint* pCategoryId, char* pResourceType + , uint* pResourceHash, char* pPath, void* pUnknown ); + + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public unsafe delegate void* GetResourceAsyncPrototype( IntPtr pFileManager, uint* pCategoryId, char* pResourceType + , uint* pResourceHash, char* pPath, void* pUnknown, bool isUnknown ); + + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate bool CheckFileStatePrototype( IntPtr unk1, ulong unk2 ); + + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate byte LoadTexFileExternPrototype( IntPtr resourceHandle, int unk1, IntPtr unk2, bool unk3, IntPtr unk4 ); + + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate byte LoadTexFileLocalPrototype( IntPtr resourceHandle, int unk1, IntPtr unk2, bool unk3 ); + + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate byte LoadMdlFileExternPrototype( IntPtr resourceHandle, IntPtr unk1, bool unk2, IntPtr unk3 ); + + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate byte LoadMdlFileLocalPrototype( IntPtr resourceHandle, IntPtr unk1, bool unk2 ); + + // Hooks + public Hook< GetResourceSyncPrototype >? GetResourceSyncHook { get; private set; } + public Hook< GetResourceAsyncPrototype >? GetResourceAsyncHook { get; private set; } + public Hook< ReadSqpackPrototype >? ReadSqpackHook { get; private set; } + public Hook< CheckFileStatePrototype >? CheckFileStateHook { get; private set; } + public Hook< LoadTexFileExternPrototype >? LoadTexFileExternHook { get; private set; } + public Hook< LoadMdlFileExternPrototype >? LoadMdlFileExternHook { get; private set; } + + // Unmanaged functions + public ReadFilePrototype? ReadFile { get; private set; } + public CheckFileStatePrototype? CheckFileState { get; private set; } + public LoadTexFileLocalPrototype? LoadTexFileLocal { get; private set; } + public LoadMdlFileLocalPrototype? LoadMdlFileLocal { get; private set; } + + public bool LogAllFiles = false; + public Regex? LogFileFilter = null; + + + public ResourceLoader( Penumbra penumbra ) { - public Penumbra Penumbra { get; set; } + Penumbra = penumbra; + Crc32 = new Crc32(); + } - public bool IsEnabled { get; set; } + public unsafe void Init() + { + var readFileAddress = + Dalamud.SigScanner.ScanText( "E8 ?? ?? ?? ?? 84 C0 0F 84 ?? 00 00 00 4C 8B C3 BA 05" ); + GeneralUtil.PrintDebugAddress( "ReadFile", readFileAddress ); - public Crc32 Crc32 { get; } + var readSqpackAddress = + Dalamud.SigScanner.ScanText( "E8 ?? ?? ?? ?? EB 05 E8 ?? ?? ?? ?? 84 C0 0F 84 ?? 00 00 00 4C 8B C3" ); + GeneralUtil.PrintDebugAddress( "ReadSqPack", readSqpackAddress ); + var getResourceSyncAddress = + Dalamud.SigScanner.ScanText( "E8 ?? ?? 00 00 48 8D 8F ?? ?? 00 00 48 89 87 ?? ?? 00 00" ); + GeneralUtil.PrintDebugAddress( "GetResourceSync", getResourceSyncAddress ); - // Delegate prototypes - [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - public unsafe delegate byte ReadFilePrototype( IntPtr pFileHandler, SeFileDescriptor* pFileDesc, int priority, bool isSync ); + var getResourceAsyncAddress = + Dalamud.SigScanner.ScanText( "E8 ?? ?? ?? 00 48 8B D8 EB ?? F0 FF 83 ?? ?? 00 00" ); + GeneralUtil.PrintDebugAddress( "GetResourceAsync", getResourceAsyncAddress ); - [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - public unsafe delegate byte ReadSqpackPrototype( IntPtr pFileHandler, SeFileDescriptor* pFileDesc, int priority, bool isSync ); + var checkFileStateAddress = Dalamud.SigScanner.ScanText( "E8 ?? ?? ?? ?? 48 85 c0 74 ?? 45 0f b6 ce 48 89 44 24" ); + GeneralUtil.PrintDebugAddress( "CheckFileState", checkFileStateAddress ); - [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - public unsafe delegate void* GetResourceSyncPrototype( IntPtr pFileManager, uint* pCategoryId, char* pResourceType - , uint* pResourceHash, char* pPath, void* pUnknown ); + var loadTexFileLocalAddress = + Dalamud.SigScanner.ScanText( "48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 30 49 8B F0 44 88 4C 24 20" ); + GeneralUtil.PrintDebugAddress( "LoadTexFileLocal", loadTexFileLocalAddress ); - [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - public unsafe delegate void* GetResourceAsyncPrototype( IntPtr pFileManager, uint* pCategoryId, char* pResourceType - , uint* pResourceHash, char* pPath, void* pUnknown, bool isUnknown ); + var loadTexFileExternAddress = + Dalamud.SigScanner.ScanText( "E8 ?? ?? ?? ?? 0F B6 E8 48 8B CB E8" ); + GeneralUtil.PrintDebugAddress( "LoadTexFileExtern", loadTexFileExternAddress ); - // Hooks - public Hook< GetResourceSyncPrototype >? GetResourceSyncHook { get; private set; } - public Hook< GetResourceAsyncPrototype >? GetResourceAsyncHook { get; private set; } - public Hook< ReadSqpackPrototype >? ReadSqpackHook { get; private set; } + var loadMdlFileLocalAddress = + Dalamud.SigScanner.ScanText( "40 55 53 56 57 41 56 41 57 48 8D 6C 24 D1 48 81 EC 98 00 00 00" ); + GeneralUtil.PrintDebugAddress( "LoadMdlFileLocal", loadMdlFileLocalAddress ); - // Unmanaged functions - public ReadFilePrototype? ReadFile { get; private set; } + var loadMdlFileExternAddress = + Dalamud.SigScanner.ScanText( "E8 ?? ?? ?? ?? EB 02 B0 F1" ); + GeneralUtil.PrintDebugAddress( "LoadMdlFileExtern", loadMdlFileExternAddress ); + ReadSqpackHook = new Hook< ReadSqpackPrototype >( readSqpackAddress, ReadSqpackHandler ); + GetResourceSyncHook = new Hook< GetResourceSyncPrototype >( getResourceSyncAddress, GetResourceSyncHandler ); + GetResourceAsyncHook = new Hook< GetResourceAsyncPrototype >( getResourceAsyncAddress, GetResourceAsyncHandler ); - public bool LogAllFiles = false; - public Regex? LogFileFilter = null; + ReadFile = Marshal.GetDelegateForFunctionPointer< ReadFilePrototype >( readFileAddress ); + LoadTexFileLocal = Marshal.GetDelegateForFunctionPointer< LoadTexFileLocalPrototype >( loadTexFileLocalAddress ); + LoadMdlFileLocal = Marshal.GetDelegateForFunctionPointer< LoadMdlFileLocalPrototype >( loadMdlFileLocalAddress ); + CheckFileStateHook = new Hook< CheckFileStatePrototype >( checkFileStateAddress, CheckFileStateDetour ); + LoadTexFileExternHook = new Hook< LoadTexFileExternPrototype >( loadTexFileExternAddress, LoadTexFileExternDetour ); + LoadMdlFileExternHook = new Hook< LoadMdlFileExternPrototype >( loadMdlFileExternAddress, LoadMdlFileExternDetour ); + } - public ResourceLoader( Penumbra penumbra ) + private static bool CheckFileStateDetour( IntPtr _, ulong _2 ) + => true; + + private byte LoadTexFileExternDetour( IntPtr resourceHandle, int unk1, IntPtr unk2, bool unk3, IntPtr _ ) + => LoadTexFileLocal!.Invoke( resourceHandle, unk1, unk2, unk3 ); + + private byte LoadMdlFileExternDetour( IntPtr resourceHandle, IntPtr unk1, bool unk2, IntPtr _ ) + => LoadMdlFileLocal!.Invoke( resourceHandle, unk1, unk2 ); + + private unsafe void* GetResourceSyncHandler( + IntPtr pFileManager, + uint* pCategoryId, + char* pResourceType, + uint* pResourceHash, + char* pPath, + void* pUnknown + ) + => GetResourceHandler( true, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, false ); + + private unsafe void* GetResourceAsyncHandler( + IntPtr pFileManager, + uint* pCategoryId, + char* pResourceType, + uint* pResourceHash, + char* pPath, + void* pUnknown, + bool isUnknown + ) + => GetResourceHandler( false, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); + + private unsafe void* CallOriginalHandler( + bool isSync, + IntPtr pFileManager, + uint* pCategoryId, + char* pResourceType, + uint* pResourceHash, + char* pPath, + void* pUnknown, + bool isUnknown + ) + { + if( isSync ) { - Penumbra = penumbra; - Crc32 = new Crc32(); - } - - public unsafe void Init() - { - var readFileAddress = - Dalamud.SigScanner.ScanText( "E8 ?? ?? ?? ?? 84 C0 0F 84 ?? 00 00 00 4C 8B C3 BA 05" ); - GeneralUtil.PrintDebugAddress( "ReadFile", readFileAddress ); - - var readSqpackAddress = - Dalamud.SigScanner.ScanText( "E8 ?? ?? ?? ?? EB 05 E8 ?? ?? ?? ?? 84 C0 0F 84 ?? 00 00 00 4C 8B C3" ); - GeneralUtil.PrintDebugAddress( "ReadSqPack", readSqpackAddress ); - - var getResourceSyncAddress = - Dalamud.SigScanner.ScanText( "E8 ?? ?? 00 00 48 8D 8F ?? ?? 00 00 48 89 87 ?? ?? 00 00" ); - GeneralUtil.PrintDebugAddress( "GetResourceSync", getResourceSyncAddress ); - - var getResourceAsyncAddress = - Dalamud.SigScanner.ScanText( "E8 ?? ?? ?? 00 48 8B D8 EB ?? F0 FF 83 ?? ?? 00 00" ); - GeneralUtil.PrintDebugAddress( "GetResourceAsync", getResourceAsyncAddress ); - - - ReadSqpackHook = new Hook< ReadSqpackPrototype >( readSqpackAddress, ReadSqpackHandler ); - GetResourceSyncHook = new Hook< GetResourceSyncPrototype >( getResourceSyncAddress, GetResourceSyncHandler ); - GetResourceAsyncHook = new Hook< GetResourceAsyncPrototype >( getResourceAsyncAddress, GetResourceAsyncHandler ); - - ReadFile = Marshal.GetDelegateForFunctionPointer< ReadFilePrototype >( readFileAddress ); - } - - - private unsafe void* GetResourceSyncHandler( - IntPtr pFileManager, - uint* pCategoryId, - char* pResourceType, - uint* pResourceHash, - char* pPath, - void* pUnknown - ) - => GetResourceHandler( true, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, false ); - - private unsafe void* GetResourceAsyncHandler( - IntPtr pFileManager, - uint* pCategoryId, - char* pResourceType, - uint* pResourceHash, - char* pPath, - void* pUnknown, - bool isUnknown - ) - => GetResourceHandler( false, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); - - private unsafe void* CallOriginalHandler( - bool isSync, - IntPtr pFileManager, - uint* pCategoryId, - char* pResourceType, - uint* pResourceHash, - char* pPath, - void* pUnknown, - bool isUnknown - ) - { - if( isSync ) + if( GetResourceSyncHook == null ) { - if( GetResourceSyncHook == null ) - { - PluginLog.Error( "[GetResourceHandler] GetResourceSync is null." ); - return null; - } - - return GetResourceSyncHook.Original( pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown ); - } - - if( GetResourceAsyncHook == null ) - { - PluginLog.Error( "[GetResourceHandler] GetResourceAsync is null." ); + PluginLog.Error( "[GetResourceHandler] GetResourceSync is null." ); return null; } - return GetResourceAsyncHook.Original( pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); + return GetResourceSyncHook.Original( pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown ); } - private unsafe void* GetResourceHandler( - bool isSync, - IntPtr pFileManager, - uint* pCategoryId, - char* pResourceType, - uint* pResourceHash, - char* pPath, - void* pUnknown, - bool isUnknown - ) + if( GetResourceAsyncHook == null ) { - string file; - var modManager = Service< ModManager >.Get(); + PluginLog.Error( "[GetResourceHandler] GetResourceAsync is null." ); + return null; + } - if( !Penumbra.Config.IsEnabled || modManager == null ) + return GetResourceAsyncHook.Original( pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); + } + + private unsafe void* GetResourceHandler( + bool isSync, + IntPtr pFileManager, + uint* pCategoryId, + char* pResourceType, + uint* pResourceHash, + char* pPath, + void* pUnknown, + bool isUnknown + ) + { + string file; + var modManager = Service< ModManager >.Get(); + + if( !Penumbra.Config.IsEnabled || modManager == null ) + { + if( LogAllFiles ) { - if( LogAllFiles ) + file = Marshal.PtrToStringAnsi( new IntPtr( pPath ) )!; + if( LogFileFilter == null || LogFileFilter.IsMatch( file ) ) { - file = Marshal.PtrToStringAnsi( new IntPtr( pPath ) )!; - if( LogFileFilter == null || LogFileFilter.IsMatch( file ) ) - { - PluginLog.Information( "[GetResourceHandler] {0}", file ); - } + PluginLog.Information( "[GetResourceHandler] {0}", file ); } - - return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); } - file = Marshal.PtrToStringAnsi( new IntPtr( pPath ) )!; - var gameFsPath = GamePath.GenerateUncheckedLower( file ); - var replacementPath = modManager.ResolveSwappedOrReplacementPath( gameFsPath ); - if( LogAllFiles && ( LogFileFilter == null || LogFileFilter.IsMatch( file ) ) ) - { - PluginLog.Information( "[GetResourceHandler] {0}", file ); - } - - // path must be < 260 because statically defined array length :( - if( replacementPath == null ) - { - return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); - } - - var path = Encoding.ASCII.GetBytes( replacementPath ); - - var bPath = stackalloc byte[path.Length + 1]; - Marshal.Copy( path, 0, new IntPtr( bPath ), path.Length ); - pPath = ( char* )bPath; - - Crc32.Init(); - Crc32.Update( path ); - *pResourceHash = Crc32.Checksum; - - PluginLog.Verbose( "[GetResourceHandler] resolved {GamePath} to {NewPath}", gameFsPath, replacementPath ); - return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); } - - private unsafe byte ReadSqpackHandler( IntPtr pFileHandler, SeFileDescriptor* pFileDesc, int priority, bool isSync ) + file = Marshal.PtrToStringAnsi( new IntPtr( pPath ) )!; + var gameFsPath = GamePath.GenerateUncheckedLower( file ); + var replacementPath = modManager.ResolveSwappedOrReplacementPath( gameFsPath ); + if( LogAllFiles && ( LogFileFilter == null || LogFileFilter.IsMatch( file ) ) ) { - if( ReadFile == null || pFileDesc == null || pFileDesc->ResourceHandle == null ) - { - PluginLog.Error( "THIS SHOULD NOT HAPPEN" ); - return ReadSqpackHook?.Original( pFileHandler, pFileDesc, priority, isSync ) ?? 0; - } - - var gameFsPath = Marshal.PtrToStringAnsi( new IntPtr( pFileDesc->ResourceHandle->FileName() ) ); - - var isRooted = Path.IsPathRooted( gameFsPath ); - - if( gameFsPath == null || gameFsPath.Length >= 260 || !isRooted ) - { - return ReadSqpackHook?.Original( pFileHandler, pFileDesc, priority, isSync ) ?? 0; - } - - PluginLog.Debug( "loading modded file: {GameFsPath}", gameFsPath ); - - pFileDesc->FileMode = FileMode.LoadUnpackedResource; - - // note: must be utf16 - var utfPath = Encoding.Unicode.GetBytes( gameFsPath ); - - Marshal.Copy( utfPath, 0, new IntPtr( &pFileDesc->UtfFileName ), utfPath.Length ); - - var fd = stackalloc byte[0x20 + utfPath.Length + 0x16]; - Marshal.Copy( utfPath, 0, new IntPtr( fd + 0x21 ), utfPath.Length ); - - pFileDesc->FileDescriptor = fd; - - return ReadFile( pFileHandler, pFileDesc, priority, isSync ); + PluginLog.Information( "[GetResourceHandler] {0}", file ); } - public void Enable() + // path must be < 260 because statically defined array length :( + if( replacementPath == null ) { - if( IsEnabled ) - { - return; - } - - if( ReadSqpackHook == null || GetResourceSyncHook == null || GetResourceAsyncHook == null ) - { - PluginLog.Error( "[GetResourceHandler] Could not activate hooks because at least one was not set." ); - return; - } - - ReadSqpackHook.Enable(); - GetResourceSyncHook.Enable(); - GetResourceAsyncHook.Enable(); - - IsEnabled = true; + return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); } - public void Disable() + var path = Encoding.ASCII.GetBytes( replacementPath ); + + var bPath = stackalloc byte[path.Length + 1]; + Marshal.Copy( path, 0, new IntPtr( bPath ), path.Length ); + pPath = ( char* )bPath; + + Crc32.Init(); + Crc32.Update( path ); + *pResourceHash = Crc32.Checksum; + + PluginLog.Verbose( "[GetResourceHandler] resolved {GamePath} to {NewPath}", gameFsPath, replacementPath ); + + return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); + } + + + private unsafe byte ReadSqpackHandler( IntPtr pFileHandler, SeFileDescriptor* pFileDesc, int priority, bool isSync ) + { + if( ReadFile == null || pFileDesc == null || pFileDesc->ResourceHandle == null ) { - if( !IsEnabled ) - { - return; - } - - ReadSqpackHook?.Disable(); - GetResourceSyncHook?.Disable(); - GetResourceAsyncHook?.Disable(); - - IsEnabled = false; + PluginLog.Error( "THIS SHOULD NOT HAPPEN" ); + return ReadSqpackHook?.Original( pFileHandler, pFileDesc, priority, isSync ) ?? 0; } - public void Dispose() + var gameFsPath = Marshal.PtrToStringAnsi( new IntPtr( pFileDesc->ResourceHandle->FileName() ) ); + + var isRooted = Path.IsPathRooted( gameFsPath ); + + if( gameFsPath == null || gameFsPath.Length >= 260 || !isRooted ) { - Disable(); - ReadSqpackHook?.Dispose(); - GetResourceSyncHook?.Dispose(); - GetResourceAsyncHook?.Dispose(); + return ReadSqpackHook?.Original( pFileHandler, pFileDesc, priority, isSync ) ?? 0; } + + PluginLog.Debug( "loading modded file: {GameFsPath}", gameFsPath ); + + pFileDesc->FileMode = FileMode.LoadUnpackedResource; + + // note: must be utf16 + var utfPath = Encoding.Unicode.GetBytes( gameFsPath ); + + Marshal.Copy( utfPath, 0, new IntPtr( &pFileDesc->UtfFileName ), utfPath.Length ); + + var fd = stackalloc byte[0x20 + utfPath.Length + 0x16]; + Marshal.Copy( utfPath, 0, new IntPtr( fd + 0x21 ), utfPath.Length ); + + pFileDesc->FileDescriptor = fd; + + return ReadFile( pFileHandler, pFileDesc, priority, isSync ); + } + + public void Enable() + { + if( IsEnabled ) + { + return; + } + + if( ReadSqpackHook == null || GetResourceSyncHook == null || GetResourceAsyncHook == null || CheckFileStateHook == null || LoadTexFileExternHook == null || LoadMdlFileExternHook == null) + { + PluginLog.Error( "[GetResourceHandler] Could not activate hooks because at least one was not set." ); + return; + } + + ReadSqpackHook.Enable(); + GetResourceSyncHook.Enable(); + GetResourceAsyncHook.Enable(); + CheckFileStateHook.Enable(); + LoadTexFileExternHook.Enable(); + LoadMdlFileExternHook.Enable(); + + IsEnabled = true; + } + + public void Disable() + { + if( !IsEnabled ) + { + return; + } + + ReadSqpackHook?.Disable(); + GetResourceSyncHook?.Disable(); + GetResourceAsyncHook?.Disable(); + CheckFileStateHook?.Disable(); + LoadTexFileExternHook?.Disable(); + LoadMdlFileExternHook?.Disable(); + IsEnabled = false; + } + + public void Dispose() + { + Disable(); + ReadSqpackHook?.Dispose(); + GetResourceSyncHook?.Dispose(); + GetResourceAsyncHook?.Dispose(); + CheckFileStateHook?.Dispose(); + LoadTexFileExternHook?.Dispose(); + LoadMdlFileExternHook?.Dispose(); } } \ No newline at end of file diff --git a/Penumbra/Penumbra.json b/Penumbra/Penumbra.json index ec60e72c..85b613eb 100644 --- a/Penumbra/Penumbra.json +++ b/Penumbra/Penumbra.json @@ -7,7 +7,7 @@ "RepoUrl": "https://github.com/xivdev/Penumbra", "ApplicableVersion": "any", "Tags": [ "modding" ], - "DalamudApiLevel": 69420, + "DalamudApiLevel": 5, "LoadPriority": 69420, "IconUrl": "https://raw.githubusercontent.com/xivdev/Penumbra/master/images/icon.png" } \ No newline at end of file diff --git a/Penumbra/UI/MenuTabs/TabResourceManager.cs b/Penumbra/UI/MenuTabs/TabResourceManager.cs index 344c4d70..ecdaa87e 100644 --- a/Penumbra/UI/MenuTabs/TabResourceManager.cs +++ b/Penumbra/UI/MenuTabs/TabResourceManager.cs @@ -94,7 +94,7 @@ public partial class SettingsInterface using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem ); - var resourceHandler = *( ResourceManager** )( Dalamud.SigScanner.Module.BaseAddress + 0x1D93AC0 ); + var resourceHandler = *( ResourceManager** )( Dalamud.SigScanner.Module.BaseAddress + 0x1E5B440 ); if( resourceHandler == null ) {