1
0
mirror of synced 2024-11-28 09:30:51 +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
// 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)
meanScale /= 2;
#endif

View File

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

View File

@ -1,18 +1,20 @@
#include <hex/api/content_registry.hpp>
#include <hex/data_processor/node.hpp>
#include <hex/api/localization.hpp>
#include <hex/helpers/crypto.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/api/localization.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/providers/provider.hpp>
#include <cctype>
#include <random>
#include <nlohmann/json.hpp>
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
#include <hex/ui/imgui_data_visualizers.hpp>
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() {
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");
@ -797,6 +882,8 @@ namespace hex::plugin::builtin {
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<NodeVisualizerDigram>("hex.builtin.nodes.visualizer", "hex.builtin.nodes.visualizer.digram");
}
}

View File

@ -48,6 +48,10 @@ namespace hex::plugin::builtin {
this->m_dataOverlays.clear();
});
EventManager::subscribe<EventDataChanged>(this, [this] {
this->processNodes();
});
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 3000, [&, this] {
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, {
@ -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() {
@ -81,6 +94,7 @@ namespace hex::plugin::builtin {
EventManager::unsubscribe<EventFileLoaded>(this);
EventManager::unsubscribe<EventProjectFileStore>(this);
EventManager::unsubscribe<EventProjectFileLoad>(this);
EventManager::unsubscribe<EventDataChanged>(this);
}
@ -129,6 +143,7 @@ namespace hex::plugin::builtin {
}
void ViewDataProcessor::processNodes() {
if (this->m_dataOverlays.size() != this->m_endNodes.size()) {
for (auto overlay : this->m_dataOverlays)
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) {
const bool hasError = this->m_currNodeError.has_value() && this->m_currNodeError->first == node;
for (auto &node : this->m_nodes) {
const bool hasError = this->m_currNodeError.has_value() && this->m_currNodeError->first == node;
if (hasError)
ImNodes::PushColorStyle(ImNodesCol_NodeOutline, 0xFF0000FF);
if (hasError)
ImNodes::PushColorStyle(ImNodesCol_NodeOutline, 0xFF0000FF);
ImNodes::BeginNode(node->getId());
ImNodes::BeginNode(node->getId());
ImNodes::BeginNodeTitleBar();
ImGui::TextUnformatted(LangEntry(node->getUnlocalizedTitle()));
ImNodes::EndNodeTitleBar();
ImNodes::BeginNodeTitleBar();
ImGui::TextUnformatted(LangEntry(node->getUnlocalizedTitle()));
ImNodes::EndNodeTitleBar();
node->drawNode();
node->drawNode();
for (auto &attribute : node->getAttributes()) {
ImNodesPinShape pinShape;
for (auto &attribute : node->getAttributes()) {
ImNodesPinShape pinShape;
switch (attribute.getType()) {
case dp::Attribute::Type::Integer:
pinShape = ImNodesPinShape_Circle;
break;
case dp::Attribute::Type::Float:
pinShape = ImNodesPinShape_Triangle;
break;
case dp::Attribute::Type::Buffer:
pinShape = ImNodesPinShape_Quad;
break;
switch (attribute.getType()) {
case dp::Attribute::Type::Integer:
pinShape = ImNodesPinShape_Circle;
break;
case dp::Attribute::Type::Float:
pinShape = ImNodesPinShape_Triangle;
break;
case dp::Attribute::Type::Buffer:
pinShape = ImNodesPinShape_Quad;
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::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();
}
ImNodes::EndNode();
if (hasError)
ImNodes::PopColorStyle();
}
ImNodes::EndNode();
for (const auto &link : this->m_links)
ImNodes::Link(link.getId(), link.getFromId(), link.getToId());
if (hasError)
ImNodes::PopColorStyle();
ImNodes::MiniMap(0.2F, ImNodesMiniMapLocation_BottomRight);
ImNodes::EndNodeEditor();
}
ImGui::EndChild();
for (const auto &link : this->m_links)
ImNodes::Link(link.getId(), link.getFromId(), link.getToId());
if (ImGui::IconButton(ICON_VS_DEBUG_START, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_continuousEvaluation)
this->processNodes();
ImNodes::MiniMap(0.2F, ImNodesMiniMapLocation_BottomRight);
ImNodes::EndNodeEditor();
ImGui::SameLine();
ImGui::Checkbox("Continuous evaluation", &this->m_continuousEvaluation);
{
int linkId;
@ -391,8 +415,6 @@ namespace hex::plugin::builtin {
this->eraseNodes(selectedNodes);
}
}
this->processNodes();
}
ImGui::End();
}
@ -440,6 +462,8 @@ namespace hex::plugin::builtin {
}
void ViewDataProcessor::loadNodes(const std::string &data) {
if (!ImHexApi::Provider::isValid()) return;
using json = nlohmann::json;
json input = json::parse(data);
@ -536,6 +560,8 @@ namespace hex::plugin::builtin {
dp::Node::setIdCounter(maxNodeId + 1);
dp::Attribute::setIdCounter(maxAttrId + 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.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" },