diff --git a/lib/external/pattern_language b/lib/external/pattern_language index 157436f55..49f3fb30d 160000 --- a/lib/external/pattern_language +++ b/lib/external/pattern_language @@ -1 +1 @@ -Subproject commit 157436f55f681fc3b59264741cd9f49ec036cb65 +Subproject commit 49f3fb30dcb3044a2bb535958b7b954c81ec23ef diff --git a/lib/libimhex/include/hex/helpers/binary_pattern.hpp b/lib/libimhex/include/hex/helpers/binary_pattern.hpp new file mode 100644 index 000000000..b648f36d7 --- /dev/null +++ b/lib/libimhex/include/hex/helpers/binary_pattern.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include + +#include + +#include + +namespace hex { + + class BinaryPattern { + public: + struct Pattern { + u8 mask, value; + }; + + BinaryPattern() = default; + explicit BinaryPattern(const std::string &pattern) : m_patterns(parseBinaryPatternString(pattern)) { } + + [[nodiscard]] bool isValid() const { return !this->m_patterns.empty(); } + + [[nodiscard]] bool matches(const std::vector &bytes) const { + if (bytes.size() < this->m_patterns.size()) + return false; + + for (u32 i = 0; i < this->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 = this->m_patterns[offset]; + + return (byte & pattern.mask) == pattern.value; + } + + [[nodiscard]] size_t getSize() const { + return this->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; + }; + +} \ No newline at end of file diff --git a/plugins/builtin/include/content/views/view_find.hpp b/plugins/builtin/include/content/views/view_find.hpp index 2aa9cbb23..373344e3c 100644 --- a/plugins/builtin/include/content/views/view_find.hpp +++ b/plugins/builtin/include/content/views/view_find.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -76,7 +77,7 @@ namespace hex::plugin::builtin { struct BinaryPattern { std::string input; - std::vector pattern; + hex::BinaryPattern pattern; u32 alignment = 1; } binaryPattern; diff --git a/plugins/builtin/source/content/pl_pragmas.cpp b/plugins/builtin/source/content/pl_pragmas.cpp index 3da29a872..9c5fbae8a 100644 --- a/plugins/builtin/source/content/pl_pragmas.cpp +++ b/plugins/builtin/source/content/pl_pragmas.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include @@ -20,6 +22,10 @@ namespace hex::plugin::builtin { ContentRegistry::PatternLanguage::addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return magic::isValidMIMEType(value); }); + + ContentRegistry::PatternLanguage::addPragma("magic", [](pl::PatternLanguage&, const std::string &) { + return true; + }); } } \ 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 14180dd0c..d6d52eaa5 100644 --- a/plugins/builtin/source/content/views/view_find.cpp +++ b/plugins/builtin/source/content/views/view_find.cpp @@ -22,7 +22,7 @@ namespace hex::plugin::builtin { if (this->m_searchTask.isRunning()) return { }; - if (!this->m_occurrenceTree->overlapping({ address, address }).empty()) + if (!this->m_occurrenceTree->overlapping({ address, address + size }).empty()) return HighlightColor(); else return std::nullopt; @@ -34,7 +34,7 @@ namespace hex::plugin::builtin { if (this->m_searchTask.isRunning()) return; - auto occurrences = this->m_occurrenceTree->overlapping({ address, address }); + auto occurrences = this->m_occurrenceTree->overlapping({ address, address + size }); if (occurrences.empty()) return; @@ -93,62 +93,6 @@ namespace hex::plugin::builtin { }); } - - std::vector ViewFind::parseBinaryPatternString(std::string string) { - std::vector result; - - if (string.length() < 2) - return { }; - - bool inString = false; - while (string.length() > 0) { - BinaryPattern 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; - } - template static std::tuple, size_t> parseNumericValue(const std::string &string) { static_assert(sizeof(StorageType) >= sizeof(Type)); @@ -365,7 +309,7 @@ namespace hex::plugin::builtin { reader.seek(searchRegion.getStartAddress()); reader.setEndAddress(searchRegion.getEndAddress()); - const size_t patternSize = settings.pattern.size(); + const size_t patternSize = settings.pattern.getSize(); if (settings.alignment == 1) { u32 matchedBytes = 0; @@ -373,9 +317,9 @@ namespace hex::plugin::builtin { auto byte = *it; task.update(it.getAddress()); - if ((byte & settings.pattern[matchedBytes].mask) == settings.pattern[matchedBytes].value) { + if (settings.pattern.matchesByte(byte, matchedBytes)) { matchedBytes++; - if (matchedBytes == settings.pattern.size()) { + if (matchedBytes == settings.pattern.getSize()) { auto occurrenceAddress = it.getAddress() - (patternSize - 1); results.push_back(Occurrence { Region { occurrenceAddress, patternSize }, Occurrence::DecodeType::Binary, std::endian::native }); @@ -397,7 +341,7 @@ namespace hex::plugin::builtin { bool match = true; for (u32 i = 0; i < patternSize; i++) { - if ((data[i] & settings.pattern[i].mask) != settings.pattern[i].value) { + if (settings.pattern.matchesByte(data[i], i)) { match = false; break; } @@ -693,8 +637,8 @@ namespace hex::plugin::builtin { 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 = parseBinaryPatternString(settings.input); - this->m_settingsValid = !settings.pattern.empty() && settings.alignment > 0; + settings.pattern = hex::BinaryPattern(settings.input); + this->m_settingsValid = settings.pattern.isValid() && settings.alignment > 0; ImGui::EndTabItem(); } diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index e969e991a..762fcdd51 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -765,6 +766,64 @@ namespace hex::plugin::builtin { return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r'); }); + // Format: [ AA BB CC DD ] @ 0x12345678 + runtime.addPragma("magic", [provider, &foundCorrectType](pl::PatternLanguage &, const std::string &value) -> bool { + const auto pattern = [value = value] mutable -> std::optional { + value = wolv::util::trim(value); + + if (value.empty()) + return std::nullopt; + + if (!value.starts_with('[')) + return std::nullopt; + + value = value.substr(1); + + auto end = value.find(']'); + if (end == std::string::npos) + return std::nullopt; + + value = value.substr(0, end - 1); + value = wolv::util::trim(value); + + return BinaryPattern(value); + }(); + + const auto address = [value = value] mutable -> std::optional { + value = wolv::util::trim(value); + + if (value.empty()) + return std::nullopt; + + auto start = value.find('@'); + if (start == std::string::npos) + return std::nullopt; + + value = value.substr(start + 1); + value = wolv::util::trim(value); + + size_t end = 0; + auto result = std::stoull(value, &end, 0); + if (end != value.length()) + return std::nullopt; + + return result; + }(); + + if (!address) + return false; + if (!pattern) + return false; + + std::vector bytes(pattern->getSize()); + provider->read(*address, bytes.data(), bytes.size()); + + if (pattern->matches(bytes)) + foundCorrectType = true; + + return true; + }); + this->m_possiblePatternFiles.clear(); std::error_code errorCode;