1
0
mirror of synced 2025-01-18 09:04:52 +01:00

impr: Replace horrible pattern extra data class with a more modular system

This commit is contained in:
WerWolv 2023-04-17 16:18:48 +02:00
parent 535aeb5e39
commit 99a736df27
26 changed files with 705 additions and 720 deletions

@ -1 +1 @@
Subproject commit e48ba7b3892eee151ed815e5b37ec33abbc66acd
Subproject commit 76f8317e8e7c761879a34227bed22a4e647ea353

@ -1 +1 @@
Subproject commit 1ae5969bd65e422523ed9d42bf715bd387075797
Subproject commit ce644cf16223f4e335a49e8f3e3cc3d9d9afe4ae

View File

@ -262,6 +262,18 @@ namespace hex {
}
/**
* @brief Provides access to the current provider's pattern language runtime
* @return Runtime
*/
pl::PatternLanguage& getRuntime();
/**
* @brief Provides access to the current provider's pattern language runtime's lock
* @return Lock
*/
std::scoped_lock<std::mutex> getRuntimeLock();
/**
* @brief Configures the pattern language runtime using ImHex's default settings
* @param runtime The pattern language runtime to configure
@ -440,7 +452,7 @@ namespace hex {
add(impl::Entry {
unlocalizedCategory.c_str(),
unlocalizedName.c_str(),
[=] {
[=, ...args = std::forward<Args>(args)] mutable {
auto node = std::make_unique<T>(std::forward<Args>(args)...);
node->setUnlocalizedName(unlocalizedName);
return node;

View File

@ -94,7 +94,7 @@ namespace hex {
* @param token Token returned by subscribe
*/
static void unsubscribe(const EventList::iterator &token) noexcept {
s_events.remove(*token);
s_events.erase(token);
}
/**
@ -167,6 +167,7 @@ namespace hex {
EVENT_DEF(EventPatternEditorChanged, const std::string&);
EVENT_DEF(EventStoreContentDownloaded, const std::fs::path&);
EVENT_DEF(EventStoreContentRemoved, const std::fs::path&);
EVENT_DEF(EventImHexClosing);
EVENT_DEF(RequestOpenWindow, std::string);
EVENT_DEF(RequestSelectionChange, Region);

View File

@ -0,0 +1,78 @@
#pragma once
#include <hex/api/imhex_api.hpp>
#include <hex/api/event.hpp>
#include <hex/providers/provider.hpp>
#include <concepts>
#include <map>
#include <utility>
namespace hex {
template<typename T>
class PerProvider {
public:
PerProvider() { this->onCreate(); }
PerProvider(const PerProvider&) = delete;
PerProvider(PerProvider&&) = delete;
PerProvider& operator=(const PerProvider&) = delete;
PerProvider& operator=(PerProvider&&) = delete;
PerProvider(T data) : m_data({ { ImHexApi::Provider::get(), std::move(data) } }) { this->onCreate(); }
~PerProvider() = default;
T* operator->() {
return &this->get();
}
const T* operator->() const {
return &this->get();
}
T& get(prv::Provider *provider = ImHexApi::Provider::get()) {
return this->m_data[provider];
}
const T& get(prv::Provider *provider = ImHexApi::Provider::get()) const {
return this->m_data[provider];
}
T& operator*() {
return this->get();
}
const T& operator*() const {
return this->get();
}
PerProvider& operator=(T data) {
this->m_data = std::move(data);
return *this;
}
operator T&() {
return this->get();
}
private:
void onCreate() {
(void)EventManager::subscribe<EventProviderOpened>([this](prv::Provider *provider) {
this->m_data.emplace(provider, T());
});
(void)EventManager::subscribe<EventProviderClosed>([this](prv::Provider *provider){
this->m_data.erase(provider);
});
EventManager::subscribe<EventImHexClosing>([this] {
this->m_data.clear();
});
}
private:
std::map<prv::Provider *, T> m_data;
};
}

View File

@ -13,6 +13,7 @@
#include <hex/api/imhex_api.hpp>
#include <hex/api/event.hpp>
#include <hex/providers/provider.hpp>
#include <hex/providers/provider_data.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/api/localization.hpp>

View File

@ -259,6 +259,18 @@ namespace hex {
return functionName;
}
pl::PatternLanguage& getRuntime() {
static PerProvider<pl::PatternLanguage> runtime;
return *runtime;
}
std::scoped_lock<std::mutex> getRuntimeLock() {
static std::mutex runtimeLock;
return std::scoped_lock(runtimeLock);
}
void configureRuntime(pl::PatternLanguage &runtime, prv::Provider *provider) {
runtime.reset();

View File

@ -267,7 +267,7 @@ namespace hex::init {
// This is a bit of a hack but necessary because when ImHex gets closed, all plugins are unloaded in order for
// destructors to be called correctly. To prevent crashes when ImHex exits, we need to delete all shared data
EventManager::clear();
EventManager::post<EventImHexClosing>();
while (ImHexApi::Provider::isValid())
ImHexApi::Provider::remove(ImHexApi::Provider::get());
@ -335,6 +335,8 @@ namespace hex::init {
fs::setFileBrowserErrorCallback(nullptr);
EventManager::clear();
return true;
}

View File

@ -1,145 +0,0 @@
#pragma once
#include <hex/api/content_registry.hpp>
#include <hex/providers/provider.hpp>
#include <pl/pattern_language.hpp>
#include <hex/data_processor/attribute.hpp>
#include <hex/data_processor/node.hpp>
#include <hex/data_processor/link.hpp>
#include <hex/helpers/logger.hpp>
#include <map>
#include <imnodes.h>
#include <imnodes_internal.h>
namespace hex::plugin::builtin {
class ProviderExtraData {
public:
struct Data {
Data() : patternLanguage(), bookmarks(), dataProcessor(), editor(), hashes(), yara() {
log::debug("Creating new extra data instance");
}
bool dataDirty = false;
struct PatternLanguage {
struct PatternVariable {
bool inVariable;
bool outVariable;
pl::core::Token::ValueType type;
pl::core::Token::Literal value;
};
enum class EnvVarType
{
Integer,
Float,
String,
Bool
};
struct EnvVar {
u64 id;
std::string name;
pl::core::Token::Literal value;
EnvVarType type;
bool operator==(const EnvVar &other) const {
return this->id == other.id;
}
};
std::string sourceCode;
std::mutex runtimeMutex;
std::unique_ptr<pl::PatternLanguage> runtime = std::make_unique<pl::PatternLanguage>();
std::vector<std::pair<pl::core::LogConsole::Level, std::string>> console;
bool executionDone = true;
std::optional<pl::core::err::PatternLanguageError> lastEvaluationError;
std::vector<std::pair<pl::core::LogConsole::Level, std::string>> lastEvaluationLog;
std::map<std::string, pl::core::Token::Literal> lastEvaluationOutVars;
std::map<std::string, PatternVariable> patternVariables;
std::map<u64, pl::api::Section> sections;
std::list<EnvVar> envVarEntries;
} patternLanguage;
std::list<ImHexApi::Bookmarks::Entry> bookmarks;
struct DataProcessor {
struct Workspace {
std::unique_ptr<ImNodesContext, void(*)(ImNodesContext*)> context = { []{
ImNodesContext *ctx = ImNodes::CreateContext();
ctx->Style = ImNodes::GetStyle();
ctx->Io = ImNodes::GetIO();
ctx->AttributeFlagStack = GImNodes->AttributeFlagStack;
return ctx;
}(), ImNodes::DestroyContext };
std::list<std::unique_ptr<dp::Node>> nodes;
std::list<dp::Node*> endNodes;
std::list<dp::Link> links;
std::vector<hex::prv::Overlay *> dataOverlays;
std::optional<dp::Node::NodeError> currNodeError;
};
Workspace mainWorkspace;
std::vector<Workspace*> workspaceStack;
} dataProcessor;
struct HexEditor {
std::optional<u64> selectionStart = std::nullopt, selectionEnd = std::nullopt;
float scrollPosition = 0.0F;
} editor;
struct Hashes {
std::vector<ContentRegistry::Hashes::Hash::Function> hashFunctions;
} hashes;
struct Yara {
struct YaraMatch {
std::string identifier;
std::string variable;
u64 address;
size_t size;
bool wholeDataMatch;
mutable u32 highlightId;
mutable u32 tooltipId;
};
std::vector<std::pair<std::fs::path, std::fs::path>> rules;
std::vector<YaraMatch> matches;
std::vector<YaraMatch*> sortedMatches;
} yara;
};
static Data& getCurrent() {
return get(ImHexApi::Provider::get());
}
static Data& get(const hex::prv::Provider *provider) {
return s_data[provider];
}
static void erase(hex::prv::Provider *provider) {
s_data.erase(provider);
}
static bool markDirty() {
return getCurrent().dataDirty = true;
}
private:
ProviderExtraData() = default;
static inline std::map<const hex::prv::Provider*, Data> s_data = {};
};
}

View File

@ -10,7 +10,7 @@ namespace hex::plugin::builtin {
class PopupFileChooser : public Popup<PopupFileChooser> {
public:
PopupFileChooser(const std::vector<std::fs::path> &files, const std::vector<nfdfilteritem_t> &validExtensions, bool multiple, const std::function<void(std::fs::path)> &callback)
: hex::Popup<PopupFileChooser>("hex.builtin.common.choose_file", false),
: hex::Popup<PopupFileChooser>("hex.builtin.common.choose_file"),
m_indices({ }), m_files(files),
m_openCallback(callback),
m_validExtensions(validExtensions), m_multiple(multiple) { }

View File

@ -15,14 +15,15 @@ namespace hex::plugin::builtin {
void drawContent() override;
private:
static bool importBookmarks(hex::prv::Provider *provider, const nlohmann::json &json);
static bool exportBookmarks(hex::prv::Provider *provider, nlohmann::json &json);
bool importBookmarks(hex::prv::Provider *provider, const nlohmann::json &json);
bool exportBookmarks(hex::prv::Provider *provider, nlohmann::json &json);
void registerMenuItems();
private:
std::string m_currFilter;
std::list<ImHexApi::Bookmarks::Entry>::iterator m_dragStartIterator;
PerProvider<std::list<ImHexApi::Bookmarks::Entry>> m_bookmarks;
};
}

View File

@ -7,7 +7,7 @@
#include <hex/data_processor/node.hpp>
#include <hex/data_processor/link.hpp>
#include "content/helpers/provider_extra_data.hpp"
#include <imnodes_internal.h>
#include <array>
#include <string>
@ -16,8 +16,26 @@ namespace hex::plugin::builtin {
class ViewDataProcessor : public View {
public:
using Workspace = ProviderExtraData::Data::DataProcessor::Workspace;
struct Workspace {
Workspace() = default;
std::unique_ptr<ImNodesContext, void(*)(ImNodesContext*)> context = { []{
ImNodesContext *ctx = ImNodes::CreateContext();
ctx->Style = ImNodes::GetStyle();
ctx->Io = ImNodes::GetIO();
ctx->AttributeFlagStack = GImNodes->AttributeFlagStack;
return ctx;
}(), ImNodes::DestroyContext };
std::list<std::unique_ptr<dp::Node>> nodes;
std::list<dp::Node*> endNodes;
std::list<dp::Link> links;
std::vector<hex::prv::Overlay *> dataOverlays;
std::optional<dp::Node::NodeError> currNodeError;
};
public:
ViewDataProcessor();
~ViewDataProcessor() override;
@ -29,12 +47,14 @@ namespace hex::plugin::builtin {
static std::unique_ptr<dp::Node> loadNode(const nlohmann::json &data);
static void loadNodes(Workspace &workspace, const nlohmann::json &data);
private:
static void eraseLink(Workspace &workspace, int id);
static void eraseNodes(Workspace &workspace, const std::vector<int> &ids);
static void processNodes(Workspace &workspace);
void reloadCustomNodes();
std::vector<Workspace*> &getWorkspaceStack() { return *this->m_workspaceStack; }
private:
bool m_updateNodePositions = false;
int m_rightClickedId = -1;
@ -48,6 +68,9 @@ namespace hex::plugin::builtin {
};
std::vector<CustomNode> m_customNodes;
PerProvider<Workspace> m_mainWorkspace;
PerProvider<std::vector<Workspace*>> m_workspaceStack;
};
}

View File

@ -18,12 +18,15 @@ namespace hex::plugin::builtin {
void drawContent() override;
private:
static bool importHashes(prv::Provider *provider, const nlohmann::json &json);
static bool exportHashes(prv::Provider *provider, nlohmann::json &json);
bool importHashes(prv::Provider *provider, const nlohmann::json &json);
bool exportHashes(prv::Provider *provider, nlohmann::json &json);
private:
ContentRegistry::Hashes::Hash *m_selectedHash = nullptr;
std::string m_newHashName;
PerProvider<std::vector<ContentRegistry::Hashes::Hash::Function>> m_hashFunctions;
};
}

View File

@ -6,7 +6,6 @@
#include <hex/helpers/concepts.hpp>
#include <hex/helpers/encoding_file.hpp>
#include <content/helpers/provider_extra_data.hpp>
#include <ui/hex_editor.hpp>
namespace hex::plugin::builtin {
@ -74,6 +73,9 @@ namespace hex::plugin::builtin {
bool m_shouldOpenPopup = false;
std::unique_ptr<Popup> m_currPopup;
PerProvider<std::optional<u64>> m_selectionStart, m_selectionEnd;
PerProvider<float> m_scrollPosition;
};
}

View File

@ -1,4 +1,4 @@
#pragma once
#pragma once
#include <hex/ui/view.hpp>
#include <hex/providers/provider.hpp>
@ -7,7 +7,6 @@
#include <pl/pattern_language.hpp>
#include <pl/core/errors/error.hpp>
#include <content/helpers/provider_extra_data.hpp>
#include <content/providers/memory_file_provider.hpp>
#include <ui/hex_editor.hpp>
@ -102,7 +101,32 @@ namespace hex::plugin::builtin {
};
private:
using PlData = ProviderExtraData::Data::PatternLanguage;
struct PatternVariable {
bool inVariable;
bool outVariable;
pl::core::Token::ValueType type;
pl::core::Token::Literal value;
};
enum class EnvVarType
{
Integer,
Float,
String,
Bool
};
struct EnvVar {
u64 id;
std::string name;
pl::core::Token::Literal value;
EnvVarType type;
bool operator==(const EnvVar &other) const {
return this->id == other.id;
}
};
std::unique_ptr<pl::PatternLanguage> m_parserRuntime;
@ -130,10 +154,22 @@ namespace hex::plugin::builtin {
ui::HexEditor m_sectionHexEditor;
PerProvider<std::string> m_sourceCode;
PerProvider<std::vector<std::pair<pl::core::LogConsole::Level, std::string>>> m_console;
PerProvider<bool> m_executionDone = true;
PerProvider<std::optional<pl::core::err::PatternLanguageError>> m_lastEvaluationError;
PerProvider<std::vector<std::pair<pl::core::LogConsole::Level, std::string>>> m_lastEvaluationLog;
PerProvider<std::map<std::string, pl::core::Token::Literal>> m_lastEvaluationOutVars;
PerProvider<std::map<std::string, PatternVariable>> m_patternVariables;
PerProvider<std::map<u64, pl::api::Section>> m_sections;
PerProvider<std::list<EnvVar>> m_envVarEntries;
private:
void drawConsole(ImVec2 size, const std::vector<std::pair<pl::core::LogConsole::Level, std::string>> &console);
void drawEnvVars(ImVec2 size, std::list<PlData::EnvVar> &envVars);
void drawVariableSettings(ImVec2 size, std::map<std::string, PlData::PatternVariable> &patternVariables);
void drawEnvVars(ImVec2 size, std::list<EnvVar> &envVars);
void drawVariableSettings(ImVec2 size, std::map<std::string, PatternVariable> &patternVariables);
void drawSectionSelector(ImVec2 size, std::map<u64, pl::api::Section> &sections);
void drawPatternTooltip(pl::ptrn::Pattern *pattern);

View File

@ -17,6 +17,22 @@ namespace hex::plugin::builtin {
void drawContent() override;
private:
struct YaraMatch {
std::string identifier;
std::string variable;
u64 address;
size_t size;
bool wholeDataMatch;
mutable u32 highlightId;
mutable u32 tooltipId;
};
private:
PerProvider<std::vector<std::pair<std::fs::path, std::fs::path>>> m_rules;
PerProvider<std::vector<YaraMatch>> m_matches;
PerProvider<std::vector<YaraMatch*>> m_sortedMatches;
u32 m_selectedRule = 0;
TaskHolder m_matcherTask;

View File

@ -7,9 +7,6 @@
#include <hex/helpers/logger.hpp>
#include <hex/providers/provider.hpp>
#include <content/views/view_data_processor.hpp>
#include <content/helpers/provider_extra_data.hpp>
#include <content/helpers/diagrams.hpp>
#include <cctype>
@ -22,7 +19,6 @@
#include <hex/ui/imgui_imhex_extensions.h>
#include <wolv/utils/core.hpp>
#include <wolv/utils/guards.hpp>
namespace hex::plugin::builtin {
@ -1166,10 +1162,10 @@ namespace hex::plugin::builtin {
}
void process() override {
auto &pl = ProviderExtraData::getCurrent().patternLanguage;
auto lock = ContentRegistry::PatternLanguage::getRuntimeLock();
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
std::scoped_lock lock(pl.runtimeMutex);
const auto &outVars = pl.runtime->getOutVariables();
const auto &outVars = runtime.getOutVariables();
if (outVars.contains(this->m_name)) {
std::visit(wolv::util::overloaded {
@ -1201,303 +1197,6 @@ namespace hex::plugin::builtin {
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(wolv::util::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"];
this->setUnlocalizedTitle(this->m_name);
this->setAttributes({
{ dp::Attribute(dp::Attribute::IOType::Out, this->getType(), "hex.builtin.nodes.common.input") }
});
}
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::In, 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"];
this->setUnlocalizedTitle(this->m_name);
this->setAttributes({
{ dp::Attribute(dp::Attribute::IOType::In, this->getType(), "hex.builtin.nodes.common.output") }
});
}
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()))) {
auto &data = ProviderExtraData::getCurrent().dataProcessor;
data.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;
};
auto prevContext = ImNodes::GetCurrentContext();
ImNodes::SetCurrentContext(this->m_workspace.context.get());
ON_SCOPE_EXIT { ImNodes::SetCurrentContext(prevContext); };
// 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: {
const auto &value = this->getIntegerOnInput(*index);
input->setValue(value);
break;
}
case dp::Attribute::Type::Float: {
const auto &value = this->getFloatOnInput(*index);
input->setValue(value);
break;
}
case dp::Attribute::Type::Buffer: {
const 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() {
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");
@ -1565,10 +1264,6 @@ namespace hex::plugin::builtin {
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<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");
}
}

View File

@ -8,8 +8,6 @@
#include <imgui.h>
#include <content/helpers/provider_extra_data.hpp>
#include <content/providers/file_provider.hpp>
#include <wolv/io/fs.hpp>
@ -131,10 +129,6 @@ namespace hex::plugin::builtin {
}
});
EventManager::subscribe<EventProviderDeleted>([](hex::prv::Provider *provider) {
ProviderExtraData::erase(provider);
});
EventManager::subscribe<EventRegionSelected>([](const ImHexApi::HexEditor::ProviderRegion &region) {
ImHexApi::HexEditor::impl::setCurrentSelection(region);
});

View File

@ -11,15 +11,13 @@
#include <nlohmann/json.hpp>
#include <cstring>
#include <content/helpers/provider_extra_data.hpp>
#include <wolv/io/file.hpp>
#include <wolv/utils/guards.hpp>
namespace hex::plugin::builtin {
ViewBookmarks::ViewBookmarks() : View("hex.builtin.view.bookmarks.name") {
EventManager::subscribe<RequestAddBookmark>(this, [](Region region, std::string name, std::string comment, color_t color) {
EventManager::subscribe<RequestAddBookmark>(this, [this](Region region, std::string name, std::string comment, color_t color) {
if (name.empty()) {
name = hex::format("hex.builtin.view.bookmarks.default_title"_lang, region.address, region.address + region.size - 1);
}
@ -27,7 +25,7 @@ namespace hex::plugin::builtin {
if (color == 0x00)
color = ImGui::GetColorU32(ImGuiCol_Header);
ProviderExtraData::getCurrent().bookmarks.push_back({
this->m_bookmarks->push_back({
region,
name,
std::move(comment),
@ -37,13 +35,13 @@ namespace hex::plugin::builtin {
ImHexApi::Provider::markDirty();
EventManager::post<EventBookmarkCreated>(ProviderExtraData::getCurrent().bookmarks.back());
EventManager::post<EventBookmarkCreated>(this->m_bookmarks->back());
});
ImHexApi::HexEditor::addBackgroundHighlightingProvider([](u64 address, const u8* data, size_t size, bool) -> std::optional<color_t> {
ImHexApi::HexEditor::addBackgroundHighlightingProvider([this](u64 address, const u8* data, size_t size, bool) -> std::optional<color_t> {
hex::unused(data);
for (const auto &bookmark : ProviderExtraData::getCurrent().bookmarks) {
for (const auto &bookmark : *this->m_bookmarks) {
if (Region { address, size }.isWithin(bookmark.region))
return bookmark.color;
}
@ -51,9 +49,9 @@ namespace hex::plugin::builtin {
return std::nullopt;
});
ImHexApi::HexEditor::addTooltipProvider([](u64 address, const u8 *data, size_t size) {
ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8 *data, size_t size) {
hex::unused(data);
for (const auto &bookmark : ProviderExtraData::getCurrent().bookmarks) {
for (const auto &bookmark : *this->m_bookmarks) {
if (!Region { address, size }.isWithin(bookmark.region))
continue;
@ -113,19 +111,19 @@ namespace hex::plugin::builtin {
ProjectFile::registerPerProviderHandler({
.basePath = "bookmarks.json",
.required = false,
.load = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
.load = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
auto fileContent = tar.readString(basePath);
if (fileContent.empty())
return true;
auto data = nlohmann::json::parse(fileContent.begin(), fileContent.end());
ProviderExtraData::get(provider).bookmarks.clear();
return ViewBookmarks::importBookmarks(provider, data);
this->m_bookmarks->clear();
return this->importBookmarks(provider, data);
},
.store = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
.store = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
nlohmann::json data;
bool result = ViewBookmarks::exportBookmarks(provider, data);
bool result = this->exportBookmarks(provider, data);
tar.writeString(basePath, data.dump(4));
return result;
@ -187,14 +185,13 @@ namespace hex::plugin::builtin {
ImGui::NewLine();
if (ImGui::BeginChild("##bookmarks")) {
auto &bookmarks = ProviderExtraData::getCurrent().bookmarks;
if (bookmarks.empty()) {
if (this->m_bookmarks->empty()) {
ImGui::TextFormattedCentered("hex.builtin.view.bookmarks.no_bookmarks"_lang);
}
int id = 1;
auto bookmarkToRemove = bookmarks.end();
for (auto iter = bookmarks.begin(); iter != bookmarks.end(); iter++) {
auto bookmarkToRemove = this->m_bookmarks->end();
for (auto iter = this->m_bookmarks->begin(); iter != this->m_bookmarks->end(); iter++) {
auto &[region, name, comment, color, locked] = *iter;
if (!this->m_currFilter.empty()) {
@ -219,16 +216,16 @@ namespace hex::plugin::builtin {
bool open = true;
if (!ImGui::CollapsingHeader(hex::format("{}###bookmark", name).c_str(), locked ? nullptr : &open)) {
if (ImGui::IsMouseClicked(0) && ImGui::IsItemActivated() && this->m_dragStartIterator == bookmarks.end())
if (ImGui::IsMouseClicked(0) && ImGui::IsItemActivated() && this->m_dragStartIterator == this->m_bookmarks->end())
this->m_dragStartIterator = iter;
if (ImGui::IsItemHovered() && this->m_dragStartIterator != bookmarks.end()) {
if (ImGui::IsItemHovered() && this->m_dragStartIterator != this->m_bookmarks->end()) {
std::iter_swap(iter, this->m_dragStartIterator);
this->m_dragStartIterator = iter;
}
if (!ImGui::IsMouseDown(0))
this->m_dragStartIterator = bookmarks.end();
this->m_dragStartIterator = this->m_bookmarks->end();
} else {
const auto rowHeight = ImGui::GetTextLineHeightWithSpacing() + 2 * ImGui::GetStyle().FramePadding.y;
if (ImGui::BeginTable("##bookmark_table", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
@ -329,8 +326,8 @@ namespace hex::plugin::builtin {
bookmarkToRemove = iter;
}
if (bookmarkToRemove != bookmarks.end()) {
bookmarks.erase(bookmarkToRemove);
if (bookmarkToRemove != this->m_bookmarks->end()) {
this->m_bookmarks->erase(bookmarkToRemove);
}
}
ImGui::EndChild();
@ -342,7 +339,6 @@ namespace hex::plugin::builtin {
if (!json.contains("bookmarks"))
return false;
auto &bookmarks = ProviderExtraData::get(provider).bookmarks;
for (const auto &bookmark : json["bookmarks"]) {
if (!bookmark.contains("name") || !bookmark.contains("comment") || !bookmark.contains("color") || !bookmark.contains("region") || !bookmark.contains("locked"))
continue;
@ -351,7 +347,7 @@ namespace hex::plugin::builtin {
if (!region.contains("address") || !region.contains("size"))
continue;
bookmarks.push_back({
this->m_bookmarks.get(provider).push_back({
.region = { region["address"], region["size"] },
.name = bookmark["name"],
.comment = bookmark["comment"],
@ -366,7 +362,7 @@ namespace hex::plugin::builtin {
bool ViewBookmarks::exportBookmarks(prv::Provider *provider, nlohmann::json &json) {
json["bookmarks"] = nlohmann::json::array();
size_t index = 0;
for (const auto &bookmark : ProviderExtraData::get(provider).bookmarks) {
for (const auto &bookmark : this->m_bookmarks.get(provider)) {
json["bookmarks"][index] = {
{ "name", bookmark.name },
{ "comment", bookmark.comment },
@ -395,10 +391,10 @@ namespace hex::plugin::builtin {
ContentRegistry::Interface::addMenuItemSeparator({ "hex.builtin.menu.file", "hex.builtin.menu.file.import" }, 3000);
/* Import bookmarks */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.import", "hex.builtin.menu.file.import.bookmark" }, 3050, Shortcut::None, []{
fs::openFileBrowser(fs::DialogMode::Open, { { "Bookmarks File", "hexbm"} }, [&](const std::fs::path &path) {
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.import", "hex.builtin.menu.file.import.bookmark" }, 3050, Shortcut::None, [this]{
fs::openFileBrowser(fs::DialogMode::Open, { { "Bookmarks File", "hexbm"} }, [&, this](const std::fs::path &path) {
try {
importBookmarks(ImHexApi::Provider::get(), nlohmann::json::parse(wolv::io::File(path, wolv::io::File::Mode::Read).readString()));
this->importBookmarks(ImHexApi::Provider::get(), nlohmann::json::parse(wolv::io::File(path, wolv::io::File::Mode::Read).readString()));
} catch (...) { }
});
}, ImHexApi::Provider::isValid);
@ -407,15 +403,15 @@ namespace hex::plugin::builtin {
/* Export bookmarks */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.export", "hex.builtin.menu.file.export.bookmark" }, 6250, Shortcut::None, []{
fs::openFileBrowser(fs::DialogMode::Save, { { "Bookmarks File", "hexbm"} }, [&](const std::fs::path &path) {
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.export", "hex.builtin.menu.file.export.bookmark" }, 6250, Shortcut::None, [this]{
fs::openFileBrowser(fs::DialogMode::Save, { { "Bookmarks File", "hexbm"} }, [&, this](const std::fs::path &path) {
nlohmann::json json;
exportBookmarks(ImHexApi::Provider::get(), json);
this->exportBookmarks(ImHexApi::Provider::get(), json);
wolv::io::File(path, wolv::io::File::Mode::Create).writeString(json.dump(4));
});
}, []{
return ImHexApi::Provider::isValid() && !ProviderExtraData::getCurrent().bookmarks.empty();
}, [this]{
return ImHexApi::Provider::isValid() && !this->m_bookmarks->empty();
});
}

View File

@ -11,45 +11,338 @@
#include <imnodes_internal.h>
#include <nlohmann/json.hpp>
#include <content/helpers/provider_extra_data.hpp>
#include <wolv/io/file.hpp>
#include <wolv/utils/guards.hpp>
namespace hex::plugin::builtin {
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(wolv::util::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"];
this->setUnlocalizedTitle(this->m_name);
this->setAttributes({
{ dp::Attribute(dp::Attribute::IOType::Out, this->getType(), "hex.builtin.nodes.common.input") }
});
}
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::In, 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"];
this->setUnlocalizedTitle(this->m_name);
this->setAttributes({
{ dp::Attribute(dp::Attribute::IOType::In, this->getType(), "hex.builtin.nodes.common.output") }
});
}
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:
explicit NodeCustom(ViewDataProcessor *dataProcessor) : Node("hex.builtin.nodes.custom.custom.header", {}), m_dataProcessor(dataProcessor) { }
~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()))) {
this->m_dataProcessor->getWorkspaceStack().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;
};
auto prevContext = ImNodes::GetCurrentContext();
ImNodes::SetCurrentContext(this->m_workspace.context.get());
ON_SCOPE_EXIT { ImNodes::SetCurrentContext(prevContext); };
// 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: {
const auto &value = this->getIntegerOnInput(*index);
input->setValue(value);
break;
}
case dp::Attribute::Type::Float: {
const auto &value = this->getFloatOnInput(*index);
input->setValue(value);
break;
}
case dp::Attribute::Type::Buffer: {
const 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"] = this->m_dataProcessor->saveNodes(this->m_workspace);
}
void load(const nlohmann::json &j) override {
this->m_dataProcessor->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;
ViewDataProcessor *m_dataProcessor;
ViewDataProcessor::Workspace m_workspace;
};
ViewDataProcessor::ViewDataProcessor() : View("hex.builtin.view.data_processor.name") {
ContentRegistry::DataProcessorNode::add<NodeCustom>("hex.builtin.nodes.custom", "hex.builtin.nodes.custom.custom", this);
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");
ProjectFile::registerPerProviderHandler({
.basePath = "data_processor.json",
.required = false,
.load = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
auto save = tar.readString(basePath);
auto &data = ProviderExtraData::get(provider).dataProcessor;
ViewDataProcessor::loadNodes(data.mainWorkspace, save);
ViewDataProcessor::loadNodes(this->m_mainWorkspace.get(provider), save);
this->m_updateNodePositions = true;
return true;
},
.store = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
auto &data = ProviderExtraData::get(provider).dataProcessor;
tar.writeString(basePath, ViewDataProcessor::saveNodes(data.mainWorkspace).dump(4));
.store = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
tar.writeString(basePath, ViewDataProcessor::saveNodes(this->m_mainWorkspace.get(provider)).dump(4));
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<EventProviderCreated>(this, [this](auto *provider) {
this->m_mainWorkspace.get(provider) = { };
this->m_workspaceStack.get(provider).push_back(&this->m_mainWorkspace.get(provider));
});
EventManager::subscribe<EventProviderChanged>(this, [this](const auto &, const auto &) {
auto &data = ProviderExtraData::getCurrent().dataProcessor;
for (auto *workspace : data.workspaceStack) {
EventManager::subscribe<EventProviderChanged>(this, [this](const auto *, const auto *) {
for (auto *workspace : *this->m_workspaceStack) {
for (auto &node : workspace->nodes) {
node->setCurrentOverlay(nullptr);
}
@ -60,48 +353,39 @@ namespace hex::plugin::builtin {
this->m_updateNodePositions = true;
});
EventManager::subscribe<EventDataChanged>(this, [] {
auto &workspace = *ProviderExtraData::getCurrent().dataProcessor.workspaceStack.back();
ViewDataProcessor::processNodes(workspace);
EventManager::subscribe<EventDataChanged>(this, [this] {
ViewDataProcessor::processNodes(*this->m_workspaceStack->back());
});
/* Import bookmarks */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.import", "hex.builtin.menu.file.import.data_processor" }, 4050, Shortcut::None, [this]{
auto &data = ProviderExtraData::getCurrent().dataProcessor;
fs::openFileBrowser(fs::DialogMode::Open, { {"hex.builtin.view.data_processor.name"_lang, "hexnode" } },
[&](const std::fs::path &path) {
wolv::io::File file(path, wolv::io::File::Mode::Read);
if (file.isValid()) {
ViewDataProcessor::loadNodes(data.mainWorkspace, file.readString());
ViewDataProcessor::loadNodes(*this->m_mainWorkspace, file.readString());
this->m_updateNodePositions = true;
}
});
}, ImHexApi::Provider::isValid);
/* Export bookmarks */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.export", "hex.builtin.menu.file.export.data_processor" }, 8050, Shortcut::None, []{
auto &data = ProviderExtraData::getCurrent().dataProcessor;
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.export", "hex.builtin.menu.file.export.data_processor" }, 8050, Shortcut::None, [this]{
fs::openFileBrowser(fs::DialogMode::Save, { {"hex.builtin.view.data_processor.name"_lang, "hexnode" } },
[&](const std::fs::path &path) {
[&, this](const std::fs::path &path) {
wolv::io::File file(path, wolv::io::File::Mode::Create);
if (file.isValid())
file.writeString(ViewDataProcessor::saveNodes(data.mainWorkspace).dump(4));
file.writeString(ViewDataProcessor::saveNodes(*this->m_mainWorkspace).dump(4));
});
}, []{
auto &data = ProviderExtraData::getCurrent().dataProcessor;
return !data.workspaceStack.empty() && !data.workspaceStack.back()->nodes.empty() && ImHexApi::Provider::isValid();
}, [this]{
return !this->m_workspaceStack->empty() && !this->m_workspaceStack->back()->nodes.empty() && ImHexApi::Provider::isValid();
});
ContentRegistry::FileHandler::add({ ".hexnode" }, [this](const auto &path) {
wolv::io::File file(path, wolv::io::File::Mode::Read);
if (!file.isValid()) return false;
auto &data = ProviderExtraData::getCurrent().dataProcessor;
ViewDataProcessor::loadNodes(data.mainWorkspace, file.readString());
ViewDataProcessor::loadNodes(*this->m_mainWorkspace, file.readString());
this->m_updateNodePositions = true;
return true;
@ -117,7 +401,7 @@ namespace hex::plugin::builtin {
}
void ViewDataProcessor::eraseLink(ProviderExtraData::Data::DataProcessor::Workspace &workspace, int id) {
void ViewDataProcessor::eraseLink(Workspace &workspace, int id) {
auto link = std::find_if(workspace.links.begin(), workspace.links.end(), [&id](auto link) { return link.getId() == id; });
if (link == workspace.links.end())
@ -134,7 +418,7 @@ namespace hex::plugin::builtin {
ImHexApi::Provider::markDirty();
}
void ViewDataProcessor::eraseNodes(ProviderExtraData::Data::DataProcessor::Workspace &workspace, const std::vector<int> &ids) {
void ViewDataProcessor::eraseNodes(Workspace &workspace, const std::vector<int> &ids) {
for (int id : ids) {
auto node = std::find_if(workspace.nodes.begin(), workspace.nodes.end(),
[&id](const auto &node) {
@ -162,7 +446,7 @@ namespace hex::plugin::builtin {
ImHexApi::Provider::markDirty();
}
void ViewDataProcessor::processNodes(ProviderExtraData::Data::DataProcessor::Workspace &workspace) {
void ViewDataProcessor::processNodes(Workspace &workspace) {
if (workspace.dataOverlays.size() != workspace.endNodes.size()) {
for (auto overlay : workspace.dataOverlays)
ImHexApi::Provider::get()->deleteOverlay(overlay);
@ -220,8 +504,7 @@ namespace hex::plugin::builtin {
}
void ViewDataProcessor::drawContent() {
auto &data = ProviderExtraData::getCurrent().dataProcessor;
auto &workspace = *data.workspaceStack.back();
auto &workspace = *this->m_workspaceStack->back();
bool popWorkspace = false;
if (ImGui::Begin(View::toWindowName("hex.builtin.view.data_processor.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
@ -360,6 +643,8 @@ namespace hex::plugin::builtin {
ImNodes::BeginNodeEditor();
for (auto &node : workspace.nodes) {
ImNodes::SnapNodeToGrid(node->getId());
const bool hasError = workspace.currNodeError.has_value() && workspace.currNodeError->node == node.get();
if (hasError)
@ -471,7 +756,7 @@ namespace hex::plugin::builtin {
if (workspace.nodes.empty())
ImGui::TextFormattedCentered("{}", "hex.builtin.view.data_processor.help_text"_lang);
if (data.workspaceStack.size() > 1) {
if (this->m_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))) {
popWorkspace = true;
@ -559,7 +844,7 @@ namespace hex::plugin::builtin {
ImGui::End();
if (popWorkspace) {
data.workspaceStack.pop_back();
this->m_workspaceStack->pop_back();
this->m_updateNodePositions = true;
}
}

View File

@ -184,7 +184,7 @@ namespace hex::plugin::builtin {
const auto height = ImGui::GetContentRegionAvail().y;
if (ImGui::BeginTable("##binary_diff", 2, ImGuiTableFlags_None, ImVec2(0, height - 200_scaled))) {
if (ImGui::BeginTable("##binary_diff", 2, ImGuiTableFlags_None, ImVec2(0, height - 250_scaled))) {
ImGui::TableSetupColumn("hex.builtin.view.diff.provider_a"_lang);
ImGui::TableSetupColumn("hex.builtin.view.diff.provider_b"_lang);
ImGui::TableHeadersRow();

View File

@ -1,5 +1,4 @@
#include "content/views/view_hashes.hpp"
#include "content/helpers/provider_extra_data.hpp"
#include <hex/api/project_file_manager.hpp>
#include <hex/helpers/crypto.hpp>
@ -9,18 +8,18 @@
namespace hex::plugin::builtin {
ViewHashes::ViewHashes() : View("hex.builtin.view.hashes.name") {
EventManager::subscribe<EventRegionSelected>(this, [](const auto &providerRegion) {
for (auto &function : ProviderExtraData::get(providerRegion.getProvider()).hashes.hashFunctions)
EventManager::subscribe<EventRegionSelected>(this, [this](const auto &providerRegion) {
for (auto &function : this->m_hashFunctions.get(providerRegion.getProvider()))
function.reset();
});
ImHexApi::HexEditor::addTooltipProvider([](u64 address, const u8 *data, size_t size) {
ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8 *data, size_t size) {
hex::unused(data);
auto selection = ImHexApi::HexEditor::getSelection();
if (ImGui::GetIO().KeyShift) {
auto &hashFunctions = ProviderExtraData::get(selection->getProvider()).hashes.hashFunctions;
auto &hashFunctions = this->m_hashFunctions.get(selection->getProvider());
if (!hashFunctions.empty() && selection.has_value() && selection->overlaps(Region { address, size })) {
ImGui::BeginTooltip();
@ -62,20 +61,20 @@ namespace hex::plugin::builtin {
ProjectFile::registerPerProviderHandler({
.basePath = "hashes.json",
.required = false,
.load = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
.load = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
auto fileContent = tar.readString(basePath);
if (fileContent.empty())
return true;
auto data = nlohmann::json::parse(fileContent.begin(), fileContent.end());
ProviderExtraData::get(provider).hashes.hashFunctions.clear();
this->m_hashFunctions->clear();
return ViewHashes::importHashes(provider, data);
return this->importHashes(provider, data);
},
.store = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
.store = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
nlohmann::json data;
bool result = ViewHashes::exportHashes(provider, data);
bool result = this->exportHashes(provider, data);
tar.writeString(basePath, data.dump(4));
return result;
@ -96,8 +95,6 @@ namespace hex::plugin::builtin {
}
if (ImGui::Begin(View::toWindowName("hex.builtin.view.hashes.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
auto &hashFunctions = ProviderExtraData::get(ImHexApi::Provider::get()).hashes.hashFunctions;
if (ImGui::BeginCombo("hex.builtin.view.hashes.function"_lang, this->m_selectedHash != nullptr ? LangEntry(this->m_selectedHash->getUnlocalizedName()) : "")) {
for (const auto hash : hashes) {
@ -134,7 +131,7 @@ namespace hex::plugin::builtin {
ImGui::BeginDisabled(this->m_newHashName.empty() || this->m_selectedHash == nullptr);
if (ImGui::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
if (this->m_selectedHash != nullptr)
hashFunctions.push_back(this->m_selectedHash->create(this->m_newHashName));
this->m_hashFunctions->push_back(this->m_selectedHash->create(this->m_newHashName));
}
ImGui::EndDisabled();
@ -149,8 +146,8 @@ namespace hex::plugin::builtin {
auto selection = ImHexApi::HexEditor::getSelection();
std::optional<u32> indexToRemove;
for (u32 i = 0; i < hashFunctions.size(); i++) {
auto &function = hashFunctions[i];
for (u32 i = 0; i < this->m_hashFunctions->size(); i++) {
auto &function = (*this->m_hashFunctions)[i];
ImGui::PushID(i);
@ -195,7 +192,7 @@ namespace hex::plugin::builtin {
}
if (indexToRemove.has_value()) {
hashFunctions.erase(hashFunctions.begin() + indexToRemove.value());
this->m_hashFunctions->erase(this->m_hashFunctions->begin() + indexToRemove.value());
}
ImGui::EndTable();
@ -213,7 +210,6 @@ namespace hex::plugin::builtin {
const auto &hashes = ContentRegistry::Hashes::impl::getHashes();
auto &hashFunctions = ProviderExtraData::get(provider).hashes.hashFunctions;
for (const auto &hash : json["hashes"]) {
if (!hash.contains("name") || !hash.contains("type"))
continue;
@ -224,7 +220,7 @@ namespace hex::plugin::builtin {
auto newFunction = newHash->create(hash["name"]);
newFunction.getType()->load(hash["settings"]);
hashFunctions.push_back(std::move(newFunction));
this->m_hashFunctions.get(provider).push_back(std::move(newFunction));
break;
}
}
@ -236,7 +232,7 @@ namespace hex::plugin::builtin {
bool ViewHashes::exportHashes(prv::Provider *provider, nlohmann::json &json) {
json["hashes"] = nlohmann::json::array();
size_t index = 0;
for (const auto &hashFunction : ProviderExtraData::get(provider).hashes.hashFunctions) {
for (const auto &hashFunction : this->m_hashFunctions.get(provider)) {
json["hashes"][index] = {
{ "name", hashFunction.getName() },
{ "type", hashFunction.getType()->getUnlocalizedName() },

View File

@ -12,7 +12,6 @@
#include <content/popups/popup_file_chooser.hpp>
#include <imgui_internal.h>
#include <nlohmann/json.hpp>
#include <thread>
@ -676,10 +675,8 @@ namespace hex::plugin::builtin {
// Remove selection
ShortcutManager::addShortcut(this, Keys::Escape, [this] {
auto provider = ImHexApi::Provider::get();
auto &data = ProviderExtraData::get(provider).editor;
data.selectionStart.reset();
data.selectionEnd.reset();
this->m_selectionStart->reset();
this->m_selectionEnd->reset();
EventManager::post<EventRegionSelected>(ImHexApi::HexEditor::ProviderRegion{ this->getSelection(), provider });
});
@ -849,10 +846,8 @@ namespace hex::plugin::builtin {
auto provider = ImHexApi::Provider::get();
if (region == Region::Invalid()) {
auto &providerData = ProviderExtraData::get(provider).editor;
providerData.selectionStart.reset();
providerData.selectionEnd.reset();
this->m_selectionStart->reset();
this->m_selectionEnd->reset();
EventManager::post<EventRegionSelected>(ImHexApi::HexEditor::ProviderRegion({ Region::Invalid(), nullptr }));
return;
@ -871,14 +866,12 @@ namespace hex::plugin::builtin {
EventManager::subscribe<EventProviderChanged>(this, [this](auto *oldProvider, auto *newProvider) {
if (oldProvider != nullptr) {
auto &oldData = ProviderExtraData::get(oldProvider).editor;
auto selection = this->m_hexEditor.getSelection();
if (selection != Region::Invalid()) {
oldData.selectionStart = selection.getStartAddress();
oldData.selectionEnd = selection.getEndAddress();
oldData.scrollPosition = this->m_hexEditor.getScrollPosition();
this->m_selectionStart.get(oldProvider) = selection.getStartAddress();
this->m_selectionEnd.get(oldProvider) = selection.getEndAddress();
this->m_scrollPosition.get(oldProvider) = this->m_hexEditor.getScrollPosition();
}
}
@ -886,10 +879,8 @@ namespace hex::plugin::builtin {
this->m_hexEditor.setScrollPosition(0);
if (newProvider != nullptr) {
auto &newData = ProviderExtraData::get(newProvider).editor;
this->m_hexEditor.setSelectionUnchecked(newData.selectionStart, newData.selectionEnd);
this->m_hexEditor.setScrollPosition(newData.scrollPosition);
this->m_hexEditor.setSelectionUnchecked(this->m_selectionStart.get(newProvider), this->m_selectionEnd.get(newProvider));
this->m_hexEditor.setScrollPosition(this->m_scrollPosition.get(newProvider));
}
this->m_hexEditor.forceUpdateScrollPosition();

View File

@ -5,8 +5,6 @@
#include <pl/patterns/pattern.hpp>
#include <content/helpers/provider_extra_data.hpp>
namespace hex::plugin::builtin {
ViewPatternData::ViewPatternData() : View("hex.builtin.view.pattern_data.name") {
@ -32,18 +30,19 @@ namespace hex::plugin::builtin {
if (ImGui::Begin(View::toWindowName("hex.builtin.view.pattern_data.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (ImHexApi::Provider::isValid()) {
auto provider = ImHexApi::Provider::get();
auto &patternLanguage = ProviderExtraData::get(provider).patternLanguage;
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
const auto &patterns = [&] -> const auto& {
if (provider->isReadable() && patternLanguage.runtime != nullptr && patternLanguage.executionDone)
return ProviderExtraData::get(provider).patternLanguage.runtime->getAllPatterns();
if (provider->isReadable())
return runtime.getAllPatterns();
else {
static const std::vector<std::shared_ptr<pl::ptrn::Pattern>> empty;
return empty;
}
}();
if (!patternLanguage.executionDone)
if (runtime.isRunning())
this->m_patternDrawer.reset();
this->m_patternDrawer.draw(patterns);

View File

@ -14,7 +14,6 @@
#include <hex/api/project_file_manager.hpp>
#include <hex/helpers/magic.hpp>
#include <content/helpers/provider_extra_data.hpp>
#include <content/popups/popup_file_chooser.hpp>
#include <content/popups/popup_question.hpp>
@ -110,7 +109,6 @@ namespace hex::plugin::builtin {
auto provider = ImHexApi::Provider::get();
if (ImHexApi::Provider::isValid() && provider->isAvailable()) {
auto &extraData = ProviderExtraData::get(provider).patternLanguage;
auto textEditorSize = ImGui::GetContentRegionAvail();
textEditorSize.y *= 3.75 / 5.0;
@ -122,19 +120,19 @@ namespace hex::plugin::builtin {
if (ImGui::BeginTabBar("##settings")) {
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.console"_lang)) {
this->drawConsole(settingsSize, extraData.console);
this->drawConsole(settingsSize, *this->m_console);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.env_vars"_lang)) {
this->drawEnvVars(settingsSize, extraData.envVarEntries);
this->drawEnvVars(settingsSize, *this->m_envVarEntries);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.settings"_lang)) {
this->drawVariableSettings(settingsSize, extraData.patternVariables);
this->drawVariableSettings(settingsSize, *this->m_patternVariables);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.sections"_lang)) {
this->drawSectionSelector(settingsSize, extraData.sections);
this->drawSectionSelector(settingsSize, *this->m_sections);
ImGui::EndTabItem();
}
@ -143,10 +141,10 @@ namespace hex::plugin::builtin {
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);
auto &runtime = ProviderExtraData::getCurrent().patternLanguage.runtime;
if (runtime != nullptr && runtime->isRunning()) {
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
if (runtime.isRunning()) {
if (ImGui::IconButton(ICON_VS_DEBUG_STOP, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed)))
runtime->abort();
runtime.abort();
} else {
if (ImGui::IconButton(ICON_VS_DEBUG_START, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_triggerEvaluation) {
this->m_triggerEvaluation = false;
@ -171,8 +169,8 @@ namespace hex::plugin::builtin {
ImGui::SameLine();
ImGui::TextFormatted("{} / {}",
runtime->getCreatedPatternCount(),
runtime->getMaximumPatternCount());
runtime.getCreatedPatternCount(),
runtime.getMaximumPatternCount());
}
if (this->m_textEditor.IsTextChanged()) {
@ -258,7 +256,7 @@ namespace hex::plugin::builtin {
ImGui::PopStyleColor(1);
}
void ViewPatternEditor::drawEnvVars(ImVec2 size, std::list<PlData::EnvVar> &envVars) {
void ViewPatternEditor::drawEnvVars(ImVec2 size, std::list<EnvVar> &envVars) {
static u32 envVarCounter = 1;
if (ImGui::BeginChild("##env_vars", size, true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
@ -283,7 +281,7 @@ namespace hex::plugin::builtin {
if (ImGui::BeginCombo("", Types[static_cast<int>(type)])) {
for (size_t i = 0; i < Types.size(); i++) {
if (ImGui::Selectable(Types[i]))
type = static_cast<PlData::EnvVarType>(i);
type = static_cast<EnvVarType>(i);
}
ImGui::EndCombo();
@ -300,7 +298,7 @@ namespace hex::plugin::builtin {
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
switch (type) {
using enum PlData::EnvVarType;
using enum EnvVarType;
case Integer:
{
i64 displayValue = hex::get_or<i128>(value, 0);
@ -335,7 +333,7 @@ namespace hex::plugin::builtin {
ImGui::TableNextColumn();
if (ImGui::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
envVars.insert(std::next(iter), { envVarCounter++, "", i128(0), PlData::EnvVarType::Integer });
envVars.insert(std::next(iter), { envVarCounter++, "", i128(0), EnvVarType::Integer });
}
ImGui::SameLine();
@ -359,7 +357,7 @@ namespace hex::plugin::builtin {
ImGui::EndChild();
}
void ViewPatternEditor::drawVariableSettings(ImVec2 size, std::map<std::string, PlData::PatternVariable> &patternVariables) {
void ViewPatternEditor::drawVariableSettings(ImVec2 size, std::map<std::string, PatternVariable> &patternVariables) {
if (ImGui::BeginChild("##settings", size, true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
if (patternVariables.empty()) {
ImGui::TextFormattedCentered("hex.builtin.view.pattern_editor.no_in_out_vars"_lang);
@ -413,6 +411,8 @@ namespace hex::plugin::builtin {
}
void ViewPatternEditor::drawSectionSelector(ImVec2 size, std::map<u64, pl::api::Section> &sections) {
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
if (ImGui::BeginTable("##sections_table", 3, ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, size)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.builtin.common.name"_lang, ImGuiTableColumnFlags_WidthStretch, 0.5F);
@ -439,14 +439,14 @@ namespace hex::plugin::builtin {
auto hexEditor = auto(this->m_sectionHexEditor);
hexEditor.setBackgroundHighlightCallback([this, id](u64 address, const u8 *, size_t) -> std::optional<color_t> {
hexEditor.setBackgroundHighlightCallback([this, id, &runtime](u64 address, const u8 *, size_t) -> std::optional<color_t> {
if (this->m_runningEvaluators != 0)
return std::nullopt;
if (!ImHexApi::Provider::isValid())
return std::nullopt;
std::optional<ImColor> color;
for (const auto &pattern : ProviderExtraData::getCurrent().patternLanguage.runtime->getPatternsAtAddress(address, id)) {
for (const auto &pattern : runtime.getPatternsAtAddress(address, id)) {
if (pattern->getVisibility() != pl::ptrn::Visibility::Visible)
continue;
@ -462,25 +462,23 @@ namespace hex::plugin::builtin {
auto patternProvider = ImHexApi::Provider::get();
this->m_sectionWindowDrawer[patternProvider] = [id, patternProvider, dataProvider = std::move(dataProvider), hexEditor, patternDrawer = ui::PatternDrawer()] mutable {
this->m_sectionWindowDrawer[patternProvider] = [this, id, patternProvider, dataProvider = std::move(dataProvider), hexEditor, patternDrawer = ui::PatternDrawer(), &runtime] mutable {
hexEditor.setProvider(dataProvider.get());
hexEditor.draw(480_scaled);
patternDrawer.setSelectionCallback([&](const auto &region) {
hexEditor.setSelection(region);
});
auto &patternLanguage = ProviderExtraData::get(patternProvider).patternLanguage;
const auto &patterns = [&] -> const auto& {
if (patternProvider->isReadable() && patternLanguage.runtime != nullptr && patternLanguage.executionDone)
return ProviderExtraData::get(patternProvider).patternLanguage.runtime->getAllPatterns(id);
const auto &patterns = [&, this] -> const auto& {
if (patternProvider->isReadable() && *this->m_executionDone)
return runtime.getAllPatterns(id);
else {
static const std::vector<std::shared_ptr<pl::ptrn::Pattern>> empty;
return empty;
}
}();
if (patternLanguage.executionDone)
if (*this->m_executionDone)
patternDrawer.draw(patterns, 150_scaled);
};
}
@ -509,28 +507,27 @@ namespace hex::plugin::builtin {
this->m_sectionWindowDrawer.erase(provider);
}
auto &extraData = ProviderExtraData::get(provider).patternLanguage;
if (!this->m_lastEvaluationProcessed) {
extraData.console = extraData.lastEvaluationLog;
*this->m_console = this->m_lastEvaluationLog;
if (!this->m_lastEvaluationResult) {
if (extraData.lastEvaluationError) {
if (this->m_lastEvaluationError->has_value()) {
TextEditor::ErrorMarkers errorMarkers = {
{ extraData.lastEvaluationError->line, extraData.lastEvaluationError->message }
{ (*this->m_lastEvaluationError)->line, (*this->m_lastEvaluationError)->message }
};
this->m_textEditor.SetErrorMarkers(errorMarkers);
}
} else {
for (auto &[name, variable] : extraData.patternVariables) {
if (variable.outVariable && extraData.lastEvaluationOutVars.contains(name))
variable.value = extraData.lastEvaluationOutVars.at(name);
for (auto &[name, variable] : *this->m_patternVariables) {
if (variable.outVariable && this->m_lastEvaluationOutVars->contains(name))
variable.value = this->m_lastEvaluationOutVars->at(name);
}
EventManager::post<EventHighlightingChanged>();
}
this->m_lastEvaluationProcessed = true;
extraData.executionDone = true;
*this->m_executionDone = true;
}
}
@ -613,9 +610,10 @@ namespace hex::plugin::builtin {
ContentRegistry::PatternLanguage::configureRuntime(*this->m_parserRuntime, nullptr);
auto ast = this->m_parserRuntime->parseString(code);
auto &patternLanguage = ProviderExtraData::get(provider).patternLanguage;
patternLanguage.patternVariables.clear();
auto &patternVariables = this->m_patternVariables.get(provider);
patternVariables.clear();
if (ast) {
for (auto &node : *ast) {
@ -624,9 +622,10 @@ namespace hex::plugin::builtin {
if (type == nullptr) continue;
auto builtinType = dynamic_cast<pl::core::ast::ASTNodeBuiltinType *>(type->getType().get());
if (builtinType == nullptr) continue;
if (builtinType == nullptr)
continue;
PlData::PatternVariable variable = {
PatternVariable variable = {
.inVariable = variableDecl->isInVariable(),
.outVariable = variableDecl->isOutVariable(),
.type = builtinType->getType(),
@ -634,8 +633,8 @@ namespace hex::plugin::builtin {
};
if (variable.inVariable || variable.outVariable) {
if (!patternLanguage.patternVariables.contains(variableDecl->getName()))
patternLanguage.patternVariables[variableDecl->getName()] = variable;
if (!patternVariables.contains(variableDecl->getName()))
patternVariables[variableDecl->getName()] = variable;
}
}
}
@ -645,38 +644,36 @@ namespace hex::plugin::builtin {
}
void ViewPatternEditor::evaluatePattern(const std::string &code, prv::Provider *provider) {
auto &patternLanguage = ProviderExtraData::get(provider).patternLanguage;
this->m_runningEvaluators++;
patternLanguage.executionDone = false;
*this->m_executionDone = false;
this->m_textEditor.SetErrorMarkers({});
patternLanguage.console.clear();
this->m_console->clear();
this->m_sectionWindowDrawer.clear();
ContentRegistry::PatternLanguage::configureRuntime(*patternLanguage.runtime, provider);
EventManager::post<EventHighlightingChanged>();
EventManager::post<EventPatternExecuted>(code);
TaskManager::createTask("hex.builtin.view.pattern_editor.evaluating", TaskManager::NoProgress, [this, &patternLanguage, code](auto &task) {
std::scoped_lock lock(patternLanguage.runtimeMutex);
auto &runtime = patternLanguage.runtime;
TaskManager::createTask("hex.builtin.view.pattern_editor.evaluating", TaskManager::NoProgress, [this, code, provider](auto &task) {
auto lock = ContentRegistry::PatternLanguage::getRuntimeLock();
task.setInterruptCallback([&runtime] { runtime->abort(); });
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
ContentRegistry::PatternLanguage::configureRuntime(runtime, provider);
task.setInterruptCallback([&runtime] { runtime.abort(); });
std::map<std::string, pl::core::Token::Literal> envVars;
for (const auto &[id, name, value, type] : patternLanguage.envVarEntries)
for (const auto &[id, name, value, type] : *this->m_envVarEntries)
envVars.insert({ name, value });
std::map<std::string, pl::core::Token::Literal> inVariables;
for (auto &[name, variable] : patternLanguage.patternVariables) {
for (auto &[name, variable] : *this->m_patternVariables) {
if (variable.inVariable)
inVariables[name] = variable.value;
}
runtime->setDangerousFunctionCallHandler([this]{
runtime.setDangerousFunctionCallHandler([this]{
this->m_dangerousFunctionCalled = true;
while (this->m_dangerousFunctionsAllowed == DangerousFunctionPerms::Ask) {
@ -687,24 +684,24 @@ namespace hex::plugin::builtin {
});
ON_SCOPE_EXIT {
patternLanguage.lastEvaluationLog = runtime->getConsoleLog();
patternLanguage.lastEvaluationOutVars = runtime->getOutVariables();
patternLanguage.sections = runtime->getSections();
*this->m_lastEvaluationLog = runtime.getConsoleLog();
*this->m_lastEvaluationOutVars = runtime.getOutVariables();
*this->m_sections = runtime.getSections();
this->m_runningEvaluators--;
this->m_lastEvaluationProcessed = false;
patternLanguage.lastEvaluationLog.emplace_back(
this->m_lastEvaluationLog->emplace_back(
pl::core::LogConsole::Level::Info,
hex::format("Evaluation took {}", runtime->getLastRunningTime())
hex::format("Evaluation took {}", runtime.getLastRunningTime())
);
};
this->m_lastEvaluationResult = runtime->executeString(code, envVars, inVariables);
this->m_lastEvaluationResult = runtime.executeString(code, envVars, inVariables);
if (!this->m_lastEvaluationResult) {
patternLanguage.lastEvaluationError = runtime->getError();
*this->m_lastEvaluationError = runtime.getError();
}
});
}
@ -729,27 +726,26 @@ namespace hex::plugin::builtin {
});
EventManager::subscribe<EventProviderOpened>(this, [this](prv::Provider *provider) {
auto &patternLanguageData = ProviderExtraData::get(provider).patternLanguage;
patternLanguageData.runtime = std::make_unique<pl::PatternLanguage>();
ContentRegistry::PatternLanguage::configureRuntime(*patternLanguageData.runtime, provider);
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
ContentRegistry::PatternLanguage::configureRuntime(runtime, provider);
TaskManager::createBackgroundTask("Analyzing file content", [this, provider, &data = patternLanguageData](auto &) {
TaskManager::createBackgroundTask("Analyzing file content", [this, provider](auto &) {
if (!this->m_autoLoadPatterns)
return;
// Copy over current pattern source code to the new provider
if (!this->m_syncPatternSourceCode) {
data.sourceCode = this->m_textEditor.GetText();
*this->m_sourceCode = this->m_textEditor.GetText();
}
std::scoped_lock lock(data.runtimeMutex);
auto runtime = std::make_unique<pl::PatternLanguage>();
ContentRegistry::PatternLanguage::configureRuntime(*runtime, provider);
auto lock = ContentRegistry::PatternLanguage::getRuntimeLock();
auto& runtime = ContentRegistry::PatternLanguage::getRuntime();
ContentRegistry::PatternLanguage::configureRuntime(runtime, provider);
auto mimeType = magic::getMIMEType(provider);
bool foundCorrectType = false;
runtime->addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) {
runtime.addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) {
hex::unused(runtime);
if (!magic::isValidMIMEType(value))
@ -775,12 +771,12 @@ namespace hex::plugin::builtin {
if (!file.isValid())
continue;
runtime->getInternals().preprocessor->preprocess(*runtime, file.readString());
runtime.getInternals().preprocessor->preprocess(runtime, file.readString());
if (foundCorrectType)
this->m_possiblePatternFiles.push_back(entry.path());
runtime->reset();
runtime.reset();
}
}
@ -789,15 +785,16 @@ namespace hex::plugin::builtin {
}
});
patternLanguageData.envVarEntries.push_back({ 0, "", 0, PlData::EnvVarType::Integer });
this->m_envVarEntries->push_back({ 0, "", 0, EnvVarType::Integer });
});
EventManager::subscribe<EventProviderChanged>(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) {
if (!this->m_syncPatternSourceCode) {
if (oldProvider != nullptr) ProviderExtraData::get(oldProvider).patternLanguage.sourceCode = this->m_textEditor.GetText();
if (oldProvider != nullptr)
this->m_sourceCode.get(oldProvider) = this->m_textEditor.GetText();
if (newProvider != nullptr)
this->m_textEditor.SetText(wolv::util::trim(ProviderExtraData::get(newProvider).patternLanguage.sourceCode));
this->m_textEditor.SetText(wolv::util::trim(this->m_sourceCode.get(newProvider)));
else
this->m_textEditor.SetText("");
}
@ -943,8 +940,10 @@ namespace hex::plugin::builtin {
if (this->m_runningEvaluators != 0)
return std::nullopt;
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
std::optional<ImColor> color;
for (const auto &pattern : ProviderExtraData::getCurrent().patternLanguage.runtime->getPatternsAtAddress(address)) {
for (const auto &pattern : runtime.getPatternsAtAddress(address)) {
if (pattern->getVisibility() != pl::ptrn::Visibility::Visible)
continue;
@ -960,7 +959,9 @@ namespace hex::plugin::builtin {
ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8 *data, size_t size) {
hex::unused(data, size);
auto patterns = ProviderExtraData::getCurrent().patternLanguage.runtime->getPatternsAtAddress(address);
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
auto patterns = runtime.getPatternsAtAddress(address);
if (!patterns.empty() && !std::all_of(patterns.begin(), patterns.end(), [](const auto &pattern) { return pattern->getVisibility() == pl::ptrn::Visibility::Hidden; })) {
ImGui::BeginTooltip();
@ -994,7 +995,7 @@ namespace hex::plugin::builtin {
std::string sourceCode = tar.readString(basePath);
if (!this->m_syncPatternSourceCode)
ProviderExtraData::get(provider).patternLanguage.sourceCode = sourceCode;
this->m_sourceCode.get(provider) = sourceCode;
if (provider == ImHexApi::Provider::get())
this->m_textEditor.SetText(sourceCode);
@ -1005,12 +1006,12 @@ namespace hex::plugin::builtin {
std::string sourceCode;
if (provider == ImHexApi::Provider::get())
ProviderExtraData::get(provider).patternLanguage.sourceCode = this->m_textEditor.GetText();
this->m_sourceCode.get(provider) = this->m_textEditor.GetText();
if (this->m_syncPatternSourceCode)
sourceCode = this->m_textEditor.GetText();
else
sourceCode = ProviderExtraData::get(provider).patternLanguage.sourceCode;
sourceCode = this->m_sourceCode.get(provider);
tar.writeString(basePath, wolv::util::trim(sourceCode));
return true;

View File

@ -6,8 +6,6 @@
#include <hex/helpers/utils.hpp>
#include <hex/helpers/fs.hpp>
#include "content/helpers/provider_extra_data.hpp"
#include <content/popups/popup_notification.hpp>
#include <content/popups/popup_file_chooser.hpp>
@ -46,7 +44,7 @@ namespace hex::plugin::builtin {
ProjectFile::registerPerProviderHandler({
.basePath = "yara.json",
.required = false,
.load = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
.load = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
auto fileContent = tar.readString(basePath);
if (fileContent.empty())
return true;
@ -60,8 +58,7 @@ namespace hex::plugin::builtin {
if (!rules.is_array())
return false;
auto &extraData = ProviderExtraData::get(provider).yara;
extraData.matches.clear();
this->m_matches.get(provider).clear();
for (auto &rule : rules) {
if (!rule.contains("name") || !rule.contains("path"))
@ -73,18 +70,17 @@ namespace hex::plugin::builtin {
if (!name.is_string() || !path.is_string())
return false;
extraData.rules.emplace_back(std::fs::path(name.get<std::string>()), std::fs::path(path.get<std::string>()));
this->m_rules.get(provider).emplace_back(std::fs::path(name.get<std::string>()), std::fs::path(path.get<std::string>()));
}
return true;
},
.store = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
.store = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
nlohmann::json data;
data["rules"] = nlohmann::json::array();
auto &extraData = ProviderExtraData::get(provider).yara;
for (auto &[name, path] : extraData.rules) {
for (auto &[name, path] : this->m_rules.get(provider)) {
data["rules"].push_back({
{ "name", wolv::util::toUTF8String(name) },
{ "path", wolv::util::toUTF8String(path) }
@ -108,15 +104,10 @@ namespace hex::plugin::builtin {
ImGui::TextUnformatted("hex.builtin.view.yara.header.rules"_lang);
ImGui::Separator();
auto &extraData = ProviderExtraData::getCurrent().yara;
auto &rules = extraData.rules;
auto &matches = extraData.matches;
auto &sortedMatches = extraData.sortedMatches;
if (ImGui::BeginListBox("##rules", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 5))) {
for (u32 i = 0; i < rules.size(); i++) {
for (u32 i = 0; i < this->m_rules->size(); i++) {
const bool selected = (this->m_selectedRule == i);
if (ImGui::Selectable(wolv::util::toUTF8String(rules[i].first).c_str(), selected)) {
if (ImGui::Selectable(wolv::util::toUTF8String((*this->m_rules)[i].first).c_str(), selected)) {
this->m_selectedRule = i;
}
}
@ -137,15 +128,15 @@ namespace hex::plugin::builtin {
PopupFileChooser::open(paths, std::vector<nfdfilteritem_t>{ { "Yara File", "yara" }, { "Yara File", "yar" } }, true,
[&](const auto &path) {
rules.push_back({ path.filename(), path });
this->m_rules->push_back({ path.filename(), path });
});
}
ImGui::SameLine();
if (ImGui::IconButton(ICON_VS_REMOVE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
if (this->m_selectedRule < rules.size()) {
rules.erase(rules.begin() + this->m_selectedRule);
this->m_selectedRule = std::min(this->m_selectedRule, (u32)rules.size() - 1);
if (this->m_selectedRule < this->m_rules->size()) {
this->m_rules->erase(this->m_rules->begin() + this->m_selectedRule);
this->m_selectedRule = std::min(this->m_selectedRule, (u32)this->m_rules->size() - 1);
}
}
@ -176,13 +167,13 @@ namespace hex::plugin::builtin {
ImGui::TableHeadersRow();
auto sortSpecs = ImGui::TableGetSortSpecs();
if (!matches.empty() && (sortSpecs->SpecsDirty || sortedMatches.empty())) {
sortedMatches.clear();
std::transform(matches.begin(), matches.end(), std::back_inserter(sortedMatches), [](auto &match) {
if (!this->m_matches->empty() && (sortSpecs->SpecsDirty || this->m_sortedMatches->empty())) {
this->m_sortedMatches->clear();
std::transform(this->m_matches->begin(), this->m_matches->end(), std::back_inserter(*this->m_sortedMatches), [](auto &match) {
return &match;
});
std::sort(sortedMatches.begin(), sortedMatches.end(), [&sortSpecs](ProviderExtraData::Data::Yara::YaraMatch *left, ProviderExtraData::Data::Yara::YaraMatch *right) -> bool {
std::sort(this->m_sortedMatches->begin(), this->m_sortedMatches->end(), [&sortSpecs](YaraMatch *left, YaraMatch *right) -> bool {
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("identifier"))
return left->identifier < right->identifier;
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("variable"))
@ -196,18 +187,18 @@ namespace hex::plugin::builtin {
});
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Descending)
std::reverse(sortedMatches.begin(), sortedMatches.end());
std::reverse(this->m_sortedMatches->begin(), this->m_sortedMatches->end());
sortSpecs->SpecsDirty = false;
}
if (!this->m_matcherTask.isRunning()) {
ImGuiListClipper clipper;
clipper.Begin(sortedMatches.size());
clipper.Begin(this->m_sortedMatches->size());
while (clipper.Step()) {
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
auto &[identifier, variableName, address, size, wholeDataMatch, highlightId, tooltipId] = *sortedMatches[i];
auto &[identifier, variableName, address, size, wholeDataMatch, highlightId, tooltipId] = *(*this->m_sortedMatches)[i];
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushID(i);
@ -260,14 +251,12 @@ namespace hex::plugin::builtin {
}
void ViewYara::clearResult() {
auto &matches = ProviderExtraData::getCurrent().yara.matches;
for (const auto &match : matches) {
for (const auto &match : *this->m_matches) {
ImHexApi::HexEditor::removeBackgroundHighlight(match.highlightId);
ImHexApi::HexEditor::removeTooltip(match.tooltipId);
}
matches.clear();
this->m_matches->clear();
this->m_consoleMessages.clear();
}
@ -277,20 +266,16 @@ namespace hex::plugin::builtin {
this->m_matcherTask = TaskManager::createTask("hex.builtin.view.yara.matching", 0, [this](auto &task) {
if (!ImHexApi::Provider::isValid()) return;
auto &extraData = ProviderExtraData::getCurrent().yara;
auto &rules = extraData.rules;
auto &matches = extraData.matches;
struct ResultContext {
Task *task = nullptr;
std::vector<ProviderExtraData::Data::Yara::YaraMatch> newMatches;
std::vector<YaraMatch> newMatches;
std::vector<std::string> consoleMessages;
};
ResultContext resultContext;
resultContext.task = &task;
for (const auto &[fileName, filePath] : rules) {
for (const auto &[fileName, filePath] : *this->m_rules) {
YR_COMPILER *compiler = nullptr;
yr_compiler_create(&compiler);
ON_SCOPE_EXIT {
@ -321,7 +306,7 @@ namespace hex::plugin::builtin {
currFilePath.data()
);
wolv::io::File file(rules[this->m_selectedRule].second, wolv::io::File::Mode::Read);
wolv::io::File file((*this->m_rules)[this->m_selectedRule].second, wolv::io::File::Mode::Read);
if (!file.isValid()) return;
if (yr_compiler_add_file(compiler, file.getHandle(), nullptr, nullptr) != 0) {
@ -437,22 +422,23 @@ namespace hex::plugin::builtin {
0);
}
TaskManager::doLater([this, &matches, resultContext] {
for (const auto &match : matches) {
TaskManager::doLater([this, resultContext] {
for (const auto &match : *this->m_matches) {
ImHexApi::HexEditor::removeBackgroundHighlight(match.highlightId);
ImHexApi::HexEditor::removeTooltip(match.tooltipId);
}
this->m_consoleMessages = resultContext.consoleMessages;
std::move(resultContext.newMatches.begin(), resultContext.newMatches.end(), std::back_inserter(matches));
std::move(resultContext.newMatches.begin(), resultContext.newMatches.end(), std::back_inserter(*this->m_matches));
auto uniques = std::set(matches.begin(), matches.end(), [](const auto &l, const auto &r) {
auto uniques = std::set(this->m_matches->begin(), this->m_matches->end(), [](const auto &l, const auto &r) {
return std::tie(l.address, l.size, l.wholeDataMatch, l.identifier, l.variable) <
std::tie(r.address, r.size, r.wholeDataMatch, r.identifier, r.variable);
});
matches.clear();
std::move(uniques.begin(), uniques.end(), std::back_inserter(matches));
this->m_matches->clear();
std::move(uniques.begin(), uniques.end(), std::back_inserter(*this->m_matches));
constexpr static color_t YaraColor = 0x70B4771F;
for (auto &match : uniques) {