From c5f5973a9dd7774e0e6c63251b0a0809e9fb60d0 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Thu, 20 Jun 2024 11:21:20 +0200 Subject: [PATCH] feat: Added per-byte highlights to the hex editor minimap --- .../include/hex/api/content_registry.hpp | 2 +- plugins/builtin/romfs/lang/en_US.json | 8 +- plugins/builtin/romfs/lang/hu_HU.json | 4 +- plugins/builtin/romfs/lang/zh_CN.json | 4 +- .../source/content/minimap_visualizers.cpp | 86 +++++++++++++++++-- plugins/ui/source/ui/hex_editor.cpp | 20 ++++- 6 files changed, 105 insertions(+), 19 deletions(-) diff --git a/lib/libimhex/include/hex/api/content_registry.hpp b/lib/libimhex/include/hex/api/content_registry.hpp index 7835c354a..b56db7bee 100644 --- a/lib/libimhex/include/hex/api/content_registry.hpp +++ b/lib/libimhex/include/hex/api/content_registry.hpp @@ -1086,7 +1086,7 @@ namespace hex { }; struct MiniMapVisualizer { - using Callback = std::function&)>; + using Callback = std::function, std::vector&)>; UnlocalizedString unlocalizedName; Callback callback; diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 2a937c4dc..4b3c9b7ef 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -175,8 +175,12 @@ "hex.builtin.menu.view.demo": "Show ImGui Demo", "hex.builtin.menu.view.fps": "Display FPS", "hex.builtin.minimap_visualizer.entropy": "Local Entropy", - "hex.builtin.minimap_visualizer.zeros": "Zeros Count", - "hex.builtin.minimap_visualizer.ascii": "ASCII Count", + "hex.builtin.minimap_visualizer.zero_count": "Zeros Count", + "hex.builtin.minimap_visualizer.zeros": "Zeros", + "hex.builtin.minimap_visualizer.ascii_count": "ASCII Count", + "hex.builtin.minimap_visualizer.byte_type": "Byte Type", + "hex.builtin.minimap_visualizer.highlights": "Highlights", + "hex.builtin.minimap_visualizer.byte_magnitude": "Byte Magnitude", "hex.builtin.nodes.arithmetic": "Arithmetic", "hex.builtin.nodes.arithmetic.add": "Addition", "hex.builtin.nodes.arithmetic.add.header": "Add", diff --git a/plugins/builtin/romfs/lang/hu_HU.json b/plugins/builtin/romfs/lang/hu_HU.json index d45df7b4c..9e2a008ba 100644 --- a/plugins/builtin/romfs/lang/hu_HU.json +++ b/plugins/builtin/romfs/lang/hu_HU.json @@ -175,8 +175,8 @@ "hex.builtin.menu.view.demo": "ImGui demó megjelenítése", "hex.builtin.menu.view.fps": "FPS megjelenítése", "hex.builtin.minimap_visualizer.entropy": "Lokális entrópia", - "hex.builtin.minimap_visualizer.zeros": "Zéró mennyiség", - "hex.builtin.minimap_visualizer.ascii": "ASCII mennyiség", + "hex.builtin.minimap_visualizer.zero_count": "Zéró mennyiség", + "hex.builtin.minimap_visualizer.ascii_count": "ASCII mennyiség", "hex.builtin.nodes.arithmetic": "Aritmetika", "hex.builtin.nodes.arithmetic.add": "Összeadás", "hex.builtin.nodes.arithmetic.add.header": "Összead", diff --git a/plugins/builtin/romfs/lang/zh_CN.json b/plugins/builtin/romfs/lang/zh_CN.json index 94616a502..958a0d4f0 100644 --- a/plugins/builtin/romfs/lang/zh_CN.json +++ b/plugins/builtin/romfs/lang/zh_CN.json @@ -201,9 +201,9 @@ "hex.builtin.menu.workspace.layout": "布局", "hex.builtin.menu.workspace.layout.lock": "锁定布局", "hex.builtin.menu.workspace.layout.save": "保存布局", - "hex.builtin.minimap_visualizer.ascii": "ASCII 计数", + "hex.builtin.minimap_visualizer.ascii_count": "ASCII 计数", "hex.builtin.minimap_visualizer.entropy": "局部熵", - "hex.builtin.minimap_visualizer.zeros": "0 计数", + "hex.builtin.minimap_visualizer.zero_count": "0 计数", "hex.builtin.nodes.arithmetic": "运算", "hex.builtin.nodes.arithmetic.add": "加法", "hex.builtin.nodes.arithmetic.add.header": "加法", diff --git a/plugins/builtin/source/content/minimap_visualizers.cpp b/plugins/builtin/source/content/minimap_visualizers.cpp index ffb758bb4..98fffbfb9 100644 --- a/plugins/builtin/source/content/minimap_visualizers.cpp +++ b/plugins/builtin/source/content/minimap_visualizers.cpp @@ -1,9 +1,13 @@ #include #include +#include +#include #include #include +#include +#include #include @@ -12,7 +16,7 @@ namespace hex::plugin::builtin { namespace { - ImColor entropyMiniMapVisualizer(const std::vector &data) { + void entropyMiniMapVisualizer(u64, std::span data, std::vector &output) { std::array frequencies = { 0 }; for (u8 byte : data) frequencies[byte] += 1; @@ -34,35 +38,99 @@ namespace hex::plugin::builtin { color = ImColor::HSV(static_cast(hue) / 0.75F, 0.8F, 1.0F); } - return color; + output.push_back(color); } - ImColor zerosMiniMapVisualizer(const std::vector &data) { + void zerosCountMiniMapVisualizer(u64, std::span data, std::vector &output) { u32 zerosCount = 0; for (u8 byte : data) { if (byte == 0x00) zerosCount += 1; } - return ImColor::HSV(0.0F, 0.0F, 1.0F - (double(zerosCount) / data.size())); + output.push_back(ImColor::HSV(0.0F, 0.0F, 1.0F - (double(zerosCount) / data.size()))); } - ImColor byteTypeMiniMapVisualizer(const std::vector &data) { + void zerosMiniMapVisualizer(u64, std::span data, std::vector &output) { + for (u8 byte : data) { + if (byte == 0x00) + output.push_back(ImColor(1.0F, 1.0F, 1.0F, 1.0F)); + else + output.push_back(ImColor(0.0F, 0.0F, 0.0F, 1.0F)); + } + } + + void byteTypeMiniMapVisualizer(u64, std::span data, std::vector &output) { + for (u8 byte : data) { + if (std::isalpha(byte)) + output.emplace_back(1.0F, 0.0F, 0.0F, 1.0F); + else if (std::isdigit(byte)) + output.emplace_back(0.0F, 1.0F, 0.0F, 1.0F); + else if (std::isspace(byte)) + output.emplace_back(0.0F, 0.0F, 1.0F, 1.0F); + else if (std::iscntrl(byte)) + output.emplace_back(0.5F, 0.5F, 0.5F, 1.0F); + else + output.emplace_back(0.0F, 0.0F, 0.0F, 1.0F); + } + } + + void asciiCountMiniMapVisualizer(u64, std::span data, std::vector &output) { u8 asciiCount = 0; for (u8 byte : data) { if (std::isprint(byte)) asciiCount += 1; } - return ImColor::HSV(0.5F, 0.5F, (double(asciiCount) / data.size())); + output.push_back(ImColor::HSV(0.5F, 0.5F, (double(asciiCount) / data.size()))); + } + + void byteMagnitudeMiniMapVisualizer(u64, std::span data, std::vector &output) { + for (u8 byte : data) { + output.push_back(ImColor::HSV(0.0F, 0.0F, static_cast(byte) / 255.0F)); + } + } + + void highlightsMiniMapVisualizer(u64 address, std::span data, std::vector &output) { + for (size_t i = 0; i < data.size(); i += 1) { + std::optional result; + for (const auto &[id, callback] : ImHexApi::HexEditor::impl::getBackgroundHighlightingFunctions()) { + if (auto color = callback(address + i, data.data() + i, 1, result.has_value()); color.has_value()) + result = color; + } + + if (!result.has_value()) { + for (const auto &[id, highlighting] : ImHexApi::HexEditor::impl::getBackgroundHighlights()) { + if (highlighting.getRegion().overlaps({ address, 1 })) { + result = highlighting.getColor(); + break; + } + } + } + + if (result.has_value()) { + result->Value.w = 1.0F; + } else { + if (auto region = ImHexApi::HexEditor::getSelection(); region.has_value()) { + if (region->overlaps({ address + i, 1 })) + result = 0x60C08080; + } + } + + output.push_back(result.value_or(ImColor())); + } } } void registerMiniMapVisualizers() { - ContentRegistry::HexEditor::addMiniMapVisualizer("hex.builtin.minimap_visualizer.entropy", entropyMiniMapVisualizer); - ContentRegistry::HexEditor::addMiniMapVisualizer("hex.builtin.minimap_visualizer.zeros", zerosMiniMapVisualizer); - ContentRegistry::HexEditor::addMiniMapVisualizer("hex.builtin.minimap_visualizer.ascii", byteTypeMiniMapVisualizer); + ContentRegistry::HexEditor::addMiniMapVisualizer("hex.builtin.minimap_visualizer.entropy", entropyMiniMapVisualizer); + ContentRegistry::HexEditor::addMiniMapVisualizer("hex.builtin.minimap_visualizer.zero_count", zerosCountMiniMapVisualizer); + ContentRegistry::HexEditor::addMiniMapVisualizer("hex.builtin.minimap_visualizer.zeros", zerosMiniMapVisualizer); + ContentRegistry::HexEditor::addMiniMapVisualizer("hex.builtin.minimap_visualizer.ascii_count", asciiCountMiniMapVisualizer); + ContentRegistry::HexEditor::addMiniMapVisualizer("hex.builtin.minimap_visualizer.byte_type", byteTypeMiniMapVisualizer); + ContentRegistry::HexEditor::addMiniMapVisualizer("hex.builtin.minimap_visualizer.highlights", highlightsMiniMapVisualizer); + ContentRegistry::HexEditor::addMiniMapVisualizer("hex.builtin.minimap_visualizer.byte_magnitude", byteMagnitudeMiniMapVisualizer); } } \ No newline at end of file diff --git a/plugins/ui/source/ui/hex_editor.cpp b/plugins/ui/source/ui/hex_editor.cpp index 965343bb7..efc83886d 100644 --- a/plugins/ui/source/ui/hex_editor.cpp +++ b/plugins/ui/source/ui/hex_editor.cpp @@ -195,7 +195,10 @@ namespace hex::ui { } void HexEditor::drawMinimap(ImVec2 characterSize) { - ImS64 numRows = m_provider == nullptr ? 0 : (m_provider->getSize() / m_bytesPerRow) + ((m_provider->getSize() % m_bytesPerRow) == 0 ? 0 : 1); + if (m_provider == nullptr) + return; + + ImS64 numRows = (m_provider->getSize() / m_bytesPerRow) + ((m_provider->getSize() % m_bytesPerRow) == 0 ? 0 : 1); auto window = ImGui::GetCurrentWindowRead(); const auto outerRect = window->Rect(); @@ -239,14 +242,25 @@ namespace hex::ui { drawList->ChannelsSetCurrent(0); std::vector rowData(m_bytesPerRow); + std::vector rowColors; const auto drawStart = std::max(0, scrollPos - grabPos); for (ImS64 y = drawStart; y < std::min(drawStart + rowCount, m_provider->getSize() / m_bytesPerRow); y += 1) { const auto rowStart = bb.Min + ImVec2(0, (y - drawStart) * rowHeight); const auto rowEnd = rowStart + ImVec2(bb.GetSize().x, rowHeight); + const auto rowSize = rowEnd - rowStart; - m_provider->read(y * m_bytesPerRow + m_provider->getBaseAddress() + m_provider->getCurrentPageAddress(), rowData.data(), rowData.size()); + const auto address = y * m_bytesPerRow + m_provider->getBaseAddress() + m_provider->getCurrentPageAddress(); + m_provider->read(address, rowData.data(), rowData.size()); - drawList->AddRectFilled(rowStart, rowEnd, m_miniMapVisualizer->callback(rowData)); + m_miniMapVisualizer->callback(address, rowData, rowColors); + + const auto cellSize = rowSize / ImVec2(rowColors.size(), 1); + ImVec2 cellPos = rowStart; + for (const auto &rowColor : rowColors) { + drawList->AddRectFilled(cellPos, cellPos + cellSize, rowColor); + cellPos.x += cellSize.x; + } + rowColors.clear(); } drawList->ChannelsMerge();