mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-01-03 06:13:45 +01:00
Add PBD Post-Processor that appends EPBD data if the loaded PBD does not contain it.
This commit is contained in:
parent
9aff388e21
commit
a7246b9d98
8 changed files with 187 additions and 52 deletions
|
|
@ -63,7 +63,7 @@ public sealed unsafe class PreBoneDeformerReplacer : IDisposable, IRequiredServi
|
|||
if (!_framework.IsInFrameworkUpdateThread)
|
||||
Penumbra.Log.Warning(
|
||||
$"{nameof(PreBoneDeformerReplacer)}.{nameof(SetupHssReplacements)}(0x{(nint)drawObject:X}, {slotIndex}) called out of framework thread");
|
||||
|
||||
|
||||
var preBoneDeformer = GetPreBoneDeformerForCharacter(drawObject);
|
||||
try
|
||||
{
|
||||
|
|
|
|||
119
Penumbra/Interop/Processing/PbdFilePostProcessor.cs
Normal file
119
Penumbra/Interop/Processing/PbdFilePostProcessor.cs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
using Dalamud.Game;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.String;
|
||||
|
||||
namespace Penumbra.Interop.Processing;
|
||||
|
||||
public sealed class PbdFilePostProcessor : IFilePostProcessor
|
||||
{
|
||||
private readonly IFileAllocator _allocator;
|
||||
private byte[] _epbdData;
|
||||
private unsafe delegate* unmanaged<ResourceHandle*, void> _loadEpbdData;
|
||||
|
||||
public ResourceType Type
|
||||
=> ResourceType.Pbd;
|
||||
|
||||
public unsafe PbdFilePostProcessor(IDataManager dataManager, XivFileAllocator allocator, ISigScanner scanner)
|
||||
{
|
||||
_allocator = allocator;
|
||||
_epbdData = SetEpbdData(dataManager);
|
||||
_loadEpbdData = (delegate* unmanaged<ResourceHandle*, void>)scanner.ScanText(Sigs.LoadEpbdData);
|
||||
}
|
||||
|
||||
public unsafe void PostProcess(ResourceHandle* resource, CiByteString originalGamePath, ReadOnlySpan<byte> additionalData)
|
||||
{
|
||||
if (_epbdData.Length is 0)
|
||||
return;
|
||||
|
||||
if (resource->LoadState is not LoadState.Success)
|
||||
{
|
||||
Penumbra.Log.Warning($"[ResourceLoader] Requested PBD at {originalGamePath} failed load ({resource->LoadState}).");
|
||||
return;
|
||||
}
|
||||
|
||||
var (data, length) = resource->GetData();
|
||||
if (length is 0 || data == nint.Zero)
|
||||
{
|
||||
Penumbra.Log.Warning($"[ResourceLoader] Requested PBD at {originalGamePath} succeeded load but has no data.");
|
||||
return;
|
||||
}
|
||||
|
||||
var span = new ReadOnlySpan<byte>((void*)data, (int)resource->FileSize);
|
||||
var reader = new PackReader(span);
|
||||
if (reader.HasData)
|
||||
{
|
||||
Penumbra.Log.Excessive($"[ResourceLoader] Successfully loaded PBD at {originalGamePath} with EPBD data.");
|
||||
return;
|
||||
}
|
||||
|
||||
var newData = AppendData(span);
|
||||
fixed (byte* ptr = newData)
|
||||
{
|
||||
// Set the appended data and the actual file size, then re-load the EPBD data via game function call.
|
||||
if (resource->SetData((nint)ptr, newData.Length))
|
||||
{
|
||||
resource->FileSize = (uint)newData.Length;
|
||||
resource->CsHandle.FileSize2 = (uint)newData.Length;
|
||||
resource->CsHandle.FileSize3 = (uint)newData.Length;
|
||||
_loadEpbdData(resource);
|
||||
// Free original data.
|
||||
_allocator.Release((void*)data, length);
|
||||
Penumbra.Log.Verbose($"[ResourceLoader] Loaded {originalGamePath} from file and appended default EPBD data.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Penumbra.Log.Warning(
|
||||
$"[ResourceLoader] Failed to append EPBD data to custom PBD at {originalGamePath}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Combine the given data with the default PBD data using the game's file allocator. </summary>
|
||||
private unsafe ReadOnlySpan<byte> AppendData(ReadOnlySpan<byte> data)
|
||||
{
|
||||
// offset has to be set, otherwise not called.
|
||||
var newLength = data.Length + _epbdData.Length;
|
||||
var memory = _allocator.Allocate(newLength);
|
||||
var span = new Span<byte>(memory, newLength);
|
||||
data.CopyTo(span);
|
||||
_epbdData.CopyTo(span[data.Length..]);
|
||||
return span;
|
||||
}
|
||||
|
||||
/// <summary> Fetch the default EPBD data from the .pbd file of the game's installation. </summary>
|
||||
private static byte[] SetEpbdData(IDataManager dataManager)
|
||||
{
|
||||
try
|
||||
{
|
||||
var file = dataManager.GetFile(GamePaths.Pbd.Path);
|
||||
if (file is null || file.Data.Length is 0)
|
||||
{
|
||||
Penumbra.Log.Warning("Default PBD file has no data.");
|
||||
return [];
|
||||
}
|
||||
|
||||
ReadOnlySpan<byte> span = file.Data;
|
||||
var reader = new PackReader(span);
|
||||
if (!reader.HasData)
|
||||
{
|
||||
Penumbra.Log.Warning("Default PBD file has no EPBD section.");
|
||||
return [];
|
||||
}
|
||||
|
||||
var offset = span.Length - (int)reader.PackLength;
|
||||
var ret = span[offset..];
|
||||
Penumbra.Log.Verbose($"Default PBD file has EPBD section of length {ret.Length} at offset {offset}.");
|
||||
return ret.ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Penumbra.Log.Error($"Unknown error getting default EPBD data:\n{ex}");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue