nodes: Added support for nested, shareable, custom data processor nodes
This commit is contained in:
parent
303dd28c7c
commit
5cc01ae89d
@ -31,7 +31,10 @@ namespace hex::dp {
|
|||||||
void setUnlocalizedName(const std::string &unlocalizedName) { this->m_unlocalizedName = unlocalizedName; }
|
void setUnlocalizedName(const std::string &unlocalizedName) { this->m_unlocalizedName = unlocalizedName; }
|
||||||
|
|
||||||
[[nodiscard]] const std::string &getUnlocalizedTitle() const { return this->m_unlocalizedTitle; }
|
[[nodiscard]] const std::string &getUnlocalizedTitle() const { return this->m_unlocalizedTitle; }
|
||||||
|
void setUnlocalizedTitle(std::string title) { this->m_unlocalizedTitle = std::move(title); }
|
||||||
|
|
||||||
[[nodiscard]] std::vector<Attribute> &getAttributes() { return this->m_attributes; }
|
[[nodiscard]] std::vector<Attribute> &getAttributes() { return this->m_attributes; }
|
||||||
|
[[nodiscard]] const std::vector<Attribute> &getAttributes() const { return this->m_attributes; }
|
||||||
|
|
||||||
void setCurrentOverlay(prv::Overlay *overlay) {
|
void setCurrentOverlay(prv::Overlay *overlay) {
|
||||||
this->m_overlay = overlay;
|
this->m_overlay = overlay;
|
||||||
@ -40,8 +43,8 @@ namespace hex::dp {
|
|||||||
virtual void drawNode() { }
|
virtual void drawNode() { }
|
||||||
virtual void process() = 0;
|
virtual void process() = 0;
|
||||||
|
|
||||||
virtual void store(nlohmann::json &j) { hex::unused(j); }
|
virtual void store(nlohmann::json &j) const { hex::unused(j); }
|
||||||
virtual void load(nlohmann::json &j) { hex::unused(j); }
|
virtual void load(const nlohmann::json &j) { hex::unused(j); }
|
||||||
|
|
||||||
struct NodeError {
|
struct NodeError {
|
||||||
Node *node;
|
Node *node;
|
||||||
@ -70,6 +73,14 @@ namespace hex::dp {
|
|||||||
Node::s_idCounter = id;
|
Node::s_idCounter = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<u8> getBufferOnInput(u32 index);
|
||||||
|
i128 getIntegerOnInput(u32 index);
|
||||||
|
long double getFloatOnInput(u32 index);
|
||||||
|
|
||||||
|
void setBufferOnOutput(u32 index, const std::vector<u8> &data);
|
||||||
|
void setIntegerOnOutput(u32 index, i128 integer);
|
||||||
|
void setFloatOnOutput(u32 index, long double floatingPoint);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_id;
|
int m_id;
|
||||||
std::string m_unlocalizedTitle, m_unlocalizedName;
|
std::string m_unlocalizedTitle, m_unlocalizedName;
|
||||||
@ -103,15 +114,14 @@ namespace hex::dp {
|
|||||||
throw NodeError { this, message };
|
throw NodeError { this, message };
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> getBufferOnInput(u32 index);
|
|
||||||
i128 getIntegerOnInput(u32 index);
|
|
||||||
long double getFloatOnInput(u32 index);
|
|
||||||
|
|
||||||
void setBufferOnOutput(u32 index, const std::vector<u8> &data);
|
|
||||||
void setIntegerOnOutput(u32 index, i128 integer);
|
|
||||||
void setFloatOnOutput(u32 index, long double floatingPoint);
|
|
||||||
|
|
||||||
void setOverlayData(u64 address, const std::vector<u8> &data);
|
void setOverlayData(u64 address, const std::vector<u8> &data);
|
||||||
|
|
||||||
|
void setAttributes(std::vector<Attribute> attributes) {
|
||||||
|
this->m_attributes = std::move(attributes);
|
||||||
|
|
||||||
|
for (auto &attr : this->m_attributes)
|
||||||
|
attr.setParentNode(this);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -102,6 +102,7 @@ namespace hex::fs {
|
|||||||
Inspectors,
|
Inspectors,
|
||||||
Themes,
|
Themes,
|
||||||
Libraries,
|
Libraries,
|
||||||
|
Nodes,
|
||||||
|
|
||||||
END
|
END
|
||||||
};
|
};
|
||||||
|
@ -2,10 +2,6 @@
|
|||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
[[noreturn]] inline void unreachable() {
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void unused(auto && ... x) {
|
inline void unused(auto && ... x) {
|
||||||
((void)x, ...);
|
((void)x, ...);
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ namespace hex::fs {
|
|||||||
result = NFD::PickFolder(outPath, defaultPath.empty() ? nullptr : defaultPath.c_str());
|
result = NFD::PickFolder(outPath, defaultPath.empty() ? nullptr : defaultPath.c_str());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
hex::unreachable();
|
std::unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == NFD_OKAY){
|
if (result == NFD_OKAY){
|
||||||
@ -271,6 +271,9 @@ namespace hex::fs {
|
|||||||
case ImHexPath::Inspectors:
|
case ImHexPath::Inspectors:
|
||||||
result = appendPath(getDefaultPaths(ImHexPath::Scripts), "inspectors");
|
result = appendPath(getDefaultPaths(ImHexPath::Scripts), "inspectors");
|
||||||
break;
|
break;
|
||||||
|
case ImHexPath::Nodes:
|
||||||
|
result = appendPath(getDefaultPaths(ImHexPath::Scripts), "nodes");
|
||||||
|
break;
|
||||||
case ImHexPath::Themes:
|
case ImHexPath::Themes:
|
||||||
result = appendPath(getDataPaths(), "themes");
|
result = appendPath(getDataPaths(), "themes");
|
||||||
break;
|
break;
|
||||||
|
@ -63,12 +63,16 @@ namespace hex::plugin::builtin {
|
|||||||
std::list<ImHexApi::Bookmarks::Entry> bookmarks;
|
std::list<ImHexApi::Bookmarks::Entry> bookmarks;
|
||||||
|
|
||||||
struct DataProcessor {
|
struct DataProcessor {
|
||||||
std::list<dp::Node*> endNodes;
|
struct Workspace {
|
||||||
std::list<std::unique_ptr<dp::Node>> nodes;
|
std::list<std::unique_ptr<dp::Node>> nodes;
|
||||||
std::list<dp::Link> links;
|
std::list<dp::Node*> endNodes;
|
||||||
|
std::list<dp::Link> links;
|
||||||
|
std::vector<hex::prv::Overlay *> dataOverlays;
|
||||||
|
std::optional<dp::Node::NodeError> currNodeError;
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<hex::prv::Overlay *> dataOverlays;
|
Workspace mainWorkspace;
|
||||||
std::optional<dp::Node::NodeError> currNodeError;
|
std::vector<Workspace*> workspaceStack;
|
||||||
} dataProcessor;
|
} dataProcessor;
|
||||||
|
|
||||||
struct HexEditor {
|
struct HexEditor {
|
||||||
@ -101,7 +105,7 @@ namespace hex::plugin::builtin {
|
|||||||
return get(ImHexApi::Provider::get());
|
return get(ImHexApi::Provider::get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static Data& get(hex::prv::Provider *provider) {
|
static Data& get(const hex::prv::Provider *provider) {
|
||||||
return s_data[provider];
|
return s_data[provider];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +119,7 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
ProviderExtraData() = default;
|
ProviderExtraData() = default;
|
||||||
static inline std::map<hex::prv::Provider*, Data> s_data = {};
|
static inline std::map<const hex::prv::Provider*, Data> s_data = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -7,6 +7,8 @@
|
|||||||
#include <hex/data_processor/node.hpp>
|
#include <hex/data_processor/node.hpp>
|
||||||
#include <hex/data_processor/link.hpp>
|
#include <hex/data_processor/link.hpp>
|
||||||
|
|
||||||
|
#include "content/helpers/provider_extra_data.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -14,24 +16,38 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
class ViewDataProcessor : public View {
|
class ViewDataProcessor : public View {
|
||||||
public:
|
public:
|
||||||
|
using Workspace = ProviderExtraData::Data::DataProcessor::Workspace;
|
||||||
|
|
||||||
ViewDataProcessor();
|
ViewDataProcessor();
|
||||||
~ViewDataProcessor() override;
|
~ViewDataProcessor() override;
|
||||||
|
|
||||||
void drawContent() override;
|
void drawContent() override;
|
||||||
|
|
||||||
|
static nlohmann::json saveNode(const dp::Node *node);
|
||||||
|
static nlohmann::json saveNodes(const Workspace &workspace);
|
||||||
|
|
||||||
|
static std::unique_ptr<dp::Node> loadNode(const nlohmann::json &data);
|
||||||
|
static void loadNodes(Workspace &workspace, const nlohmann::json &data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_justSwitchedProvider = false;
|
static void eraseLink(Workspace &workspace, int id);
|
||||||
|
static void eraseNodes(Workspace &workspace, const std::vector<int> &ids);
|
||||||
|
static void processNodes(Workspace &workspace);
|
||||||
|
|
||||||
|
void reloadCustomNodes();
|
||||||
|
private:
|
||||||
|
bool m_updateNodePositions = false;
|
||||||
int m_rightClickedId = -1;
|
int m_rightClickedId = -1;
|
||||||
ImVec2 m_rightClickedCoords;
|
ImVec2 m_rightClickedCoords;
|
||||||
|
|
||||||
bool m_continuousEvaluation = false;
|
bool m_continuousEvaluation = false;
|
||||||
|
|
||||||
void eraseLink(int id);
|
struct CustomNode {
|
||||||
void eraseNodes(const std::vector<int> &ids);
|
std::string name;
|
||||||
void processNodes();
|
nlohmann::json data;
|
||||||
|
};
|
||||||
|
|
||||||
std::string saveNodes(prv::Provider *provider);
|
std::vector<CustomNode> m_customNodes;
|
||||||
void loadNodes(prv::Provider *provider, const std::string &data);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -268,6 +268,15 @@
|
|||||||
"hex.builtin.nodes.crypto.aes.key_length": "Key length",
|
"hex.builtin.nodes.crypto.aes.key_length": "Key length",
|
||||||
"hex.builtin.nodes.crypto.aes.mode": "Mode",
|
"hex.builtin.nodes.crypto.aes.mode": "Mode",
|
||||||
"hex.builtin.nodes.crypto.aes.nonce": "Nonce",
|
"hex.builtin.nodes.crypto.aes.nonce": "Nonce",
|
||||||
|
"hex.builtin.nodes.custom": "Custom",
|
||||||
|
"hex.builtin.nodes.custom.custom": "New Node",
|
||||||
|
"hex.builtin.nodes.custom.custom.edit": "Edit",
|
||||||
|
"hex.builtin.nodes.custom.custom.edit_hint": "Hold down SHIFT to edit",
|
||||||
|
"hex.builtin.nodes.custom.custom.header": "Custom Node",
|
||||||
|
"hex.builtin.nodes.custom.input": "Custom Node Input",
|
||||||
|
"hex.builtin.nodes.custom.input.header": "Node Input",
|
||||||
|
"hex.builtin.nodes.custom.output": "Custom Node Output",
|
||||||
|
"hex.builtin.nodes.custom.output.header": "Node Output",
|
||||||
"hex.builtin.nodes.data_access": "Data access",
|
"hex.builtin.nodes.data_access": "Data access",
|
||||||
"hex.builtin.nodes.data_access.read": "Read",
|
"hex.builtin.nodes.data_access.read": "Read",
|
||||||
"hex.builtin.nodes.data_access.read.address": "Address",
|
"hex.builtin.nodes.data_access.read.address": "Address",
|
||||||
@ -540,6 +549,7 @@
|
|||||||
"hex.builtin.view.data_processor.menu.remove_link": "Remove Link",
|
"hex.builtin.view.data_processor.menu.remove_link": "Remove Link",
|
||||||
"hex.builtin.view.data_processor.menu.remove_node": "Remove Node",
|
"hex.builtin.view.data_processor.menu.remove_node": "Remove Node",
|
||||||
"hex.builtin.view.data_processor.menu.remove_selection": "Remove Selected",
|
"hex.builtin.view.data_processor.menu.remove_selection": "Remove Selected",
|
||||||
|
"hex.builtin.view.data_processor.menu.save_node": "Save Node",
|
||||||
"hex.builtin.view.data_processor.name": "Data Processor",
|
"hex.builtin.view.data_processor.name": "Data Processor",
|
||||||
"hex.builtin.view.diff.name": "Diffing",
|
"hex.builtin.view.diff.name": "Diffing",
|
||||||
"hex.builtin.view.disassembler.16bit": "16-bit",
|
"hex.builtin.view.disassembler.16bit": "16-bit",
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include <hex/helpers/logger.hpp>
|
#include <hex/helpers/logger.hpp>
|
||||||
#include <hex/providers/provider.hpp>
|
#include <hex/providers/provider.hpp>
|
||||||
|
|
||||||
|
#include <content/views/view_data_processor.hpp>
|
||||||
|
|
||||||
#include <content/helpers/provider_extra_data.hpp>
|
#include <content/helpers/provider_extra_data.hpp>
|
||||||
#include <content/helpers/diagrams.hpp>
|
#include <content/helpers/diagrams.hpp>
|
||||||
|
|
||||||
@ -18,7 +20,6 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <implot.h>
|
#include <implot.h>
|
||||||
#include <hex/ui/imgui_imhex_extensions.h>
|
#include <hex/ui/imgui_imhex_extensions.h>
|
||||||
#include <fonts/codicons_font.h>
|
|
||||||
|
|
||||||
namespace hex::plugin::builtin {
|
namespace hex::plugin::builtin {
|
||||||
|
|
||||||
@ -50,14 +51,14 @@ namespace hex::plugin::builtin {
|
|||||||
this->setBufferOnOutput(0, this->m_buffer);
|
this->setBufferOnOutput(0, this->m_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void store(nlohmann::json &j) override {
|
void store(nlohmann::json &j) const override {
|
||||||
j = nlohmann::json::object();
|
j = nlohmann::json::object();
|
||||||
|
|
||||||
j["size"] = this->m_size;
|
j["size"] = this->m_size;
|
||||||
j["data"] = this->m_buffer;
|
j["data"] = this->m_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(nlohmann::json &j) override {
|
void load(const nlohmann::json &j) override {
|
||||||
this->m_size = j["size"];
|
this->m_size = j["size"];
|
||||||
this->m_buffer = j["data"].get<std::vector<u8>>();
|
this->m_buffer = j["data"].get<std::vector<u8>>();
|
||||||
}
|
}
|
||||||
@ -81,13 +82,13 @@ namespace hex::plugin::builtin {
|
|||||||
this->setBufferOnOutput(0, hex::decodeByteString(this->m_value));
|
this->setBufferOnOutput(0, hex::decodeByteString(this->m_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void store(nlohmann::json &j) override {
|
void store(nlohmann::json &j) const override {
|
||||||
j = nlohmann::json::object();
|
j = nlohmann::json::object();
|
||||||
|
|
||||||
j["data"] = this->m_value;
|
j["data"] = this->m_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(nlohmann::json &j) override {
|
void load(const nlohmann::json &j) override {
|
||||||
this->m_value = j["data"].get<std::string>();
|
this->m_value = j["data"].get<std::string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,13 +110,13 @@ namespace hex::plugin::builtin {
|
|||||||
this->setIntegerOnOutput(0, this->m_value);
|
this->setIntegerOnOutput(0, this->m_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void store(nlohmann::json &j) override {
|
void store(nlohmann::json &j) const override {
|
||||||
j = nlohmann::json::object();
|
j = nlohmann::json::object();
|
||||||
|
|
||||||
j["data"] = this->m_value;
|
j["data"] = this->m_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(nlohmann::json &j) override {
|
void load(const nlohmann::json &j) override {
|
||||||
this->m_value = j["data"];
|
this->m_value = j["data"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,13 +138,13 @@ namespace hex::plugin::builtin {
|
|||||||
this->setFloatOnOutput(0, this->m_value);
|
this->setFloatOnOutput(0, this->m_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void store(nlohmann::json &j) override {
|
void store(nlohmann::json &j) const override {
|
||||||
j = nlohmann::json::object();
|
j = nlohmann::json::object();
|
||||||
|
|
||||||
j["data"] = this->m_value;
|
j["data"] = this->m_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(nlohmann::json &j) override {
|
void load(const nlohmann::json &j) override {
|
||||||
this->m_value = j["data"];
|
this->m_value = j["data"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,13 +167,13 @@ namespace hex::plugin::builtin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void process() override {
|
void process() override {
|
||||||
this->setBufferOnOutput(0, hex::toBytes<u8>(this->m_color.Value.x * 0xFF));
|
this->setBufferOnOutput(0, hex::toBytes<u8>(u8(this->m_color.Value.x * 0xFF)));
|
||||||
this->setBufferOnOutput(1, hex::toBytes<u8>(this->m_color.Value.y * 0xFF));
|
this->setBufferOnOutput(1, hex::toBytes<u8>(u8(this->m_color.Value.y * 0xFF)));
|
||||||
this->setBufferOnOutput(2, hex::toBytes<u8>(this->m_color.Value.z * 0xFF));
|
this->setBufferOnOutput(2, hex::toBytes<u8>(u8(this->m_color.Value.z * 0xFF)));
|
||||||
this->setBufferOnOutput(3, hex::toBytes<u8>(this->m_color.Value.w * 0xFF));
|
this->setBufferOnOutput(3, hex::toBytes<u8>(u8(this->m_color.Value.w * 0xFF)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void store(nlohmann::json &j) override {
|
void store(nlohmann::json &j) const override {
|
||||||
j = nlohmann::json::object();
|
j = nlohmann::json::object();
|
||||||
|
|
||||||
j["data"] = nlohmann::json::object();
|
j["data"] = nlohmann::json::object();
|
||||||
@ -182,7 +183,7 @@ namespace hex::plugin::builtin {
|
|||||||
j["data"]["a"] = this->m_color.Value.w;
|
j["data"]["a"] = this->m_color.Value.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(nlohmann::json &j) override {
|
void load(const nlohmann::json &j) override {
|
||||||
this->m_color = ImVec4(j["data"]["r"], j["data"]["g"], j["data"]["b"], j["data"]["a"]);
|
this->m_color = ImVec4(j["data"]["r"], j["data"]["g"], j["data"]["b"], j["data"]["a"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,13 +204,13 @@ namespace hex::plugin::builtin {
|
|||||||
void process() override {
|
void process() override {
|
||||||
}
|
}
|
||||||
|
|
||||||
void store(nlohmann::json &j) override {
|
void store(nlohmann::json &j) const override {
|
||||||
j = nlohmann::json::object();
|
j = nlohmann::json::object();
|
||||||
|
|
||||||
j["comment"] = this->m_comment;
|
j["comment"] = this->m_comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(nlohmann::json &j) override {
|
void load(const nlohmann::json &j) override {
|
||||||
this->m_comment = j["comment"].get<std::string>();
|
this->m_comment = j["comment"].get<std::string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,7 +879,7 @@ namespace hex::plugin::builtin {
|
|||||||
this->setBufferOnOutput(4, output);
|
this->setBufferOnOutput(4, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void store(nlohmann::json &j) override {
|
void store(nlohmann::json &j) const override {
|
||||||
j = nlohmann::json::object();
|
j = nlohmann::json::object();
|
||||||
|
|
||||||
j["data"] = nlohmann::json::object();
|
j["data"] = nlohmann::json::object();
|
||||||
@ -886,7 +887,7 @@ namespace hex::plugin::builtin {
|
|||||||
j["data"]["key_length"] = this->m_keyLength;
|
j["data"]["key_length"] = this->m_keyLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(nlohmann::json &j) override {
|
void load(const nlohmann::json &j) override {
|
||||||
this->m_mode = j["data"]["mode"];
|
this->m_mode = j["data"]["mode"];
|
||||||
this->m_keyLength = j["data"]["key_length"];
|
this->m_keyLength = j["data"]["key_length"];
|
||||||
}
|
}
|
||||||
@ -1122,10 +1123,301 @@ namespace hex::plugin::builtin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void store(nlohmann::json &j) const override {
|
||||||
|
j = nlohmann::json::object();
|
||||||
|
|
||||||
|
j["name"] = this->m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load(const nlohmann::json &j) override {
|
||||||
|
this->m_name = j["name"].get<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NodeCustomInput : public dp::Node {
|
||||||
|
public:
|
||||||
|
NodeCustomInput() : Node("hex.builtin.nodes.custom.input.header", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input") }) { }
|
||||||
|
~NodeCustomInput() override = default;
|
||||||
|
|
||||||
|
void drawNode() override {
|
||||||
|
ImGui::PushItemWidth(100_scaled);
|
||||||
|
if (ImGui::Combo("##type", &this->m_type, "Integer\0Float\0Buffer\0")) {
|
||||||
|
this->setAttributes({
|
||||||
|
{ dp::Attribute(dp::Attribute::IOType::Out, this->getType(), "hex.builtin.nodes.common.input") }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::InputText("##name", this->m_name)) {
|
||||||
|
this->setUnlocalizedTitle(this->m_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopItemWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setValue(auto value) { this->m_value = std::move(value); }
|
||||||
|
|
||||||
|
const std::string &getName() const { return this->m_name; }
|
||||||
|
dp::Attribute::Type getType() const {
|
||||||
|
switch (this->m_type) {
|
||||||
|
default:
|
||||||
|
case 0: return dp::Attribute::Type::Integer;
|
||||||
|
case 1: return dp::Attribute::Type::Float;
|
||||||
|
case 2: return dp::Attribute::Type::Buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void process() override {
|
||||||
|
std::visit(overloaded {
|
||||||
|
[this](i128 value) { this->setIntegerOnOutput(0, value); },
|
||||||
|
[this](long double value) { this->setFloatOnOutput(0, value); },
|
||||||
|
[this](const std::vector<u8> &value) { this->setBufferOnOutput(0, value); }
|
||||||
|
}, this->m_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void store(nlohmann::json &j) const override {
|
||||||
|
j = nlohmann::json::object();
|
||||||
|
|
||||||
|
j["name"] = this->m_name;
|
||||||
|
j["type"] = this->m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load(const nlohmann::json &j) override {
|
||||||
|
this->m_name = j["name"].get<std::string>();
|
||||||
|
this->m_type = j["type"];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_name = LangEntry(this->getUnlocalizedName());
|
||||||
|
int m_type = 0;
|
||||||
|
|
||||||
|
std::variant<i128, long double, std::vector<u8>> m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NodeCustomOutput : public dp::Node {
|
||||||
|
public:
|
||||||
|
NodeCustomOutput() : Node("hex.builtin.nodes.custom.output.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { }
|
||||||
|
~NodeCustomOutput() override = default;
|
||||||
|
|
||||||
|
void drawNode() override {
|
||||||
|
ImGui::PushItemWidth(100_scaled);
|
||||||
|
if (ImGui::Combo("##type", &this->m_type, "Integer\0Float\0Buffer\0")) {
|
||||||
|
this->setAttributes({
|
||||||
|
{ dp::Attribute(dp::Attribute::IOType::Out, this->getType(), "hex.builtin.nodes.common.output") }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::InputText("##name", this->m_name)) {
|
||||||
|
this->setUnlocalizedTitle(this->m_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopItemWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &getName() const { return this->m_name; }
|
||||||
|
dp::Attribute::Type getType() const {
|
||||||
|
switch (this->m_type) {
|
||||||
|
case 0: return dp::Attribute::Type::Integer;
|
||||||
|
case 1: return dp::Attribute::Type::Float;
|
||||||
|
case 2: return dp::Attribute::Type::Buffer;
|
||||||
|
default: return dp::Attribute::Type::Integer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void process() override {
|
||||||
|
switch (this->getType()) {
|
||||||
|
case dp::Attribute::Type::Integer: this->m_value = this->getIntegerOnInput(0); break;
|
||||||
|
case dp::Attribute::Type::Float: this->m_value = this->getFloatOnInput(0); break;
|
||||||
|
case dp::Attribute::Type::Buffer: this->m_value = this->getBufferOnInput(0); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& getValue() const { return this->m_value; }
|
||||||
|
|
||||||
|
void store(nlohmann::json &j) const override {
|
||||||
|
j = nlohmann::json::object();
|
||||||
|
|
||||||
|
j["name"] = this->m_name;
|
||||||
|
j["type"] = this->m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load(const nlohmann::json &j) override {
|
||||||
|
this->m_name = j["name"].get<std::string>();
|
||||||
|
this->m_type = j["type"];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_name = LangEntry(this->getUnlocalizedName());
|
||||||
|
int m_type = 0;
|
||||||
|
|
||||||
|
std::variant<i128, long double, std::vector<u8>> m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NodeCustom : public dp::Node {
|
||||||
|
public:
|
||||||
|
NodeCustom() : Node("hex.builtin.nodes.custom.custom.header", {}) { }
|
||||||
|
~NodeCustom() override = default;
|
||||||
|
|
||||||
|
void drawNode() override {
|
||||||
|
if (this->m_requiresAttributeUpdate) {
|
||||||
|
this->m_requiresAttributeUpdate = false;
|
||||||
|
this->setAttributes(this->findAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PushItemWidth(200_scaled);
|
||||||
|
|
||||||
|
bool editing = false;
|
||||||
|
if (this->m_editable) {
|
||||||
|
ImGui::InputTextIcon("##name", ICON_VS_SYMBOL_KEY, this->m_name);
|
||||||
|
editing = ImGui::IsItemActive();
|
||||||
|
|
||||||
|
if (ImGui::Button("hex.builtin.nodes.custom.custom.edit"_lang, ImVec2(200_scaled, ImGui::GetTextLineHeightWithSpacing()))) {
|
||||||
|
ProviderExtraData::getCurrent().dataProcessor.workspaceStack.push_back(&this->m_workspace);
|
||||||
|
this->m_requiresAttributeUpdate = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this->setUnlocalizedTitle(this->m_name);
|
||||||
|
|
||||||
|
if (this->getAttributes().empty()) {
|
||||||
|
ImGui::TextUnformatted("hex.builtin.nodes.custom.custom.edit_hint"_lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->m_editable = ImGui::GetIO().KeyShift || editing;
|
||||||
|
|
||||||
|
ImGui::PopItemWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
void process() override {
|
||||||
|
auto indexFromId = [this](u32 id) -> std::optional<u32> {
|
||||||
|
const auto &attributes = this->getAttributes();
|
||||||
|
for (u32 i = 0; i < attributes.size(); i++)
|
||||||
|
if (u32(attributes[i].getId()) == id)
|
||||||
|
return i;
|
||||||
|
return std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Forward inputs to input nodes values
|
||||||
|
for (auto &attribute : this->getAttributes()) {
|
||||||
|
auto index = indexFromId(attribute.getId());
|
||||||
|
if (!index.has_value())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (auto input = this->findInput(attribute.getUnlocalizedName()); input != nullptr) {
|
||||||
|
switch (attribute.getType()) {
|
||||||
|
case dp::Attribute::Type::Integer: {
|
||||||
|
auto value = this->getIntegerOnInput(*index);
|
||||||
|
input->setValue(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case dp::Attribute::Type::Float: {
|
||||||
|
auto value = this->getFloatOnInput(*index);
|
||||||
|
input->setValue(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case dp::Attribute::Type::Buffer: {
|
||||||
|
auto value = this->getBufferOnInput(*index);
|
||||||
|
input->setValue(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process all nodes in our workspace
|
||||||
|
for (auto &endNode : this->m_workspace.endNodes) {
|
||||||
|
endNode->resetOutputData();
|
||||||
|
|
||||||
|
for (auto &node : this->m_workspace.nodes)
|
||||||
|
node->resetProcessedInputs();
|
||||||
|
|
||||||
|
endNode->process();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward output node values to outputs
|
||||||
|
for (auto &attribute : this->getAttributes()) {
|
||||||
|
auto index = indexFromId(attribute.getId());
|
||||||
|
if (!index.has_value())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (auto output = this->findOutput(attribute.getUnlocalizedName()); output != nullptr) {
|
||||||
|
switch (attribute.getType()) {
|
||||||
|
case dp::Attribute::Type::Integer: {
|
||||||
|
auto value = std::get<i128>(output->getValue());
|
||||||
|
this->setIntegerOnOutput(*index, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case dp::Attribute::Type::Float: {
|
||||||
|
auto value = std::get<long double>(output->getValue());
|
||||||
|
this->setFloatOnOutput(*index, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case dp::Attribute::Type::Buffer: {
|
||||||
|
auto value = std::get<std::vector<u8>>(output->getValue());
|
||||||
|
this->setBufferOnOutput(*index, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void store(nlohmann::json &j) const override {
|
||||||
|
j = nlohmann::json::object();
|
||||||
|
|
||||||
|
j["nodes"] = ViewDataProcessor::saveNodes(this->m_workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void load(const nlohmann::json &j) override {
|
||||||
|
ViewDataProcessor::loadNodes(this->m_workspace, j["nodes"]);
|
||||||
|
|
||||||
|
this->m_name = LangEntry(this->getUnlocalizedTitle()).get();
|
||||||
|
this->m_requiresAttributeUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<dp::Attribute> findAttributes() {
|
||||||
|
std::vector<dp::Attribute> result;
|
||||||
|
|
||||||
|
for (auto &node : this->m_workspace.nodes) {
|
||||||
|
if (auto *inputNode = dynamic_cast<NodeCustomInput*>(node.get()); inputNode != nullptr)
|
||||||
|
result.emplace_back(dp::Attribute::IOType::In, inputNode->getType(), inputNode->getName());
|
||||||
|
else if (auto *outputNode = dynamic_cast<NodeCustomOutput*>(node.get()); outputNode != nullptr)
|
||||||
|
result.emplace_back(dp::Attribute::IOType::Out, outputNode->getType(), outputNode->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeCustomInput* findInput(const std::string &name) {
|
||||||
|
for (auto &node : this->m_workspace.nodes) {
|
||||||
|
if (auto *inputNode = dynamic_cast<NodeCustomInput*>(node.get()); inputNode != nullptr && inputNode->getName() == name)
|
||||||
|
return inputNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeCustomOutput* findOutput(const std::string &name) {
|
||||||
|
for (auto &node : this->m_workspace.nodes) {
|
||||||
|
if (auto *outputNode = dynamic_cast<NodeCustomOutput*>(node.get()); outputNode != nullptr && outputNode->getName() == name)
|
||||||
|
return outputNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_name = "hex.builtin.nodes.custom.custom.header"_lang;
|
||||||
|
|
||||||
|
bool m_editable = false;
|
||||||
|
|
||||||
|
bool m_requiresAttributeUpdate = false;
|
||||||
|
ProviderExtraData::Data::DataProcessor::Workspace m_workspace;
|
||||||
|
};
|
||||||
|
|
||||||
void registerDataProcessorNodes() {
|
void registerDataProcessorNodes() {
|
||||||
ContentRegistry::DataProcessorNode::add<NodeInteger>("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.int");
|
ContentRegistry::DataProcessorNode::add<NodeInteger>("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.int");
|
||||||
ContentRegistry::DataProcessorNode::add<NodeFloat>("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.float");
|
ContentRegistry::DataProcessorNode::add<NodeFloat>("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.float");
|
||||||
@ -1188,6 +1480,10 @@ namespace hex::plugin::builtin {
|
|||||||
ContentRegistry::DataProcessorNode::add<NodeVisualizerByteDistribution>("hex.builtin.nodes.visualizer", "hex.builtin.nodes.visualizer.byte_distribution");
|
ContentRegistry::DataProcessorNode::add<NodeVisualizerByteDistribution>("hex.builtin.nodes.visualizer", "hex.builtin.nodes.visualizer.byte_distribution");
|
||||||
|
|
||||||
ContentRegistry::DataProcessorNode::add<NodePatternLanguageOutVariable>("hex.builtin.nodes.pattern_language", "hex.builtin.nodes.pattern_language.out_var");
|
ContentRegistry::DataProcessorNode::add<NodePatternLanguageOutVariable>("hex.builtin.nodes.pattern_language", "hex.builtin.nodes.pattern_language.out_var");
|
||||||
|
|
||||||
|
ContentRegistry::DataProcessorNode::add<NodeCustom>("hex.builtin.nodes.custom", "hex.builtin.nodes.custom.custom");
|
||||||
|
ContentRegistry::DataProcessorNode::add<NodeCustomInput>("hex.builtin.nodes.custom", "hex.builtin.nodes.custom.input");
|
||||||
|
ContentRegistry::DataProcessorNode::add<NodeCustomOutput>("hex.builtin.nodes.custom", "hex.builtin.nodes.custom.output");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -159,6 +159,7 @@ namespace hex::plugin::builtin {
|
|||||||
{ "Scripts", fs::ImHexPath::Scripts },
|
{ "Scripts", fs::ImHexPath::Scripts },
|
||||||
{ "Themes", fs::ImHexPath::Themes },
|
{ "Themes", fs::ImHexPath::Themes },
|
||||||
{ "Data inspector scripts", fs::ImHexPath::Inspectors },
|
{ "Data inspector scripts", fs::ImHexPath::Inspectors },
|
||||||
|
{ "Custom data processor nodes", fs::ImHexPath::Nodes },
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,11 +3,13 @@
|
|||||||
#include <hex/api/content_registry.hpp>
|
#include <hex/api/content_registry.hpp>
|
||||||
|
|
||||||
#include <hex/helpers/file.hpp>
|
#include <hex/helpers/file.hpp>
|
||||||
|
#include <hex/helpers/logger.hpp>
|
||||||
|
|
||||||
#include <hex/providers/provider.hpp>
|
#include <hex/providers/provider.hpp>
|
||||||
#include <hex/api/project_file_manager.hpp>
|
#include <hex/api/project_file_manager.hpp>
|
||||||
|
|
||||||
#include <imnodes.h>
|
#include <imnodes.h>
|
||||||
|
#include <imnodes_internal.h>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include <content/helpers/provider_extra_data.hpp>
|
#include <content/helpers/provider_extra_data.hpp>
|
||||||
@ -20,52 +22,71 @@ namespace hex::plugin::builtin {
|
|||||||
.required = false,
|
.required = false,
|
||||||
.load = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
|
.load = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
|
||||||
auto save = tar.readString(basePath);
|
auto save = tar.readString(basePath);
|
||||||
|
auto &data = ProviderExtraData::get(provider).dataProcessor;
|
||||||
|
|
||||||
this->loadNodes(provider, save);
|
ViewDataProcessor::loadNodes(data.mainWorkspace, save);
|
||||||
|
this->m_updateNodePositions = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
.store = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
|
.store = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
|
||||||
tar.write(basePath, this->saveNodes(provider));
|
auto &data = ProviderExtraData::get(provider).dataProcessor;
|
||||||
|
|
||||||
|
tar.write(basePath, ViewDataProcessor::saveNodes(data.mainWorkspace).dump(4));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
EventManager::subscribe<EventProviderCreated>(this, [](const auto *provider) {
|
||||||
|
auto &data = ProviderExtraData::get(provider).dataProcessor;
|
||||||
|
|
||||||
|
data.mainWorkspace = { };
|
||||||
|
data.workspaceStack.push_back(&data.mainWorkspace);
|
||||||
|
});
|
||||||
|
|
||||||
EventManager::subscribe<EventProviderChanged>(this, [this](const auto &, const auto &) {
|
EventManager::subscribe<EventProviderChanged>(this, [this](const auto &, const auto &) {
|
||||||
auto &data = ProviderExtraData::getCurrent().dataProcessor;
|
auto &data = ProviderExtraData::getCurrent().dataProcessor;
|
||||||
for (auto &node : data.nodes) {
|
|
||||||
node->setCurrentOverlay(nullptr);
|
for (auto *workspace : data.workspaceStack) {
|
||||||
|
for (auto &node : workspace->nodes) {
|
||||||
|
node->setCurrentOverlay(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace->dataOverlays.clear();
|
||||||
}
|
}
|
||||||
data.dataOverlays.clear();
|
|
||||||
this->m_justSwitchedProvider = true;
|
this->m_updateNodePositions = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
EventManager::subscribe<EventDataChanged>(this, [this] {
|
EventManager::subscribe<EventDataChanged>(this, [] {
|
||||||
this->processNodes();
|
auto &workspace = *ProviderExtraData::getCurrent().dataProcessor.workspaceStack.back();
|
||||||
|
|
||||||
|
ViewDataProcessor::processNodes(workspace);
|
||||||
});
|
});
|
||||||
|
|
||||||
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 3000, [&, this] {
|
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 3000, [&] {
|
||||||
bool providerValid = ImHexApi::Provider::isValid();
|
bool providerValid = ImHexApi::Provider::isValid();
|
||||||
auto provider = ImHexApi::Provider::get();
|
|
||||||
|
|
||||||
auto &data = ProviderExtraData::getCurrent().dataProcessor;
|
auto &data = ProviderExtraData::getCurrent().dataProcessor;
|
||||||
|
|
||||||
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.file.load_processor"_lang, nullptr, false, providerValid)) {
|
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.file.load_processor"_lang, nullptr, false, providerValid)) {
|
||||||
fs::openFileBrowser(fs::DialogMode::Open, { {"hex.builtin.view.data_processor.name"_lang, "hexnode"} },
|
fs::openFileBrowser(fs::DialogMode::Open, { {"hex.builtin.view.data_processor.name"_lang, "hexnode" } },
|
||||||
[&, this](const std::fs::path &path) {
|
[&](const std::fs::path &path) {
|
||||||
fs::File file(path, fs::File::Mode::Read);
|
fs::File file(path, fs::File::Mode::Read);
|
||||||
if (file.isValid())
|
if (file.isValid()) {
|
||||||
this->loadNodes(provider, file.readString());
|
ViewDataProcessor::loadNodes(data.mainWorkspace, file.readString());
|
||||||
|
this->m_updateNodePositions = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.file.save_processor"_lang, nullptr, false, !data.nodes.empty() && providerValid)) {
|
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.file.save_processor"_lang, nullptr, false, !data.workspaceStack.empty() && !data.workspaceStack.back()->nodes.empty() && providerValid)) {
|
||||||
fs::openFileBrowser(fs::DialogMode::Save, { {"hex.builtin.view.data_processor.name"_lang, "hexnode"} },
|
fs::openFileBrowser(fs::DialogMode::Save, { {"hex.builtin.view.data_processor.name"_lang, "hexnode" } },
|
||||||
[&, this](const std::fs::path &path) {
|
[&](const std::fs::path &path) {
|
||||||
fs::File file(path, fs::File::Mode::Create);
|
fs::File file(path, fs::File::Mode::Create);
|
||||||
if (file.isValid())
|
if (file.isValid())
|
||||||
file.write(this->saveNodes(provider));
|
file.write(ViewDataProcessor::saveNodes(data.mainWorkspace).dump(4));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -74,42 +95,44 @@ namespace hex::plugin::builtin {
|
|||||||
fs::File file(path, fs::File::Mode::Read);
|
fs::File file(path, fs::File::Mode::Read);
|
||||||
if (!file.isValid()) return false;
|
if (!file.isValid()) return false;
|
||||||
|
|
||||||
this->loadNodes(ImHexApi::Provider::get(), file.readString());
|
auto &data = ProviderExtraData::getCurrent().dataProcessor;
|
||||||
|
|
||||||
|
ViewDataProcessor::loadNodes(data.mainWorkspace, file.readString());
|
||||||
|
this->m_updateNodePositions = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewDataProcessor::~ViewDataProcessor() {
|
ViewDataProcessor::~ViewDataProcessor() {
|
||||||
|
EventManager::unsubscribe<EventProviderCreated>(this);
|
||||||
|
EventManager::unsubscribe<EventProviderChanged>(this);
|
||||||
EventManager::unsubscribe<RequestChangeTheme>(this);
|
EventManager::unsubscribe<RequestChangeTheme>(this);
|
||||||
EventManager::unsubscribe<EventFileLoaded>(this);
|
EventManager::unsubscribe<EventFileLoaded>(this);
|
||||||
EventManager::unsubscribe<EventDataChanged>(this);
|
EventManager::unsubscribe<EventDataChanged>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ViewDataProcessor::eraseLink(int id) {
|
void ViewDataProcessor::eraseLink(ProviderExtraData::Data::DataProcessor::Workspace &workspace, int id) {
|
||||||
auto &data = ProviderExtraData::getCurrent().dataProcessor;
|
auto link = std::find_if(workspace.links.begin(), workspace.links.end(), [&id](auto link) { return link.getId() == id; });
|
||||||
|
|
||||||
auto link = std::find_if(data.links.begin(), data.links.end(), [&id](auto link) { return link.getId() == id; });
|
if (link == workspace.links.end())
|
||||||
|
|
||||||
if (link == data.links.end())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto &node : data.nodes) {
|
for (auto &node : workspace.nodes) {
|
||||||
for (auto &attribute : node->getAttributes()) {
|
for (auto &attribute : node->getAttributes()) {
|
||||||
attribute.removeConnectedAttribute(id);
|
attribute.removeConnectedAttribute(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.links.erase(link);
|
workspace.links.erase(link);
|
||||||
|
|
||||||
ImHexApi::Provider::markDirty();
|
ImHexApi::Provider::markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewDataProcessor::eraseNodes(const std::vector<int> &ids) {
|
void ViewDataProcessor::eraseNodes(ProviderExtraData::Data::DataProcessor::Workspace &workspace, const std::vector<int> &ids) {
|
||||||
auto &data = ProviderExtraData::getCurrent().dataProcessor;
|
|
||||||
for (int id : ids) {
|
for (int id : ids) {
|
||||||
auto node = std::find_if(data.nodes.begin(), data.nodes.end(),
|
auto node = std::find_if(workspace.nodes.begin(), workspace.nodes.end(),
|
||||||
[&id](const auto &node) {
|
[&id](const auto &node) {
|
||||||
return node->getId() == id;
|
return node->getId() == id;
|
||||||
});
|
});
|
||||||
@ -120,64 +143,81 @@ namespace hex::plugin::builtin {
|
|||||||
linksToRemove.push_back(linkId);
|
linksToRemove.push_back(linkId);
|
||||||
|
|
||||||
for (auto linkId : linksToRemove)
|
for (auto linkId : linksToRemove)
|
||||||
eraseLink(linkId);
|
eraseLink(workspace, linkId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int id : ids) {
|
for (int id : ids) {
|
||||||
auto node = std::find_if(data.nodes.begin(), data.nodes.end(), [&id](const auto &node) { return node->getId() == id; });
|
auto node = std::find_if(workspace.nodes.begin(), workspace.nodes.end(), [&id](const auto &node) { return node->getId() == id; });
|
||||||
|
|
||||||
std::erase_if(data.endNodes, [&id](const auto &node) { return node->getId() == id; });
|
std::erase_if(workspace.endNodes, [&id](const auto &node) { return node->getId() == id; });
|
||||||
|
|
||||||
data.nodes.erase(node);
|
workspace.nodes.erase(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImHexApi::Provider::markDirty();
|
ImHexApi::Provider::markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewDataProcessor::processNodes() {
|
void ViewDataProcessor::processNodes(ProviderExtraData::Data::DataProcessor::Workspace &workspace) {
|
||||||
auto &data = ProviderExtraData::getCurrent().dataProcessor;
|
if (workspace.dataOverlays.size() != workspace.endNodes.size()) {
|
||||||
|
for (auto overlay : workspace.dataOverlays)
|
||||||
if (data.dataOverlays.size() != data.endNodes.size()) {
|
|
||||||
for (auto overlay : data.dataOverlays)
|
|
||||||
ImHexApi::Provider::get()->deleteOverlay(overlay);
|
ImHexApi::Provider::get()->deleteOverlay(overlay);
|
||||||
data.dataOverlays.clear();
|
workspace.dataOverlays.clear();
|
||||||
|
|
||||||
for (u32 i = 0; i < data.endNodes.size(); i++)
|
for (u32 i = 0; i < workspace.endNodes.size(); i++)
|
||||||
data.dataOverlays.push_back(ImHexApi::Provider::get()->newOverlay());
|
workspace.dataOverlays.push_back(ImHexApi::Provider::get()->newOverlay());
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 overlayIndex = 0;
|
u32 overlayIndex = 0;
|
||||||
for (auto endNode : data.endNodes) {
|
for (auto endNode : workspace.endNodes) {
|
||||||
endNode->setCurrentOverlay(data.dataOverlays[overlayIndex]);
|
endNode->setCurrentOverlay(workspace.dataOverlays[overlayIndex]);
|
||||||
overlayIndex++;
|
overlayIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.currNodeError.reset();
|
workspace.currNodeError.reset();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (auto &endNode : data.endNodes) {
|
for (auto &endNode : workspace.endNodes) {
|
||||||
endNode->resetOutputData();
|
endNode->resetOutputData();
|
||||||
|
|
||||||
for (auto &node : data.nodes)
|
for (auto &node : workspace.nodes)
|
||||||
node->resetProcessedInputs();
|
node->resetProcessedInputs();
|
||||||
|
|
||||||
endNode->process();
|
endNode->process();
|
||||||
}
|
}
|
||||||
} catch (dp::Node::NodeError &e) {
|
} catch (dp::Node::NodeError &e) {
|
||||||
data.currNodeError = e;
|
workspace.currNodeError = e;
|
||||||
|
|
||||||
for (auto overlay : data.dataOverlays)
|
for (auto overlay : workspace.dataOverlays)
|
||||||
ImHexApi::Provider::get()->deleteOverlay(overlay);
|
ImHexApi::Provider::get()->deleteOverlay(overlay);
|
||||||
data.dataOverlays.clear();
|
workspace.dataOverlays.clear();
|
||||||
|
|
||||||
} catch (std::runtime_error &e) {
|
} catch (std::runtime_error &e) {
|
||||||
printf("Node implementation bug! %s\n", e.what());
|
log::fatal("Node implementation bug! {}\n", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewDataProcessor::reloadCustomNodes() {
|
||||||
|
this->m_customNodes.clear();
|
||||||
|
|
||||||
|
for (const auto &basePath : fs::getDefaultPaths(fs::ImHexPath::Nodes)) {
|
||||||
|
for (const auto &entry : std::fs::recursive_directory_iterator(basePath)) {
|
||||||
|
if (entry.path().extension() == ".hexnode") {
|
||||||
|
try {
|
||||||
|
nlohmann::json nodeJson = nlohmann::json::parse(fs::File(entry.path(), fs::File::Mode::Read).readString());
|
||||||
|
|
||||||
|
this->m_customNodes.push_back(CustomNode { LangEntry(nodeJson["name"]), nodeJson });
|
||||||
|
} catch (nlohmann::json::exception &e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewDataProcessor::drawContent() {
|
void ViewDataProcessor::drawContent() {
|
||||||
auto &data = ProviderExtraData::getCurrent().dataProcessor;
|
auto &data = ProviderExtraData::getCurrent().dataProcessor;
|
||||||
|
auto &workspace = *data.workspaceStack.back();
|
||||||
|
|
||||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.data_processor.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
if (ImGui::Begin(View::toWindowName("hex.builtin.view.data_processor.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||||
|
|
||||||
@ -191,8 +231,10 @@ namespace hex::plugin::builtin {
|
|||||||
ImGui::OpenPopup("Node Menu");
|
ImGui::OpenPopup("Node Menu");
|
||||||
else if (ImNodes::IsLinkHovered(&this->m_rightClickedId))
|
else if (ImNodes::IsLinkHovered(&this->m_rightClickedId))
|
||||||
ImGui::OpenPopup("Link Menu");
|
ImGui::OpenPopup("Link Menu");
|
||||||
else
|
else {
|
||||||
ImGui::OpenPopup("Context Menu");
|
ImGui::OpenPopup("Context Menu");
|
||||||
|
this->reloadCustomNodes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::BeginPopup("Context Menu")) {
|
if (ImGui::BeginPopup("Context Menu")) {
|
||||||
@ -204,14 +246,14 @@ namespace hex::plugin::builtin {
|
|||||||
ids.resize(ImNodes::NumSelectedNodes());
|
ids.resize(ImNodes::NumSelectedNodes());
|
||||||
ImNodes::GetSelectedNodes(ids.data());
|
ImNodes::GetSelectedNodes(ids.data());
|
||||||
|
|
||||||
this->eraseNodes(ids);
|
this->eraseNodes(workspace, ids);
|
||||||
ImNodes::ClearNodeSelection();
|
ImNodes::ClearNodeSelection();
|
||||||
|
|
||||||
ids.resize(ImNodes::NumSelectedLinks());
|
ids.resize(ImNodes::NumSelectedLinks());
|
||||||
ImNodes::GetSelectedLinks(ids.data());
|
ImNodes::GetSelectedLinks(ids.data());
|
||||||
|
|
||||||
for (auto id : ids)
|
for (auto id : ids)
|
||||||
this->eraseLink(id);
|
this->eraseLink(workspace, id);
|
||||||
ImNodes::ClearLinkSelection();
|
ImNodes::ClearLinkSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,6 +275,17 @@ namespace hex::plugin::builtin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginMenu("hex.builtin.nodes.custom"_lang)) {
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
for (auto &customNode : this->m_customNodes) {
|
||||||
|
if (ImGui::MenuItem(customNode.name.c_str())) {
|
||||||
|
node = loadNode(customNode.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
if (node != nullptr) {
|
if (node != nullptr) {
|
||||||
bool hasOutput = false;
|
bool hasOutput = false;
|
||||||
bool hasInput = false;
|
bool hasInput = false;
|
||||||
@ -245,10 +298,10 @@ namespace hex::plugin::builtin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasInput && !hasOutput)
|
if (hasInput && !hasOutput)
|
||||||
data.endNodes.push_back(node.get());
|
workspace.endNodes.push_back(node.get());
|
||||||
|
|
||||||
ImNodes::SetNodeScreenSpacePos(node->getId(), this->m_rightClickedCoords);
|
ImNodes::SetNodeScreenSpacePos(node->getId(), this->m_rightClickedCoords);
|
||||||
data.nodes.push_back(std::move(node));
|
workspace.nodes.push_back(std::move(node));
|
||||||
ImHexApi::Provider::markDirty();
|
ImHexApi::Provider::markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,44 +309,64 @@ namespace hex::plugin::builtin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::BeginPopup("Node Menu")) {
|
if (ImGui::BeginPopup("Node Menu")) {
|
||||||
|
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.save_node"_lang)) {
|
||||||
|
auto it = std::find_if(workspace.nodes.begin(), workspace.nodes.end(),
|
||||||
|
[this](const auto &node) {
|
||||||
|
return node->getId() == this->m_rightClickedId;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != workspace.nodes.end()) {
|
||||||
|
auto &node = *it;
|
||||||
|
fs::openFileBrowser(fs::DialogMode::Save, { {"hex.builtin.view.data_processor.name"_lang, "hexnode" } }, [&](const std::fs::path &path){
|
||||||
|
fs::File outputFile(path, fs::File::Mode::Create);
|
||||||
|
outputFile.write(ViewDataProcessor::saveNode(node.get()).dump(4));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.remove_node"_lang))
|
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.remove_node"_lang))
|
||||||
this->eraseNodes({ this->m_rightClickedId });
|
this->eraseNodes(workspace, { this->m_rightClickedId });
|
||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::BeginPopup("Link Menu")) {
|
if (ImGui::BeginPopup("Link Menu")) {
|
||||||
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.remove_link"_lang))
|
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.remove_link"_lang))
|
||||||
this->eraseLink(this->m_rightClickedId);
|
this->eraseLink(workspace, this->m_rightClickedId);
|
||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
int nodeId;
|
int nodeId;
|
||||||
if (ImNodes::IsNodeHovered(&nodeId) && data.currNodeError.has_value() && data.currNodeError->node->getId() == nodeId) {
|
if (ImNodes::IsNodeHovered(&nodeId) && workspace.currNodeError.has_value() && workspace.currNodeError->node->getId() == nodeId) {
|
||||||
ImGui::BeginTooltip();
|
ImGui::BeginTooltip();
|
||||||
ImGui::TextUnformatted("hex.builtin.common.error"_lang);
|
ImGui::TextUnformatted("hex.builtin.common.error"_lang);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::TextUnformatted(data.currNodeError->message.c_str());
|
ImGui::TextUnformatted(workspace.currNodeError->message.c_str());
|
||||||
ImGui::EndTooltip();
|
ImGui::EndTooltip();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::BeginChild("##node_editor", ImGui::GetContentRegionAvail() - ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 1.3))) {
|
if (ImGui::BeginChild("##node_editor", ImGui::GetContentRegionAvail() - ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 1.3F))) {
|
||||||
ImNodes::BeginNodeEditor();
|
ImNodes::BeginNodeEditor();
|
||||||
|
|
||||||
for (auto &node : data.nodes) {
|
for (auto &node : workspace.nodes) {
|
||||||
const bool hasError = data.currNodeError.has_value() && data.currNodeError->node == node.get();
|
const bool hasError = workspace.currNodeError.has_value() && workspace.currNodeError->node == node.get();
|
||||||
|
|
||||||
if (hasError)
|
if (hasError)
|
||||||
ImNodes::PushColorStyle(ImNodesCol_NodeOutline, 0xFF0000FF);
|
ImNodes::PushColorStyle(ImNodesCol_NodeOutline, 0xFF0000FF);
|
||||||
|
|
||||||
int nodeId = node->getId();
|
int nodeId = node->getId();
|
||||||
if (!this->m_justSwitchedProvider)
|
if (this->m_updateNodePositions) {
|
||||||
node->setPosition(ImNodes::GetNodeGridSpacePos(nodeId));
|
this->m_updateNodePositions = false;
|
||||||
else
|
|
||||||
ImNodes::SetNodeGridSpacePos(nodeId, node->getPosition());
|
ImNodes::SetNodeGridSpacePos(nodeId, node->getPosition());
|
||||||
|
} else {
|
||||||
|
if (ImNodes::ObjectPoolFind(ImNodes::EditorContextGet().Nodes, nodeId) >= 0)
|
||||||
|
node->setPosition(ImNodes::GetNodeGridSpacePos(nodeId));
|
||||||
|
}
|
||||||
|
|
||||||
ImNodes::BeginNode(nodeId);
|
ImNodes::BeginNode(nodeId);
|
||||||
|
|
||||||
@ -301,7 +374,9 @@ namespace hex::plugin::builtin {
|
|||||||
ImGui::TextUnformatted(LangEntry(node->getUnlocalizedTitle()));
|
ImGui::TextUnformatted(LangEntry(node->getUnlocalizedTitle()));
|
||||||
ImNodes::EndNodeTitleBar();
|
ImNodes::EndNodeTitleBar();
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
node->drawNode();
|
node->drawNode();
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1.0F, 1.0F));
|
||||||
|
|
||||||
for (auto &attribute : node->getAttributes()) {
|
for (auto &attribute : node->getAttributes()) {
|
||||||
ImNodesPinShape pinShape;
|
ImNodesPinShape pinShape;
|
||||||
@ -338,28 +413,50 @@ namespace hex::plugin::builtin {
|
|||||||
ImNodes::PopColorStyle();
|
ImNodes::PopColorStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &link : data.links)
|
std::vector<int> linksToRemove;
|
||||||
|
for (const auto &link : workspace.links) {
|
||||||
|
if (ImNodes::ObjectPoolFind(ImNodes::EditorContextGet().Pins, link.getFromId()) == -1 ||
|
||||||
|
ImNodes::ObjectPoolFind(ImNodes::EditorContextGet().Pins, link.getToId()) == -1) {
|
||||||
|
|
||||||
|
linksToRemove.push_back(link.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto linkId : linksToRemove)
|
||||||
|
this->eraseLink(workspace, linkId);
|
||||||
|
|
||||||
|
for (const auto &link : workspace.links) {
|
||||||
ImNodes::Link(link.getId(), link.getFromId(), link.getToId());
|
ImNodes::Link(link.getId(), link.getFromId(), link.getToId());
|
||||||
|
}
|
||||||
|
|
||||||
ImNodes::MiniMap(0.2F, ImNodesMiniMapLocation_BottomRight);
|
ImNodes::MiniMap(0.2F, ImNodesMiniMapLocation_BottomRight);
|
||||||
|
|
||||||
if (data.nodes.empty())
|
if (workspace.nodes.empty())
|
||||||
ImGui::TextFormattedCentered("{}", "hex.builtin.view.data_processor.help_text"_lang);
|
ImGui::TextFormattedCentered("{}", "hex.builtin.view.data_processor.help_text"_lang);
|
||||||
|
|
||||||
|
if (data.workspaceStack.size() > 1) {
|
||||||
|
ImGui::SetCursorPos(ImVec2(ImGui::GetContentRegionAvail().x - ImGui::GetTextLineHeightWithSpacing() * 1.2F, ImGui::GetTextLineHeightWithSpacing() * 0.2F));
|
||||||
|
if (ImGui::IconButton(ICON_VS_CLOSE, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGray))) {
|
||||||
|
data.workspaceStack.pop_back();
|
||||||
|
this->m_updateNodePositions = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImNodes::EndNodeEditor();
|
ImNodes::EndNodeEditor();
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
if (ImGui::IconButton(ICON_VS_DEBUG_START, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_continuousEvaluation)
|
if (ImGui::IconButton(ICON_VS_DEBUG_START, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_continuousEvaluation)
|
||||||
this->processNodes();
|
this->processNodes(workspace);
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::Checkbox("Continuous evaluation", &this->m_continuousEvaluation);
|
ImGui::Checkbox("Continuous evaluation", &this->m_continuousEvaluation);
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
int linkId;
|
int linkId;
|
||||||
if (ImNodes::IsLinkDestroyed(&linkId)) {
|
if (ImNodes::IsLinkDestroyed(&linkId)) {
|
||||||
this->eraseLink(linkId);
|
this->eraseLink(workspace, linkId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +466,7 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
dp::Attribute *fromAttr = nullptr, *toAttr = nullptr;
|
dp::Attribute *fromAttr = nullptr, *toAttr = nullptr;
|
||||||
for (auto &node : data.nodes) {
|
for (auto &node : workspace.nodes) {
|
||||||
for (auto &attribute : node->getAttributes()) {
|
for (auto &attribute : node->getAttributes()) {
|
||||||
if (attribute.getId() == from)
|
if (attribute.getId() == from)
|
||||||
fromAttr = &attribute;
|
fromAttr = &attribute;
|
||||||
@ -390,7 +487,7 @@ namespace hex::plugin::builtin {
|
|||||||
if (!toAttr->getConnectedAttributes().empty())
|
if (!toAttr->getConnectedAttributes().empty())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
auto newLink = data.links.emplace_back(from, to);
|
auto newLink = workspace.links.emplace_back(from, to);
|
||||||
|
|
||||||
fromAttr->addConnectedAttribute(newLink.getId(), toAttr);
|
fromAttr->addConnectedAttribute(newLink.getId(), toAttr);
|
||||||
toAttr->addConnectedAttribute(newLink.getId(), fromAttr);
|
toAttr->addConnectedAttribute(newLink.getId(), fromAttr);
|
||||||
@ -406,7 +503,7 @@ namespace hex::plugin::builtin {
|
|||||||
ImNodes::GetSelectedLinks(selectedLinks.data());
|
ImNodes::GetSelectedLinks(selectedLinks.data());
|
||||||
|
|
||||||
for (const int id : selectedLinks) {
|
for (const int id : selectedLinks) {
|
||||||
eraseLink(id);
|
eraseLink(workspace, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -418,48 +515,52 @@ namespace hex::plugin::builtin {
|
|||||||
selectedNodes.resize(static_cast<size_t>(selectedNodeCount));
|
selectedNodes.resize(static_cast<size_t>(selectedNodeCount));
|
||||||
ImNodes::GetSelectedNodes(selectedNodes.data());
|
ImNodes::GetSelectedNodes(selectedNodes.data());
|
||||||
|
|
||||||
this->eraseNodes(selectedNodes);
|
this->eraseNodes(workspace, selectedNodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->m_justSwitchedProvider = false;
|
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ViewDataProcessor::saveNodes(prv::Provider *provider) {
|
nlohmann::json ViewDataProcessor::saveNode(const dp::Node *node) {
|
||||||
auto &data = ProviderExtraData::get(provider).dataProcessor;
|
nlohmann::json output;
|
||||||
|
|
||||||
using json = nlohmann::json;
|
output["name"] = node->getUnlocalizedTitle();
|
||||||
json output;
|
output["type"] = node->getUnlocalizedName();
|
||||||
|
|
||||||
output["nodes"] = json::object();
|
nlohmann::json nodeData;
|
||||||
for (auto &node : data.nodes) {
|
node->store(nodeData);
|
||||||
auto id = node->getId();
|
output["data"] = nodeData;
|
||||||
auto &currNodeOutput = output["nodes"][std::to_string(id)];
|
|
||||||
auto pos = node->getPosition();
|
|
||||||
|
|
||||||
currNodeOutput["type"] = node->getUnlocalizedName();
|
output["attrs"] = nlohmann::json::array();
|
||||||
currNodeOutput["pos"] = {
|
u32 attrIndex = 0;
|
||||||
{"x", pos.x},
|
for (auto &attr : node->getAttributes()) {
|
||||||
{ "y", pos.y}
|
output["attrs"][attrIndex] = attr.getId();
|
||||||
};
|
attrIndex++;
|
||||||
currNodeOutput["attrs"] = json::array();
|
|
||||||
currNodeOutput["id"] = id;
|
|
||||||
|
|
||||||
json nodeData;
|
|
||||||
node->store(nodeData);
|
|
||||||
currNodeOutput["data"] = nodeData;
|
|
||||||
|
|
||||||
u32 attrIndex = 0;
|
|
||||||
for (auto &attr : node->getAttributes()) {
|
|
||||||
currNodeOutput["attrs"][attrIndex] = attr.getId();
|
|
||||||
attrIndex++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
output["links"] = json::object();
|
return output;
|
||||||
for (auto &link : data.links) {
|
}
|
||||||
|
|
||||||
|
nlohmann::json ViewDataProcessor::saveNodes(const ViewDataProcessor::Workspace &workspace) {
|
||||||
|
nlohmann::json output;
|
||||||
|
|
||||||
|
output["nodes"] = nlohmann::json::object();
|
||||||
|
for (auto &node : workspace.nodes) {
|
||||||
|
auto id = node->getId();
|
||||||
|
auto &currNodeOutput = output["nodes"][std::to_string(id)];
|
||||||
|
auto pos = node->getPosition();
|
||||||
|
|
||||||
|
currNodeOutput = saveNode(node.get());
|
||||||
|
currNodeOutput["id"] = id;
|
||||||
|
currNodeOutput["pos"] = {
|
||||||
|
{ "x", pos.x },
|
||||||
|
{ "y", pos.y }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
output["links"] = nlohmann::json::object();
|
||||||
|
for (auto &link : workspace.links) {
|
||||||
auto id = link.getId();
|
auto id = link.getId();
|
||||||
auto &currOutput = output["links"][std::to_string(id)];
|
auto &currOutput = output["links"][std::to_string(id)];
|
||||||
|
|
||||||
@ -468,41 +569,55 @@ namespace hex::plugin::builtin {
|
|||||||
currOutput["to"] = link.getToId();
|
currOutput["to"] = link.getToId();
|
||||||
}
|
}
|
||||||
|
|
||||||
return output.dump(4);
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewDataProcessor::loadNodes(prv::Provider *provider, const std::string &jsonData) {
|
std::unique_ptr<dp::Node> ViewDataProcessor::loadNode(const nlohmann::json &node) {
|
||||||
if (!ImHexApi::Provider::isValid()) return;
|
try {
|
||||||
|
|
||||||
auto &data = ProviderExtraData::get(provider).dataProcessor;
|
auto &nodeEntries = ContentRegistry::DataProcessorNode::getEntries();
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
json input = json::parse(jsonData);
|
|
||||||
|
|
||||||
u32 maxNodeId = 0;
|
|
||||||
u32 maxAttrId = 0;
|
|
||||||
u32 maxLinkId = 0;
|
|
||||||
|
|
||||||
data.nodes.clear();
|
|
||||||
data.endNodes.clear();
|
|
||||||
data.links.clear();
|
|
||||||
|
|
||||||
auto &nodeEntries = ContentRegistry::DataProcessorNode::getEntries();
|
|
||||||
for (auto &node : input["nodes"]) {
|
|
||||||
std::unique_ptr<dp::Node> newNode;
|
std::unique_ptr<dp::Node> newNode;
|
||||||
for (auto &entry : nodeEntries) {
|
for (auto &entry : nodeEntries) {
|
||||||
if (entry.name == node["type"].get<std::string>())
|
if (entry.name == node["type"].get<std::string>())
|
||||||
newNode = entry.creatorFunction();
|
newNode = entry.creatorFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newNode == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (node.contains("id"))
|
||||||
|
newNode->setId(node["id"]);
|
||||||
|
|
||||||
|
if (node.contains("name"))
|
||||||
|
newNode->setUnlocalizedTitle(node["name"]);
|
||||||
|
|
||||||
|
u32 attrIndex = 0;
|
||||||
|
for (auto &attr : newNode->getAttributes()) {
|
||||||
|
attr.setId(node["attrs"][attrIndex]);
|
||||||
|
attrIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node["data"].is_null())
|
||||||
|
newNode->load(node["data"]);
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
} catch (nlohmann::json::exception &e) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewDataProcessor::loadNodes(ViewDataProcessor::Workspace &workspace, const nlohmann::json &jsonData) {
|
||||||
|
workspace.nodes.clear();
|
||||||
|
workspace.endNodes.clear();
|
||||||
|
workspace.links.clear();
|
||||||
|
|
||||||
|
for (auto &node : jsonData["nodes"]) {
|
||||||
|
auto newNode = loadNode(node);
|
||||||
if (newNode == nullptr)
|
if (newNode == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
u32 nodeId = node["id"];
|
newNode->setPosition(ImVec2(node["pos"]["x"], node["pos"]["y"]));
|
||||||
maxNodeId = std::max(nodeId, maxNodeId);
|
|
||||||
|
|
||||||
newNode->setId(nodeId);
|
|
||||||
|
|
||||||
bool hasOutput = false;
|
bool hasOutput = false;
|
||||||
bool hasInput = false;
|
bool hasInput = false;
|
||||||
@ -514,10 +629,7 @@ namespace hex::plugin::builtin {
|
|||||||
if (attr.getIOType() == dp::Attribute::IOType::In)
|
if (attr.getIOType() == dp::Attribute::IOType::In)
|
||||||
hasInput = true;
|
hasInput = true;
|
||||||
|
|
||||||
u32 attrId = node["attrs"][attrIndex];
|
attr.setId(node["attrs"][attrIndex]);
|
||||||
maxAttrId = std::max(attrId, maxAttrId);
|
|
||||||
|
|
||||||
attr.setId(attrId);
|
|
||||||
attrIndex++;
|
attrIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,23 +637,23 @@ namespace hex::plugin::builtin {
|
|||||||
newNode->load(node["data"]);
|
newNode->load(node["data"]);
|
||||||
|
|
||||||
if (hasInput && !hasOutput)
|
if (hasInput && !hasOutput)
|
||||||
data.endNodes.push_back(newNode.get());
|
workspace.endNodes.push_back(newNode.get());
|
||||||
|
|
||||||
data.nodes.push_back(std::move(newNode));
|
workspace.nodes.push_back(std::move(newNode));
|
||||||
ImNodes::SetNodeGridSpacePos(nodeId, ImVec2(node["pos"]["x"], node["pos"]["y"]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &link : input["links"]) {
|
int maxLinkId = 0;
|
||||||
|
for (auto &link : jsonData["links"]) {
|
||||||
dp::Link newLink(link["from"], link["to"]);
|
dp::Link newLink(link["from"], link["to"]);
|
||||||
|
|
||||||
u32 linkId = link["id"];
|
int linkId = link["id"];
|
||||||
maxLinkId = std::max(linkId, maxLinkId);
|
maxLinkId = std::max(linkId, maxLinkId);
|
||||||
|
|
||||||
newLink.setID(linkId);
|
newLink.setID(linkId);
|
||||||
data.links.push_back(newLink);
|
workspace.links.push_back(newLink);
|
||||||
|
|
||||||
dp::Attribute *fromAttr = nullptr, *toAttr = nullptr;
|
dp::Attribute *fromAttr = nullptr, *toAttr = nullptr;
|
||||||
for (auto &node : data.nodes) {
|
for (auto &node : workspace.nodes) {
|
||||||
for (auto &attribute : node->getAttributes()) {
|
for (auto &attribute : node->getAttributes()) {
|
||||||
if (attribute.getId() == newLink.getFromId())
|
if (attribute.getId() == newLink.getFromId())
|
||||||
fromAttr = &attribute;
|
fromAttr = &attribute;
|
||||||
@ -566,11 +678,21 @@ namespace hex::plugin::builtin {
|
|||||||
toAttr->addConnectedAttribute(newLink.getId(), fromAttr);
|
toAttr->addConnectedAttribute(newLink.getId(), fromAttr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int maxNodeId = 0;
|
||||||
|
int maxAttrId = 0;
|
||||||
|
for (auto &node : workspace.nodes) {
|
||||||
|
maxNodeId = std::max(maxNodeId, node->getId());
|
||||||
|
|
||||||
|
for (auto &attr : node->getAttributes()) {
|
||||||
|
maxAttrId = std::max(maxAttrId, attr.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dp::Node::setIdCounter(maxNodeId + 1);
|
dp::Node::setIdCounter(maxNodeId + 1);
|
||||||
dp::Attribute::setIdCounter(maxAttrId + 1);
|
dp::Attribute::setIdCounter(maxAttrId + 1);
|
||||||
dp::Link::setIdCounter(maxLinkId + 1);
|
dp::Link::setIdCounter(maxLinkId + 1);
|
||||||
|
|
||||||
this->processNodes();
|
ViewDataProcessor::processNodes(workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -20,10 +20,12 @@ namespace hex::plugin::windows {
|
|||||||
// Explicitly trigger a segfault by writing to an invalid memory location
|
// Explicitly trigger a segfault by writing to an invalid memory location
|
||||||
// Used for debugging crashes
|
// Used for debugging crashes
|
||||||
*reinterpret_cast<u8 *>(0x10) = 0x10;
|
*reinterpret_cast<u8 *>(0x10) = 0x10;
|
||||||
|
std::unreachable();
|
||||||
} else if (ImGui::GetIO().KeyShift) {
|
} else if (ImGui::GetIO().KeyShift) {
|
||||||
// Explicitly trigger an abort by throwing an uncaught exception
|
// Explicitly trigger an abort by throwing an uncaught exception
|
||||||
// Used for debugging exception errors
|
// Used for debugging exception errors
|
||||||
throw std::runtime_error("Debug Error");
|
throw std::runtime_error("Debug Error");
|
||||||
|
std::unreachable();
|
||||||
} else {
|
} else {
|
||||||
hex::openWebpage("https://imhex.werwolv.net/debug");
|
hex::openWebpage("https://imhex.werwolv.net/debug");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user