1
0
mirror of synced 2025-02-17 18:59:21 +01:00

Huge refactoring of builtin features into an external plugin

This commit is contained in:
WerWolv 2021-01-22 18:01:39 +01:00
parent 9bc569bf9a
commit 104000fbc4
30 changed files with 1095 additions and 842 deletions

View File

@ -42,6 +42,7 @@ add_subdirectory(external/llvm)
# Plugins
add_subdirectory(plugins/libimhex)
add_subdirectory(plugins/builtin)
add_subdirectory(plugins/example)
@ -152,7 +153,6 @@ add_executable(imhex ${application_type}
source/helpers/crypto.cpp
source/helpers/patches.cpp
source/helpers/math_evaluator.cpp
source/helpers/project_file_handler.cpp
source/helpers/loader_script_handler.cpp
source/helpers/plugin_handler.cpp
@ -192,6 +192,12 @@ add_custom_command(TARGET imhex POST_BUILD
$<TARGET_FILE:libimhex>
$<TARGET_FILE_DIR:imhex>)
file(MAKE_DIRECTORY "plugins")
add_custom_command(TARGET imhex POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:builtin>
$<TARGET_FILE_DIR:imhex>/plugins)
if (WIN32)
# Install binaries directly in the prefix, usually C:\Program Files\ImHex.
set(CMAKE_INSTALL_BINDIR ".")

View File

@ -6,20 +6,12 @@
#include <bit>
#include <cstdio>
#include <ctime>
#include <string>
namespace hex {
namespace prv { class Provider; }
struct GUID {
u32 data1;
u16 data2;
u16 data3;
u8 data4[8];
};
class ViewDataInspector : public View {
public:
explicit ViewDataInspector();

View File

@ -1,8 +1,8 @@
#pragma once
#include <hex/views/view.hpp>
#include <hex/lang/pattern_data.hpp>
#include <hex/lang/evaluator.hpp>
#include <hex/lang/pattern_language.hpp>
#include <hex/providers/provider.hpp>
@ -24,6 +24,7 @@ namespace hex {
void drawContent() override;
private:
lang::PatternLanguage *m_patternLanguageRuntime;
std::vector<lang::PatternData*> &m_patternData;
std::filesystem::path m_possiblePatternFile;

View File

@ -4,7 +4,6 @@
#include <imgui.h>
#include <hex/views/view.hpp>
#include "helpers/math_evaluator.hpp"
#include <array>
#include <string>
@ -21,29 +20,6 @@ namespace hex {
void drawContent() override;
void drawMenu() override;
private:
char *m_mangledBuffer = nullptr;
std::string m_demangledName;
bool m_asciiTableShowOctal = false;
char *m_regexInput = nullptr;
char *m_regexPattern = nullptr;
char *m_replacePattern = nullptr;
std::string m_regexOutput;
std::array<float, 4> m_pickedColor;
MathEvaluator m_mathEvaluator;
std::vector<long double> m_mathHistory;
std::string m_lastMathError;
char *m_mathInput = nullptr;
void drawDemangler();
void drawASCIITable();
void drawRegexReplacer();
void drawColorPicker();
void drawMathEvaluator();
};
}

View File

@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.16)
project(builtin)
set(CMAKE_CXX_STANDARD 20)
if (NOT TARGET libimhex)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libimhex ${CMAKE_CURRENT_BINARY_DIR}/plugins/libimhex)
endif()
set(CMAKE_SHARED_LIBRARY_PREFIX "plugin")
add_library(builtin SHARED
source/plugin_builtin.cpp
source/content/command_palette_commands.cpp
source/content/data_inspector.cpp
source/content/lang_builtin_functions.cpp
source/content/settings_entries.cpp
source/content/tools_entries.cpp
source/math_evaluator.cpp
)
target_include_directories(builtin PRIVATE include)
target_link_libraries(builtin PRIVATE libimhex LLVMDemangle)

View File

@ -0,0 +1,31 @@
#include <hex/plugin.hpp>
#include "math_evaluator.hpp"
namespace hex::plugin::builtin {
void registerCommandPaletteCommands() {
hex::ContentRegistry::CommandPaletteCommands::add(
hex::ContentRegistry::CommandPaletteCommands::Type::SymbolCommand,
"#", "Calculator",
[](auto input) {
hex::MathEvaluator evaluator;
evaluator.registerStandardVariables();
evaluator.registerStandardFunctions();
std::optional<long double> result;
try {
result = evaluator.evaluate(input);
} catch (std::runtime_error &e) {}
if (result.has_value())
return hex::format("#%s = %Lf", input.data(), result.value());
else
return hex::format("#%s = ???", input.data());
});
}
}

View File

@ -0,0 +1,188 @@
#include <hex/plugin.hpp>
#include <hex/helpers/utils.hpp>
#include <cstring>
#include <ctime>
#include <imgui_internal.h>
namespace hex::plugin::builtin {
struct GUID {
u32 data1;
u16 data2;
u16 data3;
u8 data4[8];
};
void registerDataInspectorEntries() {
using Style = hex::ContentRegistry::DataInspector::NumberDisplayStyle;
hex::ContentRegistry::DataInspector::add("Binary (8 bit)", sizeof(u8), [](auto buffer, auto endian, auto style) {
std::string binary;
for (u8 i = 0; i < 8; i++)
binary += ((buffer[0] << i) & 0x80) == 0 ? '0' : '1';
return [binary] { ImGui::TextUnformatted(binary.c_str()); };
});
hex::ContentRegistry::DataInspector::add("uint8_t", sizeof(u8), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "%u" : ((style == Style::Hexadecimal) ? "0x%X" : "0o%o");
auto value = hex::format(format, *reinterpret_cast<u8*>(buffer.data()));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("int8_t", sizeof(s8), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "%d" : ((style == Style::Hexadecimal) ? "0x%X" : "0o%o");
auto value = hex::format(format, *reinterpret_cast<s8*>(buffer.data()));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("uint16_t", sizeof(u16), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "%u" : ((style == Style::Hexadecimal) ? "0x%X" : "0o%o");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u16*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("int16_t", sizeof(s16), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "%d" : ((style == Style::Hexadecimal) ? "0x%X" : "0o%o");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<s16*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("uint32_t", sizeof(u32), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "%u" : ((style == Style::Hexadecimal) ? "0x%X" : "0o%o");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u32*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("int32_t", sizeof(s32), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "%d" : ((style == Style::Hexadecimal) ? "0x%X" : "0o%o");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<s32*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("uint64_t", sizeof(u64), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "%lu" : ((style == Style::Hexadecimal) ? "0x%lX" : "0o%lo");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u64*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("int64_t", sizeof(s64), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "%ld" : ((style == Style::Hexadecimal) ? "0x%lX" : "0o%lo");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<s64*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("float (32 bit)", sizeof(float), [](auto buffer, auto endian, auto style) {
auto value = hex::format("%e", hex::changeEndianess(*reinterpret_cast<float*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("double (64 bit)", sizeof(double), [](auto buffer, auto endian, auto style) {
auto value = hex::format("%e", hex::changeEndianess(*reinterpret_cast<double*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("ASCII Character", sizeof(char8_t), [](auto buffer, auto endian, auto style) {
auto value = hex::format("'%s'", makePrintable(*reinterpret_cast<char8_t*>(buffer.data())).c_str());
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("Wide Character", sizeof(char16_t), [](auto buffer, auto endian, auto style) {
auto c = *reinterpret_cast<char16_t*>(buffer.data());
auto value = hex::format("'%lc'", c == 0 ? '\x01' : hex::changeEndianess(c, endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("UTF-8 code point", sizeof(char8_t) * 4, [](auto buffer, auto endian, auto style) {
char utf8Buffer[5] = { 0 };
char codepointString[5] = { 0 };
u32 codepoint = 0;
std::memcpy(utf8Buffer, reinterpret_cast<char8_t*>(buffer.data()), 4);
u8 codepointSize = ImTextCharFromUtf8(&codepoint, utf8Buffer, utf8Buffer + 4);
std::memcpy(codepointString, &codepoint, std::min(codepointSize, u8(4)));
auto value = hex::format("'%s' (U+%04lx)", codepoint == 0xFFFD ? "Invalid" :
codepoint < 0xFF ? makePrintable(codepoint).c_str() :
codepointString,
codepoint);
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
#if defined(OS_WINDOWS) && defined(ARCH_64_BIT)
hex::ContentRegistry::DataInspector::add("__time32_t", sizeof(__time32_t), [](auto buffer, auto endian, auto style) {
auto endianAdjustedTime = hex::changeEndianess(*reinterpret_cast<__time32_t*>(buffer.data()), endian);
std::tm * ptm = _localtime32(&endianAdjustedTime);
char timeBuffer[32];
std::string value;
if (ptm != nullptr && std::strftime(timeBuffer, 32, "%a, %d.%m.%Y %H:%M:%S", ptm))
value = timeBuffer;
else
value = "Invalid";
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("__time64_t", sizeof(__time64_t), [](auto buffer, auto endian, auto style) {
auto endianAdjustedTime = hex::changeEndianess(*reinterpret_cast<__time64_t*>(buffer.data()), endian);
std::tm * ptm = _localtime64(&endianAdjustedTime);
char timeBuffer[64];
std::string value;
if (ptm != nullptr && std::strftime(timeBuffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm))
value = timeBuffer;
else
value = "Invalid";
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
#else
hex::ContentRegistry::DataInspector::add("time_t", sizeof(time_t), [](auto buffer, auto endian, auto style) {
auto endianAdjustedTime = hex::changeEndianess(*reinterpret_cast<time_t*>(buffer.data()), endian);
std::tm * ptm = localtime(&endianAdjustedTime);
char timeBuffer[64];
std::string value;
if (ptm != nullptr && std::strftime(timeBuffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm))
value = timeBuffer;
else
value = "Invalid";
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
#endif
hex::ContentRegistry::DataInspector::add("GUID", sizeof(GUID), [](auto buffer, auto endian, auto style) {
GUID guid;
std::memcpy(&guid, buffer.data(), sizeof(GUID));
auto value = hex::format("%s{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
(hex::changeEndianess(guid.data3, endian) >> 12) <= 5 && ((guid.data4[0] >> 4) >= 8 || (guid.data4[0] >> 4) == 0) ? "" : "Invalid ",
hex::changeEndianess(guid.data1, endian),
hex::changeEndianess(guid.data2, endian),
hex::changeEndianess(guid.data3, endian),
guid.data4[0], guid.data4[1], guid.data4[2], guid.data4[3],
guid.data4[4], guid.data4[5], guid.data4[6], guid.data4[7]);
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
hex::ContentRegistry::DataInspector::add("RGBA Color", sizeof(u32), [](auto buffer, auto endian, auto style) {
ImColor value(hex::changeEndianess(*reinterpret_cast<u32*>(buffer.data()), endian));
return [value] {
ImGui::ColorButton("##inspectorColor", value,
ImGuiColorEditFlags_None,
ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
};
});
}
}

View File

@ -0,0 +1,200 @@
#include <hex/plugin.hpp>
#include <hex/lang/ast_node.hpp>
#include <hex/lang/log_console.hpp>
#include <hex/lang/evaluator.hpp>
#include <hex/helpers/utils.hpp>
#include <vector>
namespace hex::plugin::builtin {
#define LITERAL_COMPARE(literal, cond) std::visit([&](auto &&literal) { return (cond) != 0; }, literal)
#define AS_TYPE(type, value) ctx.template asType<type>(value)
void registerPatternLanguageFunctions() {
using namespace hex::lang;
/* findSequence(occurrenceIndex, byte...) */
ContentRegistry::PatternLanguageFunctions::add("findSequence", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 1, [](auto &ctx, auto params) {
auto& occurrenceIndex = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue();
std::vector<u8> sequence;
for (u32 i = 1; i < params.size(); i++) {
sequence.push_back(std::visit([&](auto &&value) -> u8 {
if (value <= 0xFF)
return value;
else
ctx.getConsole().abortEvaluation("sequence bytes need to fit into 1 byte");
}, AS_TYPE(ASTNodeIntegerLiteral, params[i])->getValue()));
}
std::vector<u8> bytes(sequence.size(), 0x00);
u32 occurrences = 0;
for (u64 offset = 0; offset < SharedData::currentProvider->getSize() - sequence.size(); offset++) {
SharedData::currentProvider->read(offset, bytes.data(), bytes.size());
if (bytes == sequence) {
if (LITERAL_COMPARE(occurrenceIndex, occurrences < occurrenceIndex)) {
occurrences++;
continue;
}
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, offset });
}
}
ctx.getConsole().abortEvaluation("failed to find sequence");
});
/* readUnsigned(address, size) */
ContentRegistry::PatternLanguageFunctions::add("readUnsigned", 2, [](auto &ctx, auto params) {
auto address = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue();
auto size = AS_TYPE(ASTNodeIntegerLiteral, params[1])->getValue();
if (LITERAL_COMPARE(address, address >= SharedData::currentProvider->getActualSize()))
ctx.getConsole().abortEvaluation("address out of range");
return std::visit([&](auto &&address, auto &&size) {
if (size <= 0 || size > 16)
ctx.getConsole().abortEvaluation("invalid read size");
u8 value[(u8)size];
SharedData::currentProvider->read(address, value, size);
switch ((u8)size) {
case 1: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned8Bit, *reinterpret_cast<u8*>(value) });
case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned16Bit, *reinterpret_cast<u16*>(value) });
case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned32Bit, *reinterpret_cast<u32*>(value) });
case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, *reinterpret_cast<u64*>(value) });
case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned128Bit, *reinterpret_cast<u128*>(value) });
default: ctx.getConsole().abortEvaluation("invalid read size");
}
}, address, size);
});
/* readSigned(address, size) */
ContentRegistry::PatternLanguageFunctions::add("readSigned", 2, [](auto &ctx, auto params) {
auto address = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue();
auto size = AS_TYPE(ASTNodeIntegerLiteral, params[1])->getValue();
if (LITERAL_COMPARE(address, address >= SharedData::currentProvider->getActualSize()))
ctx.getConsole().abortEvaluation("address out of range");
return std::visit([&](auto &&address, auto &&size) {
if (size <= 0 || size > 16)
ctx.getConsole().abortEvaluation("invalid read size");
u8 value[(u8)size];
SharedData::currentProvider->read(address, value, size);
switch ((u8)size) {
case 1: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed8Bit, *reinterpret_cast<s8*>(value) });
case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed16Bit, *reinterpret_cast<s16*>(value) });
case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed32Bit, *reinterpret_cast<s32*>(value) });
case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed64Bit, *reinterpret_cast<s64*>(value) });
case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed128Bit, *reinterpret_cast<s128*>(value) });
default: ctx.getConsole().abortEvaluation("invalid read size");
}
}, address, size);
});
/* assert(condition, message) */
ContentRegistry::PatternLanguageFunctions::add("assert", 2, [](auto &ctx, auto params) {
auto condition = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue();
auto message = AS_TYPE(ASTNodeStringLiteral, params[1])->getString();
if (LITERAL_COMPARE(condition, condition == 0))
ctx.getConsole().abortEvaluation(hex::format("assert failed \"%s\"", message.data()));
return nullptr;
});
/* warnAssert(condition, message) */
ContentRegistry::PatternLanguageFunctions::add("warnAssert", 2, [](auto ctx, auto params) {
auto condition = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue();
auto message = AS_TYPE(ASTNodeStringLiteral, params[1])->getString();
if (LITERAL_COMPARE(condition, condition == 0))
ctx.getConsole().log(LogConsole::Level::Warning, hex::format("assert failed \"%s\"", message.data()));
return nullptr;
});
/* print(values...) */
ContentRegistry::PatternLanguageFunctions::add("print", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 0, [](auto &ctx, auto params) {
std::string message;
for (auto& param : params) {
if (auto integerLiteral = dynamic_cast<ASTNodeIntegerLiteral*>(param); integerLiteral != nullptr) {
switch (integerLiteral->getType()) {
case Token::ValueType::Character: message += std::get<s8>(integerLiteral->getValue()); break;
case Token::ValueType::Unsigned8Bit: message += std::to_string(std::get<u8>(integerLiteral->getValue())); break;
case Token::ValueType::Signed8Bit: message += std::to_string(std::get<s8>(integerLiteral->getValue())); break;
case Token::ValueType::Unsigned16Bit: message += std::to_string(std::get<u16>(integerLiteral->getValue())); break;
case Token::ValueType::Signed16Bit: message += std::to_string(std::get<s16>(integerLiteral->getValue())); break;
case Token::ValueType::Unsigned32Bit: message += std::to_string(std::get<u32>(integerLiteral->getValue())); break;
case Token::ValueType::Signed32Bit: message += std::to_string(std::get<s32>(integerLiteral->getValue())); break;
case Token::ValueType::Unsigned64Bit: message += std::to_string(std::get<u64>(integerLiteral->getValue())); break;
case Token::ValueType::Signed64Bit: message += std::to_string(std::get<s64>(integerLiteral->getValue())); break;
case Token::ValueType::Unsigned128Bit: message += hex::to_string(std::get<u128>(integerLiteral->getValue())); break;
case Token::ValueType::Signed128Bit: message += hex::to_string(std::get<s128>(integerLiteral->getValue())); break;
case Token::ValueType::Float: message += std::to_string(std::get<float>(integerLiteral->getValue())); break;
case Token::ValueType::Double: message += std::to_string(std::get<double>(integerLiteral->getValue())); break;
case Token::ValueType::Boolean: message += std::get<s32>(integerLiteral->getValue()) ? "true" : "false"; break;
case Token::ValueType::CustomType: message += "< Custom Type >"; break;
}
}
else if (auto stringLiteral = dynamic_cast<ASTNodeStringLiteral*>(param); stringLiteral != nullptr)
message += stringLiteral->getString();
}
ctx.getConsole().log(LogConsole::Level::Info, message);
return nullptr;
});
/* addressof(rValueString) */
ContentRegistry::PatternLanguageFunctions::add("addressof", 1, [](auto &ctx, auto params) -> ASTNode* {
auto name = AS_TYPE(ASTNodeStringLiteral, params[0])->getString();
std::vector<std::string> path = splitString(name, ".");
auto pattern = ctx.patternFromName(path);
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(pattern->getOffset()) });
});
/* sizeof(rValueString) */
ContentRegistry::PatternLanguageFunctions::add("sizeof", 1, [](auto &ctx, auto params) -> ASTNode* {
auto name = AS_TYPE(ASTNodeStringLiteral, params[0])->getString();
std::vector<std::string> path = splitString(name, ".");
auto pattern = ctx.patternFromName(path);
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(pattern->getSize()) });
});
/* nextAfter(rValueString) */
ContentRegistry::PatternLanguageFunctions::add("nextAfter", 1, [](auto &ctx, auto params) -> ASTNode* {
auto name = AS_TYPE(ASTNodeStringLiteral, params[0])->getString();
std::vector<std::string> path = splitString(name, ".");
auto pattern = ctx.patternFromName(path);
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(pattern->getOffset() + pattern->getSize()) });
});
/* alignTo(alignment, value) */
ContentRegistry::PatternLanguageFunctions::add("alignTo", 2, [](auto &ctx, auto params) -> ASTNode* {
auto alignment = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue();
auto value = AS_TYPE(ASTNodeIntegerLiteral, params[1])->getValue();
auto result = std::visit([](auto &&alignment, auto &&value) {
u64 remainder = u64(value) % u64(alignment);
return remainder != 0 ? u64(value) + (u64(alignment) - remainder) : u64(value);
}, alignment, value);
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(result) });
});
}
}

View File

@ -0,0 +1,9 @@
#include <hex/plugin.hpp>
namespace hex::plugin::builtin {
void registerSettings() {
}
}

View File

@ -0,0 +1,298 @@
#include <hex/plugin.hpp>
#include <regex>
#include <llvm/Demangle/Demangle.h>
#include "math_evaluator.hpp"
namespace hex::plugin::builtin {
namespace {
void drawDemangler() {
static std::vector<char> mangledBuffer(0xF'FFFF, 0x00);
static std::string demangledName;
if (ImGui::InputText("Mangled name", mangledBuffer.data(), 0xF'FFFF)) {
demangledName = llvm::demangle(mangledBuffer.data());
}
ImGui::InputText("Demangled name", demangledName.data(), demangledName.size(), ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
}
void drawASCIITable() {
static bool asciiTableShowOctal = false;
ImGui::BeginTable("##asciitable", 4);
ImGui::TableSetupColumn("");
ImGui::TableSetupColumn("");
ImGui::TableSetupColumn("");
ImGui::TableSetupColumn("");
ImGui::TableNextColumn();
for (u8 tablePart = 0; tablePart < 4; tablePart++) {
ImGui::BeginTable("##asciitablepart", asciiTableShowOctal ? 4 : 3, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg);
ImGui::TableSetupColumn("dec");
if (asciiTableShowOctal)
ImGui::TableSetupColumn("oct");
ImGui::TableSetupColumn("hex");
ImGui::TableSetupColumn("char");
ImGui::TableHeadersRow();
u32 rowCount = 0;
for (u8 i = 0; i < 0x80 / 4; i++) {
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text("%02d", i + 32 * tablePart);
if (asciiTableShowOctal) {
ImGui::TableNextColumn();
ImGui::Text("0o%02o", i + 32 * tablePart);
}
ImGui::TableNextColumn();
ImGui::Text("0x%02x", i + 32 * tablePart);
ImGui::TableNextColumn();
ImGui::Text("%s", makePrintable(i + 32 * tablePart).c_str());
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
rowCount++;
}
ImGui::EndTable();
ImGui::TableNextColumn();
}
ImGui::EndTable();
ImGui::Checkbox("Show octal", &asciiTableShowOctal);
ImGui::NewLine();
}
void drawRegexReplacer() {
static std::vector<char> regexInput(0xF'FFFF, 0x00);;
static std::vector<char> regexPattern(0xF'FFFF, 0x00);;
static std::vector<char> replacePattern(0xF'FFFF, 0x00);;
static std::string regexOutput(0xF'FFFF, 0x00);;
bool shouldInvalidate;
shouldInvalidate = ImGui::InputText("Regex pattern", regexPattern.data(), regexPattern.size());
shouldInvalidate = ImGui::InputText("Replace pattern", replacePattern.data(), replacePattern.size()) || shouldInvalidate;
shouldInvalidate = ImGui::InputTextMultiline("Input", regexInput.data(), regexInput.size()) || shouldInvalidate;
if (shouldInvalidate) {
try {
regexOutput = std::regex_replace(regexInput.data(), std::regex(regexPattern.data()), replacePattern.data());
} catch (std::regex_error&) {}
}
ImGui::InputTextMultiline("Output", regexOutput.data(), regexOutput.size(), ImVec2(0, 0), ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
}
void drawColorPicker() {
static std::array<float, 4> pickedColor = { 0 };
ImGui::SetNextItemWidth(300.0F);
ImGui::ColorPicker4("Color Picker", pickedColor.data(),
ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHex);
ImGui::NewLine();
}
void drawMathEvaluator() {
static std::vector<long double> mathHistory;
static std::string lastMathError;
static std::vector<char> mathInput(0xF'FFFF, 0x00);
static MathEvaluator mathEvaluator = [&]{
MathEvaluator evaluator;
evaluator.registerStandardVariables();
evaluator.registerStandardFunctions();
evaluator.setFunction("clear", [&](auto args) -> std::optional<long double> {
mathHistory.clear();
lastMathError.clear();
mathEvaluator.getVariables().clear();
mathEvaluator.registerStandardVariables();
std::memset(mathInput.data(), 0x00, mathInput.size());
return { };
}, 0, 0);
evaluator.setFunction("read", [](auto args) -> std::optional<long double> {
u8 value = 0;
auto provider = SharedData::currentProvider;
if (provider == nullptr || !provider->isReadable() || args[0] >= provider->getActualSize())
return { };
provider->read(args[0], &value, sizeof(u8));
return value;
}, 1, 1);
evaluator.setFunction("write", [](auto args) -> std::optional<long double> {
auto provider = SharedData::currentProvider;
if (provider == nullptr || !provider->isWritable() || args[0] >= provider->getActualSize())
return { };
if (args[1] > 0xFF)
return { };
u8 value = args[1];
provider->write(args[0], &value, sizeof(u8));
return { };
}, 2, 2);
return std::move(evaluator);
}();
if (ImGui::InputText("Input", mathInput.data(), mathInput.size(), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
ImGui::SetKeyboardFocusHere();
std::optional<long double> result;
try {
result = mathEvaluator.evaluate(mathInput.data());
} catch (std::invalid_argument &e) {
lastMathError = e.what();
}
if (result.has_value()) {
mathHistory.push_back(result.value());
std::memset(mathInput.data(), 0x00, mathInput.size());
lastMathError.clear();
}
}
if (!lastMathError.empty())
ImGui::TextColored(ImColor(0xA00040FF), "Last Error: %s", lastMathError.c_str());
else
ImGui::NewLine();
enum class MathDisplayType { Standard, Scientific, Engineering, Programmer } mathDisplayType;
if (ImGui::BeginTabBar("##mathFormatTabBar")) {
if (ImGui::BeginTabItem("Standard")) {
mathDisplayType = MathDisplayType::Standard;
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Scientific")) {
mathDisplayType = MathDisplayType::Scientific;
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Engineering")) {
mathDisplayType = MathDisplayType::Engineering;
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Programmer")) {
mathDisplayType = MathDisplayType::Programmer;
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
if (ImGui::BeginTable("##mathWrapper", 2)) {
ImGui::TableSetupColumn("##results");
ImGui::TableSetupColumn("##variables", ImGuiTableColumnFlags_WidthStretch, 0.7);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::BeginTable("##mathHistory", 1, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("History");
ImGuiListClipper clipper;
clipper.Begin(mathHistory.size());
ImGui::TableHeadersRow();
while (clipper.Step()) {
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
if (i == 0)
ImGui::PushStyleColor(ImGuiCol_Text, ImU32(ImColor(0xA5, 0x45, 0x45)));
ImGui::TableNextRow();
ImGui::TableNextColumn();
switch (mathDisplayType) {
case MathDisplayType::Standard:
ImGui::Text("%.3Lf", mathHistory[(mathHistory.size() - 1) - i]);
break;
case MathDisplayType::Scientific:
ImGui::Text("%.6Le", mathHistory[(mathHistory.size() - 1) - i]);
break;
case MathDisplayType::Engineering:
ImGui::Text("%s", hex::toEngineeringString(mathHistory[(mathHistory.size() - 1) - i]).c_str());
break;
case MathDisplayType::Programmer:
ImGui::Text("0x%llX (%llu)",
u64(mathHistory[(mathHistory.size() - 1) - i]),
u64(mathHistory[(mathHistory.size() - 1) - i]));
break;
}
if (i == 0)
ImGui::PopStyleColor();
}
}
clipper.End();
ImGui::EndTable();
}
ImGui::TableNextColumn();
if (ImGui::BeginTable("##mathVariables", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Value");
ImGui::TableHeadersRow();
for (const auto &[name, value] : mathEvaluator.getVariables()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted(name.c_str());
ImGui::TableNextColumn();
switch (mathDisplayType) {
case MathDisplayType::Standard:
ImGui::Text("%.3Lf", value);
break;
case MathDisplayType::Scientific:
ImGui::Text("%.6Le", value);
break;
case MathDisplayType::Engineering:
ImGui::Text("%s", hex::toEngineeringString(value).c_str());
break;
case MathDisplayType::Programmer:
ImGui::Text("0x%llX (%llu)", u64(value), u64(value));
break;
}
}
ImGui::EndTable();
}
ImGui::EndTable();
}
}
}
void registerToolEntries() {
ContentRegistry::Tools::add("Itanium/MSVC demangler", drawDemangler);
ContentRegistry::Tools::add("ASCII table", drawASCIITable);
ContentRegistry::Tools::add("Regex replacer", drawRegexReplacer);
ContentRegistry::Tools::add("Color picker", drawColorPicker);
ContentRegistry::Tools::add("Calculator", drawMathEvaluator);
}
}

View File

@ -1,4 +1,4 @@
#include "helpers/math_evaluator.hpp"
#include "math_evaluator.hpp"
#include <string>
#include <queue>

View File

@ -0,0 +1,25 @@
#include <hex/plugin.hpp>
namespace hex::plugin::builtin {
void registerDataInspectorEntries();
void registerToolEntries();
void registerPatternLanguageFunctions();
void registerCommandPaletteCommands();
void registerSettings();
}
IMHEX_PLUGIN_SETUP {
using namespace hex::plugin::builtin;
registerDataInspectorEntries();
registerToolEntries();
registerPatternLanguageFunctions();
registerCommandPaletteCommands();
registerSettings();
}

View File

@ -22,6 +22,7 @@ add_library(libimhex SHARED
source/helpers/utils.cpp
source/helpers/shared_data.cpp
source/lang/pattern_language.cpp
source/lang/preprocessor.cpp
source/lang/lexer.cpp
source/lang/parser.cpp

View File

@ -16,7 +16,7 @@ namespace hex {
class View;
namespace lang { class ASTNode; }
namespace lang { class LogConsole; }
namespace lang { class Evaluator; }
/*
The Content Registry is the heart of all features in ImHex that are in some way extendable by Plugins.
@ -83,10 +83,10 @@ namespace hex {
struct Function {
u32 parameterCount;
std::function<hex::lang::ASTNode*(hex::lang::LogConsole&, std::vector<hex::lang::ASTNode*>)> func;
std::function<hex::lang::ASTNode*(hex::lang::Evaluator&, std::vector<hex::lang::ASTNode*>)> func;
};
static void add(std::string_view name, u32 parameterCount, const std::function<hex::lang::ASTNode*(hex::lang::LogConsole&, std::vector<hex::lang::ASTNode*>)> &func);
static void add(std::string_view name, u32 parameterCount, const std::function<hex::lang::ASTNode*(hex::lang::Evaluator&, std::vector<hex::lang::ASTNode*>)> &func);
static std::map<std::string, ContentRegistry::PatternLanguageFunctions::Function>& getEntries();
};

View File

@ -6,6 +6,7 @@
#include <hex/helpers/utils.hpp>
#include <hex/lang/pattern_data.hpp>
#include <hex/lang/ast_node.hpp>
#include <hex/lang/log_console.hpp>
#include <bit>
#include <string>
@ -14,45 +15,27 @@
namespace hex::lang {
class LogConsole {
public:
enum Level {
Debug,
Info,
Warning,
Error
};
const auto& getLog() { return this->m_consoleLog; }
using EvaluateError = std::string;
void log(Level level, std::string_view message) {
switch (level) {
default:
case Level::Debug: this->m_consoleLog.emplace_back(level, "[-] " + std::string(message)); break;
case Level::Info: this->m_consoleLog.emplace_back(level, "[i] " + std::string(message)); break;
case Level::Warning: this->m_consoleLog.emplace_back(level, "[*] " + std::string(message)); break;
case Level::Error: this->m_consoleLog.emplace_back(level, "[!] " + std::string(message)); break;
}
}
[[noreturn]] void abortEvaluation(std::string_view message) {
throw EvaluateError(message);
}
private:
std::vector<std::pair<Level, std::string>> m_consoleLog;
};
class Evaluator {
public:
Evaluator(prv::Provider* &provider, std::endian defaultDataEndian);
Evaluator(prv::Provider* &provider, std::endian defaultDataEndian = std::endian::native);
std::optional<std::vector<PatternData*>> evaluate(const std::vector<ASTNode*>& ast);
LogConsole& getConsole() { return this->m_console; }
void setDefaultEndian(std::endian endian) { this->m_defaultDataEndian = endian; }
[[nodiscard]] std::endian getCurrentEndian() const { return this->m_endianStack.back(); }
PatternData* patternFromName(const std::vector<std::string> &name);
template<typename T>
T* asType(ASTNode *param) {
if (auto evaluatedParam = dynamic_cast<T*>(param); evaluatedParam != nullptr)
return evaluatedParam;
else
this->getConsole().abortEvaluation("function got wrong type of parameter");
}
private:
std::map<std::string, ASTNode*> m_types;
prv::Provider* &m_provider;
@ -63,9 +46,7 @@ namespace hex::lang {
std::vector<std::vector<PatternData*>*> m_currMembers;
LogConsole m_console;
[[nodiscard]] std::endian getCurrentEndian() const {
return this->m_endianStack.back();
}
ASTNodeIntegerLiteral* evaluateScopeResolution(ASTNodeScopeResolution *node);
ASTNodeIntegerLiteral* evaluateRValue(ASTNodeRValue *node);
@ -75,7 +56,6 @@ namespace hex::lang {
ASTNodeIntegerLiteral* evaluateTernaryExpression(ASTNodeTernaryExpression *node);
ASTNodeIntegerLiteral* evaluateMathematicalExpression(ASTNodeNumericExpression *node);
PatternData* patternFromName(const std::vector<std::string> &name);
PatternData* evaluateAttributes(ASTNode *currNode, PatternData *currPattern);
PatternData* evaluateBuiltinType(ASTNodeBuiltinType *node);
void evaluateMember(ASTNode *node, std::vector<PatternData*> &currMembers, bool increaseOffset);
@ -87,28 +67,6 @@ namespace hex::lang {
PatternData* evaluateVariable(ASTNodeVariableDecl *node);
PatternData* evaluateArray(ASTNodeArrayVariableDecl *node);
PatternData* evaluatePointer(ASTNodePointerVariableDecl *node);
template<typename T>
T* asType(ASTNode *param) {
if (auto evaluatedParam = dynamic_cast<T*>(param); evaluatedParam != nullptr)
return evaluatedParam;
else
this->m_console.abortEvaluation("function got wrong type of parameter");
}
void registerBuiltinFunctions();
#define BUILTIN_FUNCTION(name) ASTNodeIntegerLiteral* TOKEN_CONCAT(builtin_, name)(LogConsole &console, std::vector<ASTNode*> params)
BUILTIN_FUNCTION(findSequence);
BUILTIN_FUNCTION(readUnsigned);
BUILTIN_FUNCTION(readSigned);
BUILTIN_FUNCTION(assert);
BUILTIN_FUNCTION(warnAssert);
BUILTIN_FUNCTION(print);
#undef BUILTIN_FUNCTION
};
}

View File

@ -0,0 +1,47 @@
#pragma once
#include <hex.hpp>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace hex::lang {
class LogConsole {
public:
enum Level {
Debug,
Info,
Warning,
Error
};
const auto& getLog() { return this->m_consoleLog; }
using EvaluateError = std::string;
void log(Level level, std::string_view message) {
switch (level) {
default:
case Level::Debug: this->m_consoleLog.emplace_back(level, "[-] " + std::string(message)); break;
case Level::Info: this->m_consoleLog.emplace_back(level, "[i] " + std::string(message)); break;
case Level::Warning: this->m_consoleLog.emplace_back(level, "[*] " + std::string(message)); break;
case Level::Error: this->m_consoleLog.emplace_back(level, "[!] " + std::string(message)); break;
}
}
[[noreturn]] void abortEvaluation(std::string_view message) {
throw EvaluateError(message);
}
void clear() {
this->m_consoleLog.clear();
}
private:
std::vector<std::pair<Level, std::string>> m_consoleLog;
};
}

View File

@ -110,6 +110,10 @@ namespace hex::lang {
/* Token consuming */
enum class Setting{ };
constexpr static auto Normal = static_cast<Setting>(0);
constexpr static auto Not = static_cast<Setting>(1);
bool begin() {
this->m_originalPosition = this->m_curr;
this->m_matchedOptionals.clear();
@ -117,41 +121,65 @@ namespace hex::lang {
return true;
}
bool none(bool result) {
if (result) {
this->m_curr = this->m_originalPosition;
return false;
}
return true;
}
template<Setting S = Normal>
bool sequence() {
return true;
if constexpr (S == Normal)
return true;
else if constexpr (S == Not)
return false;
else
__builtin_unreachable();
}
template<Setting S = Normal>
bool sequence(Token::Type type, auto value, auto ... args) {
if (!peek(type, value)) {
if constexpr (S == Normal) {
if (!peek(type, value)) {
this->m_curr = this->m_originalPosition;
return false;
}
this->m_curr++;
if (!sequence<Normal>(args...)) {
this->m_curr = this->m_originalPosition;
return false;
}
return true;
} else if constexpr (S == Not) {
if (!peek(type, value))
return true;
this->m_curr++;
if (!sequence<Normal>(args...))
return true;
this->m_curr = this->m_originalPosition;
return false;
}
this->m_curr++;
if (!sequence(args...)) {
this->m_curr = this->m_originalPosition;
return false;
}
return true;
} else
__builtin_unreachable();
}
template<Setting S = Normal>
bool oneOf() {
return false;
if constexpr (S == Normal)
return false;
else if constexpr (S == Not)
return true;
else
__builtin_unreachable();
}
template<Setting S = Normal>
bool oneOf(Token::Type type, auto value, auto ... args) {
return sequence(type, value) || oneOf(args...);
if constexpr (S == Normal)
return sequence<Normal>(type, value) || oneOf(args...);
else if constexpr (S == Not)
return sequence<Not>(type, value) && oneOf(args...);
else
__builtin_unreachable();
}
bool variant(Token::Type type1, auto value1, Token::Type type2, auto value2) {

View File

@ -0,0 +1,48 @@
#pragma once
#include <hex.hpp>
#include <bit>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include <hex/lang/pattern_data.hpp>
#include <hex/lang/log_console.hpp>
namespace hex::prv { class Provider; }
namespace hex::lang {
class Preprocessor;
class Lexer;
class Parser;
class Validator;
class Evaluator;
class PatternLanguage {
public:
PatternLanguage(prv::Provider *provider);
~PatternLanguage();
std::optional<std::vector<PatternData*>> executeString(std::string_view string);
std::optional<std::vector<PatternData*>> executeFile(std::string_view path);
std::vector<std::pair<LogConsole::Level, std::string>> getConsoleLog();
std::optional<std::pair<u32, std::string>> getError();
private:
Preprocessor *m_preprocessor;
Lexer *m_lexer;
Parser *m_parser;
Validator *m_validator;
Evaluator *m_evaluator;
prv::Provider *m_provider;
std::endian m_defaultEndian;
std::optional<std::pair<u32, std::string>> m_currError;
};
}

View File

@ -76,7 +76,7 @@ namespace hex {
/* Pattern Language Functions */
void ContentRegistry::PatternLanguageFunctions::add(std::string_view name, u32 parameterCount, const std::function<hex::lang::ASTNode*(hex::lang::LogConsole&, std::vector<hex::lang::ASTNode*>)> &func) {
void ContentRegistry::PatternLanguageFunctions::add(std::string_view name, u32 parameterCount, const std::function<hex::lang::ASTNode*(hex::lang::Evaluator&, std::vector<hex::lang::ASTNode*>)> &func) {
getEntries()[name.data()] = Function{ parameterCount, func };
}

View File

@ -1,182 +1,10 @@
#include <hex/lang/evaluator.hpp>
#include <hex.hpp>
#include <hex/api/content_registry.hpp>
namespace hex::lang {
#define LITERAL_COMPARE(literal, cond) std::visit([&, this](auto &&literal) { return (cond) != 0; }, literal)
void Evaluator::registerBuiltinFunctions() {
/* findSequence */
ContentRegistry::PatternLanguageFunctions::add("findSequence", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 1, [this](auto &console, auto params) {
auto& occurrenceIndex = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
std::vector<u8> sequence;
for (u32 i = 1; i < params.size(); i++) {
sequence.push_back(std::visit([&](auto &&value) -> u8 {
if (value <= 0xFF)
return value;
else
console.abortEvaluation("sequence bytes need to fit into 1 byte");
}, asType<ASTNodeIntegerLiteral>(params[i])->getValue()));
}
std::vector<u8> bytes(sequence.size(), 0x00);
u32 occurrences = 0;
for (u64 offset = 0; offset < this->m_provider->getSize() - sequence.size(); offset++) {
this->m_provider->read(offset, bytes.data(), bytes.size());
if (bytes == sequence) {
if (LITERAL_COMPARE(occurrenceIndex, occurrences < occurrenceIndex)) {
occurrences++;
continue;
}
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, offset });
}
}
console.abortEvaluation("failed to find sequence");
});
/* assert */
ContentRegistry::PatternLanguageFunctions::add("readUnsigned", 2, [this](auto &console, auto params) {
auto address = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
auto size = asType<ASTNodeIntegerLiteral>(params[1])->getValue();
if (LITERAL_COMPARE(address, address >= this->m_provider->getActualSize()))
console.abortEvaluation("address out of range");
return std::visit([&, this](auto &&address, auto &&size) {
if (size <= 0 || size > 16)
console.abortEvaluation("invalid read size");
u8 value[(u8)size];
this->m_provider->read(address, value, size);
switch ((u8)size) {
case 1: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned8Bit, hex::changeEndianess(*reinterpret_cast<u8*>(value), 1, this->getCurrentEndian()) });
case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned16Bit, hex::changeEndianess(*reinterpret_cast<u16*>(value), 2, this->getCurrentEndian()) });
case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned32Bit, hex::changeEndianess(*reinterpret_cast<u32*>(value), 4, this->getCurrentEndian()) });
case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, hex::changeEndianess(*reinterpret_cast<u64*>(value), 8, this->getCurrentEndian()) });
case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned128Bit, hex::changeEndianess(*reinterpret_cast<u128*>(value), 16, this->getCurrentEndian()) });
default: console.abortEvaluation("invalid read size");
}
}, address, size);
});
ContentRegistry::PatternLanguageFunctions::add("readSigned", 2, [this](auto &console, auto params) {
auto address = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
auto size = asType<ASTNodeIntegerLiteral>(params[1])->getValue();
if (LITERAL_COMPARE(address, address >= this->m_provider->getActualSize()))
console.abortEvaluation("address out of range");
return std::visit([&, this](auto &&address, auto &&size) {
if (size <= 0 || size > 16)
console.abortEvaluation("invalid read size");
u8 value[(u8)size];
this->m_provider->read(address, value, size);
switch ((u8)size) {
case 1: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed8Bit, hex::changeEndianess(*reinterpret_cast<s8*>(value), 1, this->getCurrentEndian()) });
case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed16Bit, hex::changeEndianess(*reinterpret_cast<s16*>(value), 2, this->getCurrentEndian()) });
case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed32Bit, hex::changeEndianess(*reinterpret_cast<s32*>(value), 4, this->getCurrentEndian()) });
case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed64Bit, hex::changeEndianess(*reinterpret_cast<s64*>(value), 8, this->getCurrentEndian()) });
case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed128Bit, hex::changeEndianess(*reinterpret_cast<s128*>(value), 16, this->getCurrentEndian()) });
default: console.abortEvaluation("invalid read size");
}
}, address, size);
});
ContentRegistry::PatternLanguageFunctions::add("assert", 2, [this](auto &console, auto params) {
auto condition = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
auto message = asType<ASTNodeStringLiteral>(params[1])->getString();
if (LITERAL_COMPARE(condition, condition == 0))
console.abortEvaluation(hex::format("assert failed \"%s\"", message.data()));
return nullptr;
});
ContentRegistry::PatternLanguageFunctions::add("warnAssert", 2, [this](auto console, auto params) {
auto condition = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
auto message = asType<ASTNodeStringLiteral>(params[1])->getString();
if (LITERAL_COMPARE(condition, condition == 0))
console.log(LogConsole::Level::Warning, hex::format("assert failed \"%s\"", message.data()));
return nullptr;
});
ContentRegistry::PatternLanguageFunctions::add("print", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 0, [](auto &console, auto params) {
std::string message;
for (auto& param : params) {
if (auto integerLiteral = dynamic_cast<ASTNodeIntegerLiteral*>(param); integerLiteral != nullptr) {
switch (integerLiteral->getType()) {
case Token::ValueType::Character: message += std::get<s8>(integerLiteral->getValue()); break;
case Token::ValueType::Unsigned8Bit: message += std::to_string(std::get<u8>(integerLiteral->getValue())); break;
case Token::ValueType::Signed8Bit: message += std::to_string(std::get<s8>(integerLiteral->getValue())); break;
case Token::ValueType::Unsigned16Bit: message += std::to_string(std::get<u16>(integerLiteral->getValue())); break;
case Token::ValueType::Signed16Bit: message += std::to_string(std::get<s16>(integerLiteral->getValue())); break;
case Token::ValueType::Unsigned32Bit: message += std::to_string(std::get<u32>(integerLiteral->getValue())); break;
case Token::ValueType::Signed32Bit: message += std::to_string(std::get<s32>(integerLiteral->getValue())); break;
case Token::ValueType::Unsigned64Bit: message += std::to_string(std::get<u64>(integerLiteral->getValue())); break;
case Token::ValueType::Signed64Bit: message += std::to_string(std::get<s64>(integerLiteral->getValue())); break;
case Token::ValueType::Unsigned128Bit: message += hex::to_string(std::get<u128>(integerLiteral->getValue())); break;
case Token::ValueType::Signed128Bit: message += hex::to_string(std::get<s128>(integerLiteral->getValue())); break;
case Token::ValueType::Float: message += std::to_string(std::get<float>(integerLiteral->getValue())); break;
case Token::ValueType::Double: message += std::to_string(std::get<double>(integerLiteral->getValue())); break;
case Token::ValueType::Boolean: message += std::get<s32>(integerLiteral->getValue()) ? "true" : "false"; break;
case Token::ValueType::CustomType: message += "< Custom Type >"; break;
}
}
else if (auto stringLiteral = dynamic_cast<ASTNodeStringLiteral*>(param); stringLiteral != nullptr)
message += stringLiteral->getString();
}
console.log(LogConsole::Level::Info, message);
return nullptr;
});
ContentRegistry::PatternLanguageFunctions::add("addressof", 1, [this](auto &console, auto params) -> ASTNode* {
auto name = asType<ASTNodeStringLiteral>(params[0])->getString();
std::vector<std::string> path = splitString(name, ".");
auto pattern = this->patternFromName(path);
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(pattern->getOffset()) });
});
ContentRegistry::PatternLanguageFunctions::add("sizeof", 1, [this](auto &console, auto params) -> ASTNode* {
auto name = asType<ASTNodeStringLiteral>(params[0])->getString();
std::vector<std::string> path = splitString(name, ".");
auto pattern = this->patternFromName(path);
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(pattern->getSize()) });
});
ContentRegistry::PatternLanguageFunctions::add("nextAfter", 1, [this](auto &console, auto params) -> ASTNode* {
auto name = asType<ASTNodeStringLiteral>(params[0])->getString();
std::vector<std::string> path = splitString(name, ".");
auto pattern = this->patternFromName(path);
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(pattern->getOffset() + pattern->getSize()) });
});
ContentRegistry::PatternLanguageFunctions::add("alignTo", 2, [this](auto &console, auto params) -> ASTNode* {
auto alignment = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
auto value = asType<ASTNodeIntegerLiteral>(params[1])->getValue();
auto result = std::visit([](auto &&alignment, auto &&value) {
u64 remainder = u64(value) % u64(alignment);
return remainder != 0 ? u64(value) + (u64(alignment) - remainder) : u64(value);
}, alignment, value);
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(result) });
});
}
}

View File

@ -13,8 +13,6 @@ namespace hex::lang {
Evaluator::Evaluator(prv::Provider* &provider, std::endian defaultDataEndian)
: m_provider(provider), m_defaultDataEndian(defaultDataEndian) {
this->registerBuiltinFunctions();
}
ASTNodeIntegerLiteral* Evaluator::evaluateScopeResolution(ASTNodeScopeResolution *node) {
@ -157,7 +155,7 @@ namespace hex::lang {
this->getConsole().abortEvaluation(hex::format("invalid number of parameters for function '%s'. Expected %d", node->getFunctionName().data(), function.parameterCount));
}
return function.func(this->getConsole(), evaluatedParams);
return function.func(*this, evaluatedParams);
}
#define FLOAT_BIT_OPERATION(name) \

View File

@ -420,7 +420,7 @@ namespace hex::lang {
if (MATCHES(sequence(VALUETYPE_PADDING, SEPARATOR_SQUAREBRACKETOPEN)))
member = parsePadding();
else if (MATCHES((optional(KEYWORD_BE), optional(KEYWORD_LE)) && variant(IDENTIFIER, VALUETYPE_ANY) && sequence(IDENTIFIER, SEPARATOR_SQUAREBRACKETOPEN) && none(sequence(SEPARATOR_SQUAREBRACKETOPEN))))
else if (MATCHES((optional(KEYWORD_BE), optional(KEYWORD_LE)) && variant(IDENTIFIER, VALUETYPE_ANY) && sequence(IDENTIFIER, SEPARATOR_SQUAREBRACKETOPEN) && sequence<Not>(SEPARATOR_SQUAREBRACKETOPEN)))
member = parseMemberArrayVariable();
else if (MATCHES((optional(KEYWORD_BE), optional(KEYWORD_LE)) && variant(IDENTIFIER, VALUETYPE_ANY) && sequence(IDENTIFIER)))
member = parseMemberVariable();

View File

@ -0,0 +1,120 @@
#include <hex/lang/pattern_language.hpp>
#include <hex/providers/provider.hpp>
#include <hex/lang/preprocessor.hpp>
#include <hex/lang/lexer.hpp>
#include <hex/lang/parser.hpp>
#include <hex/lang/validator.hpp>
#include <hex/lang/evaluator.hpp>
#include <hex/lang/pattern_data.hpp>
#include <unistd.h>
namespace hex::lang {
PatternLanguage::PatternLanguage(prv::Provider *provider) : m_provider(provider) {
this->m_preprocessor = new Preprocessor();
this->m_lexer = new Lexer();
this->m_parser = new Parser();
this->m_validator = new Validator();
this->m_evaluator = new Evaluator(provider);
}
PatternLanguage::~PatternLanguage() {
delete this->m_preprocessor;
delete this->m_lexer;
delete this->m_parser;
delete this->m_validator;
delete this->m_evaluator;
}
std::optional<std::vector<PatternData*>> PatternLanguage::executeString(std::string_view string) {
this->m_currError.reset();
this->m_evaluator->getConsole().clear();
hex::lang::Preprocessor preprocessor;
std::endian defaultEndian;
preprocessor.addPragmaHandler("endian", [&defaultEndian](std::string value) {
if (value == "big") {
defaultEndian = std::endian::big;
return true;
} else if (value == "little") {
defaultEndian = std::endian::little;
return true;
} else if (value == "native") {
defaultEndian = std::endian::native;
return true;
} else
return false;
});
preprocessor.addDefaultPragmaHandlers();
auto preprocessedCode = preprocessor.preprocess(string.data());
if (!preprocessedCode.has_value()) {
this->m_currError = preprocessor.getError();
return { };
}
hex::lang::Lexer lexer;
auto tokens = lexer.lex(preprocessedCode.value());
if (!tokens.has_value()) {
this->m_currError = lexer.getError();
return { };
}
hex::lang::Parser parser;
auto ast = parser.parse(tokens.value());
if (!ast.has_value()) {
this->m_currError = parser.getError();
return { };
}
SCOPE_EXIT( for(auto &node : ast.value()) delete node; );
hex::lang::Validator validator;
auto validatorResult = validator.validate(ast.value());
if (!validatorResult) {
this->m_currError = validator.getError();
return { };
}
auto provider = SharedData::currentProvider;
hex::lang::Evaluator evaluator(provider, defaultEndian);
auto patternData = evaluator.evaluate(ast.value());
if (!patternData.has_value())
return { };
return patternData.value();
}
std::optional<std::vector<PatternData*>> PatternLanguage::executeFile(std::string_view path) {
FILE *file = fopen(path.data(), "r");
if (file == nullptr)
return { };
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
rewind(file);
std::string code(size + 1, 0x00);
fread(code.data(), size, 1, file);
fclose(file);
return this->executeString(code);
}
std::vector<std::pair<LogConsole::Level, std::string>> PatternLanguage::getConsoleLog() {
return this->m_evaluator->getConsole().getLog();
}
std::optional<std::pair<u32, std::string>> PatternLanguage::getError() {
return this->m_currError;
}
}

View File

@ -18,8 +18,7 @@ namespace hex {
}
Plugin::~Plugin() {
if (this->m_handle != nullptr)
dlclose(this->m_handle);
}
void Plugin::initializePlugin() const {

View File

@ -2,35 +2,12 @@
#include <GLFW/glfw3.h>
#include "helpers/math_evaluator.hpp"
namespace hex {
ViewCommandPalette::ViewCommandPalette() : View("Command Palette") {
this->getWindowOpenState() = true;
this->m_commandBuffer.resize(1024, 0x00);
ContentRegistry::CommandPaletteCommands::add(
ContentRegistry::CommandPaletteCommands::Type::SymbolCommand,
"#", "Calculator",
[](auto input) {
MathEvaluator evaluator;
evaluator.registerStandardVariables();
evaluator.registerStandardFunctions();
std::optional<long double> result;
try {
result = evaluator.evaluate(input);
} catch (std::runtime_error &e) {}
if (result.has_value())
return hex::format("#%s = %Lf", input.data(), result.value());
else
return hex::format("#%s = ???", input.data());
});
this->m_lastResults = this->getCommandResults("");
}

View File

@ -1,7 +1,6 @@
#include "views/view_data_inspector.hpp"
#include <hex/providers/provider.hpp>
#include <hex/helpers/utils.hpp>
#include <cstring>
@ -32,170 +31,6 @@ namespace hex {
this->m_shouldInvalidate = true;
});
ContentRegistry::DataInspector::add("Binary (8 bit)", sizeof(u8), [](auto buffer, auto endian, auto style) {
std::string binary;
for (u8 i = 0; i < 8; i++)
binary += ((buffer[0] << i) & 0x80) == 0 ? '0' : '1';
return [binary] { ImGui::TextUnformatted(binary.c_str()); };
});
ContentRegistry::DataInspector::add("uint8_t", sizeof(u8), [](auto buffer, auto endian, auto style) {
auto format = (style == NumberDisplayStyle::Decimal) ? "%u" : ((style == NumberDisplayStyle::Hexadecimal) ? "0x%X" : "0o%o");
auto value = hex::format(format, *reinterpret_cast<u8*>(buffer.data()));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("int8_t", sizeof(s8), [](auto buffer, auto endian, auto style) {
auto format = (style == NumberDisplayStyle::Decimal) ? "%d" : ((style == NumberDisplayStyle::Hexadecimal) ? "0x%X" : "0o%o");
auto value = hex::format(format, *reinterpret_cast<s8*>(buffer.data()));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("uint16_t", sizeof(u16), [](auto buffer, auto endian, auto style) {
auto format = (style == NumberDisplayStyle::Decimal) ? "%u" : ((style == NumberDisplayStyle::Hexadecimal) ? "0x%X" : "0o%o");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u16*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("int16_t", sizeof(s16), [](auto buffer, auto endian, auto style) {
auto format = (style == NumberDisplayStyle::Decimal) ? "%d" : ((style == NumberDisplayStyle::Hexadecimal) ? "0x%X" : "0o%o");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<s16*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("uint32_t", sizeof(u32), [](auto buffer, auto endian, auto style) {
auto format = (style == NumberDisplayStyle::Decimal) ? "%u" : ((style == NumberDisplayStyle::Hexadecimal) ? "0x%X" : "0o%o");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u32*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("int32_t", sizeof(s32), [](auto buffer, auto endian, auto style) {
auto format = (style == NumberDisplayStyle::Decimal) ? "%d" : ((style == NumberDisplayStyle::Hexadecimal) ? "0x%X" : "0o%o");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<s32*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("uint64_t", sizeof(u64), [](auto buffer, auto endian, auto style) {
auto format = (style == NumberDisplayStyle::Decimal) ? "%lu" : ((style == NumberDisplayStyle::Hexadecimal) ? "0x%lX" : "0o%lo");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u64*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("int64_t", sizeof(s64), [](auto buffer, auto endian, auto style) {
auto format = (style == NumberDisplayStyle::Decimal) ? "%ld" : ((style == NumberDisplayStyle::Hexadecimal) ? "0x%lX" : "0o%lo");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<s64*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("float (32 bit)", sizeof(float), [](auto buffer, auto endian, auto style) {
auto value = hex::format("%e", hex::changeEndianess(*reinterpret_cast<float*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("double (64 bit)", sizeof(double), [](auto buffer, auto endian, auto style) {
auto value = hex::format("%e", hex::changeEndianess(*reinterpret_cast<double*>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("ASCII Character", sizeof(char8_t), [](auto buffer, auto endian, auto style) {
auto value = hex::format("'%s'", makePrintable(*reinterpret_cast<char8_t*>(buffer.data())).c_str());
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("Wide Character", sizeof(char16_t), [](auto buffer, auto endian, auto style) {
auto c = *reinterpret_cast<char16_t*>(buffer.data());
auto value = hex::format("'%lc'", c == 0 ? '\x01' : hex::changeEndianess(c, endian));
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("UTF-8 code point", sizeof(char8_t) * 4, [](auto buffer, auto endian, auto style) {
char utf8Buffer[5] = { 0 };
char codepointString[5] = { 0 };
u32 codepoint = 0;
std::memcpy(utf8Buffer, reinterpret_cast<char8_t*>(buffer.data()), 4);
u8 codepointSize = ImTextCharFromUtf8(&codepoint, utf8Buffer, utf8Buffer + 4);
std::memcpy(codepointString, &codepoint, std::min(codepointSize, u8(4)));
auto value = hex::format("'%s' (U+%04lx)", codepoint == 0xFFFD ? "Invalid" :
codepoint < 0xFF ? makePrintable(codepoint).c_str() :
codepointString,
codepoint);
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
#if defined(OS_WINDOWS) && defined(ARCH_64_BIT)
ContentRegistry::DataInspector::add("__time32_t", sizeof(__time32_t), [](auto buffer, auto endian, auto style) {
auto endianAdjustedTime = hex::changeEndianess(*reinterpret_cast<__time32_t*>(buffer.data()), endian);
std::tm * ptm = _localtime32(&endianAdjustedTime);
char timeBuffer[32];
std::string value;
if (ptm != nullptr && std::strftime(timeBuffer, 32, "%a, %d.%m.%Y %H:%M:%S", ptm))
value = timeBuffer;
else
value = "Invalid";
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("__time64_t", sizeof(__time64_t), [](auto buffer, auto endian, auto style) {
auto endianAdjustedTime = hex::changeEndianess(*reinterpret_cast<__time64_t*>(buffer.data()), endian);
std::tm * ptm = _localtime64(&endianAdjustedTime);
char timeBuffer[64];
std::string value;
if (ptm != nullptr && std::strftime(timeBuffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm))
value = timeBuffer;
else
value = "Invalid";
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
#else
ContentRegistry::DataInspector::add("time_t", sizeof(time_t), [](auto buffer, auto endian, auto style) {
auto endianAdjustedTime = hex::changeEndianess(*reinterpret_cast<time_t*>(buffer.data()), endian);
std::tm * ptm = localtime(&endianAdjustedTime);
char timeBuffer[64];
std::string value;
if (ptm != nullptr && std::strftime(timeBuffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm))
value = timeBuffer;
else
value = "Invalid";
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
#endif
ContentRegistry::DataInspector::add("GUID", sizeof(GUID), [](auto buffer, auto endian, auto style) {
GUID guid;
std::memcpy(&guid, buffer.data(), sizeof(GUID));
auto value = hex::format("%s{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
(hex::changeEndianess(guid.data3, endian) >> 12) <= 5 && ((guid.data4[0] >> 4) >= 8 || (guid.data4[0] >> 4) == 0) ? "" : "Invalid ",
hex::changeEndianess(guid.data1, endian),
hex::changeEndianess(guid.data2, endian),
hex::changeEndianess(guid.data3, endian),
guid.data4[0], guid.data4[1], guid.data4[2], guid.data4[3],
guid.data4[4], guid.data4[5], guid.data4[6], guid.data4[7]);
return [value] { ImGui::TextUnformatted(value.c_str()); };
});
ContentRegistry::DataInspector::add("RGBA Color", sizeof(u32), [](auto buffer, auto endian, auto style) {
ImColor value(hex::changeEndianess(*reinterpret_cast<u32*>(buffer.data()), endian));
return [value] {
ImGui::ColorButton("##inspectorColor", value,
ImGuiColorEditFlags_None,
ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
};
});
}
ViewDataInspector::~ViewDataInspector() {

View File

@ -235,7 +235,7 @@ namespace hex {
}
ImGui::EndChild();
ImGui::SetCursorPosX((ImGui::GetContentRegionAvailWidth() - 300) / 2);
ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x - 300) / 2);
if (ImGui::Button("Disassemble", ImVec2(300, 20)))
this->m_shouldInvalidate = true;
ImGui::NewLine();

View File

@ -1,13 +1,8 @@
#include "views/view_pattern.hpp"
#include <hex/lang/preprocessor.hpp>
#include <hex/lang/parser.hpp>
#include <hex/lang/lexer.hpp>
#include <hex/lang/validator.hpp>
#include <hex/lang/evaluator.hpp>
#include "helpers/project_file_handler.hpp"
#include <hex/helpers/utils.hpp>
#include <hex/lang/preprocessor.hpp>
#include <magic.h>
@ -47,8 +42,12 @@ namespace hex {
outEnd = inEnd;
paletteIndex = TextEditor::PaletteIndex::Default;
}
else if (TokenizeCStyleIdentifier(inBegin, inEnd, outBegin, outEnd))
paletteIndex = TextEditor::PaletteIndex::Identifier;
else if (TokenizeCStyleIdentifier(inBegin, inEnd, outBegin, outEnd)) {
if (SharedData::patternLanguageFunctions.contains(std::string(outBegin, outEnd - outBegin)))
paletteIndex = TextEditor::PaletteIndex::LineNumber;
else
paletteIndex = TextEditor::PaletteIndex::Identifier;
}
else if (TokenizeCStyleNumber(inBegin, inEnd, outBegin, outEnd))
paletteIndex = TextEditor::PaletteIndex::Number;
else if (TokenizeCStyleCharacterLiteral(inBegin, inEnd, outBegin, outEnd))
@ -76,6 +75,7 @@ namespace hex {
ViewPattern::ViewPattern(std::vector<lang::PatternData*> &patternData) : View("Pattern"), m_patternData(patternData) {
this->m_patternLanguageRuntime = new lang::PatternLanguage(SharedData::currentProvider);
this->m_textEditor.SetLanguageDefinition(PatternLanguage());
this->m_textEditor.SetShowWhitespaces(false);
@ -335,62 +335,15 @@ namespace hex {
this->m_console.clear();
this->postEvent(Events::PatternChanged);
hex::lang::Preprocessor preprocessor;
std::endian defaultDataEndianess = std::endian::native;
auto result = this->m_patternLanguageRuntime->executeString(buffer);
preprocessor.addPragmaHandler("endian", [&defaultDataEndianess](std::string value) {
if (value == "big") {
defaultDataEndianess = std::endian::big;
return true;
} else if (value == "little") {
defaultDataEndianess = std::endian::little;
return true;
} else if (value == "native") {
defaultDataEndianess = std::endian::native;
return true;
} else
return false;
});
preprocessor.addDefaultPragmaHandlers();
auto preprocessedCode = preprocessor.preprocess(buffer);
if (!preprocessedCode.has_value()) {
this->m_textEditor.SetErrorMarkers({ preprocessor.getError() });
return;
if (!result.has_value()) {
this->m_textEditor.SetErrorMarkers({ this->m_patternLanguageRuntime->getError().value() });
}
hex::lang::Lexer lexer;
auto tokens = lexer.lex(preprocessedCode.value());
if (!tokens.has_value()) {
this->m_textEditor.SetErrorMarkers({ lexer.getError() });
return;
}
this->m_console = this->m_patternLanguageRuntime->getConsoleLog();
hex::lang::Parser parser;
auto ast = parser.parse(tokens.value());
if (!ast.has_value()) {
this->m_textEditor.SetErrorMarkers({ parser.getError() });
return;
}
SCOPE_EXIT( for(auto &node : ast.value()) delete node; );
hex::lang::Validator validator;
auto validatorResult = validator.validate(ast.value());
if (!validatorResult) {
this->m_textEditor.SetErrorMarkers({ validator.getError() });
return;
}
auto provider = SharedData::currentProvider;
hex::lang::Evaluator evaluator(provider, defaultDataEndianess);
auto patternData = evaluator.evaluate(ast.value());
this->m_console = evaluator.getConsole().getLog();
if (!patternData.has_value())
return;
this->m_patternData = patternData.value();
this->m_patternData = result.value();
View::postEvent(Events::PatternChanged);
}

View File

@ -1,302 +1,12 @@
#include "views/view_tools.hpp"
#include <cstring>
#include <regex>
#include <optional>
#include <hex/providers/provider.hpp>
#include <hex/helpers/utils.hpp>
#include "helpers/plugin_handler.hpp"
#include <llvm/Demangle/Demangle.h>
namespace hex {
ViewTools::ViewTools() : View("Tools") {
this->m_mangledBuffer = new char[0xF'FFFF];
std::memset(this->m_mangledBuffer, 0x00, 0xF'FFFF);
ViewTools::ViewTools() : View("Tools") { }
this->m_regexInput = new char[0xF'FFFF];
this->m_regexPattern = new char[0xF'FFFF];
this->m_replacePattern = new char[0xF'FFFF];
std::memset(this->m_regexInput, 0x00, 0xF'FFFF);
std::memset(this->m_regexPattern, 0x00, 0xF'FFFF);
std::memset(this->m_replacePattern, 0x00, 0xF'FFFF);
this->m_mathInput = new char[0xFFFF];
std::memset(this->m_mathInput, 0x00, 0xFFFF);
this->m_mathEvaluator.registerStandardVariables();
this->m_mathEvaluator.registerStandardFunctions();
this->m_mathEvaluator.setFunction("clear", [this](auto args) -> std::optional<long double> {
this->m_mathHistory.clear();
this->m_lastMathError.clear();
this->m_mathEvaluator.getVariables().clear();
this->m_mathEvaluator.registerStandardVariables();
std::memset(this->m_mathInput, 0x00, 0xFFFF);
return { };
}, 0, 0);
this->m_mathEvaluator.setFunction("read", [this](auto args) -> std::optional<long double> {
u8 value = 0;
auto provider = SharedData::currentProvider;
if (provider == nullptr || !provider->isReadable() || args[0] >= provider->getActualSize())
return { };
provider->read(args[0], &value, sizeof(u8));
return value;
}, 1, 1);
this->m_mathEvaluator.setFunction("write", [this](auto args) -> std::optional<long double> {
auto provider = SharedData::currentProvider;
if (provider == nullptr || !provider->isWritable() || args[0] >= provider->getActualSize())
return { };
if (args[1] > 0xFF)
return { };
u8 value = args[1];
provider->write(args[0], &value, sizeof(u8));
return { };
}, 2, 2);
ContentRegistry::Tools::add("Itanium/MSVC demangler", [this]{ this->drawDemangler(); });
ContentRegistry::Tools::add("ASCII table", [this]{ this->drawASCIITable(); });
ContentRegistry::Tools::add("Regex replacer", [this]{ this->drawRegexReplacer(); });
ContentRegistry::Tools::add("Color picker", [this]{ this->drawColorPicker(); });
ContentRegistry::Tools::add("Calculator", [this]{ this->drawMathEvaluator(); });
}
ViewTools::~ViewTools() {
delete[] this->m_mangledBuffer;
delete[] this->m_regexInput;
delete[] this->m_regexPattern;
delete[] this->m_replacePattern;
delete[] this->m_mathInput;
}
void ViewTools::drawDemangler() {
if (ImGui::InputText("Mangled name", this->m_mangledBuffer, 0xF'FFFF)) {
this->m_demangledName = llvm::demangle(this->m_mangledBuffer);
}
ImGui::InputText("Demangled name", this->m_demangledName.data(), this->m_demangledName.size(), ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
}
void ViewTools::drawASCIITable() {
ImGui::BeginTable("##asciitable", 4);
ImGui::TableSetupColumn("");
ImGui::TableSetupColumn("");
ImGui::TableSetupColumn("");
ImGui::TableSetupColumn("");
ImGui::TableNextColumn();
for (u8 tablePart = 0; tablePart < 4; tablePart++) {
ImGui::BeginTable("##asciitablepart", this->m_asciiTableShowOctal ? 4 : 3, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg);
ImGui::TableSetupColumn("dec");
if (this->m_asciiTableShowOctal)
ImGui::TableSetupColumn("oct");
ImGui::TableSetupColumn("hex");
ImGui::TableSetupColumn("char");
ImGui::TableHeadersRow();
u32 rowCount = 0;
for (u8 i = 0; i < 0x80 / 4; i++) {
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text("%02d", i + 32 * tablePart);
if (this->m_asciiTableShowOctal) {
ImGui::TableNextColumn();
ImGui::Text("0o%02o", i + 32 * tablePart);
}
ImGui::TableNextColumn();
ImGui::Text("0x%02x", i + 32 * tablePart);
ImGui::TableNextColumn();
ImGui::Text("%s", makePrintable(i + 32 * tablePart).c_str());
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
rowCount++;
}
ImGui::EndTable();
ImGui::TableNextColumn();
}
ImGui::EndTable();
ImGui::Checkbox("Show octal", &this->m_asciiTableShowOctal);
ImGui::NewLine();
}
void ViewTools::drawRegexReplacer() {
bool shouldInvalidate;
shouldInvalidate = ImGui::InputText("Regex pattern", this->m_regexPattern, 0xF'FFFF);
shouldInvalidate = ImGui::InputText("Replace pattern", this->m_replacePattern, 0xF'FFFF) || shouldInvalidate;
shouldInvalidate = ImGui::InputTextMultiline("Input", this->m_regexInput, 0xF'FFFF) || shouldInvalidate;
if (shouldInvalidate) {
try {
this->m_regexOutput = std::regex_replace(this->m_regexInput, std::regex(this->m_regexPattern), this->m_replacePattern);
} catch (std::regex_error&) {}
}
ImGui::InputTextMultiline("Output", this->m_regexOutput.data(), this->m_regexOutput.size(), ImVec2(0, 0), ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
}
void ViewTools::drawColorPicker() {
ImGui::SetNextItemWidth(300.0F);
ImGui::ColorPicker4("Color Picker", this->m_pickedColor.data(),
ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHex);
ImGui::NewLine();
}
void ViewTools::drawMathEvaluator() {
if (ImGui::InputText("Input", this->m_mathInput, 0xFFFF, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
ImGui::SetKeyboardFocusHere();
std::optional<long double> result;
try {
result = this->m_mathEvaluator.evaluate(this->m_mathInput);
} catch (std::invalid_argument &e) {
this->m_lastMathError = e.what();
}
if (result.has_value()) {
this->m_mathHistory.push_back(result.value());
std::memset(this->m_mathInput, 0x00, 0xFFFF);
this->m_lastMathError.clear();
}
}
if (!this->m_lastMathError.empty())
ImGui::TextColored(ImColor(0xA00040FF), "Last Error: %s", this->m_lastMathError.c_str());
else
ImGui::NewLine();
enum class MathDisplayType { Standard, Scientific, Engineering, Programmer } mathDisplayType;
if (ImGui::BeginTabBar("##mathFormatTabBar")) {
if (ImGui::BeginTabItem("Standard")) {
mathDisplayType = MathDisplayType::Standard;
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Scientific")) {
mathDisplayType = MathDisplayType::Scientific;
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Engineering")) {
mathDisplayType = MathDisplayType::Engineering;
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Programmer")) {
mathDisplayType = MathDisplayType::Programmer;
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
if (ImGui::BeginTable("##mathWrapper", 2)) {
ImGui::TableSetupColumn("##results");
ImGui::TableSetupColumn("##variables", ImGuiTableColumnFlags_WidthStretch, 0.7);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::BeginTable("##mathHistory", 1, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("History");
ImGuiListClipper clipper;
clipper.Begin(this->m_mathHistory.size());
ImGui::TableHeadersRow();
while (clipper.Step()) {
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
if (i == 0)
ImGui::PushStyleColor(ImGuiCol_Text, ImU32(ImColor(0xA5, 0x45, 0x45)));
ImGui::TableNextRow();
ImGui::TableNextColumn();
switch (mathDisplayType) {
case MathDisplayType::Standard:
ImGui::Text("%.3Lf", this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]);
break;
case MathDisplayType::Scientific:
ImGui::Text("%.6Le", this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]);
break;
case MathDisplayType::Engineering:
ImGui::Text("%s", hex::toEngineeringString(this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]).c_str());
break;
case MathDisplayType::Programmer:
ImGui::Text("0x%llX (%llu)",
u64(this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]),
u64(this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]));
break;
}
if (i == 0)
ImGui::PopStyleColor();
}
}
clipper.End();
ImGui::EndTable();
}
ImGui::TableNextColumn();
if (ImGui::BeginTable("##mathVariables", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Value");
ImGui::TableHeadersRow();
for (const auto &[name, value] : this->m_mathEvaluator.getVariables()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted(name.c_str());
ImGui::TableNextColumn();
switch (mathDisplayType) {
case MathDisplayType::Standard:
ImGui::Text("%.3Lf", value);
break;
case MathDisplayType::Scientific:
ImGui::Text("%.6Le", value);
break;
case MathDisplayType::Engineering:
ImGui::Text("%s", hex::toEngineeringString(value).c_str());
break;
case MathDisplayType::Programmer:
ImGui::Text("0x%llX (%llu)", u64(value), u64(value));
break;
}
}
ImGui::EndTable();
}
ImGui::EndTable();
}
}
ViewTools::~ViewTools() { }
void ViewTools::drawContent() {
if (ImGui::Begin("Tools", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {