// Draw information about the resident resource files. public unsafe void DrawDebugResidentResources() { if (!ImGui.CollapsingHeader("Resident Resources")) { return; } if (Penumbra.ResidentResources.Address == null || Penumbra.ResidentResources.Address->NumResources == 0) { return; } using var table = ImRaii.Table("##ResidentResources", 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.UnitX); if (!table) { return; } for (var i = 0; i < Penumbra.ResidentResources.Address->NumResources; ++i) { var resource = Penumbra.ResidentResources.Address->ResourceList[i]; ImGui.TableNextColumn(); ImGui.TextUnformatted($"0x{( ulong )resource:X}"); ImGui.TableNextColumn(); Text(resource); } }
// The headers for the different meta changes all have basically the same structure for different types. private void DrawEditHeader <T>(IReadOnlyCollection <T> items, string label, int numColumns, Action <T, Mod.Editor, Vector2> draw, Action <Mod.Editor, Vector2> drawNew) { const ImGuiTableFlags flags = ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.BordersInnerV; if (!ImGui.CollapsingHeader($"{items.Count} {label}")) { return; } using (var table = ImRaii.Table(label, numColumns, flags)) { if (table) { drawNew(_editor !, _iconSize); ImGui.Separator(); foreach (var(item, index) in items.ToArray().WithIndex()) { using var id = ImRaii.PushId(index); draw(item, _editor !, _iconSize); } } } ImGui.NewLine(); }
private unsafe void DrawResourceMap(ResourceCategory category, uint ext, StdMap <uint, Pointer <ResourceHandle> > *map) { if (map == null) { return; } var label = GetNodeLabel(( uint )category, ext, map->Count); using var tree = ImRaii.TreeNode(label); if (!tree || map->Count == 0) { return; } using var table = ImRaii.Table("##table", 4, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg); if (!table) { return; } ImGui.TableSetupColumn("Hash", ImGuiTableColumnFlags.WidthFixed, _hashColumnWidth); ImGui.TableSetupColumn("Ptr", ImGuiTableColumnFlags.WidthFixed, _hashColumnWidth); ImGui.TableSetupColumn("Path", ImGuiTableColumnFlags.WidthFixed, _pathColumnWidth); ImGui.TableSetupColumn("Refs", ImGuiTableColumnFlags.WidthFixed, _refsColumnWidth); ImGui.TableHeadersRow(); ResourceLoader.IterateResourceMap(map, (hash, r) => { // Filter unwanted names. if (_resourceManagerFilter.Length != 0 && !r->FileName.ToString().Contains(_resourceManagerFilter, StringComparison.InvariantCultureIgnoreCase)) { return; } var address = $"0x{( ulong )r:X}"; ImGuiUtil.TextNextColumn($"0x{hash:X8}"); ImGui.TableNextColumn(); ImGuiUtil.CopyOnClickSelectable(address); var resource = (Interop.Structs.ResourceHandle *)r; ImGui.TableNextColumn(); Text(resource); if (ImGui.IsItemClicked()) { var data = Interop.Structs.ResourceHandle.GetData(resource); if (data != null) { var length = ( int )Interop.Structs.ResourceHandle.GetLength(resource); ImGui.SetClipboardText(string.Join(" ", new ReadOnlySpan <byte>(data, length).ToArray().Select(b => b.ToString("X2")))); } } ImGuiUtil.HoverTooltip("Click to copy byte-wise file data to clipboard, if any."); ImGuiUtil.TextNextColumn(r->RefCount.ToString()); }); }
// Draw general information about mod and collection state. private void DrawDebugTabGeneral() { if (!ImGui.CollapsingHeader("General")) { return; } using var table = ImRaii.Table("##DebugGeneralTable", 2, ImGuiTableFlags.SizingFixedFit, new Vector2(-1, ImGui.GetTextLineHeightWithSpacing() * 1)); if (!table) { return; } var manager = Penumbra.ModManager; PrintValue("Penumbra Version", $"{Penumbra.Version} {DebugVersionString}"); PrintValue("Git Commit Hash", Penumbra.CommitHash); PrintValue("Current Collection", Penumbra.CollectionManager.Current.Name); PrintValue(" has Cache", Penumbra.CollectionManager.Current.HasCache.ToString()); PrintValue("Default Collection", Penumbra.CollectionManager.Default.Name); PrintValue(" has Cache", Penumbra.CollectionManager.Default.HasCache.ToString()); PrintValue("Mod Manager BasePath", manager.BasePath.Name); PrintValue("Mod Manager BasePath-Full", manager.BasePath.FullName); PrintValue("Mod Manager BasePath IsRooted", Path.IsPathRooted(Penumbra.Config.ModDirectory).ToString()); PrintValue("Mod Manager BasePath Exists", Directory.Exists(manager.BasePath.FullName).ToString()); PrintValue("Mod Manager Valid", manager.Valid.ToString()); PrintValue("Path Resolver Enabled", _window._penumbra.PathResolver.Enabled.ToString()); PrintValue("Music Manager Streaming Disabled", (!_window._penumbra.MusicManager.StreamingEnabled).ToString()); PrintValue("Web Server Enabled", (_window._penumbra.WebServer != null).ToString()); }
private static bool DrawMatrixInput(float width, ref Matrix4x4 matrix) { using var table = ImRaii.Table(string.Empty, 5, ImGuiTableFlags.BordersInner | ImGuiTableFlags.SizingFixedFit); if (!table) { return(false); } var changes = false; ImGui.TableNextColumn(); ImGui.TableNextColumn(); ImGuiUtil.Center("R"); ImGui.TableNextColumn(); ImGuiUtil.Center("G"); ImGui.TableNextColumn(); ImGuiUtil.Center("B"); ImGui.TableNextColumn(); ImGuiUtil.Center("A"); var inputWidth = width / 6; ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.Text("R "); changes |= DragFloat("##RR", inputWidth, ref matrix.M11); changes |= DragFloat("##RG", inputWidth, ref matrix.M12); changes |= DragFloat("##RB", inputWidth, ref matrix.M13); changes |= DragFloat("##RA", inputWidth, ref matrix.M14); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.Text("G "); changes |= DragFloat("##GR", inputWidth, ref matrix.M21); changes |= DragFloat("##GG", inputWidth, ref matrix.M22); changes |= DragFloat("##GB", inputWidth, ref matrix.M23); changes |= DragFloat("##GA", inputWidth, ref matrix.M24); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.Text("B "); changes |= DragFloat("##BR", inputWidth, ref matrix.M31); changes |= DragFloat("##BG", inputWidth, ref matrix.M32); changes |= DragFloat("##BB", inputWidth, ref matrix.M33); changes |= DragFloat("##BA", inputWidth, ref matrix.M34); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.Text("A "); changes |= DragFloat("##AR", inputWidth, ref matrix.M41); changes |= DragFloat("##AG", inputWidth, ref matrix.M42); changes |= DragFloat("##AB", inputWidth, ref matrix.M43); changes |= DragFloat("##AA", inputWidth, ref matrix.M44); return(changes); }
// Draw information about the character utility class from SE, // displaying all files, their sizes, the default files and the default sizes. public unsafe void DrawDebugCharacterUtility() { if (!ImGui.CollapsingHeader("Character Utility")) { return; } using var table = ImRaii.Table("##CharacterUtility", 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.UnitX); if (!table) { return; } for (var i = 0; i < CharacterUtility.RelevantIndices.Length; ++i) { var idx = CharacterUtility.RelevantIndices[i]; var resource = ( ResourceHandle * )Penumbra.CharacterUtility.Address->Resources[idx]; ImGui.TableNextColumn(); ImGui.TextUnformatted($"0x{( ulong )resource:X}"); ImGui.TableNextColumn(); Text(resource); ImGui.TableNextColumn(); ImGui.Selectable($"0x{resource->GetData().Data:X}"); if (ImGui.IsItemClicked()) { var(data, length) = resource->GetData(); if (data != IntPtr.Zero && length > 0) { ImGui.SetClipboardText(string.Join("\n", new ReadOnlySpan <byte>(( byte * )data, length).ToArray().Select(b => b.ToString("X2")))); } } ImGuiUtil.HoverTooltip("Click to copy bytes to clipboard."); ImGui.TableNextColumn(); ImGui.TextUnformatted($"{resource->GetData().Length}"); ImGui.TableNextColumn(); ImGui.Selectable($"0x{Penumbra.CharacterUtility.DefaultResources[ i ].Address:X}"); if (ImGui.IsItemClicked()) { ImGui.SetClipboardText(string.Join("\n", new ReadOnlySpan <byte>(( byte * )Penumbra.CharacterUtility.DefaultResources[i].Address, Penumbra.CharacterUtility.DefaultResources[i].Size).ToArray().Select(b => b.ToString("X2")))); } ImGuiUtil.HoverTooltip("Click to copy bytes to clipboard."); ImGui.TableNextColumn(); ImGui.TextUnformatted($"{Penumbra.CharacterUtility.DefaultResources[ i ].Size}"); } }
// Draw resources with unusual reference count. private static unsafe void DrawResourceProblems() { var header = ImGui.CollapsingHeader("Resource Problems"); ImGuiUtil.HoverTooltip("Draw resources with unusually high reference count to detect overflows."); if (!header) { return; } using var table = ImRaii.Table("##ProblemsTable", 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit); if (!table) { return; } ResourceLoader.IterateResources((_, r) => { if (r->RefCount < 10000) { return; } ImGui.TableNextColumn(); ImGui.TextUnformatted(r->Category.ToString()); ImGui.TableNextColumn(); ImGui.TextUnformatted(r->FileType.ToString("X")); ImGui.TableNextColumn(); ImGui.TextUnformatted(r->Id.ToString("X")); ImGui.TableNextColumn(); ImGui.TextUnformatted((( ulong )r).ToString("X")); ImGui.TableNextColumn(); ImGui.TextUnformatted(r->RefCount.ToString()); ImGui.TableNextColumn(); ref var name = ref r->FileName; if (name.Capacity > 15) { ImGuiNative.igTextUnformatted(name.BufferPtr, name.BufferPtr + name.Length); } else { fixed(byte *ptr = name.Buffer) { ImGuiNative.igTextUnformatted(ptr, ptr + name.Length); } } });
// Draw information about which draw objects correspond to which game objects // and which paths are due to be loaded by which collection. private unsafe void DrawPathResolverDebug() { if (!ImGui.CollapsingHeader("Path Resolver")) { return; } using var drawTree = ImRaii.TreeNode("Draw Object to Object"); if (drawTree) { using var table = ImRaii.Table("###DrawObjectResolverTable", 5, ImGuiTableFlags.SizingFixedFit); if (table) { foreach (var(ptr, (c, idx)) in _window._penumbra.PathResolver.DrawObjectToObject) { ImGui.TableNextColumn(); ImGui.TextUnformatted(ptr.ToString("X")); ImGui.TableNextColumn(); ImGui.TextUnformatted(idx.ToString()); ImGui.TableNextColumn(); ImGui.TextUnformatted(Dalamud.Objects[idx]?.Address.ToString() ?? "NULL"); ImGui.TableNextColumn(); ImGui.TextUnformatted(Dalamud.Objects[idx]?.Name.ToString() ?? "NULL"); ImGui.TableNextColumn(); ImGui.TextUnformatted(c.Name); } } } drawTree.Dispose(); using var pathTree = ImRaii.TreeNode("Path Collections"); if (pathTree) { using var table = ImRaii.Table("###PathCollectionResolverTable", 2, ImGuiTableFlags.SizingFixedFit); if (table) { foreach (var(path, collection) in _window._penumbra.PathResolver.PathCollections) { ImGui.TableNextColumn(); ImGuiNative.igTextUnformatted(path.Path, path.Path + path.Length); ImGui.TableNextColumn(); ImGui.TextUnformatted(collection.Name); } } } }
// Draw all resources currently replaced by Penumbra and (if existing) the resources they replace. // Resources are collected by iterating through the private static unsafe void DrawDebugTabReplacedResources() { if (!ImGui.CollapsingHeader("Replaced Resources")) { return; } Penumbra.ResourceLoader.UpdateDebugInfo(); if (Penumbra.ResourceLoader.DebugList.Count == 0) { return; } using var table = ImRaii.Table("##ReplacedResources", 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.UnitX); if (!table) { return; } foreach (var data in Penumbra.ResourceLoader.DebugList.Values.ToArray()) { if (data.ManipulatedPath.Crc64 == 0) { continue; } var refCountManip = data.ManipulatedResource == null ? 0 : data.ManipulatedResource->RefCount; var refCountOrig = data.OriginalResource == null ? 0 : data.OriginalResource->RefCount; ImGui.TableNextColumn(); ImGui.TextUnformatted(data.ManipulatedPath.ToString()); ImGui.TableNextColumn(); ImGui.TextUnformatted((( ulong )data.ManipulatedResource).ToString("X")); ImGui.TableNextColumn(); ImGui.TextUnformatted(refCountManip.ToString()); ImGui.TableNextColumn(); ImGui.TextUnformatted(data.OriginalPath.ToString()); ImGui.TableNextColumn(); ImGui.TextUnformatted((( ulong )data.OriginalResource).ToString("X")); ImGui.TableNextColumn(); ImGui.TextUnformatted(refCountOrig.ToString()); } }
public static void Draw(ModPanel panel, int groupIdx) { using var table = ImRaii.Table(string.Empty, 4, ImGuiTableFlags.SizingFixedFit); if (!table) { return; } ImGui.TableSetupColumn("idx", ImGuiTableColumnFlags.WidthFixed, 60 * ImGuiHelpers.GlobalScale); ImGui.TableSetupColumn("name", ImGuiTableColumnFlags.WidthFixed, panel._window._inputTextWidth.X - 62 * ImGuiHelpers.GlobalScale); ImGui.TableSetupColumn("delete", ImGuiTableColumnFlags.WidthFixed, panel._window._iconButtonSize.X); ImGui.TableSetupColumn("priority", ImGuiTableColumnFlags.WidthFixed, 50 * ImGuiHelpers.GlobalScale); var group = panel._mod.Groups[groupIdx]; for (var optionIdx = 0; optionIdx < group.Count; ++optionIdx) { EditOption(panel, group, groupIdx, optionIdx); } DrawNewOption(panel._mod, groupIdx, panel._window._iconButtonSize); }
// Draw the effective tab if ShowAdvanced is on. public void Draw() { if (!Penumbra.Config.ShowAdvanced) { return; } using var tab = ImRaii.TabItem("Effective Changes"); if (!tab) { return; } SetupEffectiveSizes(); DrawFilters(); using var child = ImRaii.Child("##EffectiveChangesTab", -Vector2.One, false); if (!child) { return; } var height = ImGui.GetTextLineHeightWithSpacing() + 2 * ImGui.GetStyle().CellPadding.Y; var skips = ImGuiClip.GetNecessarySkips(height); using var table = ImRaii.Table("##EffectiveChangesTable", 3, ImGuiTableFlags.RowBg); if (!table) { return; } ImGui.TableSetupColumn("##gamePath", ImGuiTableColumnFlags.WidthFixed, _effectiveLeftTextLength); ImGui.TableSetupColumn(string.Empty, ImGuiTableColumnFlags.WidthFixed, _effectiveArrowLength); ImGui.TableSetupColumn("##file", ImGuiTableColumnFlags.WidthFixed, _effectiveRightTextLength); DrawEffectiveRows(Penumbra.CollectionManager.Current, skips, height, _effectiveFilePathFilter.Length > 0 || _effectiveGamePathFilter.Length > 0); }
// Draw a simple clipped table containing all changed items. private void DrawChangedItemTab() { // Functions in here for less pollution. bool FilterChangedItem(KeyValuePair <string, (SingleArray <IMod>, object?)> item) => (_changedItemFilter.IsEmpty || ChangedItemName(item.Key, item.Value.Item2) .Contains(_changedItemFilter.Lower, StringComparison.InvariantCultureIgnoreCase)) && (_changedItemModFilter.IsEmpty || item.Value.Item1.Any(m => m.Name.Contains(_changedItemModFilter))); void DrawChangedItemColumn(KeyValuePair <string, (SingleArray <IMod>, object?)> item) { ImGui.TableNextColumn(); DrawChangedItem(item.Key, item.Value.Item2, false); ImGui.TableNextColumn(); if (item.Value.Item1.Count > 0) { ImGui.TextUnformatted(item.Value.Item1[0].Name); if (item.Value.Item1.Count > 1) { ImGuiUtil.HoverTooltip(string.Join("\n", item.Value.Item1.Skip(1).Select(m => m.Name))); } } ImGui.TableNextColumn(); if (item.Value.Item2 is Item it) { using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.ItemId.Value()); ImGuiUtil.RightAlign($"({( ( Quad )it.ModelMain ).A})"); } } using var tab = ImRaii.TabItem("Changed Items"); if (!tab) { return; } // Draw filters. var varWidth = ImGui.GetContentRegionAvail().X - 400 * ImGuiHelpers.GlobalScale - ImGui.GetStyle().ItemSpacing.X; ImGui.SetNextItemWidth(400 * ImGuiHelpers.GlobalScale); LowerString.InputWithHint("##changedItemsFilter", "Filter Item...", ref _changedItemFilter, 128); ImGui.SameLine(); ImGui.SetNextItemWidth(varWidth); LowerString.InputWithHint("##changedItemsModFilter", "Filter Mods...", ref _changedItemModFilter, 128); using var child = ImRaii.Child("##changedItemsChild", -Vector2.One); if (!child) { return; } // Draw table of changed items. var height = ImGui.GetTextLineHeightWithSpacing() + 2 * ImGui.GetStyle().CellPadding.Y; var skips = ImGuiClip.GetNecessarySkips(height); using var list = ImRaii.Table("##changedItems", 3, ImGuiTableFlags.RowBg, -Vector2.One); if (!list) { return; } const ImGuiTableColumnFlags flags = ImGuiTableColumnFlags.NoResize | ImGuiTableColumnFlags.WidthFixed; ImGui.TableSetupColumn("items", flags, 400 * ImGuiHelpers.GlobalScale); ImGui.TableSetupColumn("mods", flags, varWidth - 100 * ImGuiHelpers.GlobalScale); ImGui.TableSetupColumn("id", flags, 100 * ImGuiHelpers.GlobalScale); var items = Penumbra.CollectionManager.Current.ChangedItems; var rest = _changedItemFilter.IsEmpty && _changedItemModFilter.IsEmpty ? ImGuiClip.ClippedDraw(items, skips, DrawChangedItemColumn, items.Count) : ImGuiClip.FilteredClippedDraw(items, skips, FilterChangedItem, DrawChangedItemColumn); ImGuiClip.DrawEndDummy(rest, height); }
// Draw information about the models, materials and resources currently loaded by the local player. private static unsafe void DrawPlayerModelInfo() { var player = Dalamud.ClientState.LocalPlayer; var name = player?.Name.ToString() ?? "NULL"; if (!ImGui.CollapsingHeader($"Player Model Info: {name}##Draw") || player == null) { return; } var model = ( CharacterBase * )(( Character * )player.Address)->GameObject.GetDrawObject(); if (model == null) { return; } using var table = ImRaii.Table($"##{name}DrawTable", 5, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit); if (!table) { return; } ImGui.TableNextColumn(); ImGui.TableHeader("Slot"); ImGui.TableNextColumn(); ImGui.TableHeader("Imc Ptr"); ImGui.TableNextColumn(); ImGui.TableHeader("Imc File"); ImGui.TableNextColumn(); ImGui.TableHeader("Model Ptr"); ImGui.TableNextColumn(); ImGui.TableHeader("Model File"); for (var i = 0; i < model->SlotCount; ++i) { var imc = ( ResourceHandle * )model->IMCArray[i]; ImGui.TableNextRow(); ImGui.TableNextColumn(); ImGui.TextUnformatted($"Slot {i}"); ImGui.TableNextColumn(); ImGui.TextUnformatted(imc == null ? "NULL" : $"0x{( ulong )imc:X}"); ImGui.TableNextColumn(); if (imc != null) { Text(imc); } var mdl = ( RenderModel * )model->ModelArray[i]; ImGui.TableNextColumn(); ImGui.TextUnformatted(mdl == null ? "NULL" : $"0x{( ulong )mdl:X}"); if (mdl == null || mdl->ResourceHandle == null || mdl->ResourceHandle->Category != ResourceCategory.Chara) { continue; } ImGui.TableNextColumn(); { Text(mdl->ResourceHandle); } } }