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

nodes: Added Digram visualizer node

This commit is contained in:
WerWolv 2022-02-05 22:19:32 +01:00
parent f9668f4ba6
commit ca57f91bfa
6 changed files with 165 additions and 44 deletions

View File

@ -173,7 +173,7 @@ namespace hex::init {
// On Macs with a retina display (basically all modern ones we care about), the OS reports twice // On Macs with a retina display (basically all modern ones we care about), the OS reports twice
// the actual monitor scale for some obscure reason. Get rid of this here so ImHex doesn't look // the actual monitor scale for some obscure reason. Get rid of this here so ImHex doesn't look
// extremely huge with native scaling on MacOS. // extremely huge with native scaling on macOS.
#if defined(OS_MACOS) #if defined(OS_MACOS)
meanScale /= 2; meanScale /= 2;
#endif #endif

View File

@ -35,6 +35,8 @@ namespace hex::plugin::builtin {
std::optional<dp::Node::NodeError> m_currNodeError; std::optional<dp::Node::NodeError> m_currNodeError;
bool m_continuousEvaluation = false;
void eraseLink(u32 id); void eraseLink(u32 id);
void eraseNodes(const std::vector<int> &ids); void eraseNodes(const std::vector<int> &ids);
void processNodes(); void processNodes();

View File

@ -1,18 +1,20 @@
#include <hex/api/content_registry.hpp> #include <hex/api/content_registry.hpp>
#include <hex/data_processor/node.hpp> #include <hex/data_processor/node.hpp>
#include <hex/api/localization.hpp>
#include <hex/helpers/crypto.hpp> #include <hex/helpers/crypto.hpp>
#include <hex/helpers/utils.hpp> #include <hex/helpers/utils.hpp>
#include <hex/api/localization.hpp> #include <hex/helpers/logger.hpp>
#include <hex/providers/provider.hpp> #include <hex/providers/provider.hpp>
#include <cctype> #include <cctype>
#include <random>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <imgui.h> #include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h> #include <hex/ui/imgui_imhex_extensions.h>
#include <hex/ui/imgui_data_visualizers.hpp>
namespace hex::plugin::builtin { namespace hex::plugin::builtin {
@ -751,6 +753,89 @@ namespace hex::plugin::builtin {
} }
}; };
class NodeVisualizerDigram : public dp::Node {
public:
NodeVisualizerDigram() : Node("hex.builtin.nodes.visualizer.digram.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.visualizer.digram.input") }) { }
void drawNode() override {
const auto viewSize = scaled({ 200, 200 });
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImU32(ImColor(0, 0, 0)));
if (ImGui::BeginChild("##visualizer", viewSize, true)) {
auto drawList = ImGui::GetWindowDrawList();
float xStep = (viewSize.x * 0.95F) / 0xFF;
float yStep = (viewSize.y * 0.95F) / 0xFF;
for (size_t i = 0; i < ((this->m_buffer.size() == 0) ? 0 : this->m_buffer.size() - 1); i++) {
const auto &[x, y] = std::pair { this->m_buffer[i] * xStep, this->m_buffer[i + 1] * yStep };
auto color = ImLerp(ImColor(0xFF, 0x6D, 0x01).Value, ImColor(0x01, 0x93, 0xFF).Value, float(i) / this->m_buffer.size());
color.w = this->m_opacityBuffer[i];
auto pos = ImGui::GetWindowPos() + ImVec2(viewSize.x * 0.025F, viewSize.y * 0.025F) + ImVec2(x, y);
drawList->AddRectFilled(pos, pos + ImVec2(xStep, yStep), ImColor(color));
}
}
ImGui::EndChild();
ImGui::PopStyleColor();
}
void process() override {
constexpr static auto SampleSize = 0x9000;
const static size_t SequenceCount = std::ceil(std::sqrt(SampleSize));
this->m_buffer.clear();
auto buffer = this->getBufferOnInput(0);
if (buffer.size() < SampleSize)
this->m_buffer = buffer;
else {
std::random_device randomDevice;
std::mt19937_64 random(randomDevice());
std::map<u64, std::vector<u8>> orderedData;
for (u32 i = 0; i < SequenceCount; i++) {
ssize_t offset = random() % buffer.size();
std::vector<u8> sequence;
sequence.reserve(SampleSize);
std::copy(buffer.begin() + offset, buffer.begin() + offset + std::min<size_t>(SequenceCount, buffer.size() - offset), std::back_inserter(sequence));
orderedData.insert({ offset, sequence });
}
this->m_buffer.reserve(SampleSize);
u64 lastEnd = 0x00;
for (const auto &[offset, sequence] : orderedData) {
if (offset < lastEnd)
this->m_buffer.resize(this->m_buffer.size() - (lastEnd - offset));
std::copy(sequence.begin(), sequence.end(), std::back_inserter(this->m_buffer));
lastEnd = offset + sequence.size();
}
}
this->m_opacityBuffer.resize(this->m_buffer.size());
std::map<u64, size_t> heatMap;
for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size() - 1); i++) {
auto count = ++heatMap[this->m_buffer[i] << 8 | heatMap[i + 1]];
this->m_highestCount = std::max(this->m_highestCount, count);
}
for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size() - 1); i++) {
this->m_opacityBuffer[i] = std::min(0.2F + (float(heatMap[this->m_buffer[i] << 8 | this->m_buffer[i + 1]]) / float(this->m_highestCount / 1000)), 1.0F);
}
}
private:
std::vector<u8> m_buffer;
std::vector<float> m_opacityBuffer;
size_t m_highestCount = 0;
};
void registerDataProcessorNodes() { void registerDataProcessorNodes() {
ContentRegistry::DataProcessorNode::add<NodeInteger>("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.int"); ContentRegistry::DataProcessorNode::add<NodeInteger>("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.int");
ContentRegistry::DataProcessorNode::add<NodeFloat>("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.float"); ContentRegistry::DataProcessorNode::add<NodeFloat>("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.float");
@ -797,6 +882,8 @@ namespace hex::plugin::builtin {
ContentRegistry::DataProcessorNode::add<NodeDecodingHex>("hex.builtin.nodes.decoding", "hex.builtin.nodes.decoding.hex"); ContentRegistry::DataProcessorNode::add<NodeDecodingHex>("hex.builtin.nodes.decoding", "hex.builtin.nodes.decoding.hex");
ContentRegistry::DataProcessorNode::add<NodeCryptoAESDecrypt>("hex.builtin.nodes.crypto", "hex.builtin.nodes.crypto.aes"); ContentRegistry::DataProcessorNode::add<NodeCryptoAESDecrypt>("hex.builtin.nodes.crypto", "hex.builtin.nodes.crypto.aes");
ContentRegistry::DataProcessorNode::add<NodeVisualizerDigram>("hex.builtin.nodes.visualizer", "hex.builtin.nodes.visualizer.digram");
} }
} }

View File

@ -48,6 +48,10 @@ namespace hex::plugin::builtin {
this->m_dataOverlays.clear(); this->m_dataOverlays.clear();
}); });
EventManager::subscribe<EventDataChanged>(this, [this] {
this->processNodes();
});
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 3000, [&, this] { ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 3000, [&, this] {
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.file.load_processor"_lang)) { if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.file.load_processor"_lang)) {
hex::openFileBrowser("hex.builtin.view.data_processor.menu.file.load_processor"_lang, DialogMode::Open, { hex::openFileBrowser("hex.builtin.view.data_processor.menu.file.load_processor"_lang, DialogMode::Open, {
@ -71,6 +75,15 @@ namespace hex::plugin::builtin {
}); });
} }
}); });
ContentRegistry::FileHandler::add({ ".hexnode" }, [this](const auto &path) {
File file(path, File::Mode::Read);
if (!file.isValid()) return false;
this->loadNodes(file.readString());
return true;
});
} }
ViewDataProcessor::~ViewDataProcessor() { ViewDataProcessor::~ViewDataProcessor() {
@ -81,6 +94,7 @@ namespace hex::plugin::builtin {
EventManager::unsubscribe<EventFileLoaded>(this); EventManager::unsubscribe<EventFileLoaded>(this);
EventManager::unsubscribe<EventProjectFileStore>(this); EventManager::unsubscribe<EventProjectFileStore>(this);
EventManager::unsubscribe<EventProjectFileLoad>(this); EventManager::unsubscribe<EventProjectFileLoad>(this);
EventManager::unsubscribe<EventDataChanged>(this);
} }
@ -129,6 +143,7 @@ namespace hex::plugin::builtin {
} }
void ViewDataProcessor::processNodes() { void ViewDataProcessor::processNodes() {
if (this->m_dataOverlays.size() != this->m_endNodes.size()) { if (this->m_dataOverlays.size() != this->m_endNodes.size()) {
for (auto overlay : this->m_dataOverlays) for (auto overlay : this->m_dataOverlays)
ImHexApi::Provider::get()->deleteOverlay(overlay); ImHexApi::Provider::get()->deleteOverlay(overlay);
@ -271,60 +286,69 @@ namespace hex::plugin::builtin {
} }
} }
ImNodes::BeginNodeEditor(); if (ImGui::BeginChild("##node_editor", ImGui::GetContentRegionAvail() - ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 1.3))) {
ImNodes::BeginNodeEditor();
for (auto &node : this->m_nodes) { for (auto &node : this->m_nodes) {
const bool hasError = this->m_currNodeError.has_value() && this->m_currNodeError->first == node; const bool hasError = this->m_currNodeError.has_value() && this->m_currNodeError->first == node;
if (hasError) if (hasError)
ImNodes::PushColorStyle(ImNodesCol_NodeOutline, 0xFF0000FF); ImNodes::PushColorStyle(ImNodesCol_NodeOutline, 0xFF0000FF);
ImNodes::BeginNode(node->getId()); ImNodes::BeginNode(node->getId());
ImNodes::BeginNodeTitleBar(); ImNodes::BeginNodeTitleBar();
ImGui::TextUnformatted(LangEntry(node->getUnlocalizedTitle())); ImGui::TextUnformatted(LangEntry(node->getUnlocalizedTitle()));
ImNodes::EndNodeTitleBar(); ImNodes::EndNodeTitleBar();
node->drawNode(); node->drawNode();
for (auto &attribute : node->getAttributes()) { for (auto &attribute : node->getAttributes()) {
ImNodesPinShape pinShape; ImNodesPinShape pinShape;
switch (attribute.getType()) { switch (attribute.getType()) {
case dp::Attribute::Type::Integer: case dp::Attribute::Type::Integer:
pinShape = ImNodesPinShape_Circle; pinShape = ImNodesPinShape_Circle;
break; break;
case dp::Attribute::Type::Float: case dp::Attribute::Type::Float:
pinShape = ImNodesPinShape_Triangle; pinShape = ImNodesPinShape_Triangle;
break; break;
case dp::Attribute::Type::Buffer: case dp::Attribute::Type::Buffer:
pinShape = ImNodesPinShape_Quad; pinShape = ImNodesPinShape_Quad;
break; break;
}
if (attribute.getIOType() == dp::Attribute::IOType::In) {
ImNodes::BeginInputAttribute(attribute.getId(), pinShape);
ImGui::TextUnformatted(LangEntry(attribute.getUnlocalizedName()));
ImNodes::EndInputAttribute();
} else if (attribute.getIOType() == dp::Attribute::IOType::Out) {
ImNodes::BeginOutputAttribute(attribute.getId(), ImNodesPinShape(pinShape + 1));
ImGui::TextUnformatted(LangEntry(attribute.getUnlocalizedName()));
ImNodes::EndOutputAttribute();
}
} }
if (attribute.getIOType() == dp::Attribute::IOType::In) { ImNodes::EndNode();
ImNodes::BeginInputAttribute(attribute.getId(), pinShape);
ImGui::TextUnformatted(LangEntry(attribute.getUnlocalizedName())); if (hasError)
ImNodes::EndInputAttribute(); ImNodes::PopColorStyle();
} else if (attribute.getIOType() == dp::Attribute::IOType::Out) {
ImNodes::BeginOutputAttribute(attribute.getId(), ImNodesPinShape(pinShape + 1));
ImGui::TextUnformatted(LangEntry(attribute.getUnlocalizedName()));
ImNodes::EndOutputAttribute();
}
} }
ImNodes::EndNode(); for (const auto &link : this->m_links)
ImNodes::Link(link.getId(), link.getFromId(), link.getToId());
if (hasError) ImNodes::MiniMap(0.2F, ImNodesMiniMapLocation_BottomRight);
ImNodes::PopColorStyle();
ImNodes::EndNodeEditor();
} }
ImGui::EndChild();
for (const auto &link : this->m_links) if (ImGui::IconButton(ICON_VS_DEBUG_START, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_continuousEvaluation)
ImNodes::Link(link.getId(), link.getFromId(), link.getToId()); this->processNodes();
ImNodes::MiniMap(0.2F, ImNodesMiniMapLocation_BottomRight); ImGui::SameLine();
ImGui::Checkbox("Continuous evaluation", &this->m_continuousEvaluation);
ImNodes::EndNodeEditor();
{ {
int linkId; int linkId;
@ -391,8 +415,6 @@ namespace hex::plugin::builtin {
this->eraseNodes(selectedNodes); this->eraseNodes(selectedNodes);
} }
} }
this->processNodes();
} }
ImGui::End(); ImGui::End();
} }
@ -440,6 +462,8 @@ namespace hex::plugin::builtin {
} }
void ViewDataProcessor::loadNodes(const std::string &data) { void ViewDataProcessor::loadNodes(const std::string &data) {
if (!ImHexApi::Provider::isValid()) return;
using json = nlohmann::json; using json = nlohmann::json;
json input = json::parse(data); json input = json::parse(data);
@ -536,6 +560,8 @@ namespace hex::plugin::builtin {
dp::Node::setIdCounter(maxNodeId + 1); dp::Node::setIdCounter(maxNodeId + 1);
dp::Attribute::setIdCounter(maxAttrId + 1); dp::Attribute::setIdCounter(maxAttrId + 1);
dp::Link::setIdCounter(maxLinkId + 1); dp::Link::setIdCounter(maxLinkId + 1);
this->processNodes();
} }
} }

View File

@ -0,0 +1,2 @@
DisableFormat: true
SortIncludes: Never

View File

@ -603,6 +603,10 @@ namespace hex::plugin::builtin {
{ "hex.builtin.nodes.crypto.aes.mode", "Mode" }, { "hex.builtin.nodes.crypto.aes.mode", "Mode" },
{ "hex.builtin.nodes.crypto.aes.key_length", "Key length" }, { "hex.builtin.nodes.crypto.aes.key_length", "Key length" },
{ "hex.builtin.nodes.visualizer", "Visualizers" },
{ "hex.builtin.nodes.visualizer.digram", "Digram" },
{ "hex.builtin.nodes.visualizer.digram.header", "Digram Visualizer" },
{ "hex.builtin.nodes.visualizer.digram.input", "Input" },
{ "hex.builtin.tools.demangler", "Itanium/MSVC demangler" }, { "hex.builtin.tools.demangler", "Itanium/MSVC demangler" },