mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Update for 6.0
This commit is contained in:
parent
3bcd7a44da
commit
1d254e5856
7 changed files with 280 additions and 302 deletions
|
|
@ -9,9 +9,9 @@ namespace Penumbra.GameData.Structs
|
||||||
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
|
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
|
||||||
public class CharacterEquipment
|
public class CharacterEquipment
|
||||||
{
|
{
|
||||||
public const int MainWeaponOffset = 0x0F08;
|
public const int MainWeaponOffset = 0x0C78;
|
||||||
public const int OffWeaponOffset = 0x0F70;
|
public const int OffWeaponOffset = 0x0CE0;
|
||||||
public const int EquipmentOffset = 0x1040;
|
public const int EquipmentOffset = 0xDB0;
|
||||||
public const int EquipmentSlots = 10;
|
public const int EquipmentSlots = 10;
|
||||||
public const int WeaponSlots = 2;
|
public const int WeaponSlots = 2;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ namespace Penumbra.PlayerWatch
|
||||||
{
|
{
|
||||||
public static class CharacterFactory
|
public static class CharacterFactory
|
||||||
{
|
{
|
||||||
private static ConstructorInfo? _characterConstructor = null;
|
private static ConstructorInfo? _characterConstructor;
|
||||||
|
|
||||||
private static void Initialize()
|
private static void Initialize()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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 );
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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?
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// 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 <see cref="RefGet{TObject,TFieldType}"/> to access a ref to a field on an object without invoking any
|
|
||||||
/// unsafe code too.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="fieldName">The name of the field to grab a reference to</param>
|
|
||||||
/// <typeparam name="TObject">The object that holds the field</typeparam>
|
|
||||||
/// <typeparam name="TField">The type of the underlying field</typeparam>
|
|
||||||
/// <returns>A delegate that will return a reference to a particular field - zero copy</returns>
|
|
||||||
/// <exception cref="MissingFieldException"></exception>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -45,7 +45,7 @@ namespace Penumbra.Interop
|
||||||
var module = Dalamud.SigScanner.Module.BaseAddress.ToInt64();
|
var module = Dalamud.SigScanner.Module.BaseAddress.ToInt64();
|
||||||
var loadPlayerResourcesAddress =
|
var loadPlayerResourcesAddress =
|
||||||
Dalamud.SigScanner.ScanText(
|
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 );
|
GeneralUtil.PrintDebugAddress( "LoadPlayerResources", loadPlayerResourcesAddress );
|
||||||
|
|
||||||
var unloadPlayerResourcesAddress =
|
var unloadPlayerResourcesAddress =
|
||||||
|
|
|
||||||
|
|
@ -11,262 +11,321 @@ using Penumbra.Structs;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
using FileMode = Penumbra.Structs.FileMode;
|
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
|
var getResourceAsyncAddress =
|
||||||
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
Dalamud.SigScanner.ScanText( "E8 ?? ?? ?? 00 48 8B D8 EB ?? F0 FF 83 ?? ?? 00 00" );
|
||||||
public unsafe delegate byte ReadFilePrototype( IntPtr pFileHandler, SeFileDescriptor* pFileDesc, int priority, bool isSync );
|
GeneralUtil.PrintDebugAddress( "GetResourceAsync", getResourceAsyncAddress );
|
||||||
|
|
||||||
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
var checkFileStateAddress = Dalamud.SigScanner.ScanText( "E8 ?? ?? ?? ?? 48 85 c0 74 ?? 45 0f b6 ce 48 89 44 24" );
|
||||||
public unsafe delegate byte ReadSqpackPrototype( IntPtr pFileHandler, SeFileDescriptor* pFileDesc, int priority, bool isSync );
|
GeneralUtil.PrintDebugAddress( "CheckFileState", checkFileStateAddress );
|
||||||
|
|
||||||
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
var loadTexFileLocalAddress =
|
||||||
public unsafe delegate void* GetResourceSyncPrototype( IntPtr pFileManager, uint* pCategoryId, char* pResourceType
|
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" );
|
||||||
, uint* pResourceHash, char* pPath, void* pUnknown );
|
GeneralUtil.PrintDebugAddress( "LoadTexFileLocal", loadTexFileLocalAddress );
|
||||||
|
|
||||||
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
|
var loadTexFileExternAddress =
|
||||||
public unsafe delegate void* GetResourceAsyncPrototype( IntPtr pFileManager, uint* pCategoryId, char* pResourceType
|
Dalamud.SigScanner.ScanText( "E8 ?? ?? ?? ?? 0F B6 E8 48 8B CB E8" );
|
||||||
, uint* pResourceHash, char* pPath, void* pUnknown, bool isUnknown );
|
GeneralUtil.PrintDebugAddress( "LoadTexFileExtern", loadTexFileExternAddress );
|
||||||
|
|
||||||
// Hooks
|
var loadMdlFileLocalAddress =
|
||||||
public Hook< GetResourceSyncPrototype >? GetResourceSyncHook { get; private set; }
|
Dalamud.SigScanner.ScanText( "40 55 53 56 57 41 56 41 57 48 8D 6C 24 D1 48 81 EC 98 00 00 00" );
|
||||||
public Hook< GetResourceAsyncPrototype >? GetResourceAsyncHook { get; private set; }
|
GeneralUtil.PrintDebugAddress( "LoadMdlFileLocal", loadMdlFileLocalAddress );
|
||||||
public Hook< ReadSqpackPrototype >? ReadSqpackHook { get; private set; }
|
|
||||||
|
|
||||||
// Unmanaged functions
|
var loadMdlFileExternAddress =
|
||||||
public ReadFilePrototype? ReadFile { get; private set; }
|
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;
|
ReadFile = Marshal.GetDelegateForFunctionPointer< ReadFilePrototype >( readFileAddress );
|
||||||
public Regex? LogFileFilter = null;
|
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;
|
if( GetResourceSyncHook == null )
|
||||||
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 )
|
PluginLog.Error( "[GetResourceHandler] GetResourceSync is 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." );
|
|
||||||
return 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(
|
if( GetResourceAsyncHook == null )
|
||||||
bool isSync,
|
|
||||||
IntPtr pFileManager,
|
|
||||||
uint* pCategoryId,
|
|
||||||
char* pResourceType,
|
|
||||||
uint* pResourceHash,
|
|
||||||
char* pPath,
|
|
||||||
void* pUnknown,
|
|
||||||
bool isUnknown
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
string file;
|
PluginLog.Error( "[GetResourceHandler] GetResourceAsync is null." );
|
||||||
var modManager = Service< ModManager >.Get();
|
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 ) )!;
|
PluginLog.Information( "[GetResourceHandler] {0}", file );
|
||||||
if( LogFileFilter == null || LogFileFilter.IsMatch( 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 );
|
return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file = Marshal.PtrToStringAnsi( new IntPtr( pPath ) )!;
|
||||||
private unsafe byte ReadSqpackHandler( IntPtr pFileHandler, SeFileDescriptor* pFileDesc, int priority, bool isSync )
|
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.Information( "[GetResourceHandler] {0}", file );
|
||||||
{
|
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Enable()
|
// path must be < 260 because statically defined array length :(
|
||||||
|
if( replacementPath == null )
|
||||||
{
|
{
|
||||||
if( IsEnabled )
|
return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown );
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 )
|
PluginLog.Error( "THIS SHOULD NOT HAPPEN" );
|
||||||
{
|
return ReadSqpackHook?.Original( pFileHandler, pFileDesc, priority, isSync ) ?? 0;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadSqpackHook?.Disable();
|
|
||||||
GetResourceSyncHook?.Disable();
|
|
||||||
GetResourceAsyncHook?.Disable();
|
|
||||||
|
|
||||||
IsEnabled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
return ReadSqpackHook?.Original( pFileHandler, pFileDesc, priority, isSync ) ?? 0;
|
||||||
ReadSqpackHook?.Dispose();
|
|
||||||
GetResourceSyncHook?.Dispose();
|
|
||||||
GetResourceAsyncHook?.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
"RepoUrl": "https://github.com/xivdev/Penumbra",
|
"RepoUrl": "https://github.com/xivdev/Penumbra",
|
||||||
"ApplicableVersion": "any",
|
"ApplicableVersion": "any",
|
||||||
"Tags": [ "modding" ],
|
"Tags": [ "modding" ],
|
||||||
"DalamudApiLevel": 69420,
|
"DalamudApiLevel": 5,
|
||||||
"LoadPriority": 69420,
|
"LoadPriority": 69420,
|
||||||
"IconUrl": "https://raw.githubusercontent.com/xivdev/Penumbra/master/images/icon.png"
|
"IconUrl": "https://raw.githubusercontent.com/xivdev/Penumbra/master/images/icon.png"
|
||||||
}
|
}
|
||||||
|
|
@ -94,7 +94,7 @@ public partial class SettingsInterface
|
||||||
|
|
||||||
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem );
|
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 )
|
if( resourceHandler == null )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue