mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +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)
|
||||
=> _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)
|
||||
{
|
||||
var eqdpFile = _eqdpCache.EqdpFile(race, accessory);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.String;
|
||||
using Penumbra.String.Classes;
|
||||
|
|
@ -74,22 +76,22 @@ internal partial record ResolveContext
|
|||
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:
|
||||
// Resolving a material path through the game's code can dereference null pointers for equipment materials.
|
||||
return ModelType switch
|
||||
{
|
||||
ModelType.Human when SlotIndex < 10 && mtrlFileName[8] != (byte)'b' => ResolveEquipmentMaterialPath(modelPath, imcPath, mtrlFileName),
|
||||
ModelType.DemiHuman => ResolveEquipmentMaterialPath(modelPath, imcPath, mtrlFileName),
|
||||
ModelType.Weapon => ResolveWeaponMaterialPath(modelPath, imcPath, mtrlFileName),
|
||||
ModelType.Human when SlotIndex < 10 && mtrlFileName[8] != (byte)'b' => ResolveEquipmentMaterialPath(modelPath, imc, mtrlFileName),
|
||||
ModelType.DemiHuman => ResolveEquipmentMaterialPath(modelPath, imc, mtrlFileName),
|
||||
ModelType.Weapon => ResolveWeaponMaterialPath(modelPath, imc, 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
// 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;
|
||||
if (setIdLow > 50)
|
||||
{
|
||||
var variant = ResolveMaterialVariant(imcPath);
|
||||
var variant = ResolveMaterialVariant(imc);
|
||||
var fileName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(mtrlFileName);
|
||||
|
||||
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;
|
||||
if (metaCache == null)
|
||||
var imcFileData = imc->GetDataSpan();
|
||||
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;
|
||||
}
|
||||
|
||||
var entry = metaCache.GetImcEntry(imcPath, Slot, Equipment.Variant, out var exists);
|
||||
var entry = ImcFile.GetEntry(imcFileData, Slot, Equipment.Variant, out var exists);
|
||||
if (!exists)
|
||||
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);
|
||||
}
|
||||
|
||||
public unsafe ResourceNode? CreateNodeFromModel(Model* mdl, Utf8GamePath imcPath)
|
||||
public unsafe ResourceNode? CreateNodeFromModel(Model* mdl, ResourceHandle* imc)
|
||||
{
|
||||
if (mdl == null || mdl->ModelResourceHandle == null)
|
||||
return null;
|
||||
|
|
@ -169,7 +169,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
|
|||
continue;
|
||||
|
||||
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 (Global.WithUiData)
|
||||
|
|
@ -358,7 +358,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
|
|||
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)
|
||||
return ByteString.Empty;
|
||||
|
|
@ -367,7 +367,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
|
|||
if (name.IsEmpty)
|
||||
return ByteString.Empty;
|
||||
|
||||
if (name[0] == (byte)'|')
|
||||
if (stripPrefix && name[0] == (byte)'|')
|
||||
{
|
||||
var pos = name.IndexOf((byte)'|', 1);
|
||||
if (pos < 0)
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ public class ResourceTree
|
|||
}
|
||||
|
||||
var mdl = model->Models[i];
|
||||
var mdlNode = slotContext.CreateNodeFromModel(mdl, imcNode?.GamePath ?? Utf8GamePath.Empty);
|
||||
var mdlNode = slotContext.CreateNodeFromModel(mdl, imc);
|
||||
if (mdlNode != null)
|
||||
{
|
||||
if (globalContext.WithUiData)
|
||||
|
|
@ -135,7 +135,7 @@ public class ResourceTree
|
|||
}
|
||||
|
||||
var mdl = subObject->Models[i];
|
||||
var mdlNode = slotContext.CreateNodeFromModel(mdl, imcNode?.GamePath ?? Utf8GamePath.Empty);
|
||||
var mdlNode = slotContext.CreateNodeFromModel(mdl, imc);
|
||||
if (mdlNode != null)
|
||||
{
|
||||
if (globalContext.WithUiData)
|
||||
|
|
|
|||
|
|
@ -168,11 +168,19 @@ public unsafe class ImcFile : MetaBaseFile
|
|||
if (file == null)
|
||||
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);
|
||||
if (entry == null)
|
||||
{
|
||||
exists = false;
|
||||
return new ImcEntry();
|
||||
}
|
||||
|
||||
exists = true;
|
||||
return *entry;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue