1
0
mirror of synced 2025-01-18 00:56:49 +01:00

feat: Add search options for string encoding and endianness (#1490)

Added search options for string encoding (UTF-8, UTF-16, UTF-32) and
endianness (Little, Big) in the hex editor. This enhancement allows
users to customize the search process based on different string
encodings and byte orders.

Affected files:
- `plugins/builtin/romfs/lang/de_DE.json`
- `plugins/builtin/romfs/lang/en_US.json`
- `plugins/builtin/romfs/lang/es_ES.json`
- `plugins/builtin/romfs/lang/it_IT.json`
- `plugins/builtin/romfs/lang/ja_JP.json`
- `plugins/builtin/romfs/lang/ko_KR.json`
- `plugins/builtin/romfs/lang/pt_BR.json`
- `plugins/builtin/romfs/lang/zh_CN.json`
- `plugins/builtin/romfs/lang/zh_TW.json`
- `plugins/builtin/source/content/views/view_hex_editor.cpp`

Resolves: #1325

---------

Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
PerikiyoXD 2024-01-27 14:13:41 +01:00 committed by GitHub
parent 5747b72a41
commit 11f75f72ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 428 additions and 185 deletions

View File

@ -89,6 +89,8 @@ add_imhex_plugin(
source/content/tutorials/tutorials.cpp
source/content/tutorials/introduction.cpp
source/content/popups/hex_editor/popup_hex_editor_find.cpp
source/content/views/view_hex_editor.cpp
source/content/views/view_pattern_editor.cpp
source/content/views/view_pattern_data.cpp

View File

@ -0,0 +1,58 @@
#pragma once
#include "content/views/view_hex_editor.hpp"
#include "hex/api/task_manager.hpp"
#include <atomic>
#include <optional>
#include <string>
namespace hex::plugin::builtin {
class PopupFind : public ViewHexEditor::Popup {
public:
explicit PopupFind(ViewHexEditor *editor) noexcept;
~PopupFind() override;
void draw(ViewHexEditor *editor) override;
private:
void drawSearchDirectionButtons();
void drawTabContents();
std::optional<Region> findByteSequence(const std::vector<u8> &sequence);
std::string m_inputString;
std::vector<u8> m_searchByteSequence;
std::optional<Region> m_foundRegion = std::nullopt;
bool m_requestFocus = true;
std::atomic<bool> m_requestSearch = false;
std::atomic<bool> m_searchBackwards = false;
std::atomic<bool> m_reachedEnd = false;
enum class Encoding {
UTF8,
UTF16,
UTF32
};
enum class Endianness {
Little,
Big
};
enum class SearchMode : bool {
ByteSequence,
String
};
std::atomic<Encoding> m_stringEncoding = Encoding::UTF8;
std::atomic<Endianness> m_stringEndianness = Endianness::Little;
std::atomic<SearchMode> m_searchMode = SearchMode::ByteSequence;
TaskHolder m_searchTask;
void processInputString();
};
} // namespace hex::plugin::builtin

View File

@ -662,6 +662,14 @@
"hex.builtin.view.hex_editor.search.find": "Suchen",
"hex.builtin.view.hex_editor.search.hex": "Hex",
"hex.builtin.view.hex_editor.search.string": "String",
"hex.builtin.view.hex_editor.search.string.encoding": "Kodierung",
"hex.builtin.view.hex_editor.search.string.encoding.utf8": "UTF-8",
"hex.builtin.view.hex_editor.search.string.encoding.utf16": "UTF-16",
"hex.builtin.view.hex_editor.search.string.encoding.utf32": "UTF-32",
"hex.builtin.view.hex_editor.search.string.endianness": "Endianness",
"hex.builtin.view.hex_editor.search.string.endianness.little": "Little",
"hex.builtin.view.hex_editor.search.string.endianness.big": "Big",
"hex.builtin.view.hex_editor.search.no_more_results": "Keine weiteren Ergebnisse",
"hex.builtin.view.hex_editor.select.offset.begin": "Beginn",
"hex.builtin.view.hex_editor.select.offset.end": "Ende",
"hex.builtin.view.hex_editor.select.offset.region": "Region",

View File

@ -779,6 +779,14 @@
"hex.builtin.view.hex_editor.search.find": "Find",
"hex.builtin.view.hex_editor.search.hex": "Hex",
"hex.builtin.view.hex_editor.search.string": "String",
"hex.builtin.view.hex_editor.search.string.encoding": "Encoding",
"hex.builtin.view.hex_editor.search.string.encoding.utf8": "UTF-8",
"hex.builtin.view.hex_editor.search.string.encoding.utf16": "UTF-16",
"hex.builtin.view.hex_editor.search.string.encoding.utf32": "UTF-32",
"hex.builtin.view.hex_editor.search.string.endianness": "Endianness",
"hex.builtin.view.hex_editor.search.string.endianness.little": "Little",
"hex.builtin.view.hex_editor.search.string.endianness.big": "Big",
"hex.builtin.view.hex_editor.search.no_more_results": "No more results",
"hex.builtin.view.hex_editor.select.offset.begin": "Begin",
"hex.builtin.view.hex_editor.select.offset.end": "End",
"hex.builtin.view.hex_editor.select.offset.region": "Region",

View File

@ -660,6 +660,14 @@
"hex.builtin.view.hex_editor.search.find": "Buscar",
"hex.builtin.view.hex_editor.search.hex": "Hexadecimal",
"hex.builtin.view.hex_editor.search.string": "Cadena",
"hex.builtin.view.hex_editor.search.string.encoding": "Codificación",
"hex.builtin.view.hex_editor.search.string.encoding.utf8": "UTF-8",
"hex.builtin.view.hex_editor.search.string.encoding.utf16": "UTF-16",
"hex.builtin.view.hex_editor.search.string.encoding.utf32": "UTF-32",
"hex.builtin.view.hex_editor.search.string.endianness": "Endianness",
"hex.builtin.view.hex_editor.search.string.endianness.little": "Little",
"hex.builtin.view.hex_editor.search.string.endianness.big": "Big",
"hex.builtin.view.hex_editor.search.no_more_results": "No se encontraron más resultados",
"hex.builtin.view.hex_editor.select.offset.begin": "Inicio",
"hex.builtin.view.hex_editor.select.offset.end": "Fin",
"hex.builtin.view.hex_editor.select.offset.region": "Región",

View File

@ -660,6 +660,14 @@
"hex.builtin.view.hex_editor.search.find": "Cerca",
"hex.builtin.view.hex_editor.search.hex": "Hex",
"hex.builtin.view.hex_editor.search.string": "Stringa",
"hex.builtin.view.hex_editor.search.string.encoding": "Codifica",
"hex.builtin.view.hex_editor.search.string.encoding.utf8": "UTF-8",
"hex.builtin.view.hex_editor.search.string.encoding.utf16": "UTF-16",
"hex.builtin.view.hex_editor.search.string.encoding.utf32": "UTF-32",
"hex.builtin.view.hex_editor.search.string.endianness": "Endianness",
"hex.builtin.view.hex_editor.search.string.endianness.little": "Little",
"hex.builtin.view.hex_editor.search.string.endianness.big": "Big",
"hex.builtin.view.hex_editor.search.no_more_results": "Nessun risultato trovato",
"hex.builtin.view.hex_editor.select.offset.begin": "",
"hex.builtin.view.hex_editor.select.offset.end": "",
"hex.builtin.view.hex_editor.select.offset.region": "",
@ -835,4 +843,4 @@
"hex.builtin.welcome.update.link": "https://github.com/WerWolv/ImHex/releases/latest",
"hex.builtin.welcome.update.title": "Nuovo aggiornamento disponibile!"
}
}
}

View File

@ -660,6 +660,14 @@
"hex.builtin.view.hex_editor.search.find": "検索",
"hex.builtin.view.hex_editor.search.hex": "16進数",
"hex.builtin.view.hex_editor.search.string": "文字列",
"hex.builtin.view.hex_editor.search.string.encoding": "エンコード",
"hex.builtin.view.hex_editor.search.string.encoding.utf8": "UTF-8",
"hex.builtin.view.hex_editor.search.string.encoding.utf16": "UTF-16",
"hex.builtin.view.hex_editor.search.string.encoding.utf32": "UTF-32",
"hex.builtin.view.hex_editor.search.string.endianness": "バイトオーダ",
"hex.builtin.view.hex_editor.search.string.endianness.little": "リトルエンディアン",
"hex.builtin.view.hex_editor.search.string.endianness.big": "ビッグエンディアン",
"hex.builtin.view.hex_editor.search.no_more_results": "結果がありません",
"hex.builtin.view.hex_editor.select.offset.begin": "",
"hex.builtin.view.hex_editor.select.offset.end": "",
"hex.builtin.view.hex_editor.select.offset.region": "",

View File

@ -681,6 +681,14 @@
"hex.builtin.view.hex_editor.search.find": "찾기",
"hex.builtin.view.hex_editor.search.hex": "헥스",
"hex.builtin.view.hex_editor.search.string": "문자열",
"hex.builtin.view.hex_editor.search.string.encoding": "エンコード",
"hex.builtin.view.hex_editor.search.string.encoding.utf8": "UTF-8",
"hex.builtin.view.hex_editor.search.string.encoding.utf16": "UTF-16",
"hex.builtin.view.hex_editor.search.string.encoding.utf32": "UTF-32",
"hex.builtin.view.hex_editor.search.string.endianness": "엔디안",
"hex.builtin.view.hex_editor.search.string.endianness.little": "리틀 엔디안",
"hex.builtin.view.hex_editor.search.string.endianness.big": "빅 엔디안",
"hex.builtin.view.hex_editor.search.no_more_results": "더이상 결과 없음",
"hex.builtin.view.hex_editor.select.offset.begin": "시작",
"hex.builtin.view.hex_editor.select.offset.end": "끝",
"hex.builtin.view.hex_editor.select.offset.region": "지역",

View File

@ -660,6 +660,14 @@
"hex.builtin.view.hex_editor.search.find": "Buscar",
"hex.builtin.view.hex_editor.search.hex": "Hex",
"hex.builtin.view.hex_editor.search.string": "String",
"hex.builtin.view.hex_editor.search.string.encoding": "Codificação",
"hex.builtin.view.hex_editor.search.string.encoding.utf8": "UTF-8",
"hex.builtin.view.hex_editor.search.string.encoding.utf16": "UTF-16",
"hex.builtin.view.hex_editor.search.string.encoding.utf32": "UTF-32",
"hex.builtin.view.hex_editor.search.string.endianness": "Endianness",
"hex.builtin.view.hex_editor.search.string.endianness.little": "Little",
"hex.builtin.view.hex_editor.search.string.endianness.big": "Big",
"hex.builtin.view.hex_editor.search.no_more_results": "Não há mais resultados",
"hex.builtin.view.hex_editor.select.offset.begin": "",
"hex.builtin.view.hex_editor.select.offset.end": "",
"hex.builtin.view.hex_editor.select.offset.region": "",

View File

@ -674,6 +674,14 @@
"hex.builtin.view.hex_editor.search.find": "查找",
"hex.builtin.view.hex_editor.search.hex": "Hex",
"hex.builtin.view.hex_editor.search.string": "字符串",
"hex.builtin.view.hex_editor.search.string.encoding": "编码",
"hex.builtin.view.hex_editor.search.string.encoding.utf8": "UTF-8",
"hex.builtin.view.hex_editor.search.string.encoding.utf16": "UTF-16",
"hex.builtin.view.hex_editor.search.string.encoding.utf32": "UTF-32",
"hex.builtin.view.hex_editor.search.string.endianness": "字节顺序",
"hex.builtin.view.hex_editor.search.string.endianness.little": "小端序",
"hex.builtin.view.hex_editor.search.string.endianness.big": "大端序",
"hex.builtin.view.hex_editor.search.no_more_results": "没有更多结果",
"hex.builtin.view.hex_editor.select.offset.begin": "起始",
"hex.builtin.view.hex_editor.select.offset.end": "结束",
"hex.builtin.view.hex_editor.select.offset.region": "区域",
@ -866,4 +874,4 @@
"hex.builtin.setting.general.patterns": "模式",
"hex.builtin.setting.general.network": "网络"
}
}
}

View File

@ -674,6 +674,14 @@
"hex.builtin.view.hex_editor.search.find": "尋找",
"hex.builtin.view.hex_editor.search.hex": "十六進位",
"hex.builtin.view.hex_editor.search.string": "字串",
"hex.builtin.view.hex_editor.search.string.encoding": "編碼",
"hex.builtin.view.hex_editor.search.string.encoding.utf8": "UTF-8",
"hex.builtin.view.hex_editor.search.string.encoding.utf16": "UTF-16",
"hex.builtin.view.hex_editor.search.string.encoding.utf32": "UTF-32",
"hex.builtin.view.hex_editor.search.string.endianness": "端序",
"hex.builtin.view.hex_editor.search.string.endianness.little": "小端序",
"hex.builtin.view.hex_editor.search.string.endianness.big": "大端序",
"hex.builtin.view.hex_editor.search.no_more_results": "沒有更多結果",
"hex.builtin.view.hex_editor.select.offset.begin": "開始",
"hex.builtin.view.hex_editor.select.offset.end": "結束",
"hex.builtin.view.hex_editor.select.offset.region": "區域",

View File

@ -0,0 +1,291 @@
#include "content/popups/hex_editor/popup_hex_editor_find.hpp"
#include "content/views/view_hex_editor.hpp"
#include <hex/helpers/crypto.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/providers/buffered_reader.hpp>
#include <bit>
#include <codecvt>
namespace hex::plugin::builtin {
PopupFind::PopupFind(ViewHexEditor *editor) noexcept {
EventRegionSelected::subscribe(this, [this](Region region) {
m_foundRegion = region;
});
m_foundRegion = editor->getSelection();
}
PopupFind::~PopupFind() {
EventRegionSelected::unsubscribe(this);
}
void PopupFind::draw(ViewHexEditor *editor) {
ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.file.search"_lang);
if (ImGui::BeginTabBar("##find_tabs")) {
if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.search.hex"_lang)) {
m_searchMode = SearchMode::ByteSequence;
this->drawTabContents();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.search.string"_lang)) {
m_searchMode = SearchMode::String;
this->drawTabContents();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
const auto doSearch = [this, editor](auto &) {
auto region = this->findByteSequence(m_searchByteSequence);
if (region.has_value()) {
m_foundRegion = region.value();
if (editor->getSelection() != region) {
TaskManager::doLater([editor, region] {
editor->setSelection(region->getStartAddress(), region->getEndAddress());
editor->jumpToSelection();
});
}
m_reachedEnd = false;
} else {
m_reachedEnd = true;
}
m_requestSearch = false;
m_requestFocus = true;
};
if (m_requestSearch) {
this->processInputString();
if (!m_searchTask.isRunning() && !m_searchByteSequence.empty()) {
m_searchTask = TaskManager::createTask("hex.ui.common.processing",
ImHexApi::Provider::get()->getActualSize(),
doSearch);
}
m_requestSearch = false;
}
}
void PopupFind::drawSearchDirectionButtons() {
const auto ButtonSize = ImVec2(ImGui::CalcTextSize(ICON_VS_SEARCH).x, ImGui::GetTextLineHeight()) +
ImGui::GetStyle().CellPadding * 2;
const auto ButtonColor = ImGui::GetStyleColorVec4(ImGuiCol_Text);
if (m_requestFocus) {
ImGui::SetKeyboardFocusHere(-1);
m_requestFocus = false;
}
ImGui::BeginDisabled(m_searchTask.isRunning());
{
ImGui::SameLine();
if (ImGuiExt::IconButton(ICON_VS_ARROW_UP "##up", ButtonColor, ButtonSize)) {
m_requestSearch = true;
m_searchBackwards = true;
}
ImGui::SameLine();
if (ImGuiExt::IconButton(ICON_VS_ARROW_DOWN "##down", ButtonColor, ButtonSize)) {
m_requestSearch = true;
m_searchBackwards = false;
}
}
ImGui::EndDisabled();
}
void PopupFind::drawTabContents() {
constexpr static std::array EncodingNames = {
"hex.builtin.view.hex_editor.search.string.encoding.utf8",
"hex.builtin.view.hex_editor.search.string.encoding.utf16",
"hex.builtin.view.hex_editor.search.string.encoding.utf32"
};
constexpr static std::array EndiannessNames = {
"hex.builtin.view.hex_editor.search.string.endianness.little",
"hex.builtin.view.hex_editor.search.string.endianness.big"
};
const char *searchInputIcon = nullptr;
ImGuiInputTextFlags searchInputFlags = 0;
// Set search input icon and flags
switch (m_searchMode) {
case SearchMode::ByteSequence:
searchInputIcon = ICON_VS_SYMBOL_NUMERIC;
searchInputFlags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll |
ImGuiInputTextFlags_CharsHexadecimal;
break;
case SearchMode::String:
searchInputIcon = ICON_VS_SYMBOL_KEY;
searchInputFlags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll;
break;
}
// Draw search input
if (ImGuiExt::InputTextIcon("##input", searchInputIcon, m_inputString, searchInputFlags)) {
if (!m_inputString.empty()) {
m_requestSearch = true;
m_searchBackwards = ImGui::GetIO().KeyShift;
}
}
// Draw search direction buttons
ImGui::BeginDisabled(m_inputString.empty());
this->drawSearchDirectionButtons();
ImGui::EndDisabled();
// Draw search options for string search
if (m_searchMode == SearchMode::String) {
if (ImGui::BeginCombo("hex.builtin.view.hex_editor.search.string.encoding"_lang, Lang(EncodingNames[std::to_underlying<Encoding>(m_stringEncoding.load())]))) {
if (ImGui::Selectable(Lang(EncodingNames[0]), m_stringEncoding == Encoding::UTF8)) {
m_stringEncoding = Encoding::UTF8;
}
if (ImGui::Selectable(Lang(EncodingNames[1]), m_stringEncoding == Encoding::UTF16)) {
m_stringEncoding = Encoding::UTF16;
}
if (ImGui::Selectable(Lang(EncodingNames[2]), m_stringEncoding == Encoding::UTF32)) {
m_stringEncoding = Encoding::UTF32;
}
ImGui::EndCombo();
}
ImGui::BeginDisabled(m_stringEncoding == Encoding::UTF8);
{
if (ImGui::BeginCombo("hex.builtin.view.hex_editor.search.string.endianness"_lang, Lang(EndiannessNames[std::to_underlying<Endianness>(m_stringEndianness.load())]))) {
if (ImGui::Selectable(Lang(EndiannessNames[0]), m_stringEndianness == Endianness::Little)) {
m_stringEndianness = Endianness::Little;
}
if (ImGui::Selectable(Lang(EndiannessNames[1]), m_stringEndianness == Endianness::Big)) {
m_stringEndianness = Endianness::Big;
}
ImGui::EndCombo();
}
}
ImGui::EndDisabled();
}
if (m_reachedEnd) {
ImGui::TextUnformatted("hex.builtin.view.hex_editor.search.no_more_results"_lang);
} else {
ImGui::NewLine();
}
}
std::optional<Region> PopupFind::findByteSequence(const std::vector<u8> &sequence) {
auto provider = ImHexApi::Provider::get();
prv::ProviderReader reader(provider);
auto startAbsolutePosition = provider->getBaseAddress();
auto endAbsolutePosition = provider->getBaseAddress() + provider->getActualSize() - 1;
constexpr static auto searchFunction = [](const auto &haystackBegin, const auto &haystackEnd,
const auto &needleBegin, const auto &needleEnd) {
return std::search(haystackBegin, haystackEnd, needleBegin, needleEnd);
};
if (!m_searchBackwards) {
if (m_reachedEnd || !m_foundRegion.has_value()) {
reader.seek(startAbsolutePosition);
} else {
reader.seek(m_foundRegion->getStartAddress() + 1);
}
reader.setEndAddress(endAbsolutePosition);
auto occurrence = searchFunction(reader.begin(), reader.end(), sequence.begin(), sequence.end());
if (occurrence != reader.end()) {
return Region{occurrence.getAddress(), sequence.size()};
}
} else {
if (m_reachedEnd || !m_foundRegion.has_value()) {
reader.setEndAddress(endAbsolutePosition);
} else {
reader.setEndAddress(m_foundRegion->getEndAddress() - 1);
}
reader.seek(startAbsolutePosition);
auto occurrence = searchFunction(reader.rbegin(), reader.rend(), sequence.rbegin(), sequence.rend());
if (occurrence != reader.rend()) {
return Region{occurrence.getAddress() - (sequence.size() - 1), sequence.size()};
}
}
return std::nullopt;
}
void PopupFind::processInputString() {
m_searchByteSequence.clear();
constexpr auto swapEndianness = [](auto &value, Encoding encoding, Endianness endianness) {
if (encoding == Encoding::UTF16 || encoding == Encoding::UTF32) {
std::endian endian = (endianness == Endianness::Little)
? std::endian::little
: std::endian::big;
value = hex::changeEndianess(value, endian);
}
};
switch (m_searchMode) {
case SearchMode::ByteSequence: {
m_searchByteSequence = crypt::decode16(m_inputString);
}
break;
case SearchMode::String: {
switch (m_stringEncoding) {
case Encoding::UTF8: {
std::copy(m_inputString.data(), m_inputString.data() + m_inputString.size(),
std::back_inserter(m_searchByteSequence));
}
break;
case Encoding::UTF16: {
std::wstring_convert<std::codecvt_utf8<char16_t>, char16_t> convert16;
auto utf16 = convert16.from_bytes(m_inputString);
for (auto &c: utf16) {
swapEndianness(c, Encoding::UTF16, m_stringEndianness);
}
std::copy(reinterpret_cast<const u8 *>(utf16.data()),
reinterpret_cast<const u8 *>(utf16.data() + utf16.size()),
std::back_inserter(m_searchByteSequence));
}
break;
case Encoding::UTF32: {
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert32;
auto utf32 = convert32.from_bytes(m_inputString);
for (auto &c: utf32) {
swapEndianness(c, Encoding::UTF32, m_stringEndianness);
}
std::copy(reinterpret_cast<const u8 *>(utf32.data()),
reinterpret_cast<const u8 *>(utf32.data() + utf32.size()),
std::back_inserter(m_searchByteSequence));
}
break;
}
}
break;
}
}
}

View File

@ -18,6 +18,8 @@
#include <imgui_internal.h>
#include <content/popups/popup_blocking_task.hpp>
#include "content/popups/hex_editor/popup_hex_editor_find.hpp"
using namespace std::literals::string_literals;
namespace hex::plugin::builtin {
@ -154,188 +156,6 @@ namespace hex::plugin::builtin {
Region m_region = { 0, 1 };
};
class PopupFind : public ViewHexEditor::Popup {
public:
PopupFind() {
EventRegionSelected::subscribe(this, [this](Region region) {
m_searchPosition = m_nextSearchPosition.value_or(region.getStartAddress());
m_nextSearchPosition.reset();
});
}
~PopupFind() override {
EventRegionSelected::unsubscribe(this);
}
void draw(ViewHexEditor *editor) override {
std::vector<u8> searchSequence;
ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.file.search"_lang);
if (ImGui::BeginTabBar("##find_tabs")) {
if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.search.hex"_lang)) {
if (ImGuiExt::InputTextIcon("##input", ICON_VS_SYMBOL_NUMERIC, m_input, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_CharsHexadecimal)) {
if (!m_input.empty()) {
m_shouldSearch = true;
m_backwards = false;
}
}
this->drawButtons();
if (m_shouldSearch) {
searchSequence = crypt::decode16(m_input);
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.search.string"_lang)) {
if (ImGuiExt::InputTextIcon("##input", ICON_VS_SYMBOL_KEY, m_input, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
if (!m_input.empty()) {
m_shouldSearch = true;
m_backwards = false;
}
}
this->drawButtons();
if (m_shouldSearch) {
searchSequence.clear();
std::copy(m_input.begin(), m_input.end(), std::back_inserter(searchSequence));
if (!searchSequence.empty() && searchSequence.back() == 0x00)
searchSequence.pop_back();
}
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
if (!m_searchTask.isRunning() && !searchSequence.empty() && m_shouldSearch) {
m_searchTask = TaskManager::createTask("hex.ui.common.processing", ImHexApi::Provider::get()->getActualSize(), [this, editor, searchSequence](auto &) {
for (u8 retry = 0; retry < 2; retry++) {
auto region = this->findSequence(searchSequence, m_backwards);
if (region.has_value()) {
if (editor->getSelection() == region) {
if (m_nextSearchPosition.has_value())
m_searchPosition = m_nextSearchPosition.value();
m_nextSearchPosition.reset();
} else {
TaskManager::doLater([editor, region]{
editor->setSelection(region->getStartAddress(), region->getEndAddress());
editor->jumpToSelection();
});
break;
}
} else {
m_reachedEnd = true;
}
}
m_shouldSearch = false;
m_requestFocus = true;
});
}
}
private:
void drawButtons() {
const auto ButtonSize = ImVec2(ImGui::CalcTextSize(ICON_VS_SEARCH).x, ImGui::GetTextLineHeight()) + ImGui::GetStyle().CellPadding * 2;
const auto ButtonColor = ImGui::GetStyleColorVec4(ImGuiCol_Text);
if (m_requestFocus) {
ImGui::SetKeyboardFocusHere(-1);
m_requestFocus = false;
}
ImGui::BeginDisabled(m_searchTask.isRunning());
{
ImGui::SameLine();
if (ImGuiExt::IconButton(ICON_VS_SEARCH "##search", ButtonColor, ButtonSize)) {
m_shouldSearch = true;
m_backwards = false;
m_reachedEnd = false;
m_searchPosition.reset();
m_nextSearchPosition.reset();
}
ImGui::BeginDisabled(!m_searchPosition.has_value());
{
ImGui::BeginDisabled(m_reachedEnd && m_backwards);
{
if (ImGuiExt::IconButton(ICON_VS_ARROW_UP "##up", ButtonColor, ButtonSize)) {
m_shouldSearch = true;
m_backwards = true;
m_reachedEnd = false;
}
}
ImGui::EndDisabled();
ImGui::SameLine();
ImGui::BeginDisabled(m_reachedEnd && !m_backwards);
{
if (ImGuiExt::IconButton(ICON_VS_ARROW_DOWN "##down", ButtonColor, ButtonSize)) {
m_shouldSearch = true;
m_backwards = false;
m_reachedEnd = false;
}
}
ImGui::EndDisabled();
}
ImGui::EndDisabled();
}
ImGui::EndDisabled();
}
std::optional<Region> findSequence(const std::vector<u8> &sequence, bool backwards) {
auto provider = ImHexApi::Provider::get();
prv::ProviderReader reader(provider);
reader.seek(m_searchPosition.value_or(provider->getBaseAddress()));
constexpr static auto searchFunction = [](const auto &haystackBegin, const auto &haystackEnd, const auto &needleBegin, const auto &needleEnd) {
return std::search(haystackBegin, haystackEnd, std::boyer_moore_horspool_searcher(needleBegin, needleEnd));
};
if (!backwards) {
auto occurrence = searchFunction(reader.begin(), reader.end(), sequence.begin(), sequence.end());
if (occurrence != reader.end()) {
m_nextSearchPosition = occurrence.getAddress() + sequence.size();
return Region { occurrence.getAddress(), sequence.size() };
}
} else {
auto occurrence = searchFunction(reader.rbegin(), reader.rend(), sequence.rbegin(), sequence.rend());
if (occurrence != reader.rend()) {
if (occurrence.getAddress() < sequence.size())
m_nextSearchPosition = 0x00;
else
m_nextSearchPosition = occurrence.getAddress() - sequence.size();
return Region { occurrence.getAddress() - (sequence.size() - 1), sequence.size() };
}
}
return std::nullopt;
}
std::string m_input;
std::optional<u64> m_searchPosition, m_nextSearchPosition;
bool m_requestFocus = true;
std::atomic<bool> m_shouldSearch = false;
std::atomic<bool> m_backwards = false;
std::atomic<bool> m_reachedEnd = false;
TaskHolder m_searchTask;
};
class PopupBaseAddress : public ViewHexEditor::Popup {
public:
explicit PopupBaseAddress(u64 baseAddress) : m_baseAddress(baseAddress) { }
@ -1126,7 +946,7 @@ namespace hex::plugin::builtin {
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.view.hex_editor.menu.file.search" }, ICON_VS_SEARCH, 1550,
CTRLCMD + Keys::F,
[this] {
this->openPopup<PopupFind>();
this->openPopup<PopupFind>(this);
},
ImHexApi::Provider::isValid);