From c8c3f5e753197eb9fc91544e30b53a8b1cb720e8 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sun, 28 May 2023 11:50:10 +0200 Subject: [PATCH] feat: Added primitive replace feature to find view --- lib/libimhex/source/api/keybinding.cpp | 3 + .../include/content/views/view_find.hpp | 4 + plugins/builtin/romfs/lang/en_US.json | 3 + .../source/content/views/view_find.cpp | 87 ++++++++++++++++--- 4 files changed, 87 insertions(+), 10 deletions(-) diff --git a/lib/libimhex/source/api/keybinding.cpp b/lib/libimhex/source/api/keybinding.cpp index 4f0647327..c2cf8a2ab 100644 --- a/lib/libimhex/source/api/keybinding.cpp +++ b/lib/libimhex/source/api/keybinding.cpp @@ -37,6 +37,9 @@ namespace hex { void ShortcutManager::process(const std::unique_ptr ¤tView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) { Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, focused, keyCode); + if (ImGui::GetIO().WantTextInput) + return; + if (currentView->m_shortcuts.contains(pressedShortcut)) currentView->m_shortcuts[pressedShortcut](); } diff --git a/plugins/builtin/include/content/views/view_find.hpp b/plugins/builtin/include/content/views/view_find.hpp index 373344e3c..436405919 100644 --- a/plugins/builtin/include/content/views/view_find.hpp +++ b/plugins/builtin/include/content/views/view_find.hpp @@ -28,6 +28,7 @@ namespace hex::plugin::builtin { Region region; enum class DecodeType { ASCII, Binary, UTF16, Unsigned, Signed, Float, Double } decodeType; std::endian endian = std::endian::native; + bool selected; }; struct BinaryPattern { @@ -104,6 +105,7 @@ namespace hex::plugin::builtin { TaskHolder m_searchTask, m_filterTask; bool m_settingsValid = false; + std::string m_replaceBuffer; private: static std::vector searchStrings(Task &task, prv::Provider *provider, Region searchRegion, const SearchSettings::Strings &settings); @@ -112,6 +114,8 @@ namespace hex::plugin::builtin { static std::vector searchBinaryPattern(Task &task, prv::Provider *provider, Region searchRegion, const SearchSettings::BinaryPattern &settings); static std::vector searchValue(Task &task, prv::Provider *provider, Region searchRegion, const SearchSettings::Value &settings); + void drawContextMenu(Occurrence &target, const std::string &value); + static std::vector parseBinaryPatternString(std::string string); static std::tuple, size_t> parseNumericValueInput(const std::string &input, SearchSettings::Value::Type type); diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 91f0560ea..77a9aaace 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -677,6 +677,9 @@ "hex.builtin.view.find.binary_pattern.alignment": "Alignment", "hex.builtin.view.find.context.copy": "Copy Value", "hex.builtin.view.find.context.copy_demangle": "Copy Demangled Value", + "hex.builtin.view.find.context.replace": "Replace", + "hex.builtin.view.find.context.replace.ascii": "ASCII", + "hex.builtin.view.find.context.replace.hex": "Hex", "hex.builtin.view.find.demangled": "Demangled", "hex.builtin.view.find.name": "Find", "hex.builtin.view.find.regex": "Regex", diff --git a/plugins/builtin/source/content/views/view_find.cpp b/plugins/builtin/source/content/views/view_find.cpp index d6d52eaa5..be0a412da 100644 --- a/plugins/builtin/source/content/views/view_find.cpp +++ b/plugins/builtin/source/content/views/view_find.cpp @@ -91,6 +91,16 @@ namespace hex::plugin::builtin { ImGui::EndTooltip(); }); + + ShortcutManager::addShortcut(this, CTRLCMD + Keys::A, [this] { + if (this->m_filterTask.isRunning()) + return; + if (this->m_searchTask.isRunning()) + return; + + for (auto &occurrence : *this->m_sortedOccurrences) + occurrence.selected = true; + }); } template @@ -223,8 +233,8 @@ namespace hex::plugin::builtin { countedCharacters++; if (!validChar || startAddress + countedCharacters == endAddress) { if (countedCharacters >= size_t(settings.minLength)) { - if (!(settings.nullTermination && byte != 0x00)) { - results.push_back(Occurrence { Region { startAddress, countedCharacters }, decodeType, endian }); + if (!settings.nullTermination || byte == 0x00) { + results.push_back(Occurrence { Region { startAddress, countedCharacters }, decodeType, endian, false }); } } @@ -261,7 +271,7 @@ namespace hex::plugin::builtin { auto address = occurrence.getAddress(); reader.seek(address + 1); - results.push_back(Occurrence{ Region { address, bytes.size() }, Occurrence::DecodeType::Binary, std::endian::native }); + results.push_back(Occurrence{ Region { address, bytes.size() }, Occurrence::DecodeType::Binary, std::endian::native, false }); progress = address - searchRegion.getStartAddress(); } @@ -322,7 +332,7 @@ namespace hex::plugin::builtin { if (matchedBytes == settings.pattern.getSize()) { auto occurrenceAddress = it.getAddress() - (patternSize - 1); - results.push_back(Occurrence { Region { occurrenceAddress, patternSize }, Occurrence::DecodeType::Binary, std::endian::native }); + results.push_back(Occurrence { Region { occurrenceAddress, patternSize }, Occurrence::DecodeType::Binary, std::endian::native, false }); it.setAddress(occurrenceAddress); matchedBytes = 0; } @@ -348,7 +358,7 @@ namespace hex::plugin::builtin { } if (match) - results.push_back(Occurrence { Region { address, patternSize }, Occurrence::DecodeType::Binary, std::endian::native }); + results.push_back(Occurrence { Region { address, patternSize }, Occurrence::DecodeType::Binary, std::endian::native, false }); } } @@ -419,7 +429,7 @@ namespace hex::plugin::builtin { } }(); - results.push_back(Occurrence { Region { address, size }, decodeType, settings.endian }); + results.push_back(Occurrence { Region { address, size }, decodeType, settings.endian, false }); } } @@ -507,9 +517,11 @@ namespace hex::plugin::builtin { return result; } - static void drawContextMenu(const std::string &value) { + void ViewFind::drawContextMenu(Occurrence &target, const std::string &value) { if (ImGui::IsMouseClicked(ImGuiMouseButton_Right) && ImGui::IsItemHovered()) { ImGui::OpenPopup("FindContextMenu"); + target.selected = true; + this->m_replaceBuffer.clear(); } if (ImGui::BeginPopup("FindContextMenu")) { @@ -517,6 +529,53 @@ namespace hex::plugin::builtin { ImGui::SetClipboardText(value.c_str()); if (ImGui::MenuItem("hex.builtin.view.find.context.copy_demangle"_lang)) ImGui::SetClipboardText(llvm::demangle(value).c_str()); + if (ImGui::BeginMenu("hex.builtin.view.find.context.replace"_lang)) { + if (ImGui::BeginTabBar("##replace_tabs")) { + if (ImGui::BeginTabItem("hex.builtin.view.find.context.replace.hex"_lang)) { + ImGui::InputTextIcon("##replace_input", ICON_VS_SYMBOL_NAMESPACE, this->m_replaceBuffer); + + ImGui::BeginDisabled(this->m_replaceBuffer.empty()); + if (ImGui::Button("hex.builtin.view.find.context.replace"_lang)) { + for (const auto &occurrence : *this->m_sortedOccurrences) { + if (occurrence.selected) { + auto bytes = decodeByteString(this->m_replaceBuffer); + + size_t size = std::min(occurrence.region.size, bytes.size()); + + auto provider = ImHexApi::Provider::get(); + provider->write(occurrence.region.getStartAddress(), bytes.data(), size); + } + } + } + ImGui::EndDisabled(); + + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("hex.builtin.view.find.context.replace.ascii"_lang)) { + ImGui::InputTextIcon("##replace_input", ICON_VS_SYMBOL_KEY, this->m_replaceBuffer); + + ImGui::BeginDisabled(this->m_replaceBuffer.empty()); + if (ImGui::Button("hex.builtin.view.find.context.replace"_lang)) { + for (const auto &occurrence : *this->m_sortedOccurrences) { + if (occurrence.selected) { + size_t size = std::min(occurrence.region.size, this->m_replaceBuffer.size()); + + auto provider = ImHexApi::Provider::get(); + provider->write(occurrence.region.getStartAddress(), this->m_replaceBuffer.data(), size); + } + } + } + ImGui::EndDisabled(); + + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } + + ImGui::EndMenu(); + } ImGui::EndPopup(); } @@ -840,9 +899,17 @@ namespace hex::plugin::builtin { auto value = this->decodeValue(provider, foundItem, 256); ImGui::TextFormatted("{}", value); ImGui::SameLine(); - if (ImGui::Selectable("##line", false, ImGuiSelectableFlags_SpanAllColumns)) - ImHexApi::HexEditor::setSelection(foundItem.region.getStartAddress(), foundItem.region.getSize()); - drawContextMenu(value); + if (ImGui::Selectable("##line", foundItem.selected, ImGuiSelectableFlags_SpanAllColumns)) { + if (ImGui::GetIO().KeyCtrl) { + foundItem.selected = !foundItem.selected; + } else { + for (auto &occurrence : *this->m_sortedOccurrences) + occurrence.selected = false; + foundItem.selected = true; + ImHexApi::HexEditor::setSelection(foundItem.region.getStartAddress(), foundItem.region.getSize()); + } + } + drawContextMenu(foundItem, value); ImGui::PopID(); }