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 )
{