From 104000fbc449db404e778624aab53d840b7812bf Mon Sep 17 00:00:00 2001 From: WerWolv Date: Fri, 22 Jan 2021 18:01:39 +0100 Subject: [PATCH] Huge refactoring of builtin features into an external plugin --- CMakeLists.txt | 8 +- include/views/view_data_inspector.hpp | 8 - include/views/view_pattern.hpp | 3 +- include/views/view_tools.hpp | 24 -- plugins/builtin/CMakeLists.txt | 25 ++ .../builtin/include}/math_evaluator.hpp | 0 .../content/command_palette_commands.cpp | 31 ++ .../builtin/source/content/data_inspector.cpp | 188 +++++++++++ .../source/content/lang_builtin_functions.cpp | 200 ++++++++++++ .../source/content/settings_entries.cpp | 9 + .../builtin/source/content/tools_entries.cpp | 298 ++++++++++++++++++ .../builtin/source}/math_evaluator.cpp | 2 +- plugins/builtin/source/plugin_builtin.cpp | 25 ++ plugins/libimhex/CMakeLists.txt | 1 + .../include/hex/api/content_registry.hpp | 6 +- .../libimhex/include/hex/lang/evaluator.hpp | 74 +---- .../libimhex/include/hex/lang/log_console.hpp | 47 +++ plugins/libimhex/include/hex/lang/parser.hpp | 74 +++-- .../include/hex/lang/pattern_language.hpp | 48 +++ .../libimhex/source/api/content_registry.cpp | 2 +- .../source/lang/builtin_functions.cpp | 178 +---------- plugins/libimhex/source/lang/evaluator.cpp | 4 +- plugins/libimhex/source/lang/parser.cpp | 2 +- .../libimhex/source/lang/pattern_language.cpp | 120 +++++++ source/helpers/plugin_handler.cpp | 3 +- source/views/view_command_palette.cpp | 23 -- source/views/view_data_inspector.cpp | 165 ---------- source/views/view_disassembler.cpp | 2 +- source/views/view_pattern.cpp | 73 +---- source/views/view_tools.cpp | 294 +---------------- 30 files changed, 1095 insertions(+), 842 deletions(-) create mode 100644 plugins/builtin/CMakeLists.txt rename {include/helpers => plugins/builtin/include}/math_evaluator.hpp (100%) create mode 100644 plugins/builtin/source/content/command_palette_commands.cpp create mode 100644 plugins/builtin/source/content/data_inspector.cpp create mode 100644 plugins/builtin/source/content/lang_builtin_functions.cpp create mode 100644 plugins/builtin/source/content/settings_entries.cpp create mode 100644 plugins/builtin/source/content/tools_entries.cpp rename {source/helpers => plugins/builtin/source}/math_evaluator.cpp (99%) create mode 100644 plugins/builtin/source/plugin_builtin.cpp create mode 100644 plugins/libimhex/include/hex/lang/log_console.hpp create mode 100644 plugins/libimhex/include/hex/lang/pattern_language.hpp create mode 100644 plugins/libimhex/source/lang/pattern_language.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bd6727449..409a2ba2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 $ $) +file(MAKE_DIRECTORY "plugins") +add_custom_command(TARGET imhex POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + $/plugins) + if (WIN32) # Install binaries directly in the prefix, usually C:\Program Files\ImHex. set(CMAKE_INSTALL_BINDIR ".") diff --git a/include/views/view_data_inspector.hpp b/include/views/view_data_inspector.hpp index d478dcc1a..e5d14c721 100644 --- a/include/views/view_data_inspector.hpp +++ b/include/views/view_data_inspector.hpp @@ -6,20 +6,12 @@ #include #include -#include #include namespace hex { namespace prv { class Provider; } - struct GUID { - u32 data1; - u16 data2; - u16 data3; - u8 data4[8]; - }; - class ViewDataInspector : public View { public: explicit ViewDataInspector(); diff --git a/include/views/view_pattern.hpp b/include/views/view_pattern.hpp index a8f23a925..f7fee5b26 100644 --- a/include/views/view_pattern.hpp +++ b/include/views/view_pattern.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include #include @@ -24,6 +24,7 @@ namespace hex { void drawContent() override; private: + lang::PatternLanguage *m_patternLanguageRuntime; std::vector &m_patternData; std::filesystem::path m_possiblePatternFile; diff --git a/include/views/view_tools.hpp b/include/views/view_tools.hpp index b94d32a9d..c202c3ac9 100644 --- a/include/views/view_tools.hpp +++ b/include/views/view_tools.hpp @@ -4,7 +4,6 @@ #include #include -#include "helpers/math_evaluator.hpp" #include #include @@ -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 m_pickedColor; - - MathEvaluator m_mathEvaluator; - std::vector m_mathHistory; - std::string m_lastMathError; - char *m_mathInput = nullptr; - - void drawDemangler(); - void drawASCIITable(); - void drawRegexReplacer(); - void drawColorPicker(); - void drawMathEvaluator(); }; } \ No newline at end of file diff --git a/plugins/builtin/CMakeLists.txt b/plugins/builtin/CMakeLists.txt new file mode 100644 index 000000000..17597e9f1 --- /dev/null +++ b/plugins/builtin/CMakeLists.txt @@ -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) diff --git a/include/helpers/math_evaluator.hpp b/plugins/builtin/include/math_evaluator.hpp similarity index 100% rename from include/helpers/math_evaluator.hpp rename to plugins/builtin/include/math_evaluator.hpp diff --git a/plugins/builtin/source/content/command_palette_commands.cpp b/plugins/builtin/source/content/command_palette_commands.cpp new file mode 100644 index 000000000..ba7ceb4b5 --- /dev/null +++ b/plugins/builtin/source/content/command_palette_commands.cpp @@ -0,0 +1,31 @@ +#include + +#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 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()); + }); + + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/data_inspector.cpp b/plugins/builtin/source/content/data_inspector.cpp new file mode 100644 index 000000000..410fc5fbf --- /dev/null +++ b/plugins/builtin/source/content/data_inspector.cpp @@ -0,0 +1,188 @@ +#include + +#include + +#include +#include + +#include + +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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(buffer.data()), endian)); + + return [value] { + ImGui::ColorButton("##inspectorColor", value, + ImGuiColorEditFlags_None, + ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); + }; + }); + + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/lang_builtin_functions.cpp b/plugins/builtin/source/content/lang_builtin_functions.cpp new file mode 100644 index 000000000..e9272fb08 --- /dev/null +++ b/plugins/builtin/source/content/lang_builtin_functions.cpp @@ -0,0 +1,200 @@ +#include + +#include +#include +#include + +#include + +#include + +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(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 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 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(value) }); + case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned16Bit, *reinterpret_cast(value) }); + case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned32Bit, *reinterpret_cast(value) }); + case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, *reinterpret_cast(value) }); + case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned128Bit, *reinterpret_cast(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(value) }); + case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed16Bit, *reinterpret_cast(value) }); + case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed32Bit, *reinterpret_cast(value) }); + case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed64Bit, *reinterpret_cast(value) }); + case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed128Bit, *reinterpret_cast(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(param); integerLiteral != nullptr) { + switch (integerLiteral->getType()) { + case Token::ValueType::Character: message += std::get(integerLiteral->getValue()); break; + case Token::ValueType::Unsigned8Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Signed8Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Unsigned16Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Signed16Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Unsigned32Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Signed32Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Unsigned64Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Signed64Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Unsigned128Bit: message += hex::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Signed128Bit: message += hex::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Float: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Double: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Boolean: message += std::get(integerLiteral->getValue()) ? "true" : "false"; break; + case Token::ValueType::CustomType: message += "< Custom Type >"; break; + } + } + else if (auto stringLiteral = dynamic_cast(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 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 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 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) }); + }); + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/settings_entries.cpp b/plugins/builtin/source/content/settings_entries.cpp new file mode 100644 index 000000000..8c681030c --- /dev/null +++ b/plugins/builtin/source/content/settings_entries.cpp @@ -0,0 +1,9 @@ +#include + +namespace hex::plugin::builtin { + + void registerSettings() { + + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/tools_entries.cpp b/plugins/builtin/source/content/tools_entries.cpp new file mode 100644 index 000000000..595a4ddc9 --- /dev/null +++ b/plugins/builtin/source/content/tools_entries.cpp @@ -0,0 +1,298 @@ +#include + +#include + +#include +#include "math_evaluator.hpp" + +namespace hex::plugin::builtin { + + namespace { + + void drawDemangler() { + static std::vector 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 regexInput(0xF'FFFF, 0x00);; + static std::vector regexPattern(0xF'FFFF, 0x00);; + static std::vector 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 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 mathHistory; + static std::string lastMathError; + static std::vector mathInput(0xF'FFFF, 0x00); + + static MathEvaluator mathEvaluator = [&]{ + MathEvaluator evaluator; + + evaluator.registerStandardVariables(); + evaluator.registerStandardFunctions(); + + evaluator.setFunction("clear", [&](auto args) -> std::optional { + 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 { + 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 { + 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 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); + } + +} \ No newline at end of file diff --git a/source/helpers/math_evaluator.cpp b/plugins/builtin/source/math_evaluator.cpp similarity index 99% rename from source/helpers/math_evaluator.cpp rename to plugins/builtin/source/math_evaluator.cpp index df7776033..f53c11b4a 100644 --- a/source/helpers/math_evaluator.cpp +++ b/plugins/builtin/source/math_evaluator.cpp @@ -1,4 +1,4 @@ -#include "helpers/math_evaluator.hpp" +#include "math_evaluator.hpp" #include #include diff --git a/plugins/builtin/source/plugin_builtin.cpp b/plugins/builtin/source/plugin_builtin.cpp new file mode 100644 index 000000000..ec83e9d54 --- /dev/null +++ b/plugins/builtin/source/plugin_builtin.cpp @@ -0,0 +1,25 @@ +#include + +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(); + +} + + diff --git a/plugins/libimhex/CMakeLists.txt b/plugins/libimhex/CMakeLists.txt index dd50103da..100906158 100644 --- a/plugins/libimhex/CMakeLists.txt +++ b/plugins/libimhex/CMakeLists.txt @@ -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 diff --git a/plugins/libimhex/include/hex/api/content_registry.hpp b/plugins/libimhex/include/hex/api/content_registry.hpp index ee48f8262..19edcd3c1 100644 --- a/plugins/libimhex/include/hex/api/content_registry.hpp +++ b/plugins/libimhex/include/hex/api/content_registry.hpp @@ -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)> func; + std::function)> func; }; - static void add(std::string_view name, u32 parameterCount, const std::function)> &func); + static void add(std::string_view name, u32 parameterCount, const std::function)> &func); static std::map& getEntries(); }; diff --git a/plugins/libimhex/include/hex/lang/evaluator.hpp b/plugins/libimhex/include/hex/lang/evaluator.hpp index f8f2cb486..50b85a2aa 100644 --- a/plugins/libimhex/include/hex/lang/evaluator.hpp +++ b/plugins/libimhex/include/hex/lang/evaluator.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -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> m_consoleLog; - }; - class Evaluator { public: - Evaluator(prv::Provider* &provider, std::endian defaultDataEndian); + Evaluator(prv::Provider* &provider, std::endian defaultDataEndian = std::endian::native); std::optional> evaluate(const std::vector& 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 &name); + + template + T* asType(ASTNode *param) { + if (auto evaluatedParam = dynamic_cast(param); evaluatedParam != nullptr) + return evaluatedParam; + else + this->getConsole().abortEvaluation("function got wrong type of parameter"); + } + private: std::map m_types; prv::Provider* &m_provider; @@ -63,9 +46,7 @@ namespace hex::lang { std::vector*> 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 &name); PatternData* evaluateAttributes(ASTNode *currNode, PatternData *currPattern); PatternData* evaluateBuiltinType(ASTNodeBuiltinType *node); void evaluateMember(ASTNode *node, std::vector &currMembers, bool increaseOffset); @@ -87,28 +67,6 @@ namespace hex::lang { PatternData* evaluateVariable(ASTNodeVariableDecl *node); PatternData* evaluateArray(ASTNodeArrayVariableDecl *node); PatternData* evaluatePointer(ASTNodePointerVariableDecl *node); - - template - T* asType(ASTNode *param) { - if (auto evaluatedParam = dynamic_cast(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 params) - - BUILTIN_FUNCTION(findSequence); - BUILTIN_FUNCTION(readUnsigned); - BUILTIN_FUNCTION(readSigned); - - BUILTIN_FUNCTION(assert); - BUILTIN_FUNCTION(warnAssert); - BUILTIN_FUNCTION(print); - - #undef BUILTIN_FUNCTION }; } \ No newline at end of file diff --git a/plugins/libimhex/include/hex/lang/log_console.hpp b/plugins/libimhex/include/hex/lang/log_console.hpp new file mode 100644 index 000000000..b5312a765 --- /dev/null +++ b/plugins/libimhex/include/hex/lang/log_console.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include +#include +#include +#include + +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> m_consoleLog; + }; + +} \ No newline at end of file diff --git a/plugins/libimhex/include/hex/lang/parser.hpp b/plugins/libimhex/include/hex/lang/parser.hpp index d1c2e4046..9c52a6dec 100644 --- a/plugins/libimhex/include/hex/lang/parser.hpp +++ b/plugins/libimhex/include/hex/lang/parser.hpp @@ -110,6 +110,10 @@ namespace hex::lang { /* Token consuming */ + enum class Setting{ }; + constexpr static auto Normal = static_cast(0); + constexpr static auto Not = static_cast(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 bool sequence() { - return true; + if constexpr (S == Normal) + return true; + else if constexpr (S == Not) + return false; + else + __builtin_unreachable(); } + template 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(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(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 bool oneOf() { - return false; + if constexpr (S == Normal) + return false; + else if constexpr (S == Not) + return true; + else + __builtin_unreachable(); } + template bool oneOf(Token::Type type, auto value, auto ... args) { - return sequence(type, value) || oneOf(args...); + if constexpr (S == Normal) + return sequence(type, value) || oneOf(args...); + else if constexpr (S == Not) + return sequence(type, value) && oneOf(args...); + else + __builtin_unreachable(); } bool variant(Token::Type type1, auto value1, Token::Type type2, auto value2) { diff --git a/plugins/libimhex/include/hex/lang/pattern_language.hpp b/plugins/libimhex/include/hex/lang/pattern_language.hpp new file mode 100644 index 000000000..8bb32b72c --- /dev/null +++ b/plugins/libimhex/include/hex/lang/pattern_language.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include +#include + +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> executeString(std::string_view string); + std::optional> executeFile(std::string_view path); + + std::vector> getConsoleLog(); + std::optional> 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> m_currError; + }; + +} \ No newline at end of file diff --git a/plugins/libimhex/source/api/content_registry.cpp b/plugins/libimhex/source/api/content_registry.cpp index a62d5bf4a..8da23e55f 100644 --- a/plugins/libimhex/source/api/content_registry.cpp +++ b/plugins/libimhex/source/api/content_registry.cpp @@ -76,7 +76,7 @@ namespace hex { /* Pattern Language Functions */ - void ContentRegistry::PatternLanguageFunctions::add(std::string_view name, u32 parameterCount, const std::function)> &func) { + void ContentRegistry::PatternLanguageFunctions::add(std::string_view name, u32 parameterCount, const std::function)> &func) { getEntries()[name.data()] = Function{ parameterCount, func }; } diff --git a/plugins/libimhex/source/lang/builtin_functions.cpp b/plugins/libimhex/source/lang/builtin_functions.cpp index c1fee4569..d55fe9bdb 100644 --- a/plugins/libimhex/source/lang/builtin_functions.cpp +++ b/plugins/libimhex/source/lang/builtin_functions.cpp @@ -1,182 +1,10 @@ -#include +#include + #include + 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(params[0])->getValue(); - std::vector 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(params[i])->getValue())); - } - - std::vector 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(params[0])->getValue(); - auto size = asType(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(value), 1, this->getCurrentEndian()) }); - case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned16Bit, hex::changeEndianess(*reinterpret_cast(value), 2, this->getCurrentEndian()) }); - case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned32Bit, hex::changeEndianess(*reinterpret_cast(value), 4, this->getCurrentEndian()) }); - case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, hex::changeEndianess(*reinterpret_cast(value), 8, this->getCurrentEndian()) }); - case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned128Bit, hex::changeEndianess(*reinterpret_cast(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(params[0])->getValue(); - auto size = asType(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(value), 1, this->getCurrentEndian()) }); - case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed16Bit, hex::changeEndianess(*reinterpret_cast(value), 2, this->getCurrentEndian()) }); - case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed32Bit, hex::changeEndianess(*reinterpret_cast(value), 4, this->getCurrentEndian()) }); - case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed64Bit, hex::changeEndianess(*reinterpret_cast(value), 8, this->getCurrentEndian()) }); - case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed128Bit, hex::changeEndianess(*reinterpret_cast(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(params[0])->getValue(); - auto message = asType(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(params[0])->getValue(); - auto message = asType(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(param); integerLiteral != nullptr) { - switch (integerLiteral->getType()) { - case Token::ValueType::Character: message += std::get(integerLiteral->getValue()); break; - case Token::ValueType::Unsigned8Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; - case Token::ValueType::Signed8Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; - case Token::ValueType::Unsigned16Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; - case Token::ValueType::Signed16Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; - case Token::ValueType::Unsigned32Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; - case Token::ValueType::Signed32Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; - case Token::ValueType::Unsigned64Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; - case Token::ValueType::Signed64Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; - case Token::ValueType::Unsigned128Bit: message += hex::to_string(std::get(integerLiteral->getValue())); break; - case Token::ValueType::Signed128Bit: message += hex::to_string(std::get(integerLiteral->getValue())); break; - case Token::ValueType::Float: message += std::to_string(std::get(integerLiteral->getValue())); break; - case Token::ValueType::Double: message += std::to_string(std::get(integerLiteral->getValue())); break; - case Token::ValueType::Boolean: message += std::get(integerLiteral->getValue()) ? "true" : "false"; break; - case Token::ValueType::CustomType: message += "< Custom Type >"; break; - } - } - else if (auto stringLiteral = dynamic_cast(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(params[0])->getString(); - - std::vector 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(params[0])->getString(); - - std::vector 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(params[0])->getString(); - - std::vector 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(params[0])->getValue(); - auto value = asType(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) }); - }); - } } \ No newline at end of file diff --git a/plugins/libimhex/source/lang/evaluator.cpp b/plugins/libimhex/source/lang/evaluator.cpp index e8900c257..d9de1f59e 100644 --- a/plugins/libimhex/source/lang/evaluator.cpp +++ b/plugins/libimhex/source/lang/evaluator.cpp @@ -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) \ diff --git a/plugins/libimhex/source/lang/parser.cpp b/plugins/libimhex/source/lang/parser.cpp index 67d8075c3..727d26798 100644 --- a/plugins/libimhex/source/lang/parser.cpp +++ b/plugins/libimhex/source/lang/parser.cpp @@ -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(SEPARATOR_SQUAREBRACKETOPEN))) member = parseMemberArrayVariable(); else if (MATCHES((optional(KEYWORD_BE), optional(KEYWORD_LE)) && variant(IDENTIFIER, VALUETYPE_ANY) && sequence(IDENTIFIER))) member = parseMemberVariable(); diff --git a/plugins/libimhex/source/lang/pattern_language.cpp b/plugins/libimhex/source/lang/pattern_language.cpp new file mode 100644 index 000000000..367d94a59 --- /dev/null +++ b/plugins/libimhex/source/lang/pattern_language.cpp @@ -0,0 +1,120 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +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> 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> 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> PatternLanguage::getConsoleLog() { + return this->m_evaluator->getConsole().getLog(); + } + + std::optional> PatternLanguage::getError() { + return this->m_currError; + } + +} \ No newline at end of file diff --git a/source/helpers/plugin_handler.cpp b/source/helpers/plugin_handler.cpp index fd088ec3d..ffcb74d96 100644 --- a/source/helpers/plugin_handler.cpp +++ b/source/helpers/plugin_handler.cpp @@ -18,8 +18,7 @@ namespace hex { } Plugin::~Plugin() { - if (this->m_handle != nullptr) - dlclose(this->m_handle); + } void Plugin::initializePlugin() const { diff --git a/source/views/view_command_palette.cpp b/source/views/view_command_palette.cpp index f95ad3fe0..1c546f051 100644 --- a/source/views/view_command_palette.cpp +++ b/source/views/view_command_palette.cpp @@ -2,35 +2,12 @@ #include -#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 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(""); } diff --git a/source/views/view_data_inspector.cpp b/source/views/view_data_inspector.cpp index 0552218bb..855032373 100644 --- a/source/views/view_data_inspector.cpp +++ b/source/views/view_data_inspector.cpp @@ -1,7 +1,6 @@ #include "views/view_data_inspector.hpp" #include -#include #include @@ -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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(buffer.data()), endian)); - - return [value] { - ImGui::ColorButton("##inspectorColor", value, - ImGuiColorEditFlags_None, - ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); - }; - }); } ViewDataInspector::~ViewDataInspector() { diff --git a/source/views/view_disassembler.cpp b/source/views/view_disassembler.cpp index 0262ccb25..45f89ac72 100644 --- a/source/views/view_disassembler.cpp +++ b/source/views/view_disassembler.cpp @@ -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(); diff --git a/source/views/view_pattern.cpp b/source/views/view_pattern.cpp index 32b09d65d..e3f412ca0 100644 --- a/source/views/view_pattern.cpp +++ b/source/views/view_pattern.cpp @@ -1,13 +1,8 @@ #include "views/view_pattern.hpp" -#include -#include -#include -#include -#include - #include "helpers/project_file_handler.hpp" #include +#include #include @@ -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 &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); } diff --git a/source/views/view_tools.cpp b/source/views/view_tools.cpp index 22b0262a4..a5d0e9e6b 100644 --- a/source/views/view_tools.cpp +++ b/source/views/view_tools.cpp @@ -1,302 +1,12 @@ #include "views/view_tools.hpp" -#include -#include -#include - #include -#include - -#include "helpers/plugin_handler.hpp" - -#include 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 { - 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 { - 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 { - 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 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)) {