From 11f75f72eebe6a32fb0a3d5ac904fe0904f6cec7 Mon Sep 17 00:00:00 2001 From: PerikiyoXD Date: Sat, 27 Jan 2024 14:13:41 +0100 Subject: [PATCH] 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 --- plugins/builtin/CMakeLists.txt | 2 + .../hex_editor/popup_hex_editor_find.hpp | 58 ++++ plugins/builtin/romfs/lang/de_DE.json | 8 + plugins/builtin/romfs/lang/en_US.json | 8 + plugins/builtin/romfs/lang/es_ES.json | 8 + plugins/builtin/romfs/lang/it_IT.json | 10 +- plugins/builtin/romfs/lang/ja_JP.json | 8 + plugins/builtin/romfs/lang/ko_KR.json | 8 + plugins/builtin/romfs/lang/pt_BR.json | 8 + plugins/builtin/romfs/lang/zh_CN.json | 10 +- plugins/builtin/romfs/lang/zh_TW.json | 8 + .../hex_editor/popup_hex_editor_find.cpp | 291 ++++++++++++++++++ .../source/content/views/view_hex_editor.cpp | 186 +---------- 13 files changed, 428 insertions(+), 185 deletions(-) create mode 100644 plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_find.hpp create mode 100644 plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_find.cpp diff --git a/plugins/builtin/CMakeLists.txt b/plugins/builtin/CMakeLists.txt index f5f567985..251a5564b 100644 --- a/plugins/builtin/CMakeLists.txt +++ b/plugins/builtin/CMakeLists.txt @@ -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 diff --git a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_find.hpp b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_find.hpp new file mode 100644 index 000000000..1ab18f2e8 --- /dev/null +++ b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_find.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include "content/views/view_hex_editor.hpp" +#include "hex/api/task_manager.hpp" + +#include +#include +#include + +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 findByteSequence(const std::vector &sequence); + + std::string m_inputString; + std::vector m_searchByteSequence; + std::optional m_foundRegion = std::nullopt; + + bool m_requestFocus = true; + std::atomic m_requestSearch = false; + std::atomic m_searchBackwards = false; + std::atomic m_reachedEnd = false; + + enum class Encoding { + UTF8, + UTF16, + UTF32 + }; + + enum class Endianness { + Little, + Big + }; + + enum class SearchMode : bool { + ByteSequence, + String + }; + + std::atomic m_stringEncoding = Encoding::UTF8; + std::atomic m_stringEndianness = Endianness::Little; + std::atomic m_searchMode = SearchMode::ByteSequence; + + TaskHolder m_searchTask; + + void processInputString(); + }; + +} // namespace hex::plugin::builtin \ No newline at end of file diff --git a/plugins/builtin/romfs/lang/de_DE.json b/plugins/builtin/romfs/lang/de_DE.json index dd38b338d..a9af4bcb2 100644 --- a/plugins/builtin/romfs/lang/de_DE.json +++ b/plugins/builtin/romfs/lang/de_DE.json @@ -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", diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 2695277df..2ead55a24 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -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", diff --git a/plugins/builtin/romfs/lang/es_ES.json b/plugins/builtin/romfs/lang/es_ES.json index c62b5fb8c..c5d76d787 100644 --- a/plugins/builtin/romfs/lang/es_ES.json +++ b/plugins/builtin/romfs/lang/es_ES.json @@ -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", diff --git a/plugins/builtin/romfs/lang/it_IT.json b/plugins/builtin/romfs/lang/it_IT.json index 12789be4c..f3c3f144b 100644 --- a/plugins/builtin/romfs/lang/it_IT.json +++ b/plugins/builtin/romfs/lang/it_IT.json @@ -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!" } -} +} \ No newline at end of file diff --git a/plugins/builtin/romfs/lang/ja_JP.json b/plugins/builtin/romfs/lang/ja_JP.json index 718000aa9..26e8d06dd 100644 --- a/plugins/builtin/romfs/lang/ja_JP.json +++ b/plugins/builtin/romfs/lang/ja_JP.json @@ -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": "", diff --git a/plugins/builtin/romfs/lang/ko_KR.json b/plugins/builtin/romfs/lang/ko_KR.json index 35e6ee1a2..56d59d4d7 100644 --- a/plugins/builtin/romfs/lang/ko_KR.json +++ b/plugins/builtin/romfs/lang/ko_KR.json @@ -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": "지역", diff --git a/plugins/builtin/romfs/lang/pt_BR.json b/plugins/builtin/romfs/lang/pt_BR.json index 6b52d1b48..d2ef93791 100644 --- a/plugins/builtin/romfs/lang/pt_BR.json +++ b/plugins/builtin/romfs/lang/pt_BR.json @@ -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": "", diff --git a/plugins/builtin/romfs/lang/zh_CN.json b/plugins/builtin/romfs/lang/zh_CN.json index 692cf8957..0f67b57eb 100644 --- a/plugins/builtin/romfs/lang/zh_CN.json +++ b/plugins/builtin/romfs/lang/zh_CN.json @@ -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": "网络" } -} +} \ No newline at end of file diff --git a/plugins/builtin/romfs/lang/zh_TW.json b/plugins/builtin/romfs/lang/zh_TW.json index 7c56ae2b9..344689b39 100644 --- a/plugins/builtin/romfs/lang/zh_TW.json +++ b/plugins/builtin/romfs/lang/zh_TW.json @@ -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": "區域", diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_find.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_find.cpp new file mode 100644 index 000000000..93a110314 --- /dev/null +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_find.cpp @@ -0,0 +1,291 @@ +#include "content/popups/hex_editor/popup_hex_editor_find.hpp" + +#include "content/views/view_hex_editor.hpp" + +#include +#include +#include + +#include +#include + +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(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(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 PopupFind::findByteSequence(const std::vector &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, char16_t> convert16; + auto utf16 = convert16.from_bytes(m_inputString); + + for (auto &c: utf16) { + swapEndianness(c, Encoding::UTF16, m_stringEndianness); + } + + std::copy(reinterpret_cast(utf16.data()), + reinterpret_cast(utf16.data() + utf16.size()), + std::back_inserter(m_searchByteSequence)); + } + break; + + case Encoding::UTF32: { + std::wstring_convert, char32_t> convert32; + auto utf32 = convert32.from_bytes(m_inputString); + + for (auto &c: utf32) { + swapEndianness(c, Encoding::UTF32, m_stringEndianness); + } + + std::copy(reinterpret_cast(utf32.data()), + reinterpret_cast(utf32.data() + utf32.size()), + std::back_inserter(m_searchByteSequence)); + } + break; + } + } + break; + } + } +} \ No newline at end of file diff --git a/plugins/builtin/source/content/views/view_hex_editor.cpp b/plugins/builtin/source/content/views/view_hex_editor.cpp index cd5b73c9e..d3e11003a 100644 --- a/plugins/builtin/source/content/views/view_hex_editor.cpp +++ b/plugins/builtin/source/content/views/view_hex_editor.cpp @@ -18,6 +18,8 @@ #include #include +#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 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 findSequence(const std::vector &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 m_searchPosition, m_nextSearchPosition; - - bool m_requestFocus = true; - std::atomic m_shouldSearch = false; - std::atomic m_backwards = false; - std::atomic 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(); + this->openPopup(this); }, ImHexApi::Provider::isValid);