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