Improve Imc Handling.

This commit is contained in:
Ottermandias 2024-06-18 17:52:34 +02:00
parent d7a8c9415b
commit 03d3c38ad5
16 changed files with 197 additions and 233 deletions

View file

@ -34,7 +34,7 @@ public sealed unsafe class CmpFile : MetaBaseFile
}
public CmpFile(MetaFileManager manager)
: base(manager, MetaIndex.HumanCmp)
: base(manager, manager.MarshalAllocator, MetaIndex.HumanCmp)
{
AllocateData(DefaultData.Length);
Reset();

View file

@ -87,7 +87,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
}
public ExpandedEqdpFile(MetaFileManager manager, GenderRace raceCode, bool accessory)
: base(manager, CharacterUtilityData.EqdpIdx(raceCode, accessory))
: base(manager, manager.MarshalAllocator, CharacterUtilityData.EqdpIdx(raceCode, accessory))
{
var def = (byte*)DefaultData.Data;
var blockSize = *(ushort*)(def + IdentifierSize);

View file

@ -76,7 +76,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
}
public ExpandedEqpGmpBase(MetaFileManager manager, bool gmp)
: base(manager, gmp ? MetaIndex.Gmp : MetaIndex.Eqp)
: base(manager, manager.MarshalAllocator, gmp ? MetaIndex.Gmp : MetaIndex.Eqp)
{
AllocateData(MaxSize);
Reset();

View file

@ -157,7 +157,7 @@ public sealed unsafe class EstFile : MetaBaseFile
}
public EstFile(MetaFileManager manager, EstType estType)
: base(manager, (MetaIndex)estType)
: base(manager, manager.MarshalAllocator, (MetaIndex)estType)
{
var length = DefaultData.Length;
AllocateData(length + IncreaseSize);

View file

@ -12,7 +12,7 @@ namespace Penumbra.Meta.Files;
/// Containing Flags in each byte, 0x01 set for Body, 0x02 set for Helmet.
/// Each flag corresponds to a mount row from the Mounts table and determines whether the mount disables the effect.
/// </summary>
public unsafe class EvpFile : MetaBaseFile
public unsafe class EvpFile(MetaFileManager manager) : MetaBaseFile(manager, manager.MarshalAllocator, (MetaIndex)1)
{
public const int FlagArraySize = 512;
@ -57,8 +57,4 @@ public unsafe class EvpFile : MetaBaseFile
return EvpFlag.None;
}
public EvpFile(MetaFileManager manager)
: base(manager, (MetaIndex)1) // TODO: Name
{ }
}

View file

@ -7,16 +7,10 @@ using Penumbra.String.Functions;
namespace Penumbra.Meta.Files;
public class ImcException : Exception
public class ImcException(ImcIdentifier identifier, Utf8GamePath path) : Exception
{
public readonly ImcIdentifier Identifier;
public readonly string GamePath;
public ImcException(ImcIdentifier identifier, Utf8GamePath path)
{
Identifier = identifier;
GamePath = path.ToString();
}
public readonly ImcIdentifier Identifier = identifier;
public readonly string GamePath = path.ToString();
public override string Message
=> "Could not obtain default Imc File.\n"
@ -146,7 +140,11 @@ public unsafe class ImcFile : MetaBaseFile
}
public ImcFile(MetaFileManager manager, ImcIdentifier identifier)
: base(manager, 0)
: this(manager, manager.MarshalAllocator, identifier)
{ }
public ImcFile(MetaFileManager manager, IFileAllocator alloc, ImcIdentifier identifier)
: base(manager, alloc, 0)
{
var path = identifier.GamePathString();
Path = Utf8GamePath.FromString(path, out var p) ? p : Utf8GamePath.Empty;
@ -194,7 +192,13 @@ public unsafe class ImcFile : MetaBaseFile
public void Replace(ResourceHandle* resource)
{
var (data, length) = resource->GetData();
var newData = Manager.AllocateDefaultMemory(ActualLength, 8);
if (length == ActualLength)
{
MemoryUtility.MemCpyUnchecked((byte*)data, Data, ActualLength);
return;
}
var newData = Manager.XivAllocator.Allocate(ActualLength, 8);
if (newData == null)
{
Penumbra.Log.Error($"Could not replace loaded IMC data at 0x{(ulong)resource:X}, allocation failed.");
@ -203,7 +207,7 @@ public unsafe class ImcFile : MetaBaseFile
MemoryUtility.MemCpyUnchecked(newData, Data, ActualLength);
Manager.Free(data, length);
resource->SetData((IntPtr)newData, ActualLength);
Manager.XivAllocator.Release((void*)data, length);
resource->SetData((nint)newData, ActualLength);
}
}

View file

@ -1,23 +1,75 @@
using Dalamud.Memory;
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.System.Memory;
using OtterGui.Services;
using Penumbra.GameData;
using Penumbra.Interop.Structs;
using Penumbra.String.Functions;
using CharacterUtility = Penumbra.Interop.Services.CharacterUtility;
namespace Penumbra.Meta.Files;
public unsafe class MetaBaseFile : IDisposable
public unsafe interface IFileAllocator
{
protected readonly MetaFileManager Manager;
public T* Allocate<T>(int length, int alignment = 1) where T : unmanaged;
public void Release<T>(ref T* pointer, int length) where T : unmanaged;
public void Release(void* pointer, int length)
{
var tmp = (byte*)pointer;
Release(ref tmp, length);
}
public byte* Allocate(int length, int alignment = 1)
=> Allocate<byte>(length, alignment);
}
public sealed class MarshalAllocator : IFileAllocator
{
public unsafe T* Allocate<T>(int length, int alignment = 1) where T : unmanaged
=> (T*)Marshal.AllocHGlobal(length * sizeof(T));
public unsafe void Release<T>(ref T* pointer, int length) where T : unmanaged
{
Marshal.FreeHGlobal((nint)pointer);
pointer = null;
}
}
public sealed unsafe class XivFileAllocator : IFileAllocator, IService
{
/// <summary>
/// Allocate in the games space for file storage.
/// We only need this if using any meta file.
/// </summary>
[Signature(Sigs.GetFileSpace)]
private readonly nint _getFileSpaceAddress = nint.Zero;
public XivFileAllocator(IGameInteropProvider provider)
=> provider.InitializeFromAttributes(this);
public IMemorySpace* GetFileSpace()
=> ((delegate* unmanaged<IMemorySpace*>)_getFileSpaceAddress)();
public T* Allocate<T>(int length, int alignment = 1) where T : unmanaged
=> (T*)GetFileSpace()->Malloc((ulong)(length * sizeof(T)), (ulong)alignment);
public void Release<T>(ref T* pointer, int length) where T : unmanaged
{
IMemorySpace.Free(pointer, (ulong)(length * sizeof(T)));
pointer = null;
}
}
public unsafe class MetaBaseFile(MetaFileManager manager, IFileAllocator alloc, MetaIndex idx) : IDisposable
{
protected readonly MetaFileManager Manager = manager;
protected readonly IFileAllocator Allocator = alloc;
public byte* Data { get; private set; }
public int Length { get; private set; }
public CharacterUtility.InternalIndex Index { get; }
public MetaBaseFile(MetaFileManager manager, MetaIndex idx)
{
Manager = manager;
Index = CharacterUtility.ReverseIndices[(int)idx];
}
public CharacterUtility.InternalIndex Index { get; } = CharacterUtility.ReverseIndices[(int)idx];
protected (IntPtr Data, int Length) DefaultData
=> Manager.CharacterUtility.DefaultResource(Index);
@ -30,7 +82,7 @@ public unsafe class MetaBaseFile : IDisposable
protected void AllocateData(int length)
{
Length = length;
Data = (byte*)Manager.AllocateFileMemory(length);
Data = Allocator.Allocate(length);
if (length > 0)
GC.AddMemoryPressure(length);
}
@ -38,8 +90,7 @@ public unsafe class MetaBaseFile : IDisposable
/// <summary> Free memory. </summary>
protected void ReleaseUnmanagedResources()
{
var ptr = (IntPtr)Data;
MemoryHelper.GameFree(ref ptr, (ulong)Length);
Allocator.Release(Data, Length);
if (Length > 0)
GC.RemoveMemoryPressure(Length);
@ -53,7 +104,7 @@ public unsafe class MetaBaseFile : IDisposable
if (newLength == Length)
return;
var data = (byte*)Manager.AllocateFileMemory((ulong)newLength);
var data = Allocator.Allocate(newLength);
if (newLength > Length)
{
MemoryUtility.MemCpyUnchecked(data, Data, Length);

View file

@ -1,14 +1,10 @@
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.System.Memory;
using OtterGui.Compression;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.GameData;
using Penumbra.GameData.Data;
using Penumbra.Import;
using Penumbra.Interop.Services;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Files;
using Penumbra.Mods;
using Penumbra.Mods.Groups;
@ -28,6 +24,9 @@ public unsafe class MetaFileManager
internal readonly ObjectIdentification Identifier;
internal readonly FileCompactor Compactor;
internal readonly ImcChecker ImcChecker;
internal readonly IFileAllocator MarshalAllocator = new MarshalAllocator();
internal readonly IFileAllocator XivAllocator;
public MetaFileManager(CharacterUtility characterUtility, ResidentResourceManager residentResources, IDataManager gameData,
ActiveCollectionData activeCollections, Configuration config, ValidityChecker validityChecker, ObjectIdentification identifier,
@ -42,6 +41,7 @@ public unsafe class MetaFileManager
Identifier = identifier;
Compactor = compactor;
ImcChecker = new ImcChecker(this);
XivAllocator = new XivFileAllocator(interop);
interop.InitializeFromAttributes(this);
}
@ -76,57 +76,11 @@ public unsafe class MetaFileManager
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void SetFile(MetaBaseFile? file, MetaIndex metaIndex)
{
if (file == null || !Config.EnableMods)
CharacterUtility.ResetResource(metaIndex);
else
CharacterUtility.SetResource(metaIndex, (nint)file.Data, file.Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public MetaList.MetaReverter TemporarilySetFile(MetaBaseFile? file, MetaIndex metaIndex)
=> Config.EnableMods
? file == null
? CharacterUtility.TemporarilyResetResource(metaIndex)
: CharacterUtility.TemporarilySetResource(metaIndex, (nint)file.Data, file.Length)
: MetaList.MetaReverter.Disabled;
public void ApplyDefaultFiles(ModCollection? collection)
{
if (ActiveCollections.Default != collection || !CharacterUtility.Ready || !Config.EnableMods)
return;
ResidentResources.Reload();
if (collection._cache == null)
CharacterUtility.ResetAll();
else
collection._cache.Meta.SetFiles();
}
/// <summary>
/// Allocate in the games space for file storage.
/// We only need this if using any meta file.
/// </summary>
[Signature(Sigs.GetFileSpace)]
private readonly nint _getFileSpaceAddress = nint.Zero;
public IMemorySpace* GetFileSpace()
=> ((delegate* unmanaged<IMemorySpace*>)_getFileSpaceAddress)();
public void* AllocateFileMemory(ulong length, ulong alignment = 0)
=> GetFileSpace()->Malloc(length, alignment);
public void* AllocateFileMemory(int length, int alignment = 0)
=> AllocateFileMemory((ulong)length, (ulong)alignment);
public void* AllocateDefaultMemory(ulong length, ulong alignment = 0)
=> GetFileSpace()->Malloc(length, alignment);
public void* AllocateDefaultMemory(int length, int alignment = 0)
=> IMemorySpace.GetDefaultSpace()->Malloc((ulong)length, (ulong)alignment);
public void Free(nint ptr, int length)
=> IMemorySpace.Free((void*)ptr, (ulong)length);
}