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

@ -36,7 +36,7 @@ public sealed class CollectionCache : IDisposable
=> ConflictDict.Values;
public SingleArray<ModConflicts> Conflicts(IMod mod)
=> ConflictDict.TryGetValue(mod, out SingleArray<ModConflicts> c) ? c : new SingleArray<ModConflicts>();
=> ConflictDict.TryGetValue(mod, out var c) ? c : new SingleArray<ModConflicts>();
private int _changedItemsSaveCounter = -1;
@ -125,12 +125,6 @@ public sealed class CollectionCache : IDisposable
return ret;
}
public void ForceFile(Utf8GamePath path, FullPath fullPath)
=> _manager.AddChange(ChangeData.ForcedFile(this, path, fullPath));
public void RemovePath(Utf8GamePath path)
=> _manager.AddChange(ChangeData.ForcedFile(this, path, FullPath.Empty));
public void ReloadMod(IMod mod, bool addMetaChanges)
=> _manager.AddChange(ChangeData.ModReload(this, mod, addMetaChanges));
@ -251,9 +245,6 @@ public sealed class CollectionCache : IDisposable
if (addMetaChanges)
{
_collection.IncrementCounter();
if (mod.TotalManipulations > 0)
AddMetaFiles(false);
_manager.MetaFileManager.ApplyDefaultFiles(_collection);
}
}
@ -408,11 +399,6 @@ public sealed class CollectionCache : IDisposable
}
// Add all necessary meta file redirects.
public void AddMetaFiles(bool fromFullCompute)
=> Meta.SetImcFiles(fromFullCompute);
// Identify and record all manipulated objects for this entire collection.
private void SetChangedItems()
{

View file

@ -180,8 +180,6 @@ public class CollectionCacheManager : IDisposable
foreach (var mod in _modStorage)
cache.AddModSync(mod, false);
cache.AddMetaFiles(true);
collection.IncrementCounter();
MetaFileManager.ApplyDefaultFiles(collection);

View file

@ -1,20 +1,22 @@
using Penumbra.GameData.Structs;
using Penumbra.Interop.PathResolving;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.String.Classes;
using Penumbra.String;
namespace Penumbra.Collections.Cache;
public sealed class ImcCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase<ImcIdentifier, ImcEntry>(manager, collection)
{
private readonly Dictionary<Utf8GamePath, (ImcFile, HashSet<ImcIdentifier>)> _imcFiles = [];
private readonly Dictionary<ByteString, (ImcFile, HashSet<ImcIdentifier>)> _imcFiles = [];
public override void SetFiles()
=> SetFiles(false);
{ }
public bool GetFile(Utf8GamePath path, [NotNullWhen(true)] out ImcFile? file)
public bool HasFile(ByteString path)
=> _imcFiles.ContainsKey(path);
public bool GetFile(ByteString path, [NotNullWhen(true)] out ImcFile? file)
{
if (!_imcFiles.TryGetValue(path, out var p))
{
@ -26,56 +28,31 @@ public sealed class ImcCache(MetaFileManager manager, ModCollection collection)
return true;
}
public void SetFiles(bool fromFullCompute)
{
if (fromFullCompute)
foreach (var (path, _) in _imcFiles)
Collection._cache!.ForceFileSync(path, PathDataHandler.CreateImc(path.Path, Collection));
else
foreach (var (path, _) in _imcFiles)
Collection._cache!.ForceFile(path, PathDataHandler.CreateImc(path.Path, Collection));
}
public void ResetFiles()
{
foreach (var (path, _) in _imcFiles)
Collection._cache!.ForceFile(path, FullPath.Empty);
}
protected override void IncorporateChangesInternal()
{
if (!Manager.CharacterUtility.Ready)
return;
foreach (var (identifier, (_, entry)) in this)
ApplyFile(identifier, entry);
Penumbra.Log.Verbose($"{Collection.AnonymizedName}: Loaded {Count} delayed IMC manipulations.");
}
{ }
public void Reset()
{
foreach (var (path, (file, set)) in _imcFiles)
foreach (var (_, (file, set)) in _imcFiles)
{
Collection._cache!.RemovePath(path);
file.Reset();
set.Clear();
}
_imcFiles.Clear();
Clear();
}
protected override void ApplyModInternal(ImcIdentifier identifier, ImcEntry entry)
{
++Collection.ImcChangeCounter;
if (Manager.CharacterUtility.Ready)
ApplyFile(identifier, entry);
ApplyFile(identifier, entry);
}
private void ApplyFile(ImcIdentifier identifier, ImcEntry entry)
{
var path = identifier.GamePath();
var path = identifier.GamePath().Path;
try
{
if (!_imcFiles.TryGetValue(path, out var pair))
@ -87,8 +64,6 @@ public sealed class ImcCache(MetaFileManager manager, ModCollection collection)
pair.Item2.Add(identifier);
_imcFiles[path] = pair;
var fullPath = PathDataHandler.CreateImc(pair.Item1.Path.Path, Collection);
Collection._cache!.ForceFile(path, fullPath);
}
catch (ImcException e)
{
@ -104,7 +79,7 @@ public sealed class ImcCache(MetaFileManager manager, ModCollection collection)
protected override void RevertModInternal(ImcIdentifier identifier)
{
++Collection.ImcChangeCounter;
var path = identifier.GamePath();
var path = identifier.GamePath().Path;
if (!_imcFiles.TryGetValue(path, out var pair))
return;
@ -114,17 +89,12 @@ public sealed class ImcCache(MetaFileManager manager, ModCollection collection)
if (pair.Item2.Count == 0)
{
_imcFiles.Remove(path);
Collection._cache!.ForceFile(pair.Item1.Path, FullPath.Empty);
pair.Item1.Dispose();
return;
}
var def = ImcFile.GetDefault(Manager, pair.Item1.Path, identifier.EquipSlot, identifier.Variant, out _);
if (!Apply(pair.Item1, identifier, def))
return;
var fullPath = PathDataHandler.CreateImc(pair.Item1.Path.Path, Collection);
Collection._cache!.ForceFile(pair.Item1.Path, fullPath);
Apply(pair.Item1, identifier, def);
}
public static bool Apply(ImcFile file, ImcIdentifier identifier, ImcEntry entry)

View file

@ -36,7 +36,7 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
Est.SetFiles();
Gmp.SetFiles();
Rsp.SetFiles();
Imc.SetFiles(false);
Imc.SetFiles();
}
public void Reset()
@ -113,13 +113,9 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
~MetaCache()
=> Dispose();
/// <summary> Set the currently relevant IMC files for the collection cache. </summary>
public void SetImcFiles(bool fromFullCompute)
=> Imc.SetFiles(fromFullCompute);
/// <summary> Try to obtain a manipulated IMC file. </summary>
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file)
=> Imc.GetFile(path, out file);
=> Imc.GetFile(path.Path, out file);
internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, PrimaryId primaryId)
=> Eqdp.ApplyFullEntry(primaryId, race, accessory, Meta.Files.ExpandedEqdpFile.GetDefault(manager, race, accessory, primaryId));

View file

@ -1,11 +1,8 @@
using System.Runtime;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Interop.ResourceLoading;
using Penumbra.Interop.Structs;
using Penumbra.String;
using Penumbra.String.Classes;
using Penumbra.Util;
@ -27,24 +24,16 @@ public class PathResolver : IDisposable
public unsafe PathResolver(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ResourceLoader loader,
SubfileHelper subfileHelper, PathState pathState, MetaState metaState, CollectionResolver collectionResolver, GameState gameState)
{
_performance = performance;
_config = config;
_collectionManager = collectionManager;
_subfileHelper = subfileHelper;
_pathState = pathState;
_metaState = metaState;
_gameState = gameState;
_collectionResolver = collectionResolver;
_loader = loader;
_loader.ResolvePath = ResolvePath;
_loader.FileLoaded += ImcLoadResource;
}
/// <summary> Obtain a temporary or permanent collection by local ID. </summary>
public bool CollectionByLocalId(LocalCollectionId id, out ModCollection collection)
{
collection = _collectionManager.Storage.ByLocalId(id);
return collection != ModCollection.Empty;
_performance = performance;
_config = config;
_collectionManager = collectionManager;
_subfileHelper = subfileHelper;
_pathState = pathState;
_metaState = metaState;
_gameState = gameState;
_collectionResolver = collectionResolver;
_loader = loader;
_loader.ResolvePath = ResolvePath;
}
/// <summary> Try to resolve the given game path to the replaced path. </summary>
@ -120,7 +109,6 @@ public class PathResolver : IDisposable
public unsafe void Dispose()
{
_loader.ResetResolvePath();
_loader.FileLoaded -= ImcLoadResource;
}
/// <summary> Use the default method of path replacement. </summary>
@ -130,24 +118,6 @@ public class PathResolver : IDisposable
return (resolved, _collectionManager.Active.Default.ToResolveData());
}
/// <summary> After loading an IMC file, replace its contents with the modded IMC file. </summary>
private unsafe void ImcLoadResource(ResourceHandle* resource, ByteString path, bool returnValue, bool custom,
ReadOnlySpan<byte> additionalData)
{
if (resource->FileType != ResourceType.Imc
|| !PathDataHandler.Read(additionalData, out var data)
|| data.Discriminator != PathDataHandler.Discriminator
|| !Utf8GamePath.FromByteString(path, out var gamePath)
|| !CollectionByLocalId(data.Collection, out var collection)
|| !collection.HasCache
|| !collection.GetImcFile(gamePath, out var file))
return;
file.Replace(resource);
Penumbra.Log.Verbose(
$"[ResourceLoader] Loaded {gamePath} from file and replaced with IMC from collection {collection.AnonymizedName}.");
}
/// <summary> Resolve a path from the interface collection. </summary>
private (FullPath?, ResolveData) ResolveUi(Utf8GamePath path)
=> (_collectionManager.Active.Interface.ResolvePath(path),

View file

@ -70,14 +70,15 @@ public sealed unsafe class SubfileHelper : IDisposable, IReadOnlyCollection<KeyV
public static void HandleCollection(ResolveData resolveData, ByteString path, bool nonDefault, ResourceType type, FullPath? resolved,
Utf8GamePath originalPath, out (FullPath?, ResolveData) data)
{
if (nonDefault)
resolved = type switch
{
ResourceType.Mtrl => PathDataHandler.CreateMtrl(path, resolveData.ModCollection, originalPath),
ResourceType.Avfx => PathDataHandler.CreateAvfx(path, resolveData.ModCollection),
ResourceType.Tmb => PathDataHandler.CreateTmb(path, resolveData.ModCollection),
_ => resolved,
};
resolved = type switch
{
ResourceType.Mtrl when nonDefault => PathDataHandler.CreateMtrl(path, resolveData.ModCollection, originalPath),
ResourceType.Avfx when nonDefault => PathDataHandler.CreateAvfx(path, resolveData.ModCollection),
ResourceType.Tmb when nonDefault => PathDataHandler.CreateTmb(path, resolveData.ModCollection),
ResourceType.Imc when resolveData.ModCollection.MetaCache?.Imc.HasFile(path) ?? false => PathDataHandler.CreateImc(path,
resolveData.ModCollection),
_ => resolved,
};
data = (resolved, resolveData);
}

View file

@ -1,6 +1,9 @@
using System.Collections.Frozen;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
using OtterGui.Services;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Interop.PathResolving;
using Penumbra.Interop.SafeHandles;
using Penumbra.Interop.Structs;
@ -10,6 +13,72 @@ using FileMode = Penumbra.Interop.Structs.FileMode;
namespace Penumbra.Interop.ResourceLoading;
public interface IFilePostProcessor : IService
{
public ResourceType Type { get; }
public unsafe void PostProcess(ResourceHandle* resource, ByteString originalGamePath, ReadOnlySpan<byte> additionalData);
}
public sealed class MaterialFilePostProcessor : IFilePostProcessor
{
public ResourceType Type
=> ResourceType.Mtrl;
public unsafe void PostProcess(ResourceHandle* resource, ByteString originalGamePath, ReadOnlySpan<byte> additionalData)
{
if (!PathDataHandler.ReadMtrl(additionalData, out var data))
return;
}
}
public sealed class ImcFilePostProcessor(CollectionStorage collections) : IFilePostProcessor
{
public ResourceType Type
=> ResourceType.Imc;
public unsafe void PostProcess(ResourceHandle* resource, ByteString originalGamePath, ReadOnlySpan<byte> additionalData)
{
if (!PathDataHandler.Read(additionalData, out var data) || data.Discriminator != PathDataHandler.Discriminator)
return;
var collection = collections.ByLocalId(data.Collection);
if (collection.MetaCache is not { } cache)
return;
if (!cache.Imc.GetFile(originalGamePath, out var file))
return;
file.Replace(resource);
Penumbra.Log.Information(
$"[ResourceLoader] Loaded {originalGamePath} from file and replaced with IMC from collection {collection.AnonymizedName}.");
}
}
public unsafe class FilePostProcessService : IRequiredService, IDisposable
{
private readonly ResourceLoader _resourceLoader;
private readonly FrozenDictionary<ResourceType, IFilePostProcessor> _processors;
public FilePostProcessService(ResourceLoader resourceLoader, ServiceManager services)
{
_resourceLoader = resourceLoader;
_processors = services.GetServicesImplementing<IFilePostProcessor>().ToFrozenDictionary(s => s.Type, s => s);
_resourceLoader.FileLoaded += OnFileLoaded;
}
public void Dispose()
{
_resourceLoader.FileLoaded -= OnFileLoaded;
}
private void OnFileLoaded(ResourceHandle* resource, ByteString path, bool returnValue, bool custom,
ReadOnlySpan<byte> additionalData)
{
if (_processors.TryGetValue(resource->FileType, out var processor))
processor.PostProcess(resource, path, additionalData);
}
}
public unsafe class ResourceLoader : IDisposable
{
private readonly ResourceService _resources;

View file

@ -1,6 +1,5 @@
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using Penumbra.Collections.Manager;
using Penumbra.GameData;
using Penumbra.Interop.Structs;
@ -53,17 +52,15 @@ public unsafe class CharacterUtility : IDisposable
public (nint Address, int Size) DefaultResource(InternalIndex idx)
=> _lists[idx.Value].DefaultResource;
private readonly IFramework _framework;
public readonly ActiveCollectionData Active;
private readonly IFramework _framework;
public CharacterUtility(IFramework framework, IGameInteropProvider interop, ActiveCollectionData active)
public CharacterUtility(IFramework framework, IGameInteropProvider interop)
{
interop.InitializeFromAttributes(this);
_lists = Enumerable.Range(0, RelevantIndices.Length)
.Select(idx => new MetaList(this, new InternalIndex(idx)))
.ToArray();
_framework = framework;
Active = active;
LoadingFinished += () => Penumbra.Log.Debug("Loading of CharacterUtility finished.");
LoadDefaultResources(null!);
if (!Ready)
@ -121,34 +118,6 @@ public unsafe class CharacterUtility : IDisposable
LoadingFinished.Invoke();
}
public void SetResource(MetaIndex resourceIdx, nint data, int length)
{
var idx = ReverseIndices[(int)resourceIdx];
var list = _lists[idx.Value];
list.SetResource(data, length);
}
public void ResetResource(MetaIndex resourceIdx)
{
var idx = ReverseIndices[(int)resourceIdx];
var list = _lists[idx.Value];
list.ResetResource();
}
public MetaList.MetaReverter TemporarilySetResource(MetaIndex resourceIdx, nint data, int length)
{
var idx = ReverseIndices[(int)resourceIdx];
var list = _lists[idx.Value];
return list.TemporarilySetResource(data, length);
}
public MetaList.MetaReverter TemporarilyResetResource(MetaIndex resourceIdx)
{
var idx = ReverseIndices[(int)resourceIdx];
var list = _lists[idx.Value];
return list.TemporarilyResetResource();
}
/// <summary> Return all relevant resources to the default resource. </summary>
public void ResetAll()
{

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);
}