From 75e3ef72f3dbaff37db0ba18d2770a5e7885f3ae Mon Sep 17 00:00:00 2001 From: Exter-N Date: Sat, 3 Aug 2024 20:27:16 +0200 Subject: [PATCH] RT: Fix Facewear --- .../ResolveContext.PathResolution.cs | 16 ++++++---- Penumbra/Interop/ResourceTree/ResourceTree.cs | 30 ++++++++++++------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs index c3894b05..43324516 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs @@ -16,20 +16,26 @@ namespace Penumbra.Interop.ResourceTree; internal partial record ResolveContext { + private static bool IsEquipmentOrAccessorySlot(uint slotIndex) + => slotIndex is < 10 or 16 or 17; + + private static bool IsEquipmentSlot(uint slotIndex) + => slotIndex is < 5 or 16 or 17; + private Utf8GamePath ResolveModelPath() { // Correctness: // Resolving a model path through the game's code can use EQDP metadata for human equipment models. return ModelType switch { - ModelType.Human when SlotIndex < 10 => ResolveEquipmentModelPath(), - _ => ResolveModelPathNative(), + ModelType.Human when IsEquipmentOrAccessorySlot(SlotIndex) => ResolveEquipmentModelPath(), + _ => ResolveModelPathNative(), }; } private Utf8GamePath ResolveEquipmentModelPath() { - var path = SlotIndex < 5 + var path = IsEquipmentSlot(SlotIndex) ? GamePaths.Equipment.Mdl.Path(Equipment.Set, ResolveModelRaceCode(), Slot) : GamePaths.Accessory.Mdl.Path(Equipment.Set, ResolveModelRaceCode(), Slot); return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty; @@ -41,7 +47,7 @@ internal partial record ResolveContext private unsafe GenderRace ResolveEqdpRaceCode(EquipSlot slot, PrimaryId primaryId) { var slotIndex = slot.ToIndex(); - if (slotIndex >= 10 || ModelType != ModelType.Human) + if (!IsEquipmentOrAccessorySlot(slotIndex) || ModelType != ModelType.Human) return GenderRace.MidlanderMale; var characterRaceCode = (GenderRace)((Human*)CharacterBase)->RaceSexId; @@ -82,7 +88,7 @@ internal partial record ResolveContext // Resolving a material path through the game's code can dereference null pointers for materials that involve IMC metadata. return ModelType switch { - ModelType.Human when SlotIndex is < 10 or 16 && mtrlFileName[8] != (byte)'b' + ModelType.Human when IsEquipmentOrAccessorySlot(SlotIndex) && mtrlFileName[8] != (byte)'b' => ResolveEquipmentMaterialPath(modelPath, imc, mtrlFileName), ModelType.DemiHuman => ResolveEquipmentMaterialPath(modelPath, imc, mtrlFileName), ModelType.Weapon => ResolveWeaponMaterialPath(modelPath, imc, mtrlFileName), diff --git a/Penumbra/Interop/ResourceTree/ResourceTree.cs b/Penumbra/Interop/ResourceTree/ResourceTree.cs index 6663fb40..f1507294 100644 --- a/Penumbra/Interop/ResourceTree/ResourceTree.cs +++ b/Penumbra/Interop/ResourceTree/ResourceTree.cs @@ -9,6 +9,7 @@ using Penumbra.Interop.Hooks.PostProcessing; using Penumbra.UI; using CustomizeData = FFXIVClientStructs.FFXIV.Client.Game.Character.CustomizeData; using CustomizeIndex = Dalamud.Game.ClientState.Objects.Enums.CustomizeIndex; +using ModelType = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBase.ModelType; namespace Penumbra.Interop.ResourceTree; @@ -44,8 +45,8 @@ public class ResourceTree PlayerRelated = playerRelated; CollectionName = collectionName; AnonymizedCollectionName = anonymizedCollectionName; - Nodes = new List(); - FlatNodes = new HashSet(); + Nodes = []; + FlatNodes = []; } public void ProcessPostfix(Action action) @@ -59,13 +60,13 @@ public class ResourceTree var character = (Character*)GameObjectAddress; var model = (CharacterBase*)DrawObjectAddress; var modelType = model->GetModelType(); - var human = modelType == CharacterBase.ModelType.Human ? (Human*)model : null; + var human = modelType == ModelType.Human ? (Human*)model : null; var equipment = modelType switch { - CharacterBase.ModelType.Human => new ReadOnlySpan(&human->Head, 10), - CharacterBase.ModelType.DemiHuman => new ReadOnlySpan( + ModelType.Human => new ReadOnlySpan(&human->Head, 12), + ModelType.DemiHuman => new ReadOnlySpan( Unsafe.AsPointer(ref character->DrawData.EquipmentModelIds[0]), 10), - _ => ReadOnlySpan.Empty, + _ => [], }; ModelId = character->CharacterData.ModelCharaId; CustomizeData = character->DrawData.CustomizeData; @@ -75,9 +76,18 @@ public class ResourceTree for (var i = 0u; i < model->SlotCount; ++i) { - var slotContext = i < equipment.Length - ? globalContext.CreateContext(model, i, i.ToEquipSlot(), equipment[(int)i]) - : globalContext.CreateContext(model, i); + var slotContext = modelType switch + { + ModelType.Human => i switch + { + < 10 => globalContext.CreateContext(model, i, i.ToEquipSlot(), equipment[(int)i]), + 16 or 17 => globalContext.CreateContext(model, i, EquipSlot.Head, equipment[(int)(i - 6)]), + _ => globalContext.CreateContext(model, i), + }, + _ => i < equipment.Length + ? globalContext.CreateContext(model, i, i.ToEquipSlot(), equipment[(int)i]) + : globalContext.CreateContext(model, i), + }; var imc = (ResourceHandle*)model->IMCArray[i]; var imcNode = slotContext.CreateNodeFromImc(imc); @@ -117,7 +127,7 @@ public class ResourceTree var subObject = (CharacterBase*)baseSubObject; - if (subObject->GetModelType() != CharacterBase.ModelType.Weapon) + if (subObject->GetModelType() != ModelType.Weapon) continue; var weapon = (Weapon*)subObject;