1
0
mirror of synced 2024-11-24 15:50:16 +01:00

Added Data Processor using Nodes (#152)

* Added imnodes

* Added basic data processor view. Still needs to be cleaned up

* Make sure all attached links get properly removed when a Node is deleted

* Cleanup and API exposing

* Added data provider overlays and integrate them with the data processor

* Optimized data processing

* Node UI enhancements

* Added support for all themes to the nodes editor

* Improved data processor context menus

* Fixed data processor context menu showing up everywhere

* Make hex editor context menu behave the same as data processor one

* Add different node pin types and prevent incompatible ones from being connected

* Don't require explicitly marking node as end node

* Fixed plugin copying

* Added some more nodes
This commit is contained in:
WerWolv 2021-01-30 22:39:06 +01:00 committed by GitHub
parent 3bd01c0d98
commit 5c7a529fa1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 4127 additions and 26 deletions

View File

@ -61,6 +61,7 @@ add_executable(imhex ${application_type}
source/views/view_patches.cpp
source/views/view_command_palette.cpp
source/views/view_settings.cpp
source/views/view_data_processor.cpp
${imhex_icon}
)

View File

@ -139,13 +139,15 @@ endmacro()
macro(createPackage)
file(MAKE_DIRECTORY "plugins")
foreach (plugin IN LISTS PLUGINS)
add_subdirectory("plugins/${plugin}")
add_custom_command(TARGET imhex POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:${plugin}>
$<TARGET_FILE_DIR:imhex>/plugins)
$<TARGET_FILE_DIR:imhex>/plugins/$<TARGET_FILE_NAME:${plugin}>)
endforeach()
add_custom_command(TARGET imhex POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:libimhex>

View File

@ -11,17 +11,18 @@ pkg_search_module(GLFW REQUIRED glfw3)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
add_library(imgui
source/imgui.cpp
source/imgui_demo.cpp
source/imgui_draw.cpp
source/imgui_freetype.cpp
source/imgui_impl_glfw.cpp
source/imgui_impl_opengl3.cpp
source/imgui_tables.cpp
source/imgui_widgets.cpp
source/ImGuiFileBrowser.cpp
source/TextEditor.cpp
source/imgui_imhex_extensions.cpp
source/imgui.cpp
source/imgui_demo.cpp
source/imgui_draw.cpp
source/imgui_freetype.cpp
source/imgui_impl_glfw.cpp
source/imgui_impl_opengl3.cpp
source/imgui_tables.cpp
source/imgui_widgets.cpp
source/ImGuiFileBrowser.cpp
source/TextEditor.cpp
source/imgui_imhex_extensions.cpp
source/imnodes.cpp
)
add_compile_definitions(IMGUI_IMPL_OPENGL_LOADER_GLAD)

323
external/ImGui/include/imnodes.h vendored Normal file
View File

@ -0,0 +1,323 @@
#pragma once
#include <stddef.h>
struct ImVec2;
namespace imnodes
{
enum ColorStyle
{
ColorStyle_NodeBackground = 0,
ColorStyle_NodeBackgroundHovered,
ColorStyle_NodeBackgroundSelected,
ColorStyle_NodeOutline,
ColorStyle_TitleBar,
ColorStyle_TitleBarHovered,
ColorStyle_TitleBarSelected,
ColorStyle_Link,
ColorStyle_LinkHovered,
ColorStyle_LinkSelected,
ColorStyle_Pin,
ColorStyle_PinHovered,
ColorStyle_BoxSelector,
ColorStyle_BoxSelectorOutline,
ColorStyle_GridBackground,
ColorStyle_GridLine,
ColorStyle_Count
};
enum StyleVar
{
StyleVar_GridSpacing = 0,
StyleVar_NodeCornerRounding,
StyleVar_NodePaddingHorizontal,
StyleVar_NodePaddingVertical,
StyleVar_NodeBorderThickness,
StyleVar_LinkThickness,
StyleVar_LinkLineSegmentsPerLength,
StyleVar_LinkHoverDistance,
StyleVar_PinCircleRadius,
StyleVar_PinQuadSideLength,
StyleVar_PinTriangleSideLength,
StyleVar_PinLineThickness,
StyleVar_PinHoverRadius,
StyleVar_PinOffset
};
enum StyleFlags
{
StyleFlags_None = 0,
StyleFlags_NodeOutline = 1 << 0,
StyleFlags_GridLines = 1 << 2
};
// This enum controls the way attribute pins look.
enum PinShape
{
PinShape_Circle,
PinShape_CircleFilled,
PinShape_Triangle,
PinShape_TriangleFilled,
PinShape_Quad,
PinShape_QuadFilled
};
// This enum controls the way the attribute pins behave.
enum AttributeFlags
{
AttributeFlags_None = 0,
// Allow detaching a link by left-clicking and dragging the link at a pin it is connected to.
// NOTE: the user has to actually delete the link for this to work. A deleted link can be
// detected by calling IsLinkDestroyed() after EndNodeEditor().
AttributeFlags_EnableLinkDetachWithDragClick = 1 << 0,
// Visual snapping of an in progress link will trigger IsLink Created/Destroyed events. Allows
// for previewing the creation of a link while dragging it across attributes. See here for demo:
// https://github.com/Nelarius/imnodes/issues/41#issuecomment-647132113 NOTE: the user has to
// actually delete the link for this to work. A deleted link can be detected by calling
// IsLinkDestroyed() after EndNodeEditor().
AttributeFlags_EnableLinkCreationOnSnap = 1 << 1
};
struct IO
{
struct EmulateThreeButtonMouse
{
EmulateThreeButtonMouse();
// Controls whether this feature is enabled or not.
bool enabled;
const bool* modifier; // The keyboard modifier to use with the mouse left click. Set to
// &ImGuiIO::KeyAlt by default.
} emulate_three_button_mouse;
struct LinkDetachWithModifierClick
{
LinkDetachWithModifierClick();
// Pointer to a boolean value indicating when the desired modifier is pressed. Set to NULL
// by default (i.e. this feature is disabled). To enable the feature, set the link to point
// to, for example, &ImGuiIO::KeyCtrl.
//
// Left-clicking a link with this modifier pressed will detach that link. NOTE: the user has
// to actually delete the link for this to work. A deleted link can be detected by calling
// IsLinkDestroyed() after EndNodeEditor().
const bool* modifier;
} link_detach_with_modifier_click;
IO();
};
struct Style
{
float grid_spacing;
float node_corner_rounding;
float node_padding_horizontal;
float node_padding_vertical;
float node_border_thickness;
float link_thickness;
float link_line_segments_per_length;
float link_hover_distance;
// The following variables control the look and behavior of the pins. The default size of each
// pin shape is balanced to occupy approximately the same surface area on the screen.
// The circle radius used when the pin shape is either PinShape_Circle or PinShape_CircleFilled.
float pin_circle_radius;
// The quad side length used when the shape is either PinShape_Quad or PinShape_QuadFilled.
float pin_quad_side_length;
// The equilateral triangle side length used when the pin shape is either PinShape_Triangle or
// PinShape_TriangleFilled.
float pin_triangle_side_length;
// The thickness of the line used when the pin shape is not filled.
float pin_line_thickness;
// The radius from the pin's center position inside of which it is detected as being hovered
// over.
float pin_hover_radius;
// Offsets the pins' positions from the edge of the node to the outside of the node.
float pin_offset;
// By default, StyleFlags_NodeOutline and StyleFlags_Gridlines are enabled.
StyleFlags flags;
// Set these mid-frame using Push/PopColorStyle. You can index this color array with with a
// ColorStyle enum value.
unsigned int colors[ColorStyle_Count];
Style();
};
// An editor context corresponds to a set of nodes in a single workspace (created with a single
// Begin/EndNodeEditor pair)
//
// By default, the library creates an editor context behind the scenes, so using any of the imnodes
// functions doesn't require you to explicitly create a context.
struct EditorContext;
EditorContext* EditorContextCreate();
void EditorContextFree(EditorContext*);
void EditorContextSet(EditorContext*);
ImVec2 EditorContextGetPanning();
void EditorContextResetPanning(const ImVec2& pos);
void EditorContextMoveToNode(const int node_id);
// Initialize the node editor system.
void Initialize();
void Shutdown();
IO& GetIO();
// Returns the global style struct. See the struct declaration for default values.
Style& GetStyle();
// Style presets matching the dear imgui styles of the same name.
void StyleColorsDark(); // on by default
void StyleColorsClassic();
void StyleColorsLight();
// The top-level function call. Call this before calling BeginNode/EndNode. Calling this function
// will result the node editor grid workspace being rendered.
void BeginNodeEditor();
void EndNodeEditor();
// Use PushColorStyle and PopColorStyle to modify Style::colors mid-frame.
void PushColorStyle(ColorStyle item, unsigned int color);
void PopColorStyle();
void PushStyleVar(StyleVar style_item, float value);
void PopStyleVar();
// id can be any positive or negative integer, but INT_MIN is currently reserved for internal use.
void BeginNode(int id);
void EndNode();
ImVec2 GetNodeDimensions(int id);
// Place your node title bar content (such as the node title, using ImGui::Text) between the
// following function calls. These functions have to be called before adding any attributes, or the
// layout of the node will be incorrect.
void BeginNodeTitleBar();
void EndNodeTitleBar();
// Attributes are ImGui UI elements embedded within the node. Attributes can have pin shapes
// rendered next to them. Links are created between pins.
//
// The activity status of an attribute can be checked via the IsAttributeActive() and
// IsAnyAttributeActive() function calls. This is one easy way of checking for any changes made to
// an attribute's drag float UI, for instance.
//
// Each attribute id must be unique.
// Create an input attribute block. The pin is rendered on left side.
void BeginInputAttribute(int id, PinShape shape = PinShape_CircleFilled);
void EndInputAttribute();
// Create an output attribute block. The pin is rendered on the right side.
void BeginOutputAttribute(int id, PinShape shape = PinShape_CircleFilled);
void EndOutputAttribute();
// Create a static attribute block. A static attribute has no pin, and therefore can't be linked to
// anything. However, you can still use IsAttributeActive() and IsAnyAttributeActive() to check for
// attribute activity.
void BeginStaticAttribute(int id);
void EndStaticAttribute();
// Push a single AttributeFlags value. By default, only AttributeFlags_None is set.
void PushAttributeFlag(AttributeFlags flag);
void PopAttributeFlag();
// Render a link between attributes.
// The attributes ids used here must match the ids used in Begin(Input|Output)Attribute function
// calls. The order of start_attr and end_attr doesn't make a difference for rendering the link.
void Link(int id, int start_attribute_id, int end_attribute_id);
// Enable or disable the ability to click and drag a specific node.
void SetNodeDraggable(int node_id, const bool draggable);
// The node's position can be expressed in three coordinate systems:
// * screen space coordinates, -- the origin is the upper left corner of the window.
// * editor space coordinates -- the origin is the upper left corner of the node editor window
// * grid space coordinates, -- the origin is the upper left corner of the node editor window,
// translated by the current editor panning vector (see EditorContextGetPanning() and
// EditorContextResetPanning())
// Use the following functions to get and set the node's coordinates in these coordinate systems.
void SetNodeScreenSpacePos(int node_id, const ImVec2& screen_space_pos);
void SetNodeEditorSpacePos(int node_id, const ImVec2& editor_space_pos);
void SetNodeGridSpacePos(int node_id, const ImVec2& grid_pos);
ImVec2 GetNodeScreenSpacePos(const int node_id);
ImVec2 GetNodeEditorSpacePos(const int node_id);
ImVec2 GetNodeGridSpacePos(const int node_id);
// Returns true if the current node editor canvas is being hovered over by the mouse, and is not
// blocked by any other windows.
bool IsEditorHovered();
// The following functions return true if a UI element is being hovered over by the mouse cursor.
// Assigns the id of the UI element being hovered over to the function argument. Use these functions
// after EndNodeEditor() has been called.
bool IsNodeHovered(int* node_id);
bool IsLinkHovered(int* link_id);
bool IsPinHovered(int* attribute_id);
// Use The following two functions to query the number of selected nodes or links in the current
// editor. Use after calling EndNodeEditor().
int NumSelectedNodes();
int NumSelectedLinks();
// Get the selected node/link ids. The pointer argument should point to an integer array with at
// least as many elements as the respective NumSelectedNodes/NumSelectedLinks function call
// returned.
void GetSelectedNodes(int* node_ids);
void GetSelectedLinks(int* link_ids);
// Clears the list of selected nodes/links. Useful if you want to delete a selected node or link.
void ClearNodeSelection();
void ClearLinkSelection();
// Was the previous attribute active? This will continuously return true while the left mouse button
// is being pressed over the UI content of the attribute.
bool IsAttributeActive();
// Was any attribute active? If so, sets the active attribute id to the output function argument.
bool IsAnyAttributeActive(int* attribute_id = NULL);
// Use the following functions to query a change of state for an existing link, or new link. Call
// these after EndNodeEditor().
// Did the user start dragging a new link from a pin?
bool IsLinkStarted(int* started_at_attribute_id);
// Did the user drop the dragged link before attaching it to a pin?
// There are two different kinds of situations to consider when handling this event:
// 1) a link which is created at a pin and then dropped
// 2) an existing link which is detached from a pin and then dropped
// Use the including_detached_links flag to control whether this function triggers when the user
// detaches a link and drops it.
bool IsLinkDropped(int* started_at_attribute_id = NULL, bool including_detached_links = true);
// Did the user finish creating a new link?
bool IsLinkCreated(
int* started_at_attribute_id,
int* ended_at_attribute_id,
bool* created_from_snap = NULL);
bool IsLinkCreated(
int* started_at_node_id,
int* started_at_attribute_id,
int* ended_at_node_id,
int* ended_at_attribute_id,
bool* created_from_snap = NULL);
// Was an existing link detached from a pin by the user? The detached link's id is assigned to the
// output argument link_id.
bool IsLinkDestroyed(int* link_id);
// Use the following functions to write the editor context's state to a string, or directly to a
// file. The editor context is serialized in the INI file format.
const char* SaveCurrentEditorStateToIniString(size_t* data_size = NULL);
const char* SaveEditorStateToIniString(const EditorContext* editor, size_t* data_size = NULL);
void LoadCurrentEditorStateFromIniString(const char* data, size_t data_size);
void LoadEditorStateFromIniString(EditorContext* editor, const char* data, size_t data_size);
void SaveCurrentEditorStateToIniFile(const char* file_name);
void SaveEditorStateToIniFile(const EditorContext* editor, const char* file_name);
void LoadCurrentEditorStateFromIniFile(const char* file_name);
void LoadEditorStateFromIniFile(EditorContext* editor, const char* file_name);
} // namespace imnodes

2944
external/ImGui/source/imnodes.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
#pragma once
#include <hex.hpp>
#include <imgui.h>
#include <hex/views/view.hpp>
#include <hex/data_processor/node.hpp>
#include <hex/data_processor/link.hpp>
#include <array>
#include <string>
namespace hex {
namespace prv { class Provider; }
class ViewDataProcessor : public View {
public:
ViewDataProcessor();
~ViewDataProcessor() override;
void drawContent() override;
void drawMenu() override;
private:
std::list<dp::Node*> m_endNodes;
std::list<dp::Node*> m_nodes;
std::list<dp::Link> m_links;
std::vector<prv::Overlay*> m_dataOverlays;
int m_rightClickedId = -1;
ImVec2 m_rightClickedCoords;
void eraseLink(u32 id);
void eraseNodes(const std::vector<int> &ids);
void processNodes();
};
}

View File

@ -11,6 +11,7 @@ add_library(${PROJECT_NAME} SHARED
source/content/lang_builtin_functions.cpp
source/content/settings_entries.cpp
source/content/tools_entries.cpp
source/content/data_processor_nodes.cpp
source/math_evaluator.cpp
)

View File

@ -0,0 +1,297 @@
#include <hex/plugin.hpp>
#include "math_evaluator.hpp"
namespace hex::plugin::builtin {
class NodeInteger : public dp::Node {
public:
NodeInteger() : Node("Integer", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "Value") }) {}
void drawNode() override {
ImGui::TextUnformatted("0x"); ImGui::SameLine(0, 0);
ImGui::PushItemWidth(100);
ImGui::InputScalar("##integerValue", ImGuiDataType_U64, &this->m_value, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
ImGui::PopItemWidth();
}
void process(prv::Overlay *dataOverlay) override {
std::vector<u8> data;
data.resize(sizeof(this->m_value));
std::copy(&this->m_value, &this->m_value + 1, data.data());
this->getAttributes()[0].getOutputData() = data;
}
private:
u64 m_value = 0;
};
class NodeFloat : public dp::Node {
public:
NodeFloat() : Node("Float", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Float, "Value") }) {}
void drawNode() override {
ImGui::PushItemWidth(100);
ImGui::InputScalar("##floatValue", ImGuiDataType_Float, &this->m_value, nullptr, nullptr, "%f", ImGuiInputTextFlags_CharsDecimal);
ImGui::PopItemWidth();
}
void process(prv::Overlay *dataOverlay) override {
std::vector<u8> data;
data.resize(sizeof(this->m_value));
std::copy(&this->m_value, &this->m_value + 1, data.data());
this->getAttributes()[0].getOutputData() = data;
}
private:
float m_value = 0;
};
class NodeRGBA8 : public dp::Node {
public:
NodeRGBA8() : Node("RGBA8 Color", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "Red"),
dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "Green"),
dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "Blue"),
dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "Alpha")}) {}
void drawNode() override {
ImGui::PushItemWidth(200);
ImGui::ColorPicker4("##colorPicker", &this->m_color.Value.x, ImGuiColorEditFlags_AlphaBar);
ImGui::PopItemWidth();
}
void process(prv::Overlay *dataOverlay) override {
std::vector<u8> output(sizeof(u64), 0x00);
output[0] = this->m_color.Value.x * 0xFF;
this->getAttributes()[0].getOutputData() = output;
output[0] = this->m_color.Value.y * 0xFF;
this->getAttributes()[1].getOutputData() = output;
output[0] = this->m_color.Value.z * 0xFF;
this->getAttributes()[2].getOutputData() = output;
output[0] = this->m_color.Value.w * 0xFF;
this->getAttributes()[3].getOutputData() = output;
}
private:
ImColor m_color;
};
class NodeDisplayInteger : public dp::Node {
public:
NodeDisplayInteger() : Node("Display Integer", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "Value") }) {}
void drawNode() override {
ImGui::PushItemWidth(150);
if (this->m_value.has_value())
ImGui::Text("0x%llx", this->m_value.value());
else
ImGui::TextUnformatted("???");
ImGui::PopItemWidth();
}
void process(prv::Overlay *dataOverlay) override {
auto connectedInput = this->getConnectedInputAttribute(0);
if (connectedInput == nullptr) {
this->m_value.reset();
return;
}
connectedInput->getParentNode()->process(dataOverlay);
this->m_value = *reinterpret_cast<u64*>(connectedInput->getOutputData().data());
}
private:
std::optional<u64> m_value = 0;
};
class NodeDisplayFloat : public dp::Node {
public:
NodeDisplayFloat() : Node("Display Float", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Float, "Value") }) {}
void drawNode() override {
ImGui::PushItemWidth(150);
if (this->m_value.has_value())
ImGui::Text("%f", this->m_value.value());
else
ImGui::TextUnformatted("???");
ImGui::PopItemWidth();
}
void process(prv::Overlay *dataOverlay) override {
auto connectedInput = this->getConnectedInputAttribute(0);
if (connectedInput == nullptr) {
this->m_value.reset();
return;
}
connectedInput->getParentNode()->process(dataOverlay);
this->m_value = *reinterpret_cast<float*>(connectedInput->getOutputData().data());
}
private:
std::optional<float> m_value = 0;
};
class NodeBitwiseNOT : public dp::Node {
public:
NodeBitwiseNOT() : Node("Bitwise NOT", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "Input"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "Output") }) {}
void process(prv::Overlay *dataOverlay) override {
auto connectedInput = this->getConnectedInputAttribute(0);
if (connectedInput == nullptr)
return;
connectedInput->getParentNode()->process(dataOverlay);
std::vector<u8> output = connectedInput->getOutputData();
for (auto &byte : output)
byte = ~byte;
}
};
class NodeBitwiseAND : public dp::Node {
public:
NodeBitwiseAND() : Node("Bitwise AND", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "Input A"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "Input B"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "Output") }) {}
void process(prv::Overlay *dataOverlay) override {
auto connectedInputA = this->getConnectedInputAttribute(0);
auto connectedInputB = this->getConnectedInputAttribute(1);
if (connectedInputA == nullptr || connectedInputB == nullptr)
return;
connectedInputA->getParentNode()->process(dataOverlay);
connectedInputB->getParentNode()->process(dataOverlay);
std::vector<u8> inputA = connectedInputA->getOutputData();
std::vector<u8> inputB = connectedInputB->getOutputData();
std::vector<u8> output(std::min(inputA.size(), inputB.size()), 0x00);
for (u32 i = 0; i < output.size(); i++)
output[i] = inputA[i] & inputB[i];
this->getAttributes()[2].getOutputData() = output;
}
};
class NodeBitwiseOR : public dp::Node {
public:
NodeBitwiseOR() : Node("Bitwise OR", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "Input A"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "Input B"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "Output") }) {}
void process(prv::Overlay *dataOverlay) override {
auto connectedInputA = this->getConnectedInputAttribute(0);
auto connectedInputB = this->getConnectedInputAttribute(1);
if (connectedInputA == nullptr || connectedInputB == nullptr)
return;
connectedInputA->getParentNode()->process(dataOverlay);
connectedInputB->getParentNode()->process(dataOverlay);
std::vector<u8> inputA = connectedInputA->getOutputData();
std::vector<u8> inputB = connectedInputB->getOutputData();
std::vector<u8> output(std::min(inputA.size(), inputB.size()), 0x00);
for (u32 i = 0; i < output.size(); i++)
output[i] = inputA[i] | inputB[i];
}
};
class NodeBitwiseXOR : public dp::Node {
public:
NodeBitwiseXOR() : Node("Bitwise XOR", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "Input A"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "Input B"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "Output") }) {}
void process(prv::Overlay *dataOverlay) override {
auto connectedInputA = this->getConnectedInputAttribute(0);
auto connectedInputB = this->getConnectedInputAttribute(1);
if (connectedInputA == nullptr || connectedInputB == nullptr)
return;
connectedInputA->getParentNode()->process(dataOverlay);
connectedInputB->getParentNode()->process(dataOverlay);
std::vector<u8> inputA = connectedInputA->getOutputData();
std::vector<u8> inputB = connectedInputB->getOutputData();
std::vector<u8> output(std::min(inputA.size(), inputB.size()), 0x00);
for (u32 i = 0; i < output.size(); i++)
output[i] = inputA[i] ^ inputB[i];
}
};
class NodeReadData : public dp::Node {
public:
NodeReadData() : Node("Read Data", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "Address"),
dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "Size"),
dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "Data")
}) { }
void process(prv::Overlay *dataOverlay) override {
auto connectedInputAddress = this->getConnectedInputAttribute(0);
auto connectedInputSize = this->getConnectedInputAttribute(1);
if (connectedInputAddress == nullptr || connectedInputSize == nullptr)
return;
connectedInputAddress->getParentNode()->process(dataOverlay);
connectedInputSize->getParentNode()->process(dataOverlay);
auto address = *reinterpret_cast<u64*>(connectedInputAddress->getOutputData().data());
auto size = *reinterpret_cast<u64*>(connectedInputSize->getOutputData().data());
std::vector<u8> data;
data.resize(size);
SharedData::currentProvider->readRaw(address, data.data(), size);
this->getAttributes()[2].getOutputData() = data;
}
};
class NodeWriteData : public dp::Node {
public:
NodeWriteData() : Node("Write Data", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "Address"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "Data") }) {}
void process(prv::Overlay *dataOverlay) override {
auto connectedInputAddress = this->getConnectedInputAttribute(0);
auto connectedInputData = this->getConnectedInputAttribute(1);
if (connectedInputAddress == nullptr || connectedInputData == nullptr)
return;
connectedInputAddress->getParentNode()->process(dataOverlay);
connectedInputData->getParentNode()->process(dataOverlay);
auto address = *reinterpret_cast<u64*>(connectedInputAddress->getOutputData().data());
auto data = connectedInputData->getOutputData();
dataOverlay->setAddress(address);
dataOverlay->getData() = data;
}
};
void registerDataProcessorNodes() {
ContentRegistry::DataProcessorNode::add<NodeInteger>("Constants", "Integer");
ContentRegistry::DataProcessorNode::add<NodeFloat>("Constants", "Float");
ContentRegistry::DataProcessorNode::add<NodeRGBA8>("Constants", "RGBA8 Color");
ContentRegistry::DataProcessorNode::add<NodeDisplayInteger>("Display", "Integer");
ContentRegistry::DataProcessorNode::add<NodeDisplayFloat>("Display", "Float");
ContentRegistry::DataProcessorNode::add<NodeReadData>("Data Access", "Read");
ContentRegistry::DataProcessorNode::add<NodeWriteData>("Data Access", "Write");
ContentRegistry::DataProcessorNode::add<NodeBitwiseAND>("Bitwise Operations", "AND");
ContentRegistry::DataProcessorNode::add<NodeBitwiseOR>("Bitwise Operations", "OR");
ContentRegistry::DataProcessorNode::add<NodeBitwiseXOR>("Bitwise Operations", "XOR");
ContentRegistry::DataProcessorNode::add<NodeBitwiseNOT>("Bitwise Operations", "NOT");
}
}

View File

@ -7,6 +7,7 @@ namespace hex::plugin::builtin {
void registerPatternLanguageFunctions();
void registerCommandPaletteCommands();
void registerSettings();
void registerDataProcessorNodes();
}
@ -19,6 +20,7 @@ IMHEX_PLUGIN_SETUP {
registerPatternLanguageFunctions();
registerCommandPaletteCommands();
registerSettings();
registerDataProcessorNodes();
}

View File

@ -17,6 +17,7 @@ namespace hex {
class View;
namespace lang { class ASTNode; }
namespace lang { class Evaluator; }
namespace dp { class Node; }
/*
The Content Registry is the heart of all features in ImHex that are in some way extendable by Plugins.
@ -144,6 +145,26 @@ namespace hex {
static std::vector<Entry>& getEntries();
};
struct DataProcessorNode {
using CreatorFunction = std::function<dp::Node*()>;
struct Entry {
std::string category;
std::string name;
CreatorFunction creatorFunction;
};
template<hex::derived_from<dp::Node> T, typename ... Args>
static void add(std::string_view category, std::string_view name, Args&& ... args) {
add(Entry{ category.data(), name.data(), [args...]{ return new T(std::forward<Args>(args)...); } });
}
static void addSeparator();
static std::vector<Entry>& getEntries();
private:
static void add(const Entry &entry);
};
};
}

View File

@ -0,0 +1,54 @@
#pragma once
namespace hex::dp {
class Node;
class Attribute {
public:
enum class Type {
Integer,
Float,
Buffer
};
enum class IOType {
In, Out
};
Attribute(IOType ioType, Type type, std::string_view name) : m_id(SharedData::dataProcessorNodeIdCounter++), m_ioType(ioType), m_type(type), m_name(name) {
}
~Attribute() {
for (auto &[linkId, attr] : this->getConnectedAttributes())
attr->removeConnectedAttribute(linkId);
}
[[nodiscard]] u32 getID() const { return this->m_id; }
[[nodiscard]] IOType getIOType() const { return this->m_ioType; }
[[nodiscard]] Type getType() const { return this->m_type; }
[[nodiscard]] std::string_view getName() const { return this->m_name; }
void addConnectedAttribute(u32 linkId, Attribute *to) { this->m_connectedAttributes.insert({ linkId, to }); }
void removeConnectedAttribute(u32 linkId) { printf("%d\n", this->m_connectedAttributes.erase(linkId)); }
[[nodiscard]] std::map<u32, Attribute*>& getConnectedAttributes() { return this->m_connectedAttributes; }
[[nodiscard]] Node* getParentNode() { return this->m_parentNode; }
[[nodiscard]] std::vector<u8>& getOutputData() { return this->m_outputData; }
private:
u32 m_id;
IOType m_ioType;
Type m_type;
std::string m_name;
std::map<u32, Attribute*> m_connectedAttributes;
Node *m_parentNode;
std::vector<u8> m_outputData;
friend class Node;
void setParentNode(Node *node) { this->m_parentNode = node; }
};
}

View File

@ -0,0 +1,18 @@
#pragma once
namespace hex::dp {
class Link {
public:
Link(u32 from, u32 to) : m_id(SharedData::dataProcessorNodeIdCounter++), m_from(from), m_to(to) { }
[[nodiscard]] u32 getID() const { return this->m_id; }
[[nodiscard]] u32 getFromID() const { return this->m_from; }
[[nodiscard]] u32 getToID() const { return this->m_to; }
private:
u32 m_id;
u32 m_from, m_to;
};
}

View File

@ -0,0 +1,38 @@
#pragma once
#include <hex/data_processor/attribute.hpp>
namespace hex::dp {
class Node {
public:
Node(std::string_view title, std::vector<Attribute> attributes) : m_id(SharedData::dataProcessorNodeIdCounter++), m_title(title), m_attributes(std::move(attributes)) {
for (auto &attr : this->m_attributes)
attr.setParentNode(this);
}
virtual ~Node() = default;
[[nodiscard]] u32 getID() const { return this->m_id; }
[[nodiscard]] std::string_view getTitle() const { return this->m_title; }
[[nodiscard]] std::vector<Attribute>& getAttributes() { return this->m_attributes; }
virtual void drawNode() { }
virtual void process(prv::Overlay *dataOverlay) = 0;
private:
u32 m_id;
std::string m_title;
std::vector<Attribute> m_attributes;
protected:
Attribute* getConnectedInputAttribute(u32 attributeId) {
auto &connectedAttribute = this->getAttributes()[attributeId].getConnectedAttributes();
if (connectedAttribute.empty())
return nullptr;
return connectedAttribute.begin()->second;
}
};
}

View File

@ -24,6 +24,7 @@ namespace hex::plugin::internal {
namespace hex {
namespace prv { class Provider; }
namespace dp { class Node; }
class View;
class SharedData {
@ -67,6 +68,9 @@ namespace hex {
static std::string fileBrowserValidExtensions;
static std::function<void(std::string)> fileBrowserCallback;
static std::vector<ContentRegistry::DataProcessorNode::Entry> dataProcessorNodes;
static u32 dataProcessorNodeIdCounter;
static int mainArgc;
static char **mainArgv;

View File

@ -9,6 +9,7 @@
#include <hex/views/view.hpp>
#include <hex/providers/provider.hpp>
#include <hex/helpers/shared_data.hpp>
#include <hex/data_processor/node.hpp>
#define IMHEX_PLUGIN_SETUP namespace hex::plugin::internal { \
void initializePlugin(); \

View File

@ -0,0 +1,24 @@
#pragma once
#include <hex.hpp>
#include <vector>
namespace hex::prv {
class Overlay {
public:
Overlay() { }
void setAddress(u64 address) { this->m_address = address; }
[[nodiscard]] u64 getAddress() const { return this->m_address; }
[[nodiscard]] u64 getSize() const { return this->m_data.size(); }
[[nodiscard]] std::vector<u8>& getData() { return this->m_data; }
private:
u64 m_address = 0;
std::vector<u8> m_data;
};
}

View File

@ -8,6 +8,7 @@
#include <vector>
#include <hex/helpers/shared_data.hpp>
#include <hex/providers/overlay.hpp>
namespace hex::prv {
@ -32,6 +33,10 @@ namespace hex::prv {
std::map<u64, u8>& getPatches();
void applyPatches();
[[nodiscard]] Overlay* newOverlay();
void deleteOverlay(Overlay *overlay);
[[nodiscard]] const std::list<Overlay*>& getOverlays();
u32 getPageCount();
u32 getCurrentPage() const;
void setCurrentPage(u32 page);
@ -48,6 +53,7 @@ namespace hex::prv {
u64 m_baseAddress = 0;
std::vector<std::map<u64, u8>> m_patches;
std::list<Overlay*> m_overlays;
};
}

View File

@ -88,11 +88,7 @@ namespace hex {
/* Views */
View* ContentRegistry::Views::add(View *view) {
auto &views = getEntries();
views.push_back(view);
return views.back();
return getEntries().emplace_back(view);
}
std::vector<View*>& ContentRegistry::Views::getEntries() {
@ -114,10 +110,24 @@ namespace hex {
/* Data Inspector */
void ContentRegistry::DataInspector::add(std::string_view name, size_t requiredSize, ContentRegistry::DataInspector::GeneratorFunction function) {
getEntries().push_back(Entry{ name.data(), requiredSize, function });
getEntries().push_back({ name.data(), requiredSize, std::move(function) });
}
std::vector<ContentRegistry::DataInspector::Entry>& ContentRegistry::DataInspector::getEntries() {
return SharedData::dataInspectorEntries;
}
/* Data Processor Nodes */
void ContentRegistry::DataProcessorNode::add(const Entry &entry) {
getEntries().push_back(entry);
}
void ContentRegistry::DataProcessorNode::addSeparator() {
getEntries().push_back({ "", "", []{ return nullptr; } });
}
std::vector<ContentRegistry::DataProcessorNode::Entry>& ContentRegistry::DataProcessorNode::getEntries() {
return SharedData::dataProcessorNodes;
}
}

View File

@ -24,6 +24,9 @@ namespace hex {
std::string SharedData::fileBrowserValidExtensions;
std::function<void(std::string)> SharedData::fileBrowserCallback;
std::vector<ContentRegistry::DataProcessorNode::Entry> SharedData::dataProcessorNodes;
u32 SharedData::dataProcessorNodeIdCounter = 1;
int SharedData::mainArgc;
char **SharedData::mainArgv;

View File

@ -32,6 +32,21 @@ namespace hex::prv {
this->writeRaw(patchAddress, &patch, 1);
}
Overlay* Provider::newOverlay() {
return this->m_overlays.emplace_back(new Overlay());
}
void Provider::deleteOverlay(Overlay *overlay) {
this->m_overlays.erase(std::find(this->m_overlays.begin(), this->m_overlays.end(), overlay));
delete overlay;
}
const std::list<Overlay*>& Provider::getOverlays() {
return this->m_overlays;
}
u32 Provider::getPageCount() {
return std::ceil(this->getActualSize() / double(PageSize));
}

View File

@ -19,6 +19,7 @@
#include "views/view_patches.hpp"
#include "views/view_command_palette.hpp"
#include "views/view_settings.hpp"
#include "views/view_data_processor.hpp"
#include <vector>
@ -45,6 +46,7 @@ int main(int argc, char **argv) {
ContentRegistry::Views::add<ViewCommandPalette>();
ContentRegistry::Views::add<ViewHelp>();
ContentRegistry::Views::add<ViewSettings>();
ContentRegistry::Views::add<ViewDataProcessor>();
if (argc > 1)
View::postEvent(Events::FileDropped, argv[1]);

View File

@ -0,0 +1,292 @@
#include "views/view_data_processor.hpp"
#include <hex/providers/provider.hpp>
#include <imnodes.h>
namespace hex {
ViewDataProcessor::ViewDataProcessor() : View("Data Processor") {
imnodes::Initialize();
View::subscribeEvent(Events::SettingsChanged, [this](auto) {
int theme = ContentRegistry::Settings::getSettingsData()["Interface"]["Color theme"];
switch (theme) {
default:
case 0: /* Dark theme */
imnodes::StyleColorsDark();
break;
case 1: /* Light theme */
imnodes::StyleColorsLight();
break;
case 2: /* Classic theme */
imnodes::StyleColorsClassic();
break;
}
imnodes::GetStyle().flags = imnodes::StyleFlags(imnodes::StyleFlags_NodeOutline | imnodes::StyleFlags_GridLines);
});
}
ViewDataProcessor::~ViewDataProcessor() {
for (auto &node : this->m_nodes)
delete node;
imnodes::Shutdown();
}
void ViewDataProcessor::eraseLink(u32 id) {
auto link = std::find_if(this->m_links.begin(), this->m_links.end(), [&id](auto link){ return link.getID() == id; });
if (link == this->m_links.end())
return;
for (auto &node : this->m_nodes) {
for (auto &attribute : node->getAttributes()) {
attribute.removeConnectedAttribute(id);
}
}
this->m_links.erase(link);
}
void ViewDataProcessor::eraseNodes(const std::vector<int> &ids) {
for (const int id : ids) {
auto node = std::find_if(this->m_nodes.begin(), this->m_nodes.end(), [&id](auto node){ return node->getID() == id; });
for (auto &attr : (*node)->getAttributes()) {
std::vector<u32> linksToRemove;
for (auto &[linkId, connectedAttr] : attr.getConnectedAttributes())
linksToRemove.push_back(linkId);
for (auto linkId : linksToRemove)
eraseLink(linkId);
}
}
for (const int id : ids) {
auto node = std::find_if(this->m_nodes.begin(), this->m_nodes.end(), [&id](auto node){ return node->getID() == id; });
std::erase_if(this->m_endNodes, [&id](auto node){ return node->getID() == id; });
delete *node;
this->m_nodes.erase(node);
}
}
void ViewDataProcessor::processNodes() {
if (this->m_dataOverlays.size() != this->m_endNodes.size()) {
for (auto overlay : this->m_dataOverlays)
SharedData::currentProvider->deleteOverlay(overlay);
this->m_dataOverlays.clear();
for (u32 i = 0; i < this->m_endNodes.size(); i++)
this->m_dataOverlays.push_back(SharedData::currentProvider->newOverlay());
}
u32 overlayIndex = 0;
for (auto &endNode : this->m_endNodes) {
(void)endNode->process(this->m_dataOverlays[overlayIndex]);
overlayIndex++;
}
}
void ViewDataProcessor::drawContent() {
if (ImGui::Begin("Data Processor", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) {
this->m_rightClickedCoords = ImGui::GetMousePos();
if (imnodes::IsNodeHovered(&this->m_rightClickedId))
ImGui::OpenPopup("Node Menu");
else if (imnodes::IsLinkHovered(&this->m_rightClickedId))
ImGui::OpenPopup("Link Menu");
else
ImGui::OpenPopup("Context Menu");
}
if (ImGui::BeginPopup("Context Menu")) {
dp::Node *node = nullptr;
if (imnodes::NumSelectedNodes() > 0 || imnodes::NumSelectedLinks() > 0) {
if (ImGui::MenuItem("Remove selected")) {
std::vector<int> ids;
ids.resize(imnodes::NumSelectedNodes());
imnodes::GetSelectedNodes(ids.data());
this->eraseNodes(ids);
imnodes::ClearNodeSelection();
ids.resize(imnodes::NumSelectedLinks());
imnodes::GetSelectedLinks(ids.data());
for (auto id : ids)
this->eraseLink(id);
imnodes::ClearLinkSelection();
}
}
for (const auto &[category, name, function] : ContentRegistry::DataProcessorNode::getEntries()) {
if (category.empty() && name.empty()) {
ImGui::Separator();
} else if (category.empty()) {
if (ImGui::MenuItem(name.c_str())) {
node = function();
}
} else {
if (ImGui::BeginMenu(category.c_str())) {
if (ImGui::MenuItem(name.c_str())) {
node = function();
}
ImGui::EndMenu();
}
}
}
if (node != nullptr) {
this->m_nodes.push_back(node);
bool hasOutput = false;
for (auto &attr : node->getAttributes()) {
if (attr.getIOType() == dp::Attribute::IOType::Out)
hasOutput = true;
}
if (!hasOutput)
this->m_endNodes.push_back(node);
imnodes::SetNodeScreenSpacePos(node->getID(), this->m_rightClickedCoords);
}
ImGui::EndPopup();
}
if (ImGui::BeginPopup("Node Menu")) {
if (ImGui::MenuItem("Remove Node"))
this->eraseNodes({ this->m_rightClickedId });
ImGui::EndPopup();
}
if (ImGui::BeginPopup("Link Menu")) {
if (ImGui::MenuItem("Remove Link"))
this->eraseLink(this->m_rightClickedId);
ImGui::EndPopup();
}
imnodes::BeginNodeEditor();
for (auto& node : this->m_nodes) {
imnodes::BeginNode(node->getID());
imnodes::BeginNodeTitleBar();
ImGui::TextUnformatted(node->getTitle().data());
imnodes::EndNodeTitleBar();
node->drawNode();
for (auto& attribute : node->getAttributes()) {
imnodes::PinShape pinShape;
switch (attribute.getType()) {
case dp::Attribute::Type::Integer: pinShape = imnodes::PinShape_Circle; break;
case dp::Attribute::Type::Float: pinShape = imnodes::PinShape_Triangle; break;
case dp::Attribute::Type::Buffer: pinShape = imnodes::PinShape_Quad; break;
}
if (attribute.getIOType() == dp::Attribute::IOType::In) {
imnodes::BeginInputAttribute(attribute.getID(), pinShape);
ImGui::TextUnformatted(attribute.getName().data());
imnodes::EndInputAttribute();
} else if (attribute.getIOType() == dp::Attribute::IOType::Out) {
imnodes::BeginOutputAttribute(attribute.getID(), imnodes::PinShape(pinShape + 1));
ImGui::TextUnformatted(attribute.getName().data());
imnodes::EndOutputAttribute();
}
}
imnodes::EndNode();
}
for (const auto &link : this->m_links)
imnodes::Link(link.getID(), link.getFromID(), link.getToID());
imnodes::EndNodeEditor();
{
int from, to;
if (imnodes::IsLinkCreated(&from, &to)) {
do {
dp::Attribute *fromAttr, *toAttr;
for (auto &node : this->m_nodes) {
for (auto &attribute : node->getAttributes()) {
if (attribute.getID() == from)
fromAttr = &attribute;
else if (attribute.getID() == to)
toAttr = &attribute;
}
}
if (fromAttr == nullptr || toAttr == nullptr)
break;
if (fromAttr->getType() != toAttr->getType())
break;
if (fromAttr->getIOType() == toAttr->getIOType())
break;
if (!toAttr->getConnectedAttributes().empty())
break;
auto newLink = this->m_links.emplace_back(from, to);
fromAttr->addConnectedAttribute(newLink.getID(), toAttr);
toAttr->addConnectedAttribute(newLink.getID(), fromAttr);
} while (false);
}
}
{
const int selectedLinkCount = imnodes::NumSelectedLinks();
if (selectedLinkCount > 0 && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) {
static std::vector<int> selectedLinks;
selectedLinks.resize(static_cast<size_t>(selectedLinkCount));
imnodes::GetSelectedLinks(selectedLinks.data());
for (const int id : selectedLinks) {
eraseLink(id);
}
}
}
{
const int selectedNodeCount = imnodes::NumSelectedNodes();
if (selectedNodeCount > 0 && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) {
static std::vector<int> selectedNodes;
selectedNodes.resize(static_cast<size_t>(selectedNodeCount));
imnodes::GetSelectedNodes(selectedNodes.data());
this->eraseNodes(selectedNodes);
}
}
this->processNodes();
}
ImGui::End();
}
void ViewDataProcessor::drawMenu() {
}
}

View File

@ -27,6 +27,12 @@ namespace hex {
ImU8 byte;
provider->read(off, &byte, sizeof(ImU8));
for (auto &overlay : SharedData::currentProvider->getOverlays()) {
auto overlayAddress = overlay->getAddress();
if (off >= overlayAddress && off < overlayAddress + overlay->getSize())
byte = overlay->getData()[off - overlayAddress];
}
return byte;
};
@ -159,7 +165,7 @@ namespace hex {
if (dataSize != 0x00) {
if (ImGui::Begin("Hex Editor")) {
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows))
if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows))
ImGui::OpenPopup("Edit");
if (ImGui::BeginPopup("Edit")) {

View File

@ -172,11 +172,7 @@ namespace hex {
/* Settings */
{
constexpr auto SettingsCategoryInterface = "Interface";
constexpr auto SettingColorTheme = "Color theme";
ContentRegistry::Settings::add(SettingsCategoryInterface, SettingColorTheme, 0, [](nlohmann::json &setting) {
ContentRegistry::Settings::add("Interface", "Color theme", 0, [](nlohmann::json &setting) {
static int selection = setting;
if (ImGui::Combo("##nolabel", &selection, "Dark\0Light\0Classic\0")) {
setting = selection;
@ -187,7 +183,7 @@ namespace hex {
});
View::subscribeEvent(Events::SettingsChanged, [this](auto) {
int theme = ContentRegistry::Settings::getSettingsData()[SettingsCategoryInterface][SettingColorTheme];
int theme = ContentRegistry::Settings::getSettingsData()["Interface"]["Color theme"];
switch (theme) {
default: