mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 20:24:17 +01:00
Improve Path preprocessing.
This commit is contained in:
parent
f9c45a2f3f
commit
cf1dcfcb7c
14 changed files with 209 additions and 246 deletions
|
|
@ -2,6 +2,7 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||||
using Penumbra.Api.Enums;
|
using Penumbra.Api.Enums;
|
||||||
using Penumbra.Collections;
|
using Penumbra.Collections;
|
||||||
using Penumbra.Collections.Manager;
|
using Penumbra.Collections.Manager;
|
||||||
|
using Penumbra.Interop.Processing;
|
||||||
using Penumbra.Interop.ResourceLoading;
|
using Penumbra.Interop.ResourceLoading;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
|
|
@ -20,9 +21,11 @@ public class PathResolver : IDisposable
|
||||||
private readonly MetaState _metaState;
|
private readonly MetaState _metaState;
|
||||||
private readonly GameState _gameState;
|
private readonly GameState _gameState;
|
||||||
private readonly CollectionResolver _collectionResolver;
|
private readonly CollectionResolver _collectionResolver;
|
||||||
|
private readonly GamePathPreProcessService _preprocessor;
|
||||||
|
|
||||||
public unsafe PathResolver(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ResourceLoader loader,
|
public PathResolver(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ResourceLoader loader,
|
||||||
SubfileHelper subfileHelper, PathState pathState, MetaState metaState, CollectionResolver collectionResolver, GameState gameState)
|
SubfileHelper subfileHelper, PathState pathState, MetaState metaState, CollectionResolver collectionResolver, GameState gameState,
|
||||||
|
GamePathPreProcessService preprocessor)
|
||||||
{
|
{
|
||||||
_performance = performance;
|
_performance = performance;
|
||||||
_config = config;
|
_config = config;
|
||||||
|
|
@ -31,6 +34,7 @@ public class PathResolver : IDisposable
|
||||||
_pathState = pathState;
|
_pathState = pathState;
|
||||||
_metaState = metaState;
|
_metaState = metaState;
|
||||||
_gameState = gameState;
|
_gameState = gameState;
|
||||||
|
_preprocessor = preprocessor;
|
||||||
_collectionResolver = collectionResolver;
|
_collectionResolver = collectionResolver;
|
||||||
_loader = loader;
|
_loader = loader;
|
||||||
_loader.ResolvePath = ResolvePath;
|
_loader.ResolvePath = ResolvePath;
|
||||||
|
|
@ -102,11 +106,10 @@ public class PathResolver : IDisposable
|
||||||
// so that the functions loading tex and shpk can find that path and use its collection.
|
// so that the functions loading tex and shpk can find that path and use its collection.
|
||||||
// We also need to handle defaulted materials against a non-default collection.
|
// We also need to handle defaulted materials against a non-default collection.
|
||||||
var path = resolved == null ? gamePath.Path : resolved.Value.InternalName;
|
var path = resolved == null ? gamePath.Path : resolved.Value.InternalName;
|
||||||
SubfileHelper.HandleCollection(resolveData, path, nonDefault, type, resolved, gamePath, out var pair);
|
return _preprocessor.PreProcess(resolveData, path, nonDefault, type, resolved, gamePath);
|
||||||
return pair;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_loader.ResetResolvePath();
|
_loader.ResetResolvePath();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,22 +66,6 @@ public sealed unsafe class SubfileHelper : IDisposable, IReadOnlyCollection<KeyV
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Materials, TMB, and AVFX need to be set per collection, so they can load their sub files independently of each other. </summary>
|
|
||||||
public static void HandleCollection(ResolveData resolveData, ByteString path, bool nonDefault, ResourceType type, FullPath? resolved,
|
|
||||||
Utf8GamePath originalPath, out (FullPath?, ResolveData) data)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_loader.ResourceLoaded -= SubfileContainerRequested;
|
_loader.ResourceLoaded -= SubfileContainerRequested;
|
||||||
|
|
|
||||||
16
Penumbra/Interop/Processing/AvfxPathPreProcessor.cs
Normal file
16
Penumbra/Interop/Processing/AvfxPathPreProcessor.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
using Penumbra.Api.Enums;
|
||||||
|
using Penumbra.Collections;
|
||||||
|
using Penumbra.Interop.PathResolving;
|
||||||
|
using Penumbra.String;
|
||||||
|
using Penumbra.String.Classes;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Processing;
|
||||||
|
|
||||||
|
public sealed class AvfxPathPreProcessor : IPathPreProcessor
|
||||||
|
{
|
||||||
|
public ResourceType Type
|
||||||
|
=> ResourceType.Avfx;
|
||||||
|
|
||||||
|
public FullPath? PreProcess(ResolveData resolveData, ByteString path, Utf8GamePath _, bool nonDefault, FullPath? resolved)
|
||||||
|
=> nonDefault ? PathDataHandler.CreateAvfx(path, resolveData.ModCollection) : resolved;
|
||||||
|
}
|
||||||
39
Penumbra/Interop/Processing/FilePostProcessService.cs
Normal file
39
Penumbra/Interop/Processing/FilePostProcessService.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
using System.Collections.Frozen;
|
||||||
|
using OtterGui.Services;
|
||||||
|
using Penumbra.Api.Enums;
|
||||||
|
using Penumbra.Interop.ResourceLoading;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.String;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Processing;
|
||||||
|
|
||||||
|
public interface IFilePostProcessor : IService
|
||||||
|
{
|
||||||
|
public ResourceType Type { get; }
|
||||||
|
public unsafe void PostProcess(ResourceHandle* resource, ByteString originalGamePath, ReadOnlySpan<byte> additionalData);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
Penumbra/Interop/Processing/GamePathPreProcessService.cs
Normal file
37
Penumbra/Interop/Processing/GamePathPreProcessService.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
using System.Collections.Frozen;
|
||||||
|
using OtterGui.Services;
|
||||||
|
using Penumbra.Api.Enums;
|
||||||
|
using Penumbra.Collections;
|
||||||
|
using Penumbra.String;
|
||||||
|
using Penumbra.String.Classes;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Processing;
|
||||||
|
|
||||||
|
public interface IPathPreProcessor : IService
|
||||||
|
{
|
||||||
|
public ResourceType Type { get; }
|
||||||
|
|
||||||
|
public FullPath? PreProcess(ResolveData resolveData, ByteString path, Utf8GamePath originalGamePath, bool nonDefault, FullPath? resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GamePathPreProcessService : IService
|
||||||
|
{
|
||||||
|
private readonly FrozenDictionary<ResourceType, IPathPreProcessor> _processors;
|
||||||
|
|
||||||
|
public GamePathPreProcessService(ServiceManager services)
|
||||||
|
{
|
||||||
|
_processors = services.GetServicesImplementing<IPathPreProcessor>().ToFrozenDictionary(s => s.Type, s => s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public (FullPath? Path, ResolveData Data) PreProcess(ResolveData resolveData, ByteString path, bool nonDefault, ResourceType type,
|
||||||
|
FullPath? resolved,
|
||||||
|
Utf8GamePath originalPath)
|
||||||
|
{
|
||||||
|
if (!_processors.TryGetValue(type, out var processor))
|
||||||
|
return (resolved, resolveData);
|
||||||
|
|
||||||
|
resolved = processor.PreProcess(resolveData, path, originalPath, nonDefault, resolved);
|
||||||
|
return (resolved, resolveData);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Penumbra/Interop/Processing/ImcFilePostProcessor.cs
Normal file
30
Penumbra/Interop/Processing/ImcFilePostProcessor.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
using Penumbra.Api.Enums;
|
||||||
|
using Penumbra.Collections.Manager;
|
||||||
|
using Penumbra.Interop.PathResolving;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.String;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Processing;
|
||||||
|
|
||||||
|
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}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Penumbra/Interop/Processing/ImcPathPreProcessor.cs
Normal file
18
Penumbra/Interop/Processing/ImcPathPreProcessor.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
using Penumbra.Api.Enums;
|
||||||
|
using Penumbra.Collections;
|
||||||
|
using Penumbra.Interop.PathResolving;
|
||||||
|
using Penumbra.String;
|
||||||
|
using Penumbra.String.Classes;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Processing;
|
||||||
|
|
||||||
|
public sealed class ImcPathPreProcessor : IPathPreProcessor
|
||||||
|
{
|
||||||
|
public ResourceType Type
|
||||||
|
=> ResourceType.Imc;
|
||||||
|
|
||||||
|
public FullPath? PreProcess(ResolveData resolveData, ByteString path, Utf8GamePath originalGamePath, bool _, FullPath? resolved)
|
||||||
|
=> resolveData.ModCollection.MetaCache?.Imc.HasFile(originalGamePath.Path) ?? false
|
||||||
|
? PathDataHandler.CreateImc(path, resolveData.ModCollection)
|
||||||
|
: resolved;
|
||||||
|
}
|
||||||
18
Penumbra/Interop/Processing/MaterialFilePostProcessor.cs
Normal file
18
Penumbra/Interop/Processing/MaterialFilePostProcessor.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
using Penumbra.Api.Enums;
|
||||||
|
using Penumbra.Interop.PathResolving;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.String;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Processing;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Penumbra/Interop/Processing/MtrlPathPreProcessor.cs
Normal file
16
Penumbra/Interop/Processing/MtrlPathPreProcessor.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
using Penumbra.Api.Enums;
|
||||||
|
using Penumbra.Collections;
|
||||||
|
using Penumbra.Interop.PathResolving;
|
||||||
|
using Penumbra.String;
|
||||||
|
using Penumbra.String.Classes;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Processing;
|
||||||
|
|
||||||
|
public sealed class MtrlPathPreProcessor : IPathPreProcessor
|
||||||
|
{
|
||||||
|
public ResourceType Type
|
||||||
|
=> ResourceType.Mtrl;
|
||||||
|
|
||||||
|
public FullPath? PreProcess(ResolveData resolveData, ByteString path, Utf8GamePath originalGamePath, bool nonDefault, FullPath? resolved)
|
||||||
|
=> nonDefault ? PathDataHandler.CreateMtrl(path, resolveData.ModCollection, originalGamePath) : resolved;
|
||||||
|
}
|
||||||
16
Penumbra/Interop/Processing/TmbPathPreProcessor.cs
Normal file
16
Penumbra/Interop/Processing/TmbPathPreProcessor.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
using Penumbra.Api.Enums;
|
||||||
|
using Penumbra.Collections;
|
||||||
|
using Penumbra.Interop.PathResolving;
|
||||||
|
using Penumbra.String;
|
||||||
|
using Penumbra.String.Classes;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Processing;
|
||||||
|
|
||||||
|
public sealed class TmbPathPreProcessor : IPathPreProcessor
|
||||||
|
{
|
||||||
|
public ResourceType Type
|
||||||
|
=> ResourceType.Tmb;
|
||||||
|
|
||||||
|
public FullPath? PreProcess(ResolveData resolveData, ByteString path, Utf8GamePath _, bool nonDefault, FullPath? resolved)
|
||||||
|
=> nonDefault ? PathDataHandler.CreateTmb(path, resolveData.ModCollection) : resolved;
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
using System.Collections.Frozen;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||||
using OtterGui.Services;
|
|
||||||
using Penumbra.Api.Enums;
|
using Penumbra.Api.Enums;
|
||||||
using Penumbra.Collections;
|
using Penumbra.Collections;
|
||||||
using Penumbra.Collections.Manager;
|
|
||||||
using Penumbra.Interop.PathResolving;
|
using Penumbra.Interop.PathResolving;
|
||||||
using Penumbra.Interop.SafeHandles;
|
using Penumbra.Interop.SafeHandles;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
|
|
@ -13,72 +10,6 @@ using FileMode = Penumbra.Interop.Structs.FileMode;
|
||||||
|
|
||||||
namespace Penumbra.Interop.ResourceLoading;
|
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
|
public unsafe class ResourceLoader : IDisposable
|
||||||
{
|
{
|
||||||
private readonly ResourceService _resources;
|
private readonly ResourceService _resources;
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,6 @@ public unsafe class CharacterUtility : IDisposable
|
||||||
|
|
||||||
private readonly MetaList[] _lists;
|
private readonly MetaList[] _lists;
|
||||||
|
|
||||||
public IReadOnlyList<MetaList> Lists
|
|
||||||
=> _lists;
|
|
||||||
|
|
||||||
public (nint Address, int Size) DefaultResource(InternalIndex idx)
|
public (nint Address, int Size) DefaultResource(InternalIndex idx)
|
||||||
=> _lists[idx.Value].DefaultResource;
|
=> _lists[idx.Value].DefaultResource;
|
||||||
|
|
||||||
|
|
@ -58,7 +55,7 @@ public unsafe class CharacterUtility : IDisposable
|
||||||
{
|
{
|
||||||
interop.InitializeFromAttributes(this);
|
interop.InitializeFromAttributes(this);
|
||||||
_lists = Enumerable.Range(0, RelevantIndices.Length)
|
_lists = Enumerable.Range(0, RelevantIndices.Length)
|
||||||
.Select(idx => new MetaList(this, new InternalIndex(idx)))
|
.Select(idx => new MetaList(new InternalIndex(idx)))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
_framework = framework;
|
_framework = framework;
|
||||||
LoadingFinished += () => Penumbra.Log.Debug("Loading of CharacterUtility finished.");
|
LoadingFinished += () => Penumbra.Log.Debug("Loading of CharacterUtility finished.");
|
||||||
|
|
@ -124,9 +121,6 @@ public unsafe class CharacterUtility : IDisposable
|
||||||
if (!Ready)
|
if (!Ready)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var list in _lists)
|
|
||||||
list.Dispose();
|
|
||||||
|
|
||||||
Address->HumanPbdResource = (ResourceHandle*)DefaultHumanPbdResource;
|
Address->HumanPbdResource = (ResourceHandle*)DefaultHumanPbdResource;
|
||||||
Address->TransparentTexResource = (TextureResourceHandle*)DefaultTransparentResource;
|
Address->TransparentTexResource = (TextureResourceHandle*)DefaultTransparentResource;
|
||||||
Address->DecalTexResource = (TextureResourceHandle*)DefaultDecalResource;
|
Address->DecalTexResource = (TextureResourceHandle*)DefaultDecalResource;
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,14 @@ using Penumbra.Interop.Structs;
|
||||||
|
|
||||||
namespace Penumbra.Interop.Services;
|
namespace Penumbra.Interop.Services;
|
||||||
|
|
||||||
public unsafe class MetaList : IDisposable
|
public class MetaList(CharacterUtility.InternalIndex index)
|
||||||
{
|
{
|
||||||
private readonly CharacterUtility _utility;
|
public readonly CharacterUtility.InternalIndex Index = index;
|
||||||
private readonly LinkedList<MetaReverter> _entries = new();
|
public readonly MetaIndex GlobalMetaIndex = CharacterUtility.RelevantIndices[index.Value];
|
||||||
public readonly CharacterUtility.InternalIndex Index;
|
|
||||||
public readonly MetaIndex GlobalMetaIndex;
|
|
||||||
|
|
||||||
public IReadOnlyCollection<MetaReverter> Entries
|
|
||||||
=> _entries;
|
|
||||||
|
|
||||||
private nint _defaultResourceData = nint.Zero;
|
private nint _defaultResourceData = nint.Zero;
|
||||||
private int _defaultResourceSize = 0;
|
private int _defaultResourceSize;
|
||||||
public bool Ready { get; private set; } = false;
|
public bool Ready { get; private set; }
|
||||||
|
|
||||||
public MetaList(CharacterUtility utility, CharacterUtility.InternalIndex index)
|
|
||||||
{
|
|
||||||
_utility = utility;
|
|
||||||
Index = index;
|
|
||||||
GlobalMetaIndex = CharacterUtility.RelevantIndices[index.Value];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDefaultResource(nint data, int size)
|
public void SetDefaultResource(nint data, int size)
|
||||||
{
|
{
|
||||||
|
|
@ -31,116 +19,8 @@ public unsafe class MetaList : IDisposable
|
||||||
_defaultResourceData = data;
|
_defaultResourceData = data;
|
||||||
_defaultResourceSize = size;
|
_defaultResourceSize = size;
|
||||||
Ready = _defaultResourceData != nint.Zero && size != 0;
|
Ready = _defaultResourceData != nint.Zero && size != 0;
|
||||||
if (_entries.Count <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var first = _entries.First!.Value;
|
|
||||||
SetResource(first.Data, first.Length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public (nint Address, int Size) DefaultResource
|
public (nint Address, int Size) DefaultResource
|
||||||
=> (_defaultResourceData, _defaultResourceSize);
|
=> (_defaultResourceData, _defaultResourceSize);
|
||||||
|
|
||||||
public MetaReverter TemporarilySetResource(nint data, int length)
|
|
||||||
{
|
|
||||||
Penumbra.Log.Excessive($"Temporarily set resource {GlobalMetaIndex} to 0x{(ulong)data:X} ({length} bytes).");
|
|
||||||
var reverter = new MetaReverter(this, data, length);
|
|
||||||
_entries.AddFirst(reverter);
|
|
||||||
SetResourceInternal(data, length);
|
|
||||||
return reverter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MetaReverter TemporarilyResetResource()
|
|
||||||
{
|
|
||||||
Penumbra.Log.Excessive(
|
|
||||||
$"Temporarily reset resource {GlobalMetaIndex} to default at 0x{_defaultResourceData:X} ({_defaultResourceSize} bytes).");
|
|
||||||
var reverter = new MetaReverter(this);
|
|
||||||
_entries.AddFirst(reverter);
|
|
||||||
ResetResourceInternal();
|
|
||||||
return reverter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetResource(nint data, int length)
|
|
||||||
{
|
|
||||||
Penumbra.Log.Excessive($"Set resource {GlobalMetaIndex} to 0x{(ulong)data:X} ({length} bytes).");
|
|
||||||
SetResourceInternal(data, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetResource()
|
|
||||||
{
|
|
||||||
Penumbra.Log.Excessive($"Reset resource {GlobalMetaIndex} to default at 0x{_defaultResourceData:X} ({_defaultResourceSize} bytes).");
|
|
||||||
ResetResourceInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Set the currently stored data of this resource to new values. </summary>
|
|
||||||
private void SetResourceInternal(nint data, int length)
|
|
||||||
{
|
|
||||||
if (!Ready)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var resource = _utility.Address->Resource(GlobalMetaIndex);
|
|
||||||
resource->SetData(data, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Reset the currently stored data of this resource to its default values. </summary>
|
|
||||||
private void ResetResourceInternal()
|
|
||||||
=> SetResourceInternal(_defaultResourceData, _defaultResourceSize);
|
|
||||||
|
|
||||||
private void SetResourceToDefaultCollection()
|
|
||||||
{}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_entries.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var entry in _entries)
|
|
||||||
entry.Disposed = true;
|
|
||||||
|
|
||||||
_entries.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
ResetResourceInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class MetaReverter(MetaList metaList, nint data, int length) : IDisposable
|
|
||||||
{
|
|
||||||
public static readonly MetaReverter Disabled = new(null!) { Disposed = true };
|
|
||||||
|
|
||||||
public readonly MetaList MetaList = metaList;
|
|
||||||
public readonly nint Data = data;
|
|
||||||
public readonly int Length = length;
|
|
||||||
public readonly bool Resetter;
|
|
||||||
public bool Disposed;
|
|
||||||
|
|
||||||
public MetaReverter(MetaList metaList)
|
|
||||||
: this(metaList, nint.Zero, 0)
|
|
||||||
=> Resetter = true;
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (Disposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var list = MetaList._entries;
|
|
||||||
var wasCurrent = ReferenceEquals(this, list.First?.Value);
|
|
||||||
list.Remove(this);
|
|
||||||
if (!wasCurrent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (list.Count == 0)
|
|
||||||
{
|
|
||||||
MetaList.SetResourceToDefaultCollection();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var next = list.First!.Value;
|
|
||||||
if (next.Resetter)
|
|
||||||
MetaList.ResetResourceInternal();
|
|
||||||
else
|
|
||||||
MetaList.SetResourceInternal(next.Data, next.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
Disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -179,8 +179,6 @@ public class DebugTab : Window, ITab
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
DrawData();
|
DrawData();
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
DrawDebugTabMetaLists();
|
|
||||||
ImGui.NewLine();
|
|
||||||
DrawResourceProblems();
|
DrawResourceProblems();
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
DrawPlayerModelInfo();
|
DrawPlayerModelInfo();
|
||||||
|
|
@ -788,23 +786,6 @@ public class DebugTab : Window, ITab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawDebugTabMetaLists()
|
|
||||||
{
|
|
||||||
if (!ImGui.CollapsingHeader("Metadata Changes"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
using var table = Table("##DebugMetaTable", 3, ImGuiTableFlags.SizingFixedFit);
|
|
||||||
if (!table)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var list in _characterUtility.Lists)
|
|
||||||
{
|
|
||||||
ImGuiUtil.DrawTableColumn(list.GlobalMetaIndex.ToString());
|
|
||||||
ImGuiUtil.DrawTableColumn(list.Entries.Count.ToString());
|
|
||||||
ImGuiUtil.DrawTableColumn(string.Join(", ", list.Entries.Select(e => $"0x{e.Data:X}")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Draw information about the resident resource files. </summary>
|
/// <summary> Draw information about the resident resource files. </summary>
|
||||||
private unsafe void DrawDebugResidentResources()
|
private unsafe void DrawDebugResidentResources()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue