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:
parent
5747b72a41
commit
11f75f72ee
@ -89,6 +89,8 @@ add_imhex_plugin(
|
|||||||
source/content/tutorials/tutorials.cpp
|
source/content/tutorials/tutorials.cpp
|
||||||
source/content/tutorials/introduction.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_hex_editor.cpp
|
||||||
source/content/views/view_pattern_editor.cpp
|
source/content/views/view_pattern_editor.cpp
|
||||||
source/content/views/view_pattern_data.cpp
|
source/content/views/view_pattern_data.cpp
|
||||||
|
@ -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
|
@ -662,6 +662,14 @@
|
|||||||
"hex.builtin.view.hex_editor.search.find": "Suchen",
|
"hex.builtin.view.hex_editor.search.find": "Suchen",
|
||||||
"hex.builtin.view.hex_editor.search.hex": "Hex",
|
"hex.builtin.view.hex_editor.search.hex": "Hex",
|
||||||
"hex.builtin.view.hex_editor.search.string": "String",
|
"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.begin": "Beginn",
|
||||||
"hex.builtin.view.hex_editor.select.offset.end": "Ende",
|
"hex.builtin.view.hex_editor.select.offset.end": "Ende",
|
||||||
"hex.builtin.view.hex_editor.select.offset.region": "Region",
|
"hex.builtin.view.hex_editor.select.offset.region": "Region",
|
||||||
|
@ -779,6 +779,14 @@
|
|||||||
"hex.builtin.view.hex_editor.search.find": "Find",
|
"hex.builtin.view.hex_editor.search.find": "Find",
|
||||||
"hex.builtin.view.hex_editor.search.hex": "Hex",
|
"hex.builtin.view.hex_editor.search.hex": "Hex",
|
||||||
"hex.builtin.view.hex_editor.search.string": "String",
|
"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.begin": "Begin",
|
||||||
"hex.builtin.view.hex_editor.select.offset.end": "End",
|
"hex.builtin.view.hex_editor.select.offset.end": "End",
|
||||||
"hex.builtin.view.hex_editor.select.offset.region": "Region",
|
"hex.builtin.view.hex_editor.select.offset.region": "Region",
|
||||||
|
@ -660,6 +660,14 @@
|
|||||||
"hex.builtin.view.hex_editor.search.find": "Buscar",
|
"hex.builtin.view.hex_editor.search.find": "Buscar",
|
||||||
"hex.builtin.view.hex_editor.search.hex": "Hexadecimal",
|
"hex.builtin.view.hex_editor.search.hex": "Hexadecimal",
|
||||||
"hex.builtin.view.hex_editor.search.string": "Cadena",
|
"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.begin": "Inicio",
|
||||||
"hex.builtin.view.hex_editor.select.offset.end": "Fin",
|
"hex.builtin.view.hex_editor.select.offset.end": "Fin",
|
||||||
"hex.builtin.view.hex_editor.select.offset.region": "Región",
|
"hex.builtin.view.hex_editor.select.offset.region": "Región",
|
||||||
|
@ -660,6 +660,14 @@
|
|||||||
"hex.builtin.view.hex_editor.search.find": "Cerca",
|
"hex.builtin.view.hex_editor.search.find": "Cerca",
|
||||||
"hex.builtin.view.hex_editor.search.hex": "Hex",
|
"hex.builtin.view.hex_editor.search.hex": "Hex",
|
||||||
"hex.builtin.view.hex_editor.search.string": "Stringa",
|
"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.begin": "",
|
||||||
"hex.builtin.view.hex_editor.select.offset.end": "",
|
"hex.builtin.view.hex_editor.select.offset.end": "",
|
||||||
"hex.builtin.view.hex_editor.select.offset.region": "",
|
"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.link": "https://github.com/WerWolv/ImHex/releases/latest",
|
||||||
"hex.builtin.welcome.update.title": "Nuovo aggiornamento disponibile!"
|
"hex.builtin.welcome.update.title": "Nuovo aggiornamento disponibile!"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -660,6 +660,14 @@
|
|||||||
"hex.builtin.view.hex_editor.search.find": "検索",
|
"hex.builtin.view.hex_editor.search.find": "検索",
|
||||||
"hex.builtin.view.hex_editor.search.hex": "16進数",
|
"hex.builtin.view.hex_editor.search.hex": "16進数",
|
||||||
"hex.builtin.view.hex_editor.search.string": "文字列",
|
"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.begin": "",
|
||||||
"hex.builtin.view.hex_editor.select.offset.end": "",
|
"hex.builtin.view.hex_editor.select.offset.end": "",
|
||||||
"hex.builtin.view.hex_editor.select.offset.region": "",
|
"hex.builtin.view.hex_editor.select.offset.region": "",
|
||||||
|
@ -681,6 +681,14 @@
|
|||||||
"hex.builtin.view.hex_editor.search.find": "찾기",
|
"hex.builtin.view.hex_editor.search.find": "찾기",
|
||||||
"hex.builtin.view.hex_editor.search.hex": "헥스",
|
"hex.builtin.view.hex_editor.search.hex": "헥스",
|
||||||
"hex.builtin.view.hex_editor.search.string": "문자열",
|
"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.begin": "시작",
|
||||||
"hex.builtin.view.hex_editor.select.offset.end": "끝",
|
"hex.builtin.view.hex_editor.select.offset.end": "끝",
|
||||||
"hex.builtin.view.hex_editor.select.offset.region": "지역",
|
"hex.builtin.view.hex_editor.select.offset.region": "지역",
|
||||||
|
@ -660,6 +660,14 @@
|
|||||||
"hex.builtin.view.hex_editor.search.find": "Buscar",
|
"hex.builtin.view.hex_editor.search.find": "Buscar",
|
||||||
"hex.builtin.view.hex_editor.search.hex": "Hex",
|
"hex.builtin.view.hex_editor.search.hex": "Hex",
|
||||||
"hex.builtin.view.hex_editor.search.string": "String",
|
"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.begin": "",
|
||||||
"hex.builtin.view.hex_editor.select.offset.end": "",
|
"hex.builtin.view.hex_editor.select.offset.end": "",
|
||||||
"hex.builtin.view.hex_editor.select.offset.region": "",
|
"hex.builtin.view.hex_editor.select.offset.region": "",
|
||||||
|
@ -674,6 +674,14 @@
|
|||||||
"hex.builtin.view.hex_editor.search.find": "查找",
|
"hex.builtin.view.hex_editor.search.find": "查找",
|
||||||
"hex.builtin.view.hex_editor.search.hex": "Hex",
|
"hex.builtin.view.hex_editor.search.hex": "Hex",
|
||||||
"hex.builtin.view.hex_editor.search.string": "字符串",
|
"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.begin": "起始",
|
||||||
"hex.builtin.view.hex_editor.select.offset.end": "结束",
|
"hex.builtin.view.hex_editor.select.offset.end": "结束",
|
||||||
"hex.builtin.view.hex_editor.select.offset.region": "区域",
|
"hex.builtin.view.hex_editor.select.offset.region": "区域",
|
||||||
@ -866,4 +874,4 @@
|
|||||||
"hex.builtin.setting.general.patterns": "模式",
|
"hex.builtin.setting.general.patterns": "模式",
|
||||||
"hex.builtin.setting.general.network": "网络"
|
"hex.builtin.setting.general.network": "网络"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -674,6 +674,14 @@
|
|||||||
"hex.builtin.view.hex_editor.search.find": "尋找",
|
"hex.builtin.view.hex_editor.search.find": "尋找",
|
||||||
"hex.builtin.view.hex_editor.search.hex": "十六進位",
|
"hex.builtin.view.hex_editor.search.hex": "十六進位",
|
||||||
"hex.builtin.view.hex_editor.search.string": "字串",
|
"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.begin": "開始",
|
||||||
"hex.builtin.view.hex_editor.select.offset.end": "結束",
|
"hex.builtin.view.hex_editor.select.offset.end": "結束",
|
||||||
"hex.builtin.view.hex_editor.select.offset.region": "區域",
|
"hex.builtin.view.hex_editor.select.offset.region": "區域",
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,8 @@
|
|||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
#include <content/popups/popup_blocking_task.hpp>
|
#include <content/popups/popup_blocking_task.hpp>
|
||||||
|
|
||||||
|
#include "content/popups/hex_editor/popup_hex_editor_find.hpp"
|
||||||
|
|
||||||
using namespace std::literals::string_literals;
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
namespace hex::plugin::builtin {
|
namespace hex::plugin::builtin {
|
||||||
@ -154,188 +156,6 @@ namespace hex::plugin::builtin {
|
|||||||
Region m_region = { 0, 1 };
|
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 {
|
class PopupBaseAddress : public ViewHexEditor::Popup {
|
||||||
public:
|
public:
|
||||||
explicit PopupBaseAddress(u64 baseAddress) : m_baseAddress(baseAddress) { }
|
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,
|
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.view.hex_editor.menu.file.search" }, ICON_VS_SEARCH, 1550,
|
||||||
CTRLCMD + Keys::F,
|
CTRLCMD + Keys::F,
|
||||||
[this] {
|
[this] {
|
||||||
this->openPopup<PopupFind>();
|
this->openPopup<PopupFind>(this);
|
||||||
},
|
},
|
||||||
ImHexApi::Provider::isValid);
|
ImHexApi::Provider::isValid);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user