From 6cc89f3e7c33143f950a877d8ac38e41a9ec47e2 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Wed, 6 Sep 2023 21:15:05 +0200 Subject: [PATCH] Add Emotes to Changed Items. --- Penumbra.GameData | 2 +- Penumbra/UI/ChangedItemDrawer.cs | 78 ++++++++++++++++++++++---------- Penumbra/UI/Tabs/DebugTab.cs | 72 ++++++++++++++++++++++++----- 3 files changed, 115 insertions(+), 37 deletions(-) diff --git a/Penumbra.GameData b/Penumbra.GameData index 43f6737d..bd7e7926 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 43f6737d4baa7988a5fe23096f20827bc54e6812 +Subproject commit bd7e79262a0db06e27e61b98ab0a31ebb881be2a diff --git a/Penumbra/UI/ChangedItemDrawer.cs b/Penumbra/UI/ChangedItemDrawer.cs index da4faa43..529a6246 100644 --- a/Penumbra/UI/ChangedItemDrawer.cs +++ b/Penumbra/UI/ChangedItemDrawer.cs @@ -27,22 +27,23 @@ public class ChangedItemDrawer : IDisposable [Flags] public enum ChangedItemIcon : uint { - Head = 0x0001, - Body = 0x0002, - Hands = 0x0004, - Legs = 0x0008, - Feet = 0x0010, - Ears = 0x0020, - Neck = 0x0040, - Wrists = 0x0080, - Finger = 0x0100, - Monster = 0x0200, - Demihuman = 0x0400, - Customization = 0x0800, - Action = 0x1000, - Mainhand = 0x2000, - Offhand = 0x4000, - Unknown = 0x8000, + Head = 0x00_00_01, + Body = 0x00_00_02, + Hands = 0x00_00_04, + Legs = 0x00_00_08, + Feet = 0x00_00_10, + Ears = 0x00_00_20, + Neck = 0x00_00_40, + Wrists = 0x00_00_80, + Finger = 0x00_01_00, + Monster = 0x00_02_00, + Demihuman = 0x00_04_00, + Customization = 0x00_08_00, + Action = 0x00_10_00, + Mainhand = 0x00_20_00, + Offhand = 0x00_40_00, + Unknown = 0x00_80_00, + Emote = 0x01_00_00, } public const ChangedItemIcon AllFlags = (ChangedItemIcon)0xFFFF; @@ -51,10 +52,11 @@ public class ChangedItemDrawer : IDisposable private readonly Configuration _config; private readonly ExcelSheet _items; private readonly CommunicatorService _communicator; - private readonly Dictionary _icons = new(16); + private readonly Dictionary _icons = new(16); private float _smallestIconWidth; - public ChangedItemDrawer(UiBuilder uiBuilder, IDataManager gameData, ITextureProvider textureProvider, CommunicatorService communicator, Configuration config) + public ChangedItemDrawer(UiBuilder uiBuilder, IDataManager gameData, ITextureProvider textureProvider, CommunicatorService communicator, + Configuration config) { _items = gameData.GetExcelSheet()!; uiBuilder.RunWhenUiPrepared(() => CreateEquipSlotIcons(uiBuilder, gameData, textureProvider), true); @@ -164,6 +166,7 @@ public class ChangedItemDrawer : IDisposable ChangedItemIcon.Offhand, ChangedItemIcon.Customization, ChangedItemIcon.Action, + ChangedItemIcon.Emote, ChangedItemIcon.Monster, ChangedItemIcon.Demihuman, ChangedItemIcon.Unknown, @@ -182,14 +185,12 @@ public class ChangedItemDrawer : IDisposable using var popup = ImRaii.ContextPopupItem(type.ToString()); if (popup) - { if (ImGui.MenuItem("Enable Only This")) { _config.ChangedItemFilter = type; _config.Save(); ImGui.CloseCurrentPopup(); } - } if (ImGui.IsItemHovered()) { @@ -238,6 +239,8 @@ public class ChangedItemDrawer : IDisposable { if (name.StartsWith("Action: ")) iconType = ChangedItemIcon.Action; + else if (name.StartsWith("Emote: ")) + iconType = ChangedItemIcon.Emote; else if (name.StartsWith("Customization: ")) iconType = ChangedItemIcon.Customization; break; @@ -306,6 +309,7 @@ public class ChangedItemDrawer : IDisposable ChangedItemIcon.Demihuman => "Demi-Human", ChangedItemIcon.Customization => "Customization", ChangedItemIcon.Action => "Action", + ChangedItemIcon.Emote => "Emote", ChangedItemIcon.Mainhand => "Weapon (Mainhand)", ChangedItemIcon.Offhand => "Weapon (Offhand)", _ => "Other", @@ -354,21 +358,47 @@ public class ChangedItemDrawer : IDisposable Add(ChangedItemIcon.Demihuman, textureProvider.GetTextureFromGame("ui/icon/062000/062041_hr1.tex", true)); Add(ChangedItemIcon.Customization, textureProvider.GetTextureFromGame("ui/icon/062000/062043_hr1.tex", true)); Add(ChangedItemIcon.Action, textureProvider.GetTextureFromGame("ui/icon/062000/062001_hr1.tex", true)); + Add(ChangedItemIcon.Emote, LoadEmoteTexture(gameData, uiBuilder)); + Add(ChangedItemIcon.Unknown, LoadUnknownTexture(gameData, uiBuilder)); Add(AllFlags, textureProvider.GetTextureFromGame("ui/icon/114000/114052_hr1.tex", true)); + _smallestIconWidth = _icons.Values.Min(i => i.Width); + + return true; + } + + private static unsafe TextureWrap? LoadUnknownTexture(IDataManager gameData, UiBuilder uiBuilder) + { var unk = gameData.GetFile("ui/uld/levelup2_hr1.tex"); if (unk == null) - return true; + return null; var image = unk.GetRgbaImageData(); var bytes = new byte[unk.Header.Height * unk.Header.Height * 4]; var diff = 2 * (unk.Header.Height - unk.Header.Width); for (var y = 0; y < unk.Header.Height; ++y) image.AsSpan(4 * y * unk.Header.Width, 4 * unk.Header.Width).CopyTo(bytes.AsSpan(4 * y * unk.Header.Height + diff)); - Add(ChangedItemIcon.Unknown, uiBuilder.LoadImageRaw(bytes, unk.Header.Height, unk.Header.Height, 4)); - _smallestIconWidth = _icons.Values.Min(i => i.Width); + return uiBuilder.LoadImageRaw(bytes, unk.Header.Height, unk.Header.Height, 4); + } - return true; + private static unsafe TextureWrap? LoadEmoteTexture(IDataManager gameData, UiBuilder uiBuilder) + { + var emote = gameData.GetFile("ui/icon/000000/000019_hr1.tex"); + if (emote == null) + return null; + + var image2 = emote.GetRgbaImageData(); + fixed (byte* ptr = image2) + { + var color = (uint*)ptr; + for (var i = 0; i < image2.Length / 4; ++i) + { + if (color[i] == 0xFF000000) + image2[i * 4 + 3] = 0; + } + } + + return uiBuilder.LoadImageRaw(image2, emote.Header.Width, emote.Header.Height, 4); } } diff --git a/Penumbra/UI/Tabs/DebugTab.cs b/Penumbra/UI/Tabs/DebugTab.cs index d02da883..72122722 100644 --- a/Penumbra/UI/Tabs/DebugTab.cs +++ b/Penumbra/UI/Tabs/DebugTab.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Numerics; using Dalamud.Interface; using Dalamud.Interface.Windowing; +using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Group; using FFXIVClientStructs.FFXIV.Client.Game.Object; @@ -66,6 +67,7 @@ public class DebugTab : Window, ITab private readonly FrameworkManager _framework; private readonly TextureManager _textureManager; private readonly SkinFixer _skinFixer; + private readonly IdentifierService _identifier; public DebugTab(StartTracker timer, PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ValidityChecker validityChecker, ModManager modManager, HttpApi httpApi, ActorService actorService, @@ -73,7 +75,7 @@ public class DebugTab : Window, ITab ResourceManagerService resourceManager, PenumbraIpcProviders ipc, CollectionResolver collectionResolver, DrawObjectState drawObjectState, PathState pathState, SubfileHelper subfileHelper, IdentifiedCollectionCache identifiedCollectionCache, CutsceneService cutsceneService, ModImportManager modImporter, ImportPopup importPopup, FrameworkManager framework, - TextureManager textureManager, SkinFixer skinFixer) + TextureManager textureManager, SkinFixer skinFixer, IdentifierService identifier) : base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse, false) { IsOpen = true; @@ -107,6 +109,7 @@ public class DebugTab : Window, ITab _framework = framework; _textureManager = textureManager; _skinFixer = skinFixer; + _identifier = identifier; } public ReadOnlySpan Label @@ -138,12 +141,10 @@ public class DebugTab : Window, ITab ImGui.NewLine(); DrawDebugCharacterUtility(); ImGui.NewLine(); - DrawStainTemplates(); + DrawData(); ImGui.NewLine(); DrawDebugTabMetaLists(); ImGui.NewLine(); - DrawDebugResidentResources(); - ImGui.NewLine(); DrawResourceProblems(); ImGui.NewLine(); DrawPlayerModelInfo(); @@ -370,7 +371,9 @@ public class DebugTab : Window, ITab { ImGuiUtil.DrawTableColumn($"{((GameObject*)obj.Address)->ObjectIndex}"); ImGuiUtil.DrawTableColumn($"0x{obj.Address:X}"); - ImGuiUtil.DrawTableColumn((obj.Address == nint.Zero) ? string.Empty : $"0x{(nint)((Character*)obj.Address)->GameObject.GetDrawObject():X}"); + ImGuiUtil.DrawTableColumn(obj.Address == nint.Zero + ? string.Empty + : $"0x{(nint)((Character*)obj.Address)->GameObject.GetDrawObject():X}"); var identifier = _actorService.AwaitedService.FromObject(obj, false, true, false); ImGuiUtil.DrawTableColumn(_actorService.AwaitedService.ToString(identifier)); var id = obj.ObjectKind == ObjectKind.BattleNpc ? $"{identifier.DataId} | {obj.DataId}" : identifier.DataId.ToString(); @@ -546,9 +549,49 @@ public class DebugTab : Window, ITab } } + private void DrawData() + { + if (!ImGui.CollapsingHeader("Game Data")) + return; + + DrawEmotes(); + DrawStainTemplates(); + } + + private string _emoteSearchFile = string.Empty; + private string _emoteSearchName = string.Empty; + + private void DrawEmotes() + { + using var mainTree = TreeNode("Emotes"); + if (!mainTree) + return; + + ImGui.InputText("File Name", ref _emoteSearchFile, 256); + ImGui.InputText("Emote Name", ref _emoteSearchName, 256); + using var table = Table("##table", 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit, new Vector2(-1, 12 * ImGui.GetTextLineHeightWithSpacing())); + if (!table) + return; + + var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeightWithSpacing()); + var dummy = ImGuiClip.FilteredClippedDraw(_identifier.AwaitedService.Emotes, skips, + p => p.Key.Contains(_emoteSearchFile, StringComparison.OrdinalIgnoreCase) + && (_emoteSearchName.Length == 0 + || p.Value.Any(s => s.Name.ToDalamudString().TextValue.Contains(_emoteSearchName, StringComparison.OrdinalIgnoreCase))), + p => + { + ImGui.TableNextColumn(); + ImGui.TextUnformatted(p.Key); + ImGui.TableNextColumn(); + ImGui.TextUnformatted(string.Join(", ", p.Value.Select(v => v.Name.ToDalamudString().TextValue))); + }); + ImGuiClip.DrawEndDummy(dummy, ImGui.GetTextLineHeightWithSpacing()); + } + private void DrawStainTemplates() { - if (!ImGui.CollapsingHeader("Staining Templates")) + using var mainTree = TreeNode("Staining Templates"); + if (!mainTree) return; foreach (var (key, data) in _stains.StmFile.Entries) @@ -625,16 +668,15 @@ public class DebugTab : Window, ITab ImGui.TableNextRow(); continue; } + UiHelpers.Text(resource); ImGui.TableNextColumn(); - var data = (nint)ResourceHandle.GetData(resource); + var data = (nint)ResourceHandle.GetData(resource); var length = ResourceHandle.GetLength(resource); if (ImGui.Selectable($"0x{data:X}")) - { if (data != nint.Zero && length > 0) ImGui.SetClipboardText(string.Join("\n", new ReadOnlySpan((byte*)data, (int)length).ToArray().Select(b => b.ToString("X2")))); - } ImGuiUtil.HoverTooltip("Click to copy bytes to clipboard."); ImGui.TableNextColumn(); @@ -655,7 +697,9 @@ public class DebugTab : Window, ITab ImGui.TextUnformatted($"{_characterUtility.DefaultResource(intern).Size}"); } else + { ImGui.TableNextColumn(); + } } } @@ -679,7 +723,8 @@ public class DebugTab : Window, ITab /// Draw information about the resident resource files. private unsafe void DrawDebugResidentResources() { - if (!ImGui.CollapsingHeader("Resident Resources")) + using var tree = TreeNode("Resident Resources"); + if (!tree) return; if (_residentResources.Address == null || _residentResources.Address->NumResources == 0) @@ -703,8 +748,10 @@ public class DebugTab : Window, ITab private static void DrawCopyableAddress(string label, nint address) { using (var _ = PushFont(UiBuilder.MonoFont)) + { if (ImGui.Selectable($"0x{address:X16} {label}")) ImGui.SetClipboardText($"{address:X16}"); + } ImGuiUtil.HoverTooltip("Click to copy address to clipboard."); } @@ -789,9 +836,10 @@ public class DebugTab : Window, ITab if (!header) return; - DrawCopyableAddress("CharacterUtility", _characterUtility.Address); + DrawCopyableAddress("CharacterUtility", _characterUtility.Address); DrawCopyableAddress("ResidentResourceManager", _residentResources.Address); - DrawCopyableAddress("Device", Device.Instance()); + DrawCopyableAddress("Device", Device.Instance()); + DrawDebugResidentResources(); } /// Draw resources with unusual reference count.