mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 13:14:17 +01:00
Improve Imc Handling.
This commit is contained in:
parent
d7a8c9415b
commit
03d3c38ad5
16 changed files with 197 additions and 233 deletions
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{ }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue