mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Fix IMC handling.
This commit is contained in:
parent
d07355c0f8
commit
d03a3168b0
8 changed files with 77 additions and 41 deletions
|
|
@ -19,13 +19,13 @@ public unsafe partial class ResourceLoader
|
|||
// Gather some debugging data about penumbra-loaded objects.
|
||||
public struct DebugData
|
||||
{
|
||||
public ResourceHandle* OriginalResource;
|
||||
public ResourceHandle* ManipulatedResource;
|
||||
public Utf8GamePath OriginalPath;
|
||||
public FullPath ManipulatedPath;
|
||||
public ResourceCategory Category;
|
||||
public object? ResolverInfo;
|
||||
public uint Extension;
|
||||
public Structs.ResourceHandle* OriginalResource;
|
||||
public Structs.ResourceHandle* ManipulatedResource;
|
||||
public Utf8GamePath OriginalPath;
|
||||
public FullPath ManipulatedPath;
|
||||
public ResourceCategory Category;
|
||||
public object? ResolverInfo;
|
||||
public uint Extension;
|
||||
}
|
||||
|
||||
private readonly SortedDictionary< FullPath, DebugData > _debugList = new();
|
||||
|
|
@ -44,7 +44,8 @@ public unsafe partial class ResourceLoader
|
|||
ResourceLoaded -= AddModifiedDebugInfo;
|
||||
}
|
||||
|
||||
private void AddModifiedDebugInfo( ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath, object? resolverInfo )
|
||||
private void AddModifiedDebugInfo( Structs.ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
|
||||
object? resolverInfo )
|
||||
{
|
||||
if( manipulatedPath == null )
|
||||
{
|
||||
|
|
@ -55,7 +56,7 @@ public unsafe partial class ResourceLoader
|
|||
var originalResource = FindResource( handle->Category, handle->FileType, crc );
|
||||
_debugList[ manipulatedPath.Value ] = new DebugData()
|
||||
{
|
||||
OriginalResource = originalResource,
|
||||
OriginalResource = ( Structs.ResourceHandle* )originalResource,
|
||||
ManipulatedResource = handle,
|
||||
Category = handle->Category,
|
||||
Extension = handle->FileType,
|
||||
|
|
@ -172,8 +173,8 @@ public unsafe partial class ResourceLoader
|
|||
{
|
||||
_deleteList.Add( ( data.ManipulatedPath, data with
|
||||
{
|
||||
OriginalResource = regularResource,
|
||||
ManipulatedResource = modifiedResource,
|
||||
OriginalResource = ( Structs.ResourceHandle* )regularResource,
|
||||
ManipulatedResource = ( Structs.ResourceHandle* )modifiedResource,
|
||||
} ) );
|
||||
}
|
||||
}
|
||||
|
|
@ -195,7 +196,7 @@ public unsafe partial class ResourceLoader
|
|||
private static void LogPath( Utf8GamePath path, bool synchronous )
|
||||
=> PluginLog.Information( $"[ResourceLoader] Requested {path} {( synchronous ? "synchronously." : "asynchronously." )}" );
|
||||
|
||||
private static void LogResource( ResourceHandle* handle, Utf8GamePath path, FullPath? manipulatedPath, object? _ )
|
||||
private static void LogResource( Structs.ResourceHandle* handle, Utf8GamePath path, FullPath? manipulatedPath, object? _ )
|
||||
{
|
||||
var pathString = manipulatedPath != null ? $"custom file {manipulatedPath} instead of {path}" : path.ToString();
|
||||
PluginLog.Information( $"[ResourceLoader] Loaded {pathString} to 0x{( ulong )handle:X}. (Refcount {handle->RefCount})" );
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public unsafe partial class ResourceLoader
|
|||
if( resolvedPath == null )
|
||||
{
|
||||
var retUnmodified = CallOriginalHandler( isSync, resourceManager, categoryId, resourceType, resourceHash, path, unk, isUnk );
|
||||
ResourceLoaded?.Invoke( retUnmodified, gamePath, null, data );
|
||||
ResourceLoaded?.Invoke( ( Structs.ResourceHandle* )retUnmodified, gamePath, null, data );
|
||||
return retUnmodified;
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ public unsafe partial class ResourceLoader
|
|||
*resourceHash = resolvedPath.Value.InternalName.Crc32;
|
||||
path = resolvedPath.Value.InternalName.Path;
|
||||
var retModified = CallOriginalHandler( isSync, resourceManager, categoryId, resourceType, resourceHash, path, unk, isUnk );
|
||||
ResourceLoaded?.Invoke( retModified, gamePath, resolvedPath.Value, data );
|
||||
ResourceLoaded?.Invoke( ( Structs.ResourceHandle* )retModified, gamePath, resolvedPath.Value, data );
|
||||
return retModified;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ public unsafe partial class ResourceLoader : IDisposable
|
|||
// Event fired whenever a resource is returned.
|
||||
// If the path was manipulated by penumbra, manipulatedPath will be the file path of the loaded resource.
|
||||
// resolveData is additional data returned by the current ResolvePath function and is user-defined.
|
||||
public delegate void ResourceLoadedDelegate( ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
|
||||
public delegate void ResourceLoadedDelegate( Structs.ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
|
||||
object? resolveData );
|
||||
|
||||
public event ResourceLoadedDelegate? ResourceLoaded;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
|
@ -33,11 +34,11 @@ public unsafe class ObjectReloader : IDisposable
|
|||
public static DrawState* ActorDrawState( GameObject actor )
|
||||
=> ( DrawState* )( actor.Address + 0x0104 );
|
||||
|
||||
private static delegate*< IntPtr, void > GetDisableDraw( GameObject actor )
|
||||
=> ( ( delegate*< IntPtr, void >** )actor.Address )[ 0 ][ 17 ];
|
||||
private static void DisableDraw( GameObject actor )
|
||||
=> ( ( delegate* unmanaged< IntPtr, void >** )actor.Address )[ 0 ][ 17 ]( actor.Address );
|
||||
|
||||
private static delegate*< IntPtr, void > GetEnableDraw( GameObject actor )
|
||||
=> ( ( delegate*< IntPtr, void >** )actor.Address )[ 0 ][ 16 ];
|
||||
private static void EnableDraw( GameObject actor )
|
||||
=> ( ( delegate* unmanaged< IntPtr, void >** )actor.Address )[ 0 ][ 16 ]( actor.Address );
|
||||
|
||||
public ObjectReloader( ModManager mods )
|
||||
=> _mods = mods;
|
||||
|
|
@ -57,14 +58,14 @@ public unsafe class ObjectReloader : IDisposable
|
|||
_changedSettings = false;
|
||||
}
|
||||
|
||||
private unsafe void WriteInvisible( GameObject actor, int actorIdx )
|
||||
private void WriteInvisible( GameObject actor, int actorIdx )
|
||||
{
|
||||
_currentObjectStartState = *ActorDrawState( actor );
|
||||
*ActorDrawState( actor ) |= DrawState.Invisibility;
|
||||
|
||||
if( _inGPose )
|
||||
{
|
||||
GetDisableDraw( actor )( actor.Address );
|
||||
DisableDraw( actor );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +96,7 @@ public unsafe class ObjectReloader : IDisposable
|
|||
|
||||
if( _inGPose )
|
||||
{
|
||||
GetEnableDraw( actor )( actor.Address );
|
||||
EnableDraw( actor );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||
|
||||
namespace Penumbra.Interop.Structs;
|
||||
|
||||
|
|
@ -40,18 +41,30 @@ public unsafe struct ResourceHandle
|
|||
[FieldOffset( 0x00 )]
|
||||
public void** VTable;
|
||||
|
||||
[FieldOffset( 0x08 )]
|
||||
public ResourceCategory Category;
|
||||
|
||||
[FieldOffset( 0x0C )]
|
||||
public uint FileType;
|
||||
|
||||
[FieldOffset( 0x10 )]
|
||||
public uint Id;
|
||||
|
||||
[FieldOffset( 0x48 )]
|
||||
public byte* FileNameData;
|
||||
|
||||
[FieldOffset( 0x58 )]
|
||||
public int FileNameLength;
|
||||
|
||||
[FieldOffset( 0xAC )]
|
||||
public uint RefCount;
|
||||
|
||||
// May return null.
|
||||
public static byte* GetData( ResourceHandle* handle )
|
||||
=> ( ( delegate*< ResourceHandle*, byte* > )handle->VTable[ 23 ] )( handle );
|
||||
=> ( ( delegate* unmanaged< ResourceHandle*, byte* > )handle->VTable[ 23 ] )( handle );
|
||||
|
||||
public static ulong GetLength( ResourceHandle* handle )
|
||||
=> ( ( delegate*< ResourceHandle*, ulong > )handle->VTable[ 17 ] )( handle );
|
||||
=> ( ( delegate* unmanaged< ResourceHandle*, ulong > )handle->VTable[ 17 ] )( handle );
|
||||
|
||||
|
||||
// Only use these if you know what you are doing.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Numerics;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Memory;
|
||||
|
|
@ -8,7 +7,6 @@ using Penumbra.GameData.ByteString;
|
|||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Util;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Meta.Files;
|
||||
|
||||
|
|
@ -68,8 +66,9 @@ public unsafe class ImcFile : MetaBaseFile
|
|||
public int Count
|
||||
=> CountInternal( Data );
|
||||
|
||||
public readonly int NumParts;
|
||||
public readonly Utf8GamePath Path;
|
||||
public readonly int NumParts;
|
||||
public bool ChangesSinceLoad = true;
|
||||
|
||||
private static int CountInternal( byte* data )
|
||||
=> *( ushort* )data;
|
||||
|
|
@ -179,7 +178,8 @@ public unsafe class ImcFile : MetaBaseFile
|
|||
return false;
|
||||
}
|
||||
|
||||
*variantPtr = entry;
|
||||
*variantPtr = entry;
|
||||
ChangesSinceLoad = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -192,6 +192,8 @@ public unsafe class ImcFile : MetaBaseFile
|
|||
Functions.MemCpyUnchecked( Data, ptr, file.Data.Length );
|
||||
Functions.MemSet( Data + file.Data.Length, 0, Length - file.Data.Length );
|
||||
}
|
||||
|
||||
ChangesSinceLoad = true;
|
||||
}
|
||||
|
||||
public ImcFile( Utf8GamePath path )
|
||||
|
|
@ -209,9 +211,8 @@ public unsafe class ImcFile : MetaBaseFile
|
|||
fixed( byte* ptr = file.Data )
|
||||
{
|
||||
NumParts = BitOperations.PopCount( *( ushort* )( ptr + 2 ) );
|
||||
AllocateData( file.Data.Length + sizeof( ImcEntry ) * 100 * NumParts );
|
||||
AllocateData( file.Data.Length );
|
||||
Functions.MemCpyUnchecked( Data, ptr, file.Data.Length );
|
||||
Functions.MemSet( Data + file.Data.Length, 0, sizeof( ImcEntry ) * 100 * NumParts );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -239,6 +240,7 @@ public unsafe class ImcFile : MetaBaseFile
|
|||
}
|
||||
|
||||
var requiredLength = ActualLength;
|
||||
resource->SetData( (IntPtr) Data, Length );
|
||||
if( length >= requiredLength )
|
||||
{
|
||||
Functions.MemCpyUnchecked( ( void* )data, Data, requiredLength );
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Dalamud.Logging;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.Interop.Loader;
|
||||
|
|
@ -57,7 +58,6 @@ public partial class MetaManager
|
|||
|
||||
public unsafe bool ApplyMod( ImcManipulation m, Mod.Mod mod )
|
||||
{
|
||||
const uint imcExt = 0x00696D63;
|
||||
#if USE_IMC
|
||||
if( !Manipulations.TryAdd( m, mod ) )
|
||||
{
|
||||
|
|
@ -82,12 +82,6 @@ public partial class MetaManager
|
|||
_collection.Cache.ResolvedFiles[ path ] = fullPath;
|
||||
}
|
||||
|
||||
var resource = ResourceLoader.FindResource( ResourceCategory.Chara, imcExt, ( uint )path.Path.Crc32 );
|
||||
if( resource != null )
|
||||
{
|
||||
file.Replace( ( ResourceHandle* )resource );
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
|
|
@ -109,22 +103,25 @@ public partial class MetaManager
|
|||
[Conditional( "USE_IMC" )]
|
||||
private unsafe void SetupDelegate()
|
||||
{
|
||||
Penumbra.ResourceLoader.ResourceLoadCustomization = ImcHandler;
|
||||
Penumbra.ResourceLoader.ResourceLoadCustomization = ImcLoadHandler;
|
||||
Penumbra.ResourceLoader.ResourceLoaded += ImcResourceHandler;
|
||||
}
|
||||
|
||||
[Conditional( "USE_IMC" )]
|
||||
private unsafe void RestoreDelegate()
|
||||
{
|
||||
if( Penumbra.ResourceLoader.ResourceLoadCustomization == ImcHandler )
|
||||
if( Penumbra.ResourceLoader.ResourceLoadCustomization == ImcLoadHandler )
|
||||
{
|
||||
Penumbra.ResourceLoader.ResourceLoadCustomization = _previousDelegate;
|
||||
}
|
||||
|
||||
Penumbra.ResourceLoader.ResourceLoaded -= ImcResourceHandler;
|
||||
}
|
||||
|
||||
private FullPath CreateImcPath( Utf8GamePath path )
|
||||
=> new($"|{_collection.Name}|{path}");
|
||||
|
||||
private static unsafe byte ImcHandler( Utf8GamePath gamePath, ResourceManager* resourceManager,
|
||||
private static unsafe byte ImcLoadHandler( Utf8GamePath gamePath, ResourceManager* resourceManager,
|
||||
SeFileDescriptor* fileDescriptor, int priority, bool isSync )
|
||||
{
|
||||
var split = gamePath.Path.Split( ( byte )'|', 2, true );
|
||||
|
|
@ -137,12 +134,33 @@ public partial class MetaManager
|
|||
&& collection.Cache.MetaManipulations.Imc.Files.TryGetValue(
|
||||
Utf8GamePath.FromSpan( split[ 1 ].Span, out var p, false ) ? p : Utf8GamePath.Empty, out var file ) )
|
||||
{
|
||||
PluginLog.Debug( "Loaded {GamePath:l} from file and replaced with IMC from collection {Collection:l}.", gamePath,
|
||||
collection.Name );
|
||||
file.Replace( fileDescriptor->ResourceHandle );
|
||||
file.ChangesSinceLoad = false;
|
||||
}
|
||||
|
||||
fileDescriptor->ResourceHandle->FileNameData = gamePath.Path.Path;
|
||||
fileDescriptor->ResourceHandle->FileNameLength = gamePath.Path.Length;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static unsafe void ImcResourceHandler( ResourceHandle* resource, Utf8GamePath gamePath, FullPath? _2, object? resolveData )
|
||||
{
|
||||
// Only check imcs.
|
||||
if( resource->FileType != 0x00696D63
|
||||
|| resolveData is not ModCollection collection
|
||||
|| collection.Cache == null
|
||||
|| !collection.Cache.MetaManipulations.Imc.Files.TryGetValue( gamePath, out var file )
|
||||
|| !file.ChangesSinceLoad )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PluginLog.Debug( "File {GamePath:l} was already loaded but IMC in collection {Collection:l} was changed, so reloaded.", gamePath,
|
||||
collection.Name );
|
||||
file.Replace( resource );
|
||||
file.ChangesSinceLoad = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
@ -25,7 +26,7 @@ public class CollectionManager
|
|||
private readonly ModManager _manager;
|
||||
|
||||
public string CollectionChangedTo { get; private set; } = string.Empty;
|
||||
public Dictionary< string, ModCollection > Collections { get; } = new();
|
||||
public Dictionary< string, ModCollection > Collections { get; } = new(StringComparer.InvariantCultureIgnoreCase);
|
||||
public Dictionary< string, ModCollection > CharacterCollection { get; } = new();
|
||||
|
||||
public ModCollection CurrentCollection { get; private set; } = ModCollection.Empty;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue