1
0
mirror of synced 2025-02-17 18:59:21 +01:00

feat: Added per-byte highlights to the hex editor minimap

This commit is contained in:
WerWolv 2024-06-20 11:21:20 +02:00
parent 5d59b8599d
commit c5f5973a9d
6 changed files with 105 additions and 19 deletions

View File

@ -1086,7 +1086,7 @@ namespace hex {
};
struct MiniMapVisualizer {
using Callback = std::function<ImColor(const std::vector<u8>&)>;
using Callback = std::function<void(u64, std::span<const u8>, std::vector<ImColor>&)>;
UnlocalizedString unlocalizedName;
Callback callback;

View File

@ -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",

View File

@ -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",

View File

@ -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": "加法",

View File

@ -1,9 +1,13 @@
#include <hex/api/content_registry.hpp>
#include <imgui.h>
#include <imgui_internal.h>
#include <hex/api/imhex_api.hpp>
#include <hex/ui/imgui_imhex_extensions.h>
#include <hex/helpers/utils.hpp>
#include <pl/patterns/pattern.hpp>
#include <wolv/utils/lock.hpp>
#include <wolv/utils/string.hpp>
@ -12,7 +16,7 @@ namespace hex::plugin::builtin {
namespace {
ImColor entropyMiniMapVisualizer(const std::vector<u8> &data) {
void entropyMiniMapVisualizer(u64, std::span<const u8> data, std::vector<ImColor> &output) {
std::array<u8, 256> frequencies = { 0 };
for (u8 byte : data)
frequencies[byte] += 1;
@ -34,35 +38,99 @@ namespace hex::plugin::builtin {
color = ImColor::HSV(static_cast<float>(hue) / 0.75F, 0.8F, 1.0F);
}
return color;
output.push_back(color);
}
ImColor zerosMiniMapVisualizer(const std::vector<u8> &data) {
void zerosCountMiniMapVisualizer(u64, std::span<const u8> data, std::vector<ImColor> &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<u8> &data) {
void zerosMiniMapVisualizer(u64, std::span<const u8> data, std::vector<ImColor> &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<const u8> data, std::vector<ImColor> &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<const u8> data, std::vector<ImColor> &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<const u8> data, std::vector<ImColor> &output) {
for (u8 byte : data) {
output.push_back(ImColor::HSV(0.0F, 0.0F, static_cast<float>(byte) / 255.0F));
}
}
void highlightsMiniMapVisualizer(u64 address, std::span<const u8> data, std::vector<ImColor> &output) {
for (size_t i = 0; i < data.size(); i += 1) {
std::optional<ImColor> 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);
}
}

View File

@ -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<u8> rowData(m_bytesPerRow);
std::vector<ImColor> rowColors;
const auto drawStart = std::max<ImS64>(0, scrollPos - grabPos);
for (ImS64 y = drawStart; y < std::min<ImS64>(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();