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:
parent
3bd01c0d98
commit
5c7a529fa1
@ -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}
|
||||
)
|
||||
|
@ -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>
|
||||
|
23
external/ImGui/CMakeLists.txt
vendored
23
external/ImGui/CMakeLists.txt
vendored
@ -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
323
external/ImGui/include/imnodes.h
vendored
Normal 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
2944
external/ImGui/source/imnodes.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
40
include/views/view_data_processor.hpp
Normal file
40
include/views/view_data_processor.hpp
Normal 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();
|
||||
};
|
||||
|
||||
}
|
@ -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
|
||||
)
|
||||
|
297
plugins/builtin/source/content/data_processor_nodes.cpp
Normal file
297
plugins/builtin/source/content/data_processor_nodes.cpp
Normal 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");
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
};
|
||||
|
||||
}
|
54
plugins/libimhex/include/hex/data_processor/attribute.hpp
Normal file
54
plugins/libimhex/include/hex/data_processor/attribute.hpp
Normal 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; }
|
||||
};
|
||||
|
||||
}
|
18
plugins/libimhex/include/hex/data_processor/link.hpp
Normal file
18
plugins/libimhex/include/hex/data_processor/link.hpp
Normal 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;
|
||||
};
|
||||
|
||||
}
|
38
plugins/libimhex/include/hex/data_processor/node.hpp
Normal file
38
plugins/libimhex/include/hex/data_processor/node.hpp
Normal 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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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(); \
|
||||
|
24
plugins/libimhex/include/hex/providers/overlay.hpp
Normal file
24
plugins/libimhex/include/hex/providers/overlay.hpp
Normal 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;
|
||||
};
|
||||
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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]);
|
||||
|
292
source/views/view_data_processor.cpp
Normal file
292
source/views/view_data_processor.cpp
Normal 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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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")) {
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user