mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-14 12:44:19 +01:00
ResourceTree: Are we fast yet?
This commit is contained in:
parent
b2bf6eb0f7
commit
60551c8739
5 changed files with 33 additions and 25 deletions
|
|
@ -187,11 +187,6 @@ public class MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation,
|
||||||
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file)
|
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file)
|
||||||
=> _imcCache.GetImcFile(path, out file);
|
=> _imcCache.GetImcFile(path, out file);
|
||||||
|
|
||||||
public ImcEntry GetImcEntry(Utf8GamePath path, EquipSlot slot, Variant variantIdx, out bool exists)
|
|
||||||
=> GetImcFile(path, out var file)
|
|
||||||
? file.GetEntry(Meta.Files.ImcFile.PartIndex(slot), variantIdx, out exists)
|
|
||||||
: Meta.Files.ImcFile.GetDefault(_manager, path, slot, variantIdx, out exists);
|
|
||||||
|
|
||||||
internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, SetId setId)
|
internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, SetId setId)
|
||||||
{
|
{
|
||||||
var eqdpFile = _eqdpCache.EqdpFile(race, accessory);
|
var eqdpFile = _eqdpCache.EqdpFile(race, accessory);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||||
using Penumbra.GameData.Data;
|
using Penumbra.GameData.Data;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
using Penumbra.Meta.Files;
|
||||||
using Penumbra.Meta.Manipulations;
|
using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.String;
|
using Penumbra.String;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
|
|
@ -74,22 +76,22 @@ internal partial record ResolveContext
|
||||||
return Utf8GamePath.FromByteString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
return Utf8GamePath.FromByteString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe Utf8GamePath ResolveMaterialPath(Utf8GamePath modelPath, Utf8GamePath imcPath, byte* mtrlFileName)
|
private unsafe Utf8GamePath ResolveMaterialPath(Utf8GamePath modelPath, ResourceHandle* imc, byte* mtrlFileName)
|
||||||
{
|
{
|
||||||
// Safety:
|
// Safety:
|
||||||
// Resolving a material path through the game's code can dereference null pointers for equipment materials.
|
// Resolving a material path through the game's code can dereference null pointers for equipment materials.
|
||||||
return ModelType switch
|
return ModelType switch
|
||||||
{
|
{
|
||||||
ModelType.Human when SlotIndex < 10 && mtrlFileName[8] != (byte)'b' => ResolveEquipmentMaterialPath(modelPath, imcPath, mtrlFileName),
|
ModelType.Human when SlotIndex < 10 && mtrlFileName[8] != (byte)'b' => ResolveEquipmentMaterialPath(modelPath, imc, mtrlFileName),
|
||||||
ModelType.DemiHuman => ResolveEquipmentMaterialPath(modelPath, imcPath, mtrlFileName),
|
ModelType.DemiHuman => ResolveEquipmentMaterialPath(modelPath, imc, mtrlFileName),
|
||||||
ModelType.Weapon => ResolveWeaponMaterialPath(modelPath, imcPath, mtrlFileName),
|
ModelType.Weapon => ResolveWeaponMaterialPath(modelPath, imc, mtrlFileName),
|
||||||
_ => ResolveMaterialPathNative(mtrlFileName),
|
_ => ResolveMaterialPathNative(mtrlFileName),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe Utf8GamePath ResolveEquipmentMaterialPath(Utf8GamePath modelPath, Utf8GamePath imcPath, byte* mtrlFileName)
|
private unsafe Utf8GamePath ResolveEquipmentMaterialPath(Utf8GamePath modelPath, ResourceHandle* imc, byte* mtrlFileName)
|
||||||
{
|
{
|
||||||
var variant = ResolveMaterialVariant(imcPath);
|
var variant = ResolveMaterialVariant(imc);
|
||||||
var fileName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(mtrlFileName);
|
var fileName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(mtrlFileName);
|
||||||
|
|
||||||
Span<byte> pathBuffer = stackalloc byte[260];
|
Span<byte> pathBuffer = stackalloc byte[260];
|
||||||
|
|
@ -98,7 +100,7 @@ internal partial record ResolveContext
|
||||||
return Utf8GamePath.FromSpan(pathBuffer, out var path) ? path.Clone() : Utf8GamePath.Empty;
|
return Utf8GamePath.FromSpan(pathBuffer, out var path) ? path.Clone() : Utf8GamePath.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe Utf8GamePath ResolveWeaponMaterialPath(Utf8GamePath modelPath, Utf8GamePath imcPath, byte* mtrlFileName)
|
private unsafe Utf8GamePath ResolveWeaponMaterialPath(Utf8GamePath modelPath, ResourceHandle* imc, byte* mtrlFileName)
|
||||||
{
|
{
|
||||||
var setIdHigh = Equipment.Set.Id / 100;
|
var setIdHigh = Equipment.Set.Id / 100;
|
||||||
// All MCH (20??) weapons' materials C are one and the same
|
// All MCH (20??) weapons' materials C are one and the same
|
||||||
|
|
@ -111,7 +113,7 @@ internal partial record ResolveContext
|
||||||
var setIdLow = Equipment.Set.Id % 100;
|
var setIdLow = Equipment.Set.Id % 100;
|
||||||
if (setIdLow > 50)
|
if (setIdLow > 50)
|
||||||
{
|
{
|
||||||
var variant = ResolveMaterialVariant(imcPath);
|
var variant = ResolveMaterialVariant(imc);
|
||||||
var fileName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(mtrlFileName);
|
var fileName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(mtrlFileName);
|
||||||
|
|
||||||
var mirroredSetId = (ushort)(Equipment.Set.Id - 50);
|
var mirroredSetId = (ushort)(Equipment.Set.Id - 50);
|
||||||
|
|
@ -132,16 +134,19 @@ internal partial record ResolveContext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResolveEquipmentMaterialPath(modelPath, imcPath, mtrlFileName);
|
return ResolveEquipmentMaterialPath(modelPath, imc, mtrlFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte ResolveMaterialVariant(Utf8GamePath imcPath)
|
private unsafe byte ResolveMaterialVariant(ResourceHandle* imc)
|
||||||
{
|
{
|
||||||
var metaCache = Global.Collection.MetaCache;
|
var imcFileData = imc->GetDataSpan();
|
||||||
if (metaCache == null)
|
if (imcFileData.IsEmpty)
|
||||||
|
{
|
||||||
|
Penumbra.Log.Warning($"IMC resource handle with path {GetResourceHandlePath(imc, false)} doesn't have a valid data span");
|
||||||
return Equipment.Variant.Id;
|
return Equipment.Variant.Id;
|
||||||
|
}
|
||||||
|
|
||||||
var entry = metaCache.GetImcEntry(imcPath, Slot, Equipment.Variant, out var exists);
|
var entry = ImcFile.GetEntry(imcFileData, Slot, Equipment.Variant, out var exists);
|
||||||
if (!exists)
|
if (!exists)
|
||||||
return Equipment.Variant.Id;
|
return Equipment.Variant.Id;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
|
||||||
return GetOrCreateNode(ResourceType.Tex, (nint)tex->Texture, &tex->ResourceHandle, path);
|
return GetOrCreateNode(ResourceType.Tex, (nint)tex->Texture, &tex->ResourceHandle, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe ResourceNode? CreateNodeFromModel(Model* mdl, Utf8GamePath imcPath)
|
public unsafe ResourceNode? CreateNodeFromModel(Model* mdl, ResourceHandle* imc)
|
||||||
{
|
{
|
||||||
if (mdl == null || mdl->ModelResourceHandle == null)
|
if (mdl == null || mdl->ModelResourceHandle == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -169,7 +169,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var mtrlFileName = GetMaterialFileNameBySlot(mdlResource, (uint)i);
|
var mtrlFileName = GetMaterialFileNameBySlot(mdlResource, (uint)i);
|
||||||
var mtrlNode = CreateNodeFromMaterial(mtrl, ResolveMaterialPath(path, imcPath, mtrlFileName));
|
var mtrlNode = CreateNodeFromMaterial(mtrl, ResolveMaterialPath(path, imc, mtrlFileName));
|
||||||
if (mtrlNode != null)
|
if (mtrlNode != null)
|
||||||
{
|
{
|
||||||
if (Global.WithUiData)
|
if (Global.WithUiData)
|
||||||
|
|
@ -358,7 +358,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
|
||||||
return i >= 0 && i < array.Length ? array[i] : null;
|
return i >= 0 && i < array.Length ? array[i] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static unsafe ByteString GetResourceHandlePath(ResourceHandle* handle)
|
internal static unsafe ByteString GetResourceHandlePath(ResourceHandle* handle, bool stripPrefix = true)
|
||||||
{
|
{
|
||||||
if (handle == null)
|
if (handle == null)
|
||||||
return ByteString.Empty;
|
return ByteString.Empty;
|
||||||
|
|
@ -367,7 +367,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
|
||||||
if (name.IsEmpty)
|
if (name.IsEmpty)
|
||||||
return ByteString.Empty;
|
return ByteString.Empty;
|
||||||
|
|
||||||
if (name[0] == (byte)'|')
|
if (stripPrefix && name[0] == (byte)'|')
|
||||||
{
|
{
|
||||||
var pos = name.IndexOf((byte)'|', 1);
|
var pos = name.IndexOf((byte)'|', 1);
|
||||||
if (pos < 0)
|
if (pos < 0)
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ public class ResourceTree
|
||||||
}
|
}
|
||||||
|
|
||||||
var mdl = model->Models[i];
|
var mdl = model->Models[i];
|
||||||
var mdlNode = slotContext.CreateNodeFromModel(mdl, imcNode?.GamePath ?? Utf8GamePath.Empty);
|
var mdlNode = slotContext.CreateNodeFromModel(mdl, imc);
|
||||||
if (mdlNode != null)
|
if (mdlNode != null)
|
||||||
{
|
{
|
||||||
if (globalContext.WithUiData)
|
if (globalContext.WithUiData)
|
||||||
|
|
@ -135,7 +135,7 @@ public class ResourceTree
|
||||||
}
|
}
|
||||||
|
|
||||||
var mdl = subObject->Models[i];
|
var mdl = subObject->Models[i];
|
||||||
var mdlNode = slotContext.CreateNodeFromModel(mdl, imcNode?.GamePath ?? Utf8GamePath.Empty);
|
var mdlNode = slotContext.CreateNodeFromModel(mdl, imc);
|
||||||
if (mdlNode != null)
|
if (mdlNode != null)
|
||||||
{
|
{
|
||||||
if (globalContext.WithUiData)
|
if (globalContext.WithUiData)
|
||||||
|
|
|
||||||
|
|
@ -168,11 +168,19 @@ public unsafe class ImcFile : MetaBaseFile
|
||||||
if (file == null)
|
if (file == null)
|
||||||
throw new Exception();
|
throw new Exception();
|
||||||
|
|
||||||
fixed (byte* ptr = file.Data)
|
return GetEntry(file.Data, slot, variantIdx, out exists);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImcEntry GetEntry(ReadOnlySpan<byte> imcFileData, EquipSlot slot, Variant variantIdx, out bool exists)
|
||||||
|
{
|
||||||
|
fixed (byte* ptr = imcFileData)
|
||||||
{
|
{
|
||||||
var entry = VariantPtr(ptr, PartIndex(slot), variantIdx);
|
var entry = VariantPtr(ptr, PartIndex(slot), variantIdx);
|
||||||
if (entry == null)
|
if (entry == null)
|
||||||
|
{
|
||||||
|
exists = false;
|
||||||
return new ImcEntry();
|
return new ImcEntry();
|
||||||
|
}
|
||||||
|
|
||||||
exists = true;
|
exists = true;
|
||||||
return *entry;
|
return *entry;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue