feat: Added basic 3D visualizer, moved visualizers to separate file
This commit is contained in:
parent
a9cebed903
commit
8e759d9b5f
6664
lib/external/imgui/include/imgui_impl_opengl3_loader.h
vendored
6664
lib/external/imgui/include/imgui_impl_opengl3_loader.h
vendored
File diff suppressed because it is too large
Load Diff
2
lib/external/pattern_language
vendored
2
lib/external/pattern_language
vendored
@ -1 +1 @@
|
||||
Subproject commit c18906978bec280f5e7cef77f9f3d71fc0ccbb0c
|
||||
Subproject commit ba1599ec983431c572c8612a3aecdee2794223c3
|
@ -24,6 +24,7 @@ set(LIBIMHEX_SOURCES
|
||||
source/helpers/magic.cpp
|
||||
source/helpers/crypto.cpp
|
||||
source/helpers/net.cpp
|
||||
source/helpers/opengl.cpp
|
||||
source/helpers/file.cpp
|
||||
source/helpers/socket.cpp
|
||||
source/helpers/patches.cpp
|
||||
|
@ -117,6 +117,8 @@ namespace hex {
|
||||
/* Pattern Language Function Registry. Allows adding of new functions that may be used inside the pattern language */
|
||||
namespace PatternLanguage {
|
||||
|
||||
using VisualizerFunctionCallback = std::function<void(pl::ptrn::Pattern&, pl::ptrn::Iteratable&, bool, const std::vector<pl::core::Token::Literal> &)>;
|
||||
|
||||
namespace impl {
|
||||
|
||||
struct FunctionDefinition {
|
||||
@ -129,6 +131,13 @@ namespace hex {
|
||||
bool dangerous;
|
||||
};
|
||||
|
||||
struct Visualizer {
|
||||
u32 parameterCount;
|
||||
VisualizerFunctionCallback callback;
|
||||
};
|
||||
|
||||
std::map<std::string, Visualizer> &getVisualizers();
|
||||
|
||||
}
|
||||
|
||||
void configureRuntime(pl::PatternLanguage &runtime, prv::Provider *provider);
|
||||
@ -138,6 +147,8 @@ namespace hex {
|
||||
void addFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func);
|
||||
void addDangerousFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func);
|
||||
|
||||
void addVisualizer(const std::string &name, const VisualizerFunctionCallback &func, u32 parameterCount);
|
||||
|
||||
std::map<std::string, pl::api::PragmaHandler> &getPragmas();
|
||||
std::vector<impl::FunctionDefinition> &getFunctions();
|
||||
|
||||
|
120
lib/libimhex/include/hex/helpers/opengl.hpp
Normal file
120
lib/libimhex/include/hex/helpers/opengl.hpp
Normal file
@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#include <imgui_impl_opengl3_loader.h>
|
||||
|
||||
namespace hex::gl {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
GLuint getType() {
|
||||
if constexpr (std::is_same_v<T, float>)
|
||||
return GL_FLOAT;
|
||||
else if constexpr (std::is_same_v<T, u32>)
|
||||
return GL_UNSIGNED_INT;
|
||||
else
|
||||
static_assert(hex::always_false<T>::value, "Unsupported type");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Shader {
|
||||
public:
|
||||
Shader(const std::string &vertexSource, const std::string &fragmentSource);
|
||||
~Shader();
|
||||
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
|
||||
private:
|
||||
void compile(GLuint shader, const std::string &source);
|
||||
|
||||
private:
|
||||
GLuint m_program;
|
||||
};
|
||||
|
||||
enum class BufferType {
|
||||
Vertex = GL_ARRAY_BUFFER,
|
||||
Index = GL_ELEMENT_ARRAY_BUFFER
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Buffer {
|
||||
public:
|
||||
Buffer(BufferType type, std::span<T> data);
|
||||
~Buffer();
|
||||
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
|
||||
void draw() const;
|
||||
|
||||
size_t getSize() const;
|
||||
private:
|
||||
GLuint m_buffer;
|
||||
size_t m_size;
|
||||
GLuint m_type;
|
||||
};
|
||||
|
||||
extern template class Buffer<float>;
|
||||
extern template class Buffer<u32>;
|
||||
|
||||
class VertexArray {
|
||||
public:
|
||||
VertexArray();
|
||||
~VertexArray();
|
||||
|
||||
template<typename T>
|
||||
void addBuffer(const Buffer<T> &buffer) const {
|
||||
glVertexAttribPointer(0, buffer.getSize() / sizeof(T), getType<T>(), GL_FALSE, 3 * sizeof(T), nullptr);
|
||||
glEnableVertexAttribArray(0);
|
||||
}
|
||||
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
|
||||
private:
|
||||
GLuint m_array;
|
||||
};
|
||||
|
||||
class Texture {
|
||||
public:
|
||||
Texture(u32 width, u32 height);
|
||||
~Texture();
|
||||
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
|
||||
GLuint getTexture() const;
|
||||
u32 getWidth() const;
|
||||
u32 getHeight() const;
|
||||
|
||||
void release();
|
||||
private:
|
||||
GLuint m_texture;
|
||||
u32 m_width, m_height;
|
||||
};
|
||||
|
||||
class FrameBuffer {
|
||||
public:
|
||||
FrameBuffer();
|
||||
~FrameBuffer();
|
||||
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
|
||||
void attachTexture(const Texture &texture) const;
|
||||
|
||||
private:
|
||||
GLuint m_frameBuffer, m_renderBuffer;
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -36,6 +36,7 @@ namespace ImGui {
|
||||
Texture() = default;
|
||||
Texture(const ImU8 *buffer, int size, int width = 0, int height = 0);
|
||||
explicit Texture(const char *path);
|
||||
Texture(unsigned int texture, int width, int height);
|
||||
Texture(const Texture&) = delete;
|
||||
Texture(Texture&& other) noexcept;
|
||||
|
||||
|
@ -299,6 +299,18 @@ namespace hex {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
std::map<std::string, impl::Visualizer> &impl::getVisualizers() {
|
||||
static std::map<std::string, impl::Visualizer> visualizers;
|
||||
|
||||
return visualizers;
|
||||
}
|
||||
|
||||
void addVisualizer(const std::string &name, const VisualizerFunctionCallback &function, u32 parameterCount) {
|
||||
log::debug("Registered new pattern visualizer function: {}", name);
|
||||
impl::getVisualizers()[name] = impl::Visualizer { parameterCount, function };
|
||||
}
|
||||
|
||||
std::map<std::string, pl::api::PragmaHandler> &getPragmas() {
|
||||
static std::map<std::string, pl::api::PragmaHandler> pragmas;
|
||||
|
||||
|
190
lib/libimhex/source/helpers/opengl.cpp
Normal file
190
lib/libimhex/source/helpers/opengl.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
#include <hex/helpers/opengl.hpp>
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
namespace hex::gl {
|
||||
|
||||
Shader::Shader(const std::string &vertexSource, const std::string &fragmentSource) {
|
||||
auto vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
||||
this->compile(vertexShader, vertexSource);
|
||||
|
||||
auto fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
this->compile(fragmentShader, fragmentSource);
|
||||
|
||||
ON_SCOPE_EXIT { glDeleteShader(vertexShader); glDeleteShader(fragmentShader); };
|
||||
|
||||
this->m_program = glCreateProgram();
|
||||
|
||||
glAttachShader(this->m_program, vertexShader);
|
||||
glAttachShader(this->m_program, fragmentShader);
|
||||
glLinkProgram(this->m_program);
|
||||
|
||||
int result = false;
|
||||
glGetProgramiv(this->m_program, GL_LINK_STATUS, &result);
|
||||
if (!result) {
|
||||
std::vector<char> log(512);
|
||||
glGetShaderInfoLog(this->m_program, log.size(), nullptr, log.data());
|
||||
log::error("Failed to link shader: {}", log.data());
|
||||
}
|
||||
}
|
||||
|
||||
Shader::~Shader() {
|
||||
glDeleteProgram(this->m_program);
|
||||
}
|
||||
|
||||
void Shader::bind() const {
|
||||
glUseProgram(this->m_program);
|
||||
}
|
||||
|
||||
void Shader::unbind() const {
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
void Shader::compile(GLuint shader, const std::string &source) {
|
||||
auto sourcePtr = source.c_str();
|
||||
|
||||
glShaderSource(shader, 1, &sourcePtr, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
int result = false;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
|
||||
if (!result) {
|
||||
std::vector<char> log(512);
|
||||
glGetShaderInfoLog(shader, log.size(), nullptr, log.data());
|
||||
log::error("Failed to compile shader: {}", log.data());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
Buffer<T>::Buffer(BufferType type, std::span<T> data) : m_size(data.size()), m_type(GLuint(type)) {
|
||||
glGenBuffers(1, &this->m_buffer);
|
||||
glBindBuffer(this->m_type, this->m_buffer);
|
||||
glBufferData(this->m_type, data.size_bytes(), data.data(), GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Buffer<T>::~Buffer() {
|
||||
glDeleteBuffers(1, &this->m_buffer);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Buffer<T>::bind() const {
|
||||
glBindBuffer(this->m_type, this->m_buffer);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Buffer<T>::unbind() const {
|
||||
glBindBuffer(this->m_type, 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
size_t Buffer<T>::getSize() const {
|
||||
return this->m_size;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Buffer<T>::draw() const {
|
||||
glDrawElements(GL_TRIANGLES, this->m_size, getType<T>(), nullptr);
|
||||
}
|
||||
|
||||
template class Buffer<float>;
|
||||
template class Buffer<u32>;
|
||||
|
||||
|
||||
VertexArray::VertexArray() {
|
||||
glGenVertexArrays(1, &this->m_array);
|
||||
}
|
||||
|
||||
VertexArray::~VertexArray() {
|
||||
glDeleteVertexArrays(1, &this->m_array);
|
||||
}
|
||||
|
||||
void VertexArray::bind() const {
|
||||
glBindVertexArray(this->m_array);
|
||||
}
|
||||
|
||||
void VertexArray::unbind() const {
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
|
||||
Texture::Texture(u32 width, u32 height) : m_texture(0), m_width(width), m_height(height) {
|
||||
glGenTextures(1, &this->m_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, this->m_texture);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
Texture::~Texture() {
|
||||
if (this->m_texture != 0)
|
||||
glDeleteTextures(1, &this->m_texture);
|
||||
}
|
||||
|
||||
void Texture::bind() const {
|
||||
glBindTexture(GL_TEXTURE_2D, this->m_texture);
|
||||
}
|
||||
|
||||
void Texture::unbind() const {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
GLuint Texture::getTexture() const {
|
||||
return this->m_texture;
|
||||
}
|
||||
|
||||
u32 Texture::getWidth() const {
|
||||
return this->m_width;
|
||||
}
|
||||
|
||||
u32 Texture::getHeight() const {
|
||||
return this->m_height;
|
||||
}
|
||||
|
||||
void Texture::release() {
|
||||
this->m_texture = 0;
|
||||
}
|
||||
|
||||
|
||||
FrameBuffer::FrameBuffer() {
|
||||
glGenFramebuffers(1, &this->m_frameBuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, this->m_frameBuffer);
|
||||
|
||||
glGenRenderbuffers(1, &this->m_renderBuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, this->m_renderBuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 1280, 720);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, this->m_renderBuffer);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
FrameBuffer::~FrameBuffer() {
|
||||
glDeleteFramebuffers(1, &this->m_frameBuffer);
|
||||
glDeleteRenderbuffers(1, &this->m_renderBuffer);
|
||||
}
|
||||
|
||||
void FrameBuffer::bind() const {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, this->m_frameBuffer);
|
||||
}
|
||||
|
||||
void FrameBuffer::unbind() const {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void FrameBuffer::attachTexture(const Texture &texture) const {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, this->m_frameBuffer);
|
||||
texture.bind();
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.getTexture(), 0);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
}
|
@ -69,7 +69,12 @@ namespace ImGui {
|
||||
this->m_textureId = reinterpret_cast<ImTextureID>(static_cast<intptr_t>(texture));
|
||||
}
|
||||
|
||||
Texture::Texture(unsigned int texture, int width, int height) : m_textureId(reinterpret_cast<ImTextureID>(static_cast<intptr_t>(texture))), m_width(width), m_height(height) {
|
||||
|
||||
}
|
||||
|
||||
Texture::Texture(Texture&& other) noexcept {
|
||||
glDeleteTextures(1, reinterpret_cast<GLuint*>(&this->m_textureId));
|
||||
this->m_textureId = other.m_textureId;
|
||||
this->m_width = other.m_width;
|
||||
this->m_height = other.m_height;
|
||||
@ -78,6 +83,7 @@ namespace ImGui {
|
||||
}
|
||||
|
||||
Texture& Texture::operator=(Texture&& other) noexcept {
|
||||
glDeleteTextures(1, reinterpret_cast<GLuint*>(&this->m_textureId));
|
||||
this->m_textureId = other.m_textureId;
|
||||
this->m_width = other.m_width;
|
||||
this->m_height = other.m_height;
|
||||
|
@ -183,6 +183,7 @@ namespace hex::init {
|
||||
|
||||
ContentRegistry::PatternLanguage::getFunctions().clear();
|
||||
ContentRegistry::PatternLanguage::getPragmas().clear();
|
||||
ContentRegistry::PatternLanguage::impl::getVisualizers().clear();
|
||||
|
||||
{
|
||||
auto &views = ContentRegistry::Views::getEntries();
|
||||
|
@ -10,6 +10,7 @@ add_library(${PROJECT_NAME} SHARED
|
||||
source/content/data_inspector.cpp
|
||||
source/content/pl_builtin_functions.cpp
|
||||
source/content/pl_pragmas.cpp
|
||||
source/content/pl_visualizers.cpp
|
||||
source/content/settings_entries.cpp
|
||||
source/content/tools_entries.cpp
|
||||
source/content/data_processor_nodes.cpp
|
||||
|
@ -49,6 +49,7 @@ namespace hex::plugin::builtin::ui {
|
||||
void drawArray(pl::ptrn::Pattern& pattern, pl::ptrn::Iteratable &iteratable, bool isInlined);
|
||||
u64& getDisplayEnd(const pl::ptrn::Pattern& pattern);
|
||||
void makeSelectable(const pl::ptrn::Pattern &pattern);
|
||||
void drawVisualizerButton(pl::ptrn::Pattern& pattern, pl::ptrn::Iteratable &iteratable);
|
||||
|
||||
void createLeafNode(const pl::ptrn::Pattern& pattern);
|
||||
bool createTreeNode(const pl::ptrn::Pattern& pattern);
|
||||
|
203
plugins/builtin/source/content/pl_visualizers.cpp
Normal file
203
plugins/builtin/source/content/pl_visualizers.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/api/localization.hpp>
|
||||
|
||||
#include <hex/helpers/disassembler.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/opengl.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <implot.h>
|
||||
#include <imgui_impl_opengl3_loader.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
|
||||
#include <pl/patterns/pattern.hpp>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
namespace {
|
||||
|
||||
void drawLinePlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::Iteratable &iteratable, bool, const std::vector<pl::core::Token::Literal> &) {
|
||||
if (ImPlot::BeginPlot("##plot", ImVec2(400, 250), ImPlotFlags_NoChild | ImPlotFlags_CanvasOnly)) {
|
||||
|
||||
ImPlot::SetupAxes("X", "Y", ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit);
|
||||
|
||||
ImPlot::PlotLineG("##line", [](void *data, int idx) -> ImPlotPoint {
|
||||
auto &iteratable = *static_cast<pl::ptrn::Iteratable *>(data);
|
||||
return { static_cast<double>(idx), pl::core::Token::literalToFloatingPoint(iteratable.getEntry(idx)->getValue()) };
|
||||
}, &iteratable, iteratable.getEntryCount());
|
||||
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
}
|
||||
|
||||
void drawImageVisualizer(pl::ptrn::Pattern &pattern, pl::ptrn::Iteratable &, bool shouldReset, const std::vector<pl::core::Token::Literal> &) {
|
||||
static ImGui::Texture texture;
|
||||
if (shouldReset) {
|
||||
std::vector<u8> data;
|
||||
data.resize(pattern.getSize());
|
||||
pattern.getEvaluator()->readData(pattern.getOffset(), data.data(), data.size(), pattern.getSection());
|
||||
texture = ImGui::Texture(data.data(), data.size());
|
||||
}
|
||||
|
||||
if (texture.isValid())
|
||||
ImGui::Image(texture, texture.getSize());
|
||||
}
|
||||
|
||||
void drawBitmapVisualizer(pl::ptrn::Pattern &pattern, pl::ptrn::Iteratable &, bool shouldReset, const std::vector<pl::core::Token::Literal> &arguments) {
|
||||
static ImGui::Texture texture;
|
||||
if (shouldReset) {
|
||||
auto width = pl::core::Token::literalToUnsigned(arguments[1]);
|
||||
auto height = pl::core::Token::literalToUnsigned(arguments[2]);
|
||||
|
||||
std::vector<u8> data;
|
||||
data.resize(width * height * 4);
|
||||
|
||||
pattern.getEvaluator()->readData(pattern.getOffset(), data.data(), data.size(), pattern.getSection());
|
||||
texture = ImGui::Texture(data.data(), data.size(), width, height);
|
||||
}
|
||||
|
||||
if (texture.isValid())
|
||||
ImGui::Image(texture, texture.getSize());
|
||||
}
|
||||
|
||||
void drawDisassemblyVisualizer(pl::ptrn::Pattern &pattern, pl::ptrn::Iteratable &, bool shouldReset, const std::vector<pl::core::Token::Literal> &arguments) {
|
||||
struct Disassembly {
|
||||
u64 address;
|
||||
std::vector<u8> bytes;
|
||||
std::string instruction;
|
||||
};
|
||||
|
||||
static std::vector<Disassembly> disassembly;
|
||||
if (shouldReset) {
|
||||
auto baseAddress = pl::core::Token::literalToUnsigned(arguments[1]);
|
||||
auto architecture = pl::core::Token::literalToUnsigned(arguments[2]);
|
||||
auto mode = pl::core::Token::literalToUnsigned(arguments[3]);
|
||||
|
||||
disassembly.clear();
|
||||
|
||||
csh capstone;
|
||||
if (cs_open(static_cast<cs_arch>(architecture), static_cast<cs_mode>(mode), &capstone) == CS_ERR_OK) {
|
||||
cs_option(capstone, CS_OPT_SKIPDATA, CS_OPT_ON);
|
||||
|
||||
std::vector<u8> data;
|
||||
data.resize(pattern.getSize());
|
||||
pattern.getEvaluator()->readData(pattern.getOffset(), data.data(), data.size(), pattern.getSection());
|
||||
cs_insn *instructions = nullptr;
|
||||
|
||||
size_t instructionCount = cs_disasm(capstone, data.data(), data.size(), baseAddress, 0, &instructions);
|
||||
for (size_t i = 0; i < instructionCount; i++) {
|
||||
disassembly.push_back({ instructions[i].address, { instructions[i].bytes, instructions[i].bytes + instructions[i].size }, hex::format("{} {}", instructions[i].mnemonic, instructions[i].op_str) });
|
||||
}
|
||||
cs_free(instructions, instructionCount);
|
||||
cs_close(&capstone);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("##disassembly", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY, scaled(ImVec2(0, 300)))) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.address"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.bytes"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.instruction"_lang);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (auto &entry : disassembly) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:08X}", entry.address);
|
||||
ImGui::TableNextColumn();
|
||||
std::string bytes;
|
||||
for (auto byte : entry.bytes)
|
||||
bytes += hex::format("{0:02X} ", byte);
|
||||
ImGui::TextUnformatted(bytes.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(entry.instruction.c_str());
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void draw3DVisualizer(pl::ptrn::Pattern &pattern, pl::ptrn::Iteratable &, bool shouldReset, const std::vector<pl::core::Token::Literal> &) {
|
||||
static ImGui::Texture texture;
|
||||
|
||||
if (shouldReset) {
|
||||
std::vector<float> vertices;
|
||||
vertices.resize(pattern.getSize() / sizeof(float));
|
||||
pattern.getEvaluator()->readData(pattern.getOffset(), vertices.data(), vertices.size() * sizeof(float), pattern.getSection());
|
||||
|
||||
std::vector<u32> indices;
|
||||
indices.resize(vertices.size() / 3);
|
||||
std::iota(indices.begin(), indices.end(), 0);
|
||||
|
||||
{
|
||||
gl::FrameBuffer frameBuffer;
|
||||
|
||||
gl::Texture renderTexture(512, 512);
|
||||
frameBuffer.attachTexture(renderTexture);
|
||||
|
||||
frameBuffer.bind();
|
||||
|
||||
constexpr static const char *VertexShaderSource = R"glsl(
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 in_Position;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(in_Position.x, in_Position.y, in_Position.z, 1.0);
|
||||
}
|
||||
)glsl";
|
||||
|
||||
constexpr static const char *FragmentShaderSource = R"glsl(
|
||||
#version 330 core
|
||||
out vec4 out_Color;
|
||||
|
||||
void main() {
|
||||
out_Color = vec4(1.0f, 0.5f, 0.2f, 1.0f);
|
||||
}
|
||||
)glsl";
|
||||
|
||||
gl::Shader shader(VertexShaderSource, FragmentShaderSource);
|
||||
|
||||
gl::VertexArray vertexArray;
|
||||
vertexArray.bind();
|
||||
|
||||
gl::Buffer<float> vertexBuffer(gl::BufferType::Vertex, vertices);
|
||||
gl::Buffer<u32> indexBuffer(gl::BufferType::Index, indices);
|
||||
|
||||
vertexArray.addBuffer(vertexBuffer);
|
||||
|
||||
vertexBuffer.unbind();
|
||||
vertexArray.unbind();
|
||||
|
||||
shader.bind();
|
||||
vertexArray.bind();
|
||||
|
||||
glViewport(0, 0, renderTexture.getWidth(), renderTexture.getHeight());
|
||||
glClearColor(0.00F, 0.00F, 0.00F, 0.00f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
indexBuffer.draw();
|
||||
|
||||
vertexArray.unbind();
|
||||
shader.unbind();
|
||||
frameBuffer.unbind();
|
||||
|
||||
texture = ImGui::Texture(renderTexture.getTexture(), renderTexture.getWidth(), renderTexture.getHeight());
|
||||
renderTexture.release();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Image(texture, texture.getSize(), ImVec2(0, 1), ImVec2(1, 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void registerPatternLanguageVisualizers() {
|
||||
ContentRegistry::PatternLanguage::addVisualizer("line_plot", drawLinePlotVisualizer, 0);
|
||||
ContentRegistry::PatternLanguage::addVisualizer("image", drawImageVisualizer, 0);
|
||||
ContentRegistry::PatternLanguage::addVisualizer("bitmap", drawBitmapVisualizer, 3);
|
||||
ContentRegistry::PatternLanguage::addVisualizer("disassembler", drawDisassemblyVisualizer, 4);
|
||||
ContentRegistry::PatternLanguage::addVisualizer("3d", draw3DVisualizer, 0);
|
||||
}
|
||||
|
||||
}
|
@ -14,6 +14,7 @@ namespace hex::plugin::builtin {
|
||||
void registerToolEntries();
|
||||
void registerPatternLanguageFunctions();
|
||||
void registerPatternLanguagePragmas();
|
||||
void registerPatternLanguageVisualizers();
|
||||
void registerCommandPaletteCommands();
|
||||
void registerSettings();
|
||||
void loadSettings();
|
||||
@ -51,6 +52,7 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {
|
||||
registerToolEntries();
|
||||
registerPatternLanguageFunctions();
|
||||
registerPatternLanguagePragmas();
|
||||
registerPatternLanguageVisualizers();
|
||||
registerCommandPaletteCommands();
|
||||
registerSettings();
|
||||
loadSettings();
|
||||
|
@ -20,16 +20,16 @@
|
||||
#include <string>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/api/localization.hpp>
|
||||
#include <content/helpers/math_evaluator.hpp>
|
||||
|
||||
#include <hex/helpers/disassembler.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <implot.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
|
||||
|
||||
namespace hex::plugin::builtin::ui {
|
||||
|
||||
namespace {
|
||||
@ -114,106 +114,47 @@ namespace hex::plugin::builtin::ui {
|
||||
}
|
||||
|
||||
void drawVisualizer(const std::vector<pl::core::Token::Literal> &arguments, pl::ptrn::Pattern &pattern, pl::ptrn::Iteratable &iteratable, bool reset) {
|
||||
auto visualizer = pl::core::Token::literalToString(arguments.front(), true);
|
||||
auto visualizerName = pl::core::Token::literalToString(arguments.front(), true);
|
||||
|
||||
if (visualizer == "line_plot") {
|
||||
if (ImPlot::BeginPlot("##plot", ImVec2(400, 250), ImPlotFlags_NoChild | ImPlotFlags_CanvasOnly)) {
|
||||
const auto &visualizers = ContentRegistry::PatternLanguage::impl::getVisualizers();
|
||||
|
||||
ImPlot::SetupAxes("X", "Y", ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit);
|
||||
|
||||
ImPlot::PlotLineG("##line", [](void *data, int idx) -> ImPlotPoint {
|
||||
auto &iteratable = *static_cast<pl::ptrn::Iteratable *>(data);
|
||||
return { static_cast<double>(idx), pl::core::Token::literalToFloatingPoint(iteratable.getEntry(idx)->getValue()) };
|
||||
}, &iteratable, iteratable.getEntryCount());
|
||||
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
} else if (visualizer == "image") {
|
||||
static ImGui::Texture texture;
|
||||
if (reset) {
|
||||
std::vector<u8> data;
|
||||
data.resize(pattern.getSize());
|
||||
pattern.getEvaluator()->readData(pattern.getOffset(), data.data(), data.size(), pattern.getSection());
|
||||
texture = ImGui::Texture(data.data(), data.size());
|
||||
}
|
||||
|
||||
if (texture.isValid())
|
||||
ImGui::Image(texture, texture.getSize());
|
||||
} else if (visualizer == "bitmap" && arguments.size() == 3) {
|
||||
static ImGui::Texture texture;
|
||||
if (reset) {
|
||||
auto width = pl::core::Token::literalToUnsigned(arguments[1]);
|
||||
auto height = pl::core::Token::literalToUnsigned(arguments[2]);
|
||||
|
||||
std::vector<u8> data;
|
||||
data.resize(width * height * 4);
|
||||
|
||||
pattern.getEvaluator()->readData(pattern.getOffset(), data.data(), data.size(), pattern.getSection());
|
||||
texture = ImGui::Texture(data.data(), data.size(), width, height);
|
||||
}
|
||||
|
||||
if (texture.isValid())
|
||||
ImGui::Image(texture, texture.getSize());
|
||||
} else if (visualizer == "disassembler" && arguments.size() == 4) {
|
||||
struct Disassembly {
|
||||
u64 address;
|
||||
std::vector<u8> bytes;
|
||||
std::string instruction;
|
||||
};
|
||||
|
||||
static std::vector<Disassembly> disassembly;
|
||||
if (reset) {
|
||||
auto baseAddress = pl::core::Token::literalToUnsigned(arguments[1]);
|
||||
auto architecture = pl::core::Token::literalToUnsigned(arguments[2]);
|
||||
auto mode = pl::core::Token::literalToUnsigned(arguments[3]);
|
||||
|
||||
disassembly.clear();
|
||||
|
||||
csh capstone;
|
||||
if (cs_open(static_cast<cs_arch>(architecture), static_cast<cs_mode>(mode), &capstone) == CS_ERR_OK) {
|
||||
cs_option(capstone, CS_OPT_SKIPDATA, CS_OPT_ON);
|
||||
|
||||
std::vector<u8> data;
|
||||
data.resize(pattern.getSize());
|
||||
pattern.getEvaluator()->readData(pattern.getOffset(), data.data(), data.size(), pattern.getSection());
|
||||
cs_insn *instructions = nullptr;
|
||||
|
||||
size_t instructionCount = cs_disasm(capstone, data.data(), data.size(), baseAddress, 0, &instructions);
|
||||
for (size_t i = 0; i < instructionCount; i++) {
|
||||
disassembly.push_back({ instructions[i].address, { instructions[i].bytes, instructions[i].bytes + instructions[i].size }, hex::format("{} {}", instructions[i].mnemonic, instructions[i].op_str) });
|
||||
}
|
||||
cs_free(instructions, instructionCount);
|
||||
cs_close(&capstone);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("##disassembly", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY, scaled(ImVec2(0, 300)))) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.address"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.bytes"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.instruction"_lang);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (auto &entry : disassembly) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:08X}", entry.address);
|
||||
ImGui::TableNextColumn();
|
||||
std::string bytes;
|
||||
for (auto byte : entry.bytes)
|
||||
bytes += hex::format("{0:02X} ", byte);
|
||||
ImGui::TextUnformatted(bytes.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(entry.instruction.c_str());
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
if (auto entry = visualizers.find(visualizerName); entry != visualizers.end()) {
|
||||
const auto &[name, visualizer] = *entry;
|
||||
if (visualizer.parameterCount != arguments.size() - 1) {
|
||||
ImGui::TextUnformatted("hex.builtin.pattern_drawer.visualizer.invalid_parameter_count"_lang);
|
||||
} else {
|
||||
visualizer.callback(pattern, iteratable, reset, arguments);
|
||||
}
|
||||
} else {
|
||||
ImGui::TextUnformatted("hex.builtin.pattern_drawer.unknown_visualizer"_lang);
|
||||
ImGui::TextUnformatted("hex.builtin.pattern_drawer.visualizer.unknown"_lang);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PatternDrawer::drawVisualizerButton(pl::ptrn::Pattern& pattern, pl::ptrn::Iteratable &iteratable) {
|
||||
if (const auto &arguments = pattern.getAttributeArguments("hex::visualize"); !arguments.empty()) {
|
||||
static bool shouldReset = false;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
if (ImGui::Button(pattern.getFormattedValue().c_str(), ImVec2(-1, ImGui::GetTextLineHeight()))) {
|
||||
this->m_currVisualizedPattern = &pattern;
|
||||
shouldReset = true;
|
||||
ImGui::OpenPopup("Visualizer");
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
if (ImGui::BeginPopup("Visualizer")) {
|
||||
if (this->m_currVisualizedPattern == &pattern) {
|
||||
drawVisualizer(arguments, pattern, iteratable, shouldReset);
|
||||
shouldReset = false;
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
} else {
|
||||
ImGui::TextFormatted("{}", pattern.getFormattedValue());
|
||||
}
|
||||
}
|
||||
|
||||
void PatternDrawer::createLeafNode(const pl::ptrn::Pattern& pattern) {
|
||||
@ -571,7 +512,7 @@ namespace hex::plugin::builtin::ui {
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
} else {
|
||||
ImGui::TextFormatted("{}", pattern.getFormattedValue());
|
||||
drawVisualizerButton(pattern, static_cast<pl::ptrn::Iteratable&>(pattern));
|
||||
}
|
||||
|
||||
}
|
||||
@ -622,7 +563,7 @@ namespace hex::plugin::builtin::ui {
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
} else {
|
||||
ImGui::TextFormatted("{}", pattern.getFormattedValue());
|
||||
drawVisualizerButton(pattern, static_cast<pl::ptrn::Iteratable&>(pattern));
|
||||
}
|
||||
}
|
||||
|
||||
@ -711,27 +652,7 @@ namespace hex::plugin::builtin::ui {
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (const auto &arguments = pattern.getAttributeArguments("hex::visualize"); !arguments.empty()) {
|
||||
static bool shouldReset = false;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
if (ImGui::Button(pattern.getFormattedValue().c_str(), ImVec2(-1, ImGui::GetTextLineHeight()))) {
|
||||
this->m_currVisualizedPattern = &pattern;
|
||||
shouldReset = true;
|
||||
ImGui::OpenPopup("Visualizer");
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
if (ImGui::BeginPopup("Visualizer")) {
|
||||
if (this->m_currVisualizedPattern == &pattern) {
|
||||
drawVisualizer(arguments, pattern, iteratable, shouldReset);
|
||||
shouldReset = false;
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
} else {
|
||||
ImGui::TextFormatted("{}", pattern.getFormattedValue());
|
||||
}
|
||||
drawVisualizerButton(pattern, iteratable);
|
||||
}
|
||||
|
||||
if (open) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user