From 86b83e735f62ab936b2bdbf9631f57d5f6e810a5 Mon Sep 17 00:00:00 2001 From: Asgard Date: Tue, 9 Jul 2024 16:03:40 -0600 Subject: [PATCH 1/3] Add support for facewear changing --- Brio/Capabilities/Debug/DebugCapability.cs | 1 + Brio/Files/AnamnesisCharaFile.cs | 7 ++ Brio/Game/Actor/ActorAppearanceService.cs | 21 ++++- Brio/Game/Actor/Appearance/ActorAppearance.cs | 9 ++ .../Actor/Extensions/CharacterExtensions.cs | 5 + Brio/Game/Actor/Interop/BrioDrawData.cs | 14 +++ Brio/Game/Types/FacewearTypes.cs | 33 +++++++ Brio/UI/Controls/Editors/GearEditor.cs | 66 ++++++++++++- .../UI/Controls/Selectors/FacewearSelector.cs | 92 +++++++++++++++++++ 9 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 Brio/Game/Actor/Interop/BrioDrawData.cs create mode 100644 Brio/Game/Types/FacewearTypes.cs create mode 100644 Brio/UI/Controls/Selectors/FacewearSelector.cs diff --git a/Brio/Capabilities/Debug/DebugCapability.cs b/Brio/Capabilities/Debug/DebugCapability.cs index 2e46b954..03af20dd 100644 --- a/Brio/Capabilities/Debug/DebugCapability.cs +++ b/Brio/Capabilities/Debug/DebugCapability.cs @@ -49,6 +49,7 @@ public IReadOnlyDictionary GetInterestingAddresses() ["CameraManager"] = ((nint)CameraManager.Instance()), ["ActiveCamera"] = ((nint)CameraManager.Instance()->GetActiveCamera()), ["EventFramework"] = ((nint)EventFramework.Instance()), + ["Target"] = ((nint)TargetSystem.Instance()->Target), }; return addresses.AsReadOnly(); diff --git a/Brio/Files/AnamnesisCharaFile.cs b/Brio/Files/AnamnesisCharaFile.cs index 70e3ae70..07a809d5 100644 --- a/Brio/Files/AnamnesisCharaFile.cs +++ b/Brio/Files/AnamnesisCharaFile.cs @@ -87,6 +87,7 @@ internal class AnamnesisCharaFile : JsonDocumentBase public float Transparency { get; set; } public float MuscleTone { get; set; } public float HeightMultiplier { get; set; } + public byte Glasses { get; set; } public override void GetAutoTags(ref TagCollection tags) { @@ -149,6 +150,9 @@ public static implicit operator ActorAppearance(AnamnesisCharaFile chara) appearance.Equipment.LFinger = chara.LeftRing; appearance.Equipment.RFinger = chara.RightRing; + // Facewear + appearance.Facewear = chara.Glasses; + // Extended Appearance appearance.ExtendedAppearance.Transparency = chara.Transparency; @@ -206,6 +210,9 @@ public static implicit operator AnamnesisCharaFile(ActorAppearance appearance) LeftRing = appearance.Equipment.LFinger, RightRing = appearance.Equipment.RFinger, + // Facewear + Glasses = appearance.Facewear, + // Extended Appearance Transparency = appearance.ExtendedAppearance.Transparency }; diff --git a/Brio/Game/Actor/ActorAppearanceService.cs b/Brio/Game/Actor/ActorAppearanceService.cs index bf362ed8..c01840d0 100644 --- a/Brio/Game/Actor/ActorAppearanceService.cs +++ b/Brio/Game/Actor/ActorAppearanceService.cs @@ -2,6 +2,7 @@ using Brio.Entities; using Brio.Game.Actor.Appearance; using Brio.Game.Actor.Extensions; +using Brio.Game.Actor.Interop; using Brio.Game.GPose; using Brio.IPC; using Dalamud.Game; @@ -35,6 +36,8 @@ internal class ActorAppearanceService : IDisposable private unsafe delegate nint UpdateTintDelegate(nint charaBase, nint tint); private readonly Hook _updateTintHook = null!; + private unsafe delegate* unmanaged _setFacewear; + private uint _forceNpcHackCount = 0; public bool CanTint => _configurationService.Configuration.Appearance.EnableTinting; @@ -58,6 +61,9 @@ public unsafe ActorAppearanceService(GPoseService gPoseService, ConfigurationSer var updateTintHook = Marshal.ReadInt64((nint)(CharacterBase.StaticVirtualTablePointer) + 0xC0); _updateTintHook = hooks.HookFromAddress((nint)updateTintHook, UpdateTintDetour); _updateTintHook.Enable(); + + var setFacewearAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 41 FF C5 41 83 FD ?? 72"); + _setFacewear = (delegate* unmanaged)setFacewearAddress; } public void PushForceNpcHack() => ++_forceNpcHackCount; @@ -182,7 +188,20 @@ public async Task SetCharacterAppearance(ICharacter character, Act } } } - } + + // Facewear + if(existingAppearance.Facewear != appearance.Facewear) + { + if(needsRedraw) + { + character.BrioDrawData()->Facewear = appearance.Facewear; + } + else + { + _setFacewear(&native->DrawData, 0, appearance.Facewear); + } + } + } if(options.HasFlag(AppearanceImportOptions.Weapon)) { diff --git a/Brio/Game/Actor/Appearance/ActorAppearance.cs b/Brio/Game/Actor/Appearance/ActorAppearance.cs index 01c3e0cb..2161fcc6 100644 --- a/Brio/Game/Actor/Appearance/ActorAppearance.cs +++ b/Brio/Game/Actor/Appearance/ActorAppearance.cs @@ -1,4 +1,5 @@ using Brio.Game.Actor.Extensions; +using Brio.Game.Actor.Interop; using FFXIVClientStructs.FFXIV.Client.Game.Character; using Lumina.Excel.GeneratedSheets; using DalamudCharacter = Dalamud.Game.ClientState.Objects.Types.ICharacter; @@ -8,6 +9,7 @@ namespace Brio.Game.Actor.Appearance; internal struct ActorAppearance() { public int ModelCharaId; + public byte Facewear; public ActorWeapons Weapons = new(); public ActorEquipment Equipment = new(); public ActorCustomize Customize = new(); @@ -30,6 +32,8 @@ public unsafe static ActorAppearance FromCharacter(DalamudCharacter character) actorAppearance.Equipment = *(ActorEquipment*)slot; } + actorAppearance.Facewear = character.BrioDrawData()->Facewear; + actorAppearance.Customize = *(ActorCustomize*)&native->DrawData.CustomizeData; actorAppearance.Runtime.IsHatHidden = native->DrawData.IsHatHidden; @@ -116,6 +120,9 @@ public static ActorAppearance FromBNpc(BNpcBase npc) actorAppearance.Equipment = equipment; } + // TODO: Can NPCs have facewear? + actorAppearance.Facewear = 0; + return actorAppearance; } @@ -270,6 +277,8 @@ public static ActorAppearance FromENpc(ENpcBase npc) actorAppearance.Equipment.LFinger.Stain1 = (byte)npc.Dye2LeftRing.Row; } + // TODO: Can NPCs have facewear? + actorAppearance.Facewear = 0; return actorAppearance; } diff --git a/Brio/Game/Actor/Extensions/CharacterExtensions.cs b/Brio/Game/Actor/Extensions/CharacterExtensions.cs index 2850702b..1e1d8968 100644 --- a/Brio/Game/Actor/Extensions/CharacterExtensions.cs +++ b/Brio/Game/Actor/Extensions/CharacterExtensions.cs @@ -23,6 +23,11 @@ internal static class CharacterExtensions return (StructsCharacter*)go.Address; } + public unsafe static BrioDrawData* BrioDrawData(this ICharacter go) + { + return (BrioDrawData*)&go.Native()->DrawData; + } + public unsafe static bool HasCompanionSlot(this ICharacter go) { var native = go.Native(); diff --git a/Brio/Game/Actor/Interop/BrioDrawData.cs b/Brio/Game/Actor/Interop/BrioDrawData.cs new file mode 100644 index 00000000..841693df --- /dev/null +++ b/Brio/Game/Actor/Interop/BrioDrawData.cs @@ -0,0 +1,14 @@ +using FFXIVClientStructs.FFXIV.Client.Game.Character; +using System.Runtime.InteropServices; + +namespace Brio.Game.Actor.Interop; + +[StructLayout(LayoutKind.Explicit)] +internal unsafe struct BrioDrawData +{ + [FieldOffset(0x0)] + public DrawDataContainer DawData; + + [FieldOffset(0x1D0)] + public byte Facewear; +} diff --git a/Brio/Game/Types/FacewearTypes.cs b/Brio/Game/Types/FacewearTypes.cs new file mode 100644 index 00000000..27693ce2 --- /dev/null +++ b/Brio/Game/Types/FacewearTypes.cs @@ -0,0 +1,33 @@ +using Brio.Resources; +using OneOf.Types; +using OneOf; +using Lumina.Excel.GeneratedSheets2; + +namespace Brio.Game.Types; + +[GenerateOneOf] +internal partial class FacewearUnion : OneOfBase +{ + public static implicit operator FacewearUnion(FacewearId facewearId) + { + if(facewearId.Id != 0 && GameDataProvider.Instance.Glasses.TryGetValue(facewearId.Id, out var glasses)) + return new FacewearUnion(glasses); + + return new None(); + } +} + +internal record struct FacewearId(byte Id) +{ + public static FacewearId None { get; } = new(0); + + public static implicit operator FacewearId(DyeUnion dyeUnion) => dyeUnion.Match( + dyeRow => new FacewearId((byte)dyeRow.RowId), + none => None + ); + + public static implicit operator FacewearId(int dye) => new((byte)dye); + public static implicit operator FacewearId(byte dye) => new(dye); + public static implicit operator byte(FacewearId id) => id.Id; + public static implicit operator int(FacewearId id) => id.Id; +} diff --git a/Brio/UI/Controls/Editors/GearEditor.cs b/Brio/UI/Controls/Editors/GearEditor.cs index 1a422252..b4343791 100644 --- a/Brio/UI/Controls/Editors/GearEditor.cs +++ b/Brio/UI/Controls/Editors/GearEditor.cs @@ -14,13 +14,14 @@ namespace Brio.UI.Controls.Editors; internal class GearEditor() { - private Vector2 IconSize => new(ImGui.GetTextLineHeight() * 4f); + private Vector2 IconSize => new(ImGui.GetTextLineHeight() * 3.9f); private ActorAppearanceCapability _capability = null!; private static readonly DyeSelector _dye0Selector = new("dye_0_selector"); private static readonly DyeSelector _dye1Selector = new("dye_1_selector"); private static readonly GearSelector _gearSelector = new("gear_selector"); + private static readonly FacewearSelector _facewearSelector = new("facewear_selector"); private const ActorEquipSlot _weaponSlots = ActorEquipSlot.MainHand | ActorEquipSlot.OffHand; @@ -63,6 +64,7 @@ public bool DrawGear(ref ActorAppearance currentAppearance, ActorAppearance orig didChange |= DrawGearSlot(ref currentAppearance, ref currentAppearance.Equipment.Arms, ActorEquipSlot.Hands); didChange |= DrawGearSlot(ref currentAppearance, ref currentAppearance.Equipment.Legs, ActorEquipSlot.Legs); didChange |= DrawGearSlot(ref currentAppearance, ref currentAppearance.Equipment.Feet, ActorEquipSlot.Feet); + didChange |= DrawFacewearSlot(ref currentAppearance); } } @@ -409,4 +411,66 @@ private bool DrawWeaponSlot(ref ActorAppearance appearance, ref WeaponModelId eq return didChange; } + + private bool DrawFacewearSlot(ref ActorAppearance appearance) + { + bool didChange = false; + + Vector2 faceIconSize = new Vector2(ImGui.GetTextLineHeight() * 2.3f); + + FacewearUnion facewearUnion = new FacewearId(appearance.Facewear); + var (facewearId, facewearName, facewearIcon) = facewearUnion.Match( + glasses => ((byte)glasses.RowId, glasses.Unknown3, (uint)glasses.Unknown11), + none => ((byte)0, "None", (uint)0x0) + ); + + using(ImRaii.PushId("facewear")) + { + if(ImBrio.BorderedGameIcon("##icon", facewearIcon, "Images.Head.png", size: faceIconSize)) + { + _facewearSelector.Select(facewearUnion, true); + ImGui.OpenPopup("facewear_popup"); + } + + ImGui.SameLine(); + + ImGui.SetCursorPosX(IconSize.X + (ImGui.GetStyle().FramePadding.X * 2f)); + + using(var group = ImRaii.Group()) + { + if(group.Success) + { + string description = $"Facewear: {facewearName}"; + + ImGui.Text(description); + + ImGui.SetNextItemWidth(ImGui.CalcTextSize("XXXXX").X); + int value = facewearId; + if(ImGui.InputInt("##facewearid", ref value, 0, 0, ImGuiInputTextFlags.EnterReturnsTrue)) + { + appearance.Facewear = (byte)value; + didChange |= true; + } + } + } + + using(var facewearPopup = ImRaii.Popup("facewear_popup")) + { + if(facewearPopup.Success) + { + _facewearSelector.Draw(); + if(_facewearSelector.SoftSelectionChanged && _facewearSelector.SoftSelected != null) + { + appearance.Facewear = _facewearSelector.SoftSelected.Match(glasses => (byte)glasses.RowId, none => (byte)0); + didChange |= true; + } + if(_gearSelector.SelectionChanged) + ImGui.CloseCurrentPopup(); + + } + } + } + + return didChange; + } } diff --git a/Brio/UI/Controls/Selectors/FacewearSelector.cs b/Brio/UI/Controls/Selectors/FacewearSelector.cs new file mode 100644 index 00000000..5774df83 --- /dev/null +++ b/Brio/UI/Controls/Selectors/FacewearSelector.cs @@ -0,0 +1,92 @@ +using Brio.Game.Types; +using Brio.Resources; +using Brio.UI.Controls.Stateless; +using ImGuiNET; +using Lumina.Excel.GeneratedSheets2; +using OneOf.Types; +using System.Collections.Generic; +using System.Numerics; + +namespace Brio.UI.Controls.Selectors; + +internal class FacewearSelector(string id) : Selector(id) +{ + protected override Vector2 MinimumListSize { get; } = new(300, 300); + + protected override float EntrySize => ImGui.GetTextLineHeight() * 3.2f; + protected virtual Vector2 IconSize => new(ImGui.GetTextLineHeight() * 3f); + + protected override SelectorFlags Flags { get; } = SelectorFlags.AllowSearch | SelectorFlags.ShowOptions | SelectorFlags.AdaptiveSizing; + + + private readonly List _validWeathers = []; + + protected override void PopulateList() + { + foreach(var glasses in GameDataProvider.Instance.Glasses.Values) + AddItem(glasses); + + AddItem(new None()); + } + + protected override void DrawItem(FacewearUnion union, bool isHovered) + { + var (facewearId, facewearName, facewearIcon) = union.Match( + glasses => ((byte)glasses.RowId, glasses.Unknown3, (uint)glasses.Unknown11), + none => ((byte)0, "None", (uint)0x0) + ); + + ImBrio.BorderedGameIcon("icon", facewearIcon, "Images.Head.png", description: $"{facewearName}\n{facewearId}", flags: ImGuiButtonFlags.None, size: IconSize); + } + + protected override bool Filter(FacewearUnion item, string search) + { + + return item.Match( + (glasses) => + { + if(string.IsNullOrEmpty(glasses.Unknown3)) + return false; + + var searchText = $"{glasses.Unknown3} {glasses.RowId}"; + + if(searchText.Contains(search, System.StringComparison.InvariantCultureIgnoreCase)) + return true; + + return false; + }, + none => true + ); + } + + protected override int Compare(FacewearUnion itemA, FacewearUnion itemB) + { + // None to top + if(itemA.Value is None && itemB.Value is not None) + return -1; + + if(itemA.Value is not None && itemB.Value is None) + return 1; + + // Get name + var textA = itemA.Match( + glasses => glasses.Unknown3, + none => "" + ); + + var textB = itemB.Match( + glasses => glasses.Unknown3, + none => "" + ); + + // Blank string to bottom + if(string.IsNullOrEmpty(textA) && !string.IsNullOrEmpty(textB)) + return 1; + + if(!string.IsNullOrEmpty(textA) && string.IsNullOrEmpty(textB)) + return -1; + + // Alphabetical + return string.Compare(textA, textB, System.StringComparison.InvariantCultureIgnoreCase); + } +} From 066bd28b6fe734eb9b71fc51aacc60f3925a06d2 Mon Sep 17 00:00:00 2001 From: Asgard Date: Tue, 9 Jul 2024 16:17:07 -0600 Subject: [PATCH 2/3] Cleanup --- Brio/Game/Actor/ActorAppearanceService.cs | 7 +++---- Brio/Game/Actor/Appearance/ActorAppearance.cs | 1 - Brio/Game/Types/FacewearTypes.cs | 4 ++-- Brio/IPC/GlamourerService.cs | 17 +++++++---------- Brio/Library/Sources/FileSource.cs | 2 +- Brio/UI/UIManager.cs | 2 -- 6 files changed, 13 insertions(+), 20 deletions(-) diff --git a/Brio/Game/Actor/ActorAppearanceService.cs b/Brio/Game/Actor/ActorAppearanceService.cs index c01840d0..6caa5653 100644 --- a/Brio/Game/Actor/ActorAppearanceService.cs +++ b/Brio/Game/Actor/ActorAppearanceService.cs @@ -2,7 +2,6 @@ using Brio.Entities; using Brio.Game.Actor.Appearance; using Brio.Game.Actor.Extensions; -using Brio.Game.Actor.Interop; using Brio.Game.GPose; using Brio.IPC; using Dalamud.Game; @@ -196,12 +195,12 @@ public async Task SetCharacterAppearance(ICharacter character, Act { character.BrioDrawData()->Facewear = appearance.Facewear; } - else - { + else + { _setFacewear(&native->DrawData, 0, appearance.Facewear); } } - } + } if(options.HasFlag(AppearanceImportOptions.Weapon)) { diff --git a/Brio/Game/Actor/Appearance/ActorAppearance.cs b/Brio/Game/Actor/Appearance/ActorAppearance.cs index 2161fcc6..1e2a804d 100644 --- a/Brio/Game/Actor/Appearance/ActorAppearance.cs +++ b/Brio/Game/Actor/Appearance/ActorAppearance.cs @@ -1,5 +1,4 @@ using Brio.Game.Actor.Extensions; -using Brio.Game.Actor.Interop; using FFXIVClientStructs.FFXIV.Client.Game.Character; using Lumina.Excel.GeneratedSheets; using DalamudCharacter = Dalamud.Game.ClientState.Objects.Types.ICharacter; diff --git a/Brio/Game/Types/FacewearTypes.cs b/Brio/Game/Types/FacewearTypes.cs index 27693ce2..b95d85b0 100644 --- a/Brio/Game/Types/FacewearTypes.cs +++ b/Brio/Game/Types/FacewearTypes.cs @@ -1,7 +1,7 @@ using Brio.Resources; -using OneOf.Types; -using OneOf; using Lumina.Excel.GeneratedSheets2; +using OneOf; +using OneOf.Types; namespace Brio.Game.Types; diff --git a/Brio/IPC/GlamourerService.cs b/Brio/IPC/GlamourerService.cs index f6f0cc25..906dd6df 100644 --- a/Brio/IPC/GlamourerService.cs +++ b/Brio/IPC/GlamourerService.cs @@ -3,10 +3,7 @@ using Brio.Game.GPose; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin; -using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Services; -using FFXIVClientStructs.FFXIV.Client.Game.Character; -using Glamourer.Api.IpcSubscribers; using System; using System.Linq; using System.Threading.Tasks; @@ -73,12 +70,12 @@ bool ConnectToGlamourer() return false; } - var (major, minor) = _glamourerApiVersions.Invoke(); - if(major != GlamourerApiMajor || minor < GlamourerApiMinor) - { - Brio.Log.Warning($"Glamourer API mismatch, found v{major}.{minor}"); - return false; - } + var (major, minor) = _glamourerApiVersions.Invoke(); + if(major != GlamourerApiMajor || minor < GlamourerApiMinor) + { + Brio.Log.Warning($"Glamourer API mismatch, found v{major}.{minor}"); + return false; + } Brio.Log.Debug("Glamourer integration initialized"); @@ -101,7 +98,7 @@ public Task RevertCharacter(ICharacter? character) Brio.Log.Error("Starting glamourer revert..."); - _glamourerRevertCharacter.Invoke(character!.ObjectIndex, character.DataId); + _glamourerRevertCharacter.Invoke(character!.ObjectIndex, character.DataId); return _framework.RunOnTick(async () => { diff --git a/Brio/Library/Sources/FileSource.cs b/Brio/Library/Sources/FileSource.cs index 68ed6f14..3acf4035 100644 --- a/Brio/Library/Sources/FileSource.cs +++ b/Brio/Library/Sources/FileSource.cs @@ -243,7 +243,7 @@ private IDalamudTextureWrap GetIcon() private IDalamudTextureWrap? GetPreviewImage() { - if(_isPreviewImageDisposed) + if(_isPreviewImageDisposed) return null; if(_previewImage == null || _previewImage.ImGuiHandle == 0) diff --git a/Brio/UI/UIManager.cs b/Brio/UI/UIManager.cs index 8f861b0a..435a5a9a 100644 --- a/Brio/UI/UIManager.cs +++ b/Brio/UI/UIManager.cs @@ -11,8 +11,6 @@ using Dalamud.Plugin.Services; using ImGuiNET; using System; -using System.IO; -using System.Xml.Linq; namespace Brio.UI; From 8848f23c6fba9b5224ef9143fe7fc31dc5e5553e Mon Sep 17 00:00:00 2001 From: Asgard Date: Tue, 9 Jul 2024 16:22:01 -0600 Subject: [PATCH 3/3] Facewear icon --- Brio/Resources/Embedded/Images/Facewear.png | Bin 0 -> 1900 bytes Brio/UI/Controls/Editors/GearEditor.cs | 2 +- Brio/UI/Controls/Selectors/FacewearSelector.cs | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 Brio/Resources/Embedded/Images/Facewear.png diff --git a/Brio/Resources/Embedded/Images/Facewear.png b/Brio/Resources/Embedded/Images/Facewear.png new file mode 100644 index 0000000000000000000000000000000000000000..d3ab1d248c3f2044ba77c9681495da4df25626f3 GIT binary patch literal 1900 zcmV-y2b1`TP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGqB>(^xB>_oNB=7(L2LVY$K~!i%rJ3n^ z<46ca^9mEk@fNS?dH+Y$a4?r3TvpuQ{J1Ttih={RNO8Yiuh&VsU2i|S-LIGHWjbHa z)9G^Z``RaY|MfJTZ|CWH3qRF{dp)Gk`X10NEUjR6Dh z@YP1p)wk|@d+#f4Dj<4*$3P7^s0`-%efys48rSdgthSM4*X}cJXCQ9G?!xABxdgm5 z_|%kTk%6Qd94&#qBK(<{HAI_$t6hagN)WyEM(*oim%ux4uXgKq--5x%pSa3jw+T6i z+RUdICHlSc8tk%rcWC!=ASbd@0GB`t%!6t|OSUcXt^{X)+1fu|jz5)0)iB}dD@i=n zs|hT%p@jsU%iuXYNZ_aP4PLPIeirbTN;$y4ajh^5a5D}3!=jUiqxnr$b$AA}L3IXd zd)hUSbZ+kj--uf0#pYQ8#PSUsG1CSgCUHoQm9GiJVEWymY`O$v^5F4qaDZl;Z35H1 z$vG4YJJ|$JW#u}Dui$dZARNg_OKfIuy8W@>f9?t7xBvr+Q{q_>l11aBh@rkYd}uZR z0Vu`b2b-#%$hWQ0csu*pn!IcRq_%mxwat>Xag`M8cbEfe;a_^k8IEoo>;A& zXEC6Jzh17>>0}>YmT4XES89p)w@9sg>FdgMKInt*d@^4#3zB~x9FNE8^Ye3By|1Rj z{@@)WClAuP@Am@{Cjqs5E^!^$R~-+>X}wxc>yNebUC#F;-cHApgTKGOPpf}c(|)`6 zSr|Ea2=ohZm8z<$_5PVGj@!kUufvyvJLAd?xyqk{CtX4u~FX( zdI>y)on{J9Doz3)s}H}w!+-t2ACA^&$K2D3K7nU5ay0VRBjBumG)DeM?a{}}j=drt zmdfUH^9cXBwq>(=clcK}R}ZSUs*mMVsma&-rMA|tlM27^uwhx=b9grE#~pru+^6x- z>5~e2<*hUU`EjBX;%e@&vqV3GP*m#&y4;TKU~> z=gTOUO~UsLC1-NjH3As5NdnS6SNX0nnh(LVH~0Jf{g06uyxqSHUm-?qy-#hUv%SLi zILPcf{Lh-NmH%zCHu>>4+9M+f3HU*BIvU+y>*wt+&iRIoMqUOuK&&ZVV9W;Mp6vB@ z!(l>KSX~u-09pI|JU0-<`}+EF!9_g!_)G_Gc3THk*xC zs(!xCV57PMrWi9{U5XVk)c){!z|`>O%7GyQC~M$Qot5e|?U|9y)}3p)u@w32@Cpy0 zXK{>Uv$i;PuTO!rwRnQp!Qx+)RHMQ$5Cg6ME-Fl=y^1{%yC*2^9=!I2Xr~w~JJ!1U zwMynS?pYPsxepWYJX3Io=O3@WjLbRkOmUz7RB2~CYmhO5BZ#WxZE*#9zuP+$PrTn zmcZ_?yZ;u%%mwr84{&CoR==*X>NpjZXU{wUQdJU*?+}>~F`V^-p^}yK}kGZ~jPvnaJrkC}zt0BYEB9Yj(n@7zWNS z5PYs{nWH&8&P@IE0gQFbtWY^-gQJvFCf|G!h`t00)#}TRQwJZw%3rOv+r`q=uW9NY$EJ%s^x;%3jn!2b|hYZ6)xke zuIKx2Mb!_!bFWm@Vg{=#;Z0=ce>2t}NV{4>v-aL!?Fv9X_qJ!M@BuAlga|0T$;YX2cI5K472@Lv()!R~K#%6562Q&M_;WL2%2iULHroEs9kba+;IsHow#}l{29!b29@Ktdf zr9+<8Hd61iX9A?}XFv;}BPG1)4j(~~8&%LH;9r#f{;w=S@m1Eh`XCJ*{H8&Yd?%$p mD!f!>`m|)4UFW;&I{gP%UCw%~Box>H0000 ((byte)0, "None", (uint)0x0) ); - ImBrio.BorderedGameIcon("icon", facewearIcon, "Images.Head.png", description: $"{facewearName}\n{facewearId}", flags: ImGuiButtonFlags.None, size: IconSize); + ImBrio.BorderedGameIcon("icon", facewearIcon, "Images.Facewear.png", description: $"{facewearName}\n{facewearId}", flags: ImGuiButtonFlags.None, size: IconSize); } protected override bool Filter(FacewearUnion item, string search)