From 09006588fce50b7c1fad0b24ff3fd9ec7c4126f8 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Wed, 3 Dec 2025 19:20:45 +0100 Subject: [PATCH] feat: Allow integer literals to be used in binary patterns --- lib/libimhex/CMakeLists.txt | 1 + .../include/hex/helpers/binary_pattern.hpp | 91 +-------- .../source/helpers/binary_pattern.cpp | 173 ++++++++++++++++++ .../source/content/views/view_find.cpp | 5 +- 4 files changed, 186 insertions(+), 84 deletions(-) create mode 100644 lib/libimhex/source/helpers/binary_pattern.cpp diff --git a/lib/libimhex/CMakeLists.txt b/lib/libimhex/CMakeLists.txt index a640cf966..1977cf830 100644 --- a/lib/libimhex/CMakeLists.txt +++ b/lib/libimhex/CMakeLists.txt @@ -42,6 +42,7 @@ set(LIBIMHEX_SOURCES source/helpers/keys.cpp source/helpers/udp_server.cpp source/helpers/scaling.cpp + source/helpers/binary_pattern.cpp source/test/tests.cpp diff --git a/lib/libimhex/include/hex/helpers/binary_pattern.hpp b/lib/libimhex/include/hex/helpers/binary_pattern.hpp index 15bd674d2..ea1a047df 100644 --- a/lib/libimhex/include/hex/helpers/binary_pattern.hpp +++ b/lib/libimhex/include/hex/helpers/binary_pattern.hpp @@ -10,92 +10,19 @@ namespace hex { class BinaryPattern { public: + BinaryPattern() = default; + explicit BinaryPattern(const std::string &pattern); + + [[nodiscard]] bool isValid() const; + [[nodiscard]] u64 getSize() const; + + [[nodiscard]] bool matches(const std::vector &bytes) const; + [[nodiscard]] bool matchesByte(u8 byte, u32 offset) const; + struct Pattern { u8 mask, value; }; - BinaryPattern() = default; - explicit BinaryPattern(const std::string &pattern) : m_patterns(parseBinaryPatternString(pattern)) { } - - [[nodiscard]] bool isValid() const { return !m_patterns.empty(); } - - [[nodiscard]] bool matches(const std::vector &bytes) const { - if (bytes.size() < m_patterns.size()) - return false; - - for (u32 i = 0; i < m_patterns.size(); i++) { - if (!this->matchesByte(bytes[i], i)) - return false; - } - - return true; - } - - [[nodiscard]] bool matchesByte(u8 byte, u32 offset) const { - const auto &pattern = m_patterns[offset]; - - return (byte & pattern.mask) == pattern.value; - } - - [[nodiscard]] u64 getSize() const { - return m_patterns.size(); - } - - private: - static std::vector parseBinaryPatternString(std::string string) { - std::vector result; - - if (string.length() < 2) - return { }; - - bool inString = false; - while (string.length() > 0) { - Pattern pattern = { 0, 0 }; - if (string.starts_with("\"")) { - inString = !inString; - string = string.substr(1); - continue; - } else if (inString) { - pattern = { 0xFF, u8(string.front()) }; - string = string.substr(1); - } else if (string.starts_with("??")) { - pattern = { 0x00, 0x00 }; - string = string.substr(2); - } else if ((std::isxdigit(string.front()) || string.front() == '?') && string.length() >= 2) { - const auto hex = string.substr(0, 2); - - for (const auto &c : hex) { - pattern.mask <<= 4; - pattern.value <<= 4; - - if (std::isxdigit(c)) { - pattern.mask |= 0x0F; - - if (auto hexValue = hex::hexCharToValue(c); hexValue.has_value()) - pattern.value |= hexValue.value(); - else - return { }; - } else if (c != '?') { - return { }; - } - } - - string = string.substr(2); - } else if (std::isspace(string.front())) { - string = string.substr(1); - continue; - } else { - return { }; - } - - result.push_back(pattern); - } - - if (inString) - return { }; - - return result; - } private: std::vector m_patterns; }; diff --git a/lib/libimhex/source/helpers/binary_pattern.cpp b/lib/libimhex/source/helpers/binary_pattern.cpp new file mode 100644 index 000000000..37c8aba09 --- /dev/null +++ b/lib/libimhex/source/helpers/binary_pattern.cpp @@ -0,0 +1,173 @@ +#include + +namespace hex { + + namespace { + + void skipWhitespace(std::string_view &string) { + while (string.length() > 0) { + if (!std::isspace(string.front())) + break; + string = string.substr(1); + } + } + + std::vector parseValueExpression(std::string_view &string) { + string = string.substr(1); + + // Parse bit size number + u64 bitSize = 0; + std::endian endian = std::endian::little; + while (!string.empty() && std::isdigit(string.front())) { + bitSize *= 10; + bitSize += string.front() - '0'; + + string = string.substr(1); + skipWhitespace(string); + } + + if (string.starts_with("le")) { + endian = std::endian::little; + string = string.substr(2); + } else if (string.starts_with("be")) { + endian = std::endian::big; + string = string.substr(2); + } + + if (bitSize > 64 || bitSize % 8 != 0) + return { }; + + if (string.empty() || string.front() != '(') + return { }; + + string = string.substr(1); + + i128 value = 0x00; + bool negative = false; + for (u32 i = 0; !string.empty(); i++) { + const char c = string.front(); + + if (c == ')') break; + if (i == 0 && c == '-') + negative = true; + else if (i == 0 && c == '+') + continue; + else if (std::isdigit(c)) + value = value * 10 + (c - '0'); + else + return {}; + + string = string.substr(1); + } + + if (negative) + value = -value; + + if (string.empty() || string.front() != ')') + return { }; + + string = string.substr(1); + + u128 resultValue = changeEndianness(value, bitSize / 8, endian); + std::vector result; + for (u32 bit = 0; bit < bitSize; bit += 8) { + result.emplace_back( + 0xFF, + u8((resultValue >> bit) & hex::bitmask(8)) + ); + } + + return result; + } + + std::vector parseBinaryPatternString(std::string_view string) { + std::vector result; + + if (string.length() < 2) + return { }; + + bool inString = false; + while (string.length() > 0) { + BinaryPattern::Pattern pattern = { 0, 0 }; + + if (string.starts_with("\"")) { + inString = !inString; + string = string.substr(1); + continue; + } else if (inString) { + pattern = { 0xFF, u8(string.front()) }; + string = string.substr(1); + } else if (string.starts_with("u") || string.starts_with("s")) { + auto newPatterns = parseValueExpression(string); + if (newPatterns.empty()) + return {}; + std::ranges::move(newPatterns, std::back_inserter(result)); + continue; + } else if (string.starts_with("??")) { + pattern = { 0x00, 0x00 }; + string = string.substr(2); + } else if ((std::isxdigit(string.front()) || string.front() == '?') && string.length() >= 2) { + const auto hex = string.substr(0, 2); + + for (const auto &c : hex) { + pattern.mask <<= 4; + pattern.value <<= 4; + + if (std::isxdigit(c)) { + pattern.mask |= 0x0F; + + if (auto hexValue = hex::hexCharToValue(c); hexValue.has_value()) + pattern.value |= hexValue.value(); + else + return { }; + } else if (c != '?') { + return { }; + } + } + + string = string.substr(2); + } else if (std::isspace(string.front())) { + string = string.substr(1); + continue; + } else { + return { }; + } + + result.push_back(pattern); + } + + if (inString) + return { }; + + return result; + } + + } + + BinaryPattern::BinaryPattern(const std::string &pattern) : m_patterns(parseBinaryPatternString(pattern)) { } + + bool BinaryPattern::isValid() const { return !m_patterns.empty(); } + + bool BinaryPattern::matches(const std::vector &bytes) const { + if (bytes.size() < m_patterns.size()) + return false; + + for (u32 i = 0; i < m_patterns.size(); i++) { + if (!this->matchesByte(bytes[i], i)) + return false; + } + + return true; + } + + bool BinaryPattern::matchesByte(u8 byte, u32 offset) const { + const auto &pattern = m_patterns[offset]; + + return (byte & pattern.mask) == pattern.value; + } + + u64 BinaryPattern::getSize() const { + return m_patterns.size(); + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/views/view_find.cpp b/plugins/builtin/source/content/views/view_find.cpp index a502e17ec..0b5133c25 100644 --- a/plugins/builtin/source/content/views/view_find.cpp +++ b/plugins/builtin/source/content/views/view_find.cpp @@ -962,12 +962,13 @@ namespace hex::plugin::builtin { mode = SearchSettings::Mode::BinaryPattern; - ImGuiExt::InputTextIconHint("hex.builtin.view.find.binary_pattern"_lang, ICON_VS_SYMBOL_NAMESPACE, "AA BB ?? ?D \"XYZ\"", settings.input); + if (ImGuiExt::InputTextIconHint("hex.builtin.view.find.binary_pattern"_lang, ICON_VS_SYMBOL_NAMESPACE, "AA BB ?? ?D \"XYZ\" u32be(1234)", settings.input)) { + settings.pattern = hex::BinaryPattern(settings.input); + } constexpr static u32 min = 1, max = 0x1000; ImGui::SliderScalar("hex.builtin.view.find.binary_pattern.alignment"_lang, ImGuiDataType_U32, &settings.alignment, &min, &max); - settings.pattern = hex::BinaryPattern(settings.input); m_settingsValid = settings.pattern.isValid() && settings.alignment > 0; ImGui::EndTabItem();