From df3200f93623202d45a4ddf449cd13bf5626defa Mon Sep 17 00:00:00 2001 From: iTrooz Date: Fri, 5 Dec 2025 12:04:49 +0100 Subject: [PATCH] refactor: move hex editor popups to their own files (#2536) --- plugins/builtin/CMakeLists.txt | 10 + .../popup_hex_editor_base_address.hpp | 18 + .../popup_hex_editor_decoded_string.hpp | 26 + .../hex_editor/popup_hex_editor_fill.hpp | 21 + .../hex_editor/popup_hex_editor_goto.hpp | 31 + .../hex_editor/popup_hex_editor_insert.hpp | 19 + .../hex_editor/popup_hex_editor_page_size.hpp | 18 + .../popup_hex_editor_paste_behaviour.hpp | 20 + .../hex_editor/popup_hex_editor_remove.hpp | 19 + .../hex_editor/popup_hex_editor_resize.hpp | 18 + .../hex_editor/popup_hex_editor_select.hpp | 20 + .../popup_hex_editor_base_address.cpp | 39 ++ .../popup_hex_editor_decoded_string.cpp | 48 ++ .../hex_editor/popup_hex_editor_fill.cpp | 89 +++ .../hex_editor/popup_hex_editor_find.cpp | 15 +- .../hex_editor/popup_hex_editor_goto.cpp | 122 ++++ .../hex_editor/popup_hex_editor_insert.cpp | 41 ++ .../hex_editor/popup_hex_editor_page_size.cpp | 44 ++ .../popup_hex_editor_paste_behaviour.cpp | 45 ++ .../hex_editor/popup_hex_editor_remove.cpp | 43 ++ .../hex_editor/popup_hex_editor_resize.cpp | 39 ++ .../hex_editor/popup_hex_editor_select.cpp | 80 +++ .../source/content/views/view_hex_editor.cpp | 557 +----------------- 23 files changed, 833 insertions(+), 549 deletions(-) create mode 100644 plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_base_address.hpp create mode 100644 plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_decoded_string.hpp create mode 100644 plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_fill.hpp create mode 100644 plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_goto.hpp create mode 100644 plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_insert.hpp create mode 100644 plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_page_size.hpp create mode 100644 plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_paste_behaviour.hpp create mode 100644 plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_remove.hpp create mode 100644 plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_resize.hpp create mode 100644 plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_select.hpp create mode 100644 plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_base_address.cpp create mode 100644 plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_decoded_string.cpp create mode 100644 plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_fill.cpp create mode 100644 plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_goto.cpp create mode 100644 plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_insert.cpp create mode 100644 plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_page_size.cpp create mode 100644 plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_paste_behaviour.cpp create mode 100644 plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_remove.cpp create mode 100644 plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_resize.cpp create mode 100644 plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_select.cpp diff --git a/plugins/builtin/CMakeLists.txt b/plugins/builtin/CMakeLists.txt index 499cfe79a..2217b4936 100644 --- a/plugins/builtin/CMakeLists.txt +++ b/plugins/builtin/CMakeLists.txt @@ -95,7 +95,17 @@ add_imhex_plugin( source/content/tutorials/tutorials.cpp source/content/tutorials/introduction.cpp + source/content/popups/hex_editor/popup_hex_editor_base_address.cpp + source/content/popups/hex_editor/popup_hex_editor_decoded_string.cpp + source/content/popups/hex_editor/popup_hex_editor_fill.cpp source/content/popups/hex_editor/popup_hex_editor_find.cpp + source/content/popups/hex_editor/popup_hex_editor_goto.cpp + source/content/popups/hex_editor/popup_hex_editor_insert.cpp + source/content/popups/hex_editor/popup_hex_editor_page_size.cpp + source/content/popups/hex_editor/popup_hex_editor_paste_behaviour.cpp + source/content/popups/hex_editor/popup_hex_editor_remove.cpp + source/content/popups/hex_editor/popup_hex_editor_resize.cpp + source/content/popups/hex_editor/popup_hex_editor_select.cpp source/content/views/view_hex_editor.cpp source/content/views/view_pattern_editor.cpp diff --git a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_base_address.hpp b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_base_address.hpp new file mode 100644 index 000000000..6182271c7 --- /dev/null +++ b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_base_address.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace hex::plugin::builtin { + + class PopupBaseAddress : public ViewHexEditor::Popup { + public: + explicit PopupBaseAddress(u64 baseAddress); + void draw(ViewHexEditor *editor) override; + [[nodiscard]] UnlocalizedString getTitle() const override; + + private: + static void setBaseAddress(u64 baseAddress); + u64 m_baseAddress; + }; +} \ No newline at end of file diff --git a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_decoded_string.hpp b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_decoded_string.hpp new file mode 100644 index 000000000..9da2db17c --- /dev/null +++ b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_decoded_string.hpp @@ -0,0 +1,26 @@ + +#pragma once + +#include +#include +#include +#include + +namespace hex::plugin::builtin { + + class PopupDecodedString final : public ViewHexEditor::Popup { + public: + explicit PopupDecodedString(std::string decodedString); + + void draw(ViewHexEditor *) override; + + [[nodiscard]] UnlocalizedString getTitle() const override; + + [[nodiscard]] bool canBePinned() const override; + [[nodiscard]] ImGuiWindowFlags getFlags() const override; + + private: + std::string m_decodedString; + ui::TextEditor m_editor; + }; +} diff --git a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_fill.hpp b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_fill.hpp new file mode 100644 index 000000000..a571e9636 --- /dev/null +++ b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_fill.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +namespace hex::plugin::builtin { + + class PopupFill : public ViewHexEditor::Popup { + public: + PopupFill(u64 address, size_t size); + void draw(ViewHexEditor *editor) override; + [[nodiscard]] UnlocalizedString getTitle() const override; + + private: + static void fill(u64 address, size_t size, std::string input); + u64 m_address; + u64 m_size; + std::string m_input; + }; +} \ No newline at end of file diff --git a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_goto.hpp b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_goto.hpp new file mode 100644 index 000000000..f7a0c4c05 --- /dev/null +++ b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_goto.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace hex::plugin::builtin { + + class PopupGoto : public ViewHexEditor::Popup { + public: + void draw(ViewHexEditor *editor) override; + [[nodiscard]] UnlocalizedString getTitle() const override; + bool canBePinned() const override; + + private: + enum class Mode : u8 { + Absolute, + Relative, + Begin, + End + }; + + Mode m_mode = Mode::Absolute; + std::optional m_newAddress; + bool m_requestFocus = true; + std::string m_input = "0x"; + wolv::math_eval::MathEvaluator m_evaluator; + }; +} \ No newline at end of file diff --git a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_insert.hpp b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_insert.hpp new file mode 100644 index 000000000..6750cb3ad --- /dev/null +++ b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_insert.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +namespace hex::plugin::builtin { + + class PopupInsert : public ViewHexEditor::Popup { + public: + PopupInsert(u64 address, size_t size); + void draw(ViewHexEditor *editor) override; + [[nodiscard]] UnlocalizedString getTitle() const override; + + private: + static void insert(u64 address, size_t size); + u64 m_address; + u64 m_size; + }; +} \ No newline at end of file diff --git a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_page_size.hpp b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_page_size.hpp new file mode 100644 index 000000000..9a52ba79b --- /dev/null +++ b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_page_size.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace hex::plugin::builtin { + + class PopupPageSize : public ViewHexEditor::Popup { + public: + explicit PopupPageSize(u64 pageSize); + void draw(ViewHexEditor *editor) override; + [[nodiscard]] UnlocalizedString getTitle() const override; + + private: + static void setPageSize(u64 pageSize); + u64 m_pageSize; + }; +} \ No newline at end of file diff --git a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_paste_behaviour.hpp b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_paste_behaviour.hpp new file mode 100644 index 000000000..c33254720 --- /dev/null +++ b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_paste_behaviour.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include +#include + +namespace hex::plugin::builtin { + + class PopupPasteBehaviour final : public ViewHexEditor::Popup { + public: + explicit PopupPasteBehaviour(const Region &selection, const std::function &pasteCallback); + void draw(ViewHexEditor *editor) override; + [[nodiscard]] UnlocalizedString getTitle() const override; + + private: + Region m_selection; + std::function m_pasteCallback; + }; +} \ No newline at end of file diff --git a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_remove.hpp b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_remove.hpp new file mode 100644 index 000000000..e6a9355e2 --- /dev/null +++ b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_remove.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +namespace hex::plugin::builtin { + + class PopupRemove : public ViewHexEditor::Popup { + public: + PopupRemove(u64 address, size_t size); + void draw(ViewHexEditor *editor) override; + [[nodiscard]] UnlocalizedString getTitle() const override; + + private: + static void remove(u64 address, size_t size); + u64 m_address; + u64 m_size; + }; +} \ No newline at end of file diff --git a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_resize.hpp b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_resize.hpp new file mode 100644 index 000000000..1d2ec321a --- /dev/null +++ b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_resize.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace hex::plugin::builtin { + + class PopupResize : public ViewHexEditor::Popup { + public: + explicit PopupResize(u64 currSize); + void draw(ViewHexEditor *editor) override; + [[nodiscard]] UnlocalizedString getTitle() const override; + + private: + static void resize(size_t newSize); + u64 m_size; + }; +} \ No newline at end of file diff --git a/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_select.hpp b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_select.hpp new file mode 100644 index 000000000..d89c4e6d2 --- /dev/null +++ b/plugins/builtin/include/content/popups/hex_editor/popup_hex_editor_select.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +namespace hex::plugin::builtin { + + class PopupSelect : public ViewHexEditor::Popup { + public: + PopupSelect(u64 address, size_t size); + void draw(ViewHexEditor *editor) override; + [[nodiscard]] UnlocalizedString getTitle() const override; + [[nodiscard]] bool canBePinned() const override; + + private: + Region m_region = { 0, 1 }; + bool m_justOpened = true; + }; +} \ No newline at end of file diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_base_address.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_base_address.cpp new file mode 100644 index 000000000..2452c47b6 --- /dev/null +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_base_address.cpp @@ -0,0 +1,39 @@ +#include "content/popups/hex_editor/popup_hex_editor_base_address.hpp" +#include "content/views/view_hex_editor.hpp" + +#include +#include +#include + +namespace hex::plugin::builtin { + + PopupBaseAddress::PopupBaseAddress(u64 baseAddress) : m_baseAddress(baseAddress) {} + + void PopupBaseAddress::draw(ViewHexEditor *editor) { + ImGuiExt::InputHexadecimal("##base_address", &m_baseAddress); + if (ImGui::IsItemFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { + setBaseAddress(m_baseAddress); + editor->closePopup(); + } + + ImGuiExt::ConfirmButtons("hex.ui.common.set"_lang, "hex.ui.common.cancel"_lang, + [&, this]{ + setBaseAddress(m_baseAddress); + editor->closePopup(); + }, + [&]{ + editor->closePopup(); + } + ); + } + + UnlocalizedString PopupBaseAddress::getTitle() const { + return "hex.builtin.view.hex_editor.menu.edit.set_base"; + } + + void PopupBaseAddress::setBaseAddress(u64 baseAddress) { + if (ImHexApi::Provider::isValid()) + ImHexApi::Provider::get()->setBaseAddress(baseAddress); + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_decoded_string.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_decoded_string.cpp new file mode 100644 index 000000000..f46771265 --- /dev/null +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_decoded_string.cpp @@ -0,0 +1,48 @@ +#include "content/popups/hex_editor/popup_hex_editor_decoded_string.hpp" +#include "content/views/view_hex_editor.hpp" + +#include +#include + +#include + +#include + +#include + +namespace hex::plugin::builtin { + + PopupDecodedString::PopupDecodedString(std::string decodedString) : m_decodedString(std::move(decodedString)) { + } + + void PopupDecodedString::draw(ViewHexEditor *) { + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2()); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4()); + + auto text = wolv::util::trim(wolv::util::wrapMonospacedString( + m_decodedString, + ImGui::CalcTextSize("M").x, + std::max(100_scaled, ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ScrollbarSize - ImGui::GetStyle().FrameBorderSize) + )); + + ImGui::InputTextMultiline( + "##", + text.data(), + text.size() + 1, + ImGui::GetContentRegionAvail(), + ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoHorizontalScroll + ); + + ImGui::PopStyleColor(); + ImGui::PopStyleVar(2); + } + + UnlocalizedString PopupDecodedString::getTitle() const { + return "hex.builtin.view.hex_editor.menu.edit.decoded_string.popup.title"; + } + + bool PopupDecodedString::canBePinned() const { return true; } + ImGuiWindowFlags PopupDecodedString::getFlags() const { return ImGuiWindowFlags_None; } + +} diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_fill.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_fill.cpp new file mode 100644 index 000000000..a71ceaab5 --- /dev/null +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_fill.cpp @@ -0,0 +1,89 @@ +#include "content/popups/hex_editor/popup_hex_editor_fill.hpp" +#include "content/views/view_hex_editor.hpp" + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +using namespace wolv::literals; + +namespace hex::plugin::builtin { + + PopupFill::PopupFill(u64 address, size_t size) : m_address(address), m_size(size) {} + + void PopupFill::draw(ViewHexEditor *editor) { + ImGuiExt::InputHexadecimal("hex.ui.common.address"_lang, &m_address); + ImGuiExt::InputHexadecimal("hex.ui.common.size"_lang, &m_size); + + ImGui::Separator(); + + ImGuiExt::InputTextIcon("hex.ui.common.bytes"_lang, ICON_VS_SYMBOL_NAMESPACE, m_input); + + ImGuiExt::ConfirmButtons("hex.ui.common.set"_lang, "hex.ui.common.cancel"_lang, + [&, this] { + fill(m_address, m_size, m_input); + editor->closePopup(); + }, + [&] { + editor->closePopup(); + }); + + if (ImGui::IsWindowFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { + fill(m_address, m_size, m_input); + editor->closePopup(); + } + } + + UnlocalizedString PopupFill::getTitle() const { + return "hex.builtin.view.hex_editor.menu.edit.fill"; + } + + void PopupFill::fill(u64 address, size_t size, std::string input) { + if (!ImHexApi::Provider::isValid()) + return; + + std::erase(input, ' '); + + auto bytes = crypt::decode16(input); + if (bytes.empty()) + return; + + auto provider = ImHexApi::Provider::get(); + u32 patchCount = 0; + + // Group the fill pattern into a larger chunk + constexpr static auto BatchFillSize = 1_MiB; + std::vector batchData; + if (bytes.size() < BatchFillSize) { + batchData.resize(std::min(alignTo(BatchFillSize, bytes.size()), size)); + for (u64 i = 0; i < batchData.size(); i += bytes.size()) { + auto remainingSize = std::min(batchData.size() - i, bytes.size()); + std::copy_n(bytes.begin(), remainingSize, batchData.begin() + i); + } + } else { + batchData = std::move(bytes); + } + + const auto startAddress = provider->getBaseAddress() + address; + for (u64 i = 0; i < size; i += batchData.size()) { + auto remainingSize = std::min(size - i, batchData.size()); + provider->write(startAddress + i, batchData.data(), remainingSize); + patchCount += 1; + } + provider->getUndoStack().groupOperations(patchCount, "hex.builtin.undo_operation.fill"); + + AchievementManager::unlockAchievement("hex.builtin.achievement.hex_editor", "hex.builtin.achievement.hex_editor.fill.name"); + } + +} \ No newline at end of file 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 index e34831101..0dd09e4de 100644 --- 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 @@ -1,15 +1,24 @@ #include "content/popups/hex_editor/popup_hex_editor_find.hpp" - #include "content/views/view_hex_editor.hpp" #include -#include +#include +#include +#include +#include + +#include + #include +#include + #include #include -#include +#include + +#include namespace hex::plugin::builtin { diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_goto.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_goto.cpp new file mode 100644 index 000000000..c414e16c1 --- /dev/null +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_goto.cpp @@ -0,0 +1,122 @@ +#include "content/popups/hex_editor/popup_hex_editor_goto.hpp" +#include "content/views/view_hex_editor.hpp" + +#include +#include + +#include + +#include + +#include + +#include + +namespace hex::plugin::builtin { + + void PopupGoto::draw(ViewHexEditor *editor) { + bool updateAddress = false; + if (ImGui::BeginTabBar("goto_tabs")) { + if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.absolute"_lang)) { + m_mode = Mode::Absolute; + updateAddress = true; + ImGui::EndTabItem(); + } + + ImGui::BeginDisabled(!editor->isSelectionValid()); + if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.relative"_lang)) { + m_mode = Mode::Relative; + updateAddress = true; + ImGui::EndTabItem(); + } + ImGui::EndDisabled(); + + if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.begin"_lang)) { + m_mode = Mode::Begin; + updateAddress = true; + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.end"_lang)) { + m_mode = Mode::End; + updateAddress = true; + ImGui::EndTabItem(); + } + + if (m_requestFocus){ + ImGui::SetKeyboardFocusHere(); + m_requestFocus = false; + } + + if (ImGuiExt::InputTextIcon("##input", ICON_VS_SYMBOL_OPERATOR, m_input)) { + updateAddress = true; + } + + if (updateAddress) { + if (auto result = m_evaluator.evaluate(m_input); result.has_value()) { + const auto inputResult = u64(result.value()); + auto provider = ImHexApi::Provider::get(); + + switch (m_mode) { + case Mode::Absolute: { + m_newAddress = inputResult; + } + break; + case Mode::Relative: { + const auto selection = editor->getSelection(); + m_newAddress = selection.getStartAddress() + inputResult; + } + break; + case Mode::Begin: { + m_newAddress = provider->getBaseAddress() + provider->getCurrentPageAddress() + inputResult; + } + break; + case Mode::End: { + m_newAddress = provider->getActualSize() - inputResult; + } + break; + } + } else { + m_newAddress.reset(); + } + } + + bool isOffsetValid = m_newAddress <= ImHexApi::Provider::get()->getActualSize(); + + bool executeGoto = false; + + if (ImGui::IsWindowFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { + executeGoto = true; + } + + ImGui::BeginDisabled(!m_newAddress.has_value() || !isOffsetValid); + { + const auto label = fmt::format("{} {}", "hex.builtin.view.hex_editor.menu.file.goto"_lang, m_newAddress.has_value() ? fmt::format("0x{:08X}", *m_newAddress) : "???"); + const auto buttonWidth = ImGui::GetWindowWidth() - ImGui::GetStyle().WindowPadding.x * 2; + if (ImGuiExt::DimmedButton(label.c_str(), ImVec2(buttonWidth, 0))) { + executeGoto = true; + } + } + ImGui::EndDisabled(); + + if (executeGoto && m_newAddress.has_value()) { + editor->setSelection(*m_newAddress, *m_newAddress); + editor->jumpToSelection(); + + if (!this->isPinned()) + editor->closePopup(); + } + + ImGui::EndTabBar(); + } + } + + UnlocalizedString PopupGoto::getTitle() const { + return "hex.builtin.view.hex_editor.menu.file.goto"; + } + + bool PopupGoto::canBePinned() const { + return true; + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_insert.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_insert.cpp new file mode 100644 index 000000000..9ba1959a1 --- /dev/null +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_insert.cpp @@ -0,0 +1,41 @@ +#include "content/popups/hex_editor/popup_hex_editor_insert.hpp" +#include "content/views/view_hex_editor.hpp" + +#include +#include + +#include + +namespace hex::plugin::builtin { + + PopupInsert::PopupInsert(u64 address, size_t size) : m_address(address), m_size(size) {} + + void PopupInsert::draw(ViewHexEditor *editor) { + ImGuiExt::InputHexadecimal("hex.ui.common.address"_lang, &m_address); + ImGuiExt::InputHexadecimal("hex.ui.common.size"_lang, &m_size); + + ImGuiExt::ConfirmButtons("hex.ui.common.set"_lang, "hex.ui.common.cancel"_lang, + [&, this]{ + insert(m_address, m_size); + editor->closePopup(); + }, + [&]{ + editor->closePopup(); + }); + + if (ImGui::IsWindowFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { + insert(m_address, m_size); + editor->closePopup(); + } + } + + UnlocalizedString PopupInsert::getTitle() const { + return "hex.builtin.view.hex_editor.menu.edit.insert"; + } + + void PopupInsert::insert(u64 address, size_t size) { + if (ImHexApi::Provider::isValid()) + ImHexApi::Provider::get()->insert(address, size); + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_page_size.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_page_size.cpp new file mode 100644 index 000000000..450232039 --- /dev/null +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_page_size.cpp @@ -0,0 +1,44 @@ +#include "content/popups/hex_editor/popup_hex_editor_page_size.hpp" +#include "content/views/view_hex_editor.hpp" + +#include +#include + +#include + +namespace hex::plugin::builtin { + + PopupPageSize::PopupPageSize(u64 pageSize) : m_pageSize(pageSize) {} + + void PopupPageSize::draw(ViewHexEditor *editor) { + ImGuiExt::InputHexadecimal("##page_size", &m_pageSize); + if (ImGui::IsItemFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { + setPageSize(m_pageSize); + editor->closePopup(); + } + + ImGuiExt::ConfirmButtons("hex.ui.common.set"_lang, "hex.ui.common.cancel"_lang, + [&, this]{ + setPageSize(m_pageSize); + editor->closePopup(); + }, + [&]{ + editor->closePopup(); + } + ); + } + + UnlocalizedString PopupPageSize::getTitle() const { + return "hex.builtin.view.hex_editor.menu.edit.set_page_size"; + } + + void PopupPageSize::setPageSize(u64 pageSize) { + if (ImHexApi::Provider::isValid()) { + auto provider = ImHexApi::Provider::get(); + + provider->setPageSize(pageSize); + provider->setCurrentPage(0); + } + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_paste_behaviour.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_paste_behaviour.cpp new file mode 100644 index 000000000..0f2852fdd --- /dev/null +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_paste_behaviour.cpp @@ -0,0 +1,45 @@ +#include "content/popups/hex_editor/popup_hex_editor_paste_behaviour.hpp" +#include "content/views/view_hex_editor.hpp" + +#include + +#include + +namespace hex::plugin::builtin { + + PopupPasteBehaviour::PopupPasteBehaviour(const Region &selection, const std::function &pasteCallback) + : m_selection(), m_pasteCallback(pasteCallback) { + m_selection = Region { .address=selection.getStartAddress(), .size=selection.getSize() }; + } + + void PopupPasteBehaviour::draw(ViewHexEditor *editor) { + const auto width = ImGui::GetWindowWidth(); + + ImGui::TextWrapped("%s", "hex.builtin.view.hex_editor.menu.edit.paste.popup.description"_lang.get()); + ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.edit.paste.popup.hint"_lang); + + ImGui::Separator(); + + if (ImGui::Button("hex.builtin.view.hex_editor.menu.edit.paste.popup.button.selection"_lang, ImVec2(width / 4, 0))) { + m_pasteCallback(m_selection, true); + editor->closePopup(); + } + + ImGui::SameLine(); + if (ImGui::Button("hex.builtin.view.hex_editor.menu.edit.paste.popup.button.everything"_lang, ImVec2(width / 4, 0))) { + m_pasteCallback(m_selection, false); + editor->closePopup(); + } + + ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - (width / 6)); + if (ImGui::Button("hex.ui.common.cancel"_lang, ImVec2(width / 6, 0))) { + // Cancel the action, without updating settings nor pasting. + editor->closePopup(); + } + } + + UnlocalizedString PopupPasteBehaviour::getTitle() const { + return "hex.builtin.view.hex_editor.menu.edit.paste.popup.title"; + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_remove.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_remove.cpp new file mode 100644 index 000000000..2155e8c07 --- /dev/null +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_remove.cpp @@ -0,0 +1,43 @@ +#include "content/popups/hex_editor/popup_hex_editor_remove.hpp" +#include "content/views/view_hex_editor.hpp" + +#include +#include + +#include + +#include + +namespace hex::plugin::builtin { + + PopupRemove::PopupRemove(u64 address, size_t size) : m_address(address), m_size(size) {} + + void PopupRemove::draw(ViewHexEditor *editor) { + ImGuiExt::InputHexadecimal("hex.ui.common.address"_lang, &m_address); + ImGuiExt::InputHexadecimal("hex.ui.common.size"_lang, &m_size); + + ImGuiExt::ConfirmButtons("hex.ui.common.set"_lang, "hex.ui.common.cancel"_lang, + [&, this]{ + remove(m_address, m_size); + editor->closePopup(); + }, + [&]{ + editor->closePopup(); + }); + + if (ImGui::IsWindowFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { + remove(m_address, m_size); + editor->closePopup(); + } + } + + UnlocalizedString PopupRemove::getTitle() const { + return "hex.builtin.view.hex_editor.menu.edit.remove"; + } + + void PopupRemove::remove(u64 address, size_t size) { + if (ImHexApi::Provider::isValid()) + ImHexApi::Provider::get()->remove(address, size); + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_resize.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_resize.cpp new file mode 100644 index 000000000..9e0c7ec65 --- /dev/null +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_resize.cpp @@ -0,0 +1,39 @@ +#include "content/popups/hex_editor/popup_hex_editor_resize.hpp" +#include "content/views/view_hex_editor.hpp" + +#include +#include + +#include + +namespace hex::plugin::builtin { + + PopupResize::PopupResize(u64 currSize) : m_size(currSize) {} + + void PopupResize::draw(ViewHexEditor *editor) { + ImGuiExt::InputHexadecimal("##resize", &m_size); + if (ImGui::IsItemFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { + this->resize(m_size); + editor->closePopup(); + } + + ImGuiExt::ConfirmButtons("hex.ui.common.set"_lang, "hex.ui.common.cancel"_lang, + [&, this]{ + this->resize(m_size); + editor->closePopup(); + }, + [&]{ + editor->closePopup(); + }); + } + + UnlocalizedString PopupResize::getTitle() const { + return "hex.builtin.view.hex_editor.menu.edit.resize"; + } + + void PopupResize::resize(size_t newSize) { + if (ImHexApi::Provider::isValid()) + ImHexApi::Provider::get()->resize(newSize); + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_select.cpp b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_select.cpp new file mode 100644 index 000000000..12c6f33c3 --- /dev/null +++ b/plugins/builtin/source/content/popups/hex_editor/popup_hex_editor_select.cpp @@ -0,0 +1,80 @@ +#include "content/popups/hex_editor/popup_hex_editor_select.hpp" +#include "content/views/view_hex_editor.hpp" + +#include +#include + +#include + +namespace hex::plugin::builtin { + + PopupSelect::PopupSelect(u64 address, size_t size) : m_region({address, size}) {} + + void PopupSelect::draw(ViewHexEditor *editor) { + if (ImGui::BeginTabBar("select_tabs")) { + if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.select.offset.region"_lang)) { + u64 inputA = m_region.getStartAddress(); + u64 inputB = m_region.getEndAddress(); + + if (m_justOpened) { + ImGui::SetKeyboardFocusHere(); + m_justOpened = false; + } + ImGuiExt::InputHexadecimal("hex.builtin.view.hex_editor.select.offset.begin"_lang, &inputA, ImGuiInputTextFlags_AutoSelectAll); + ImGuiExt::InputHexadecimal("hex.builtin.view.hex_editor.select.offset.end"_lang, &inputB, ImGuiInputTextFlags_AutoSelectAll); + + if (inputB < inputA) + inputB = inputA; + + m_region = { inputA, (inputB - inputA) + 1 }; + + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.select.offset.size"_lang)) { + u64 inputA = m_region.getStartAddress(); + u64 inputB = m_region.getSize(); + + if (m_justOpened) { + ImGui::SetKeyboardFocusHere(); + m_justOpened = false; + } + ImGuiExt::InputHexadecimal("hex.builtin.view.hex_editor.select.offset.begin"_lang, &inputA, ImGuiInputTextFlags_AutoSelectAll); + ImGuiExt::InputHexadecimal("hex.builtin.view.hex_editor.select.offset.size"_lang, &inputB, ImGuiInputTextFlags_AutoSelectAll); + + if (inputB <= 0) + inputB = 1; + + m_region = { inputA, inputB }; + ImGui::EndTabItem(); + } + + const auto provider = ImHexApi::Provider::get(); + bool isOffsetValid = m_region.getStartAddress() <= m_region.getEndAddress() && + m_region.getEndAddress() < provider->getActualSize(); + ImGui::BeginDisabled(!isOffsetValid); + { + if (ImGui::Button("hex.builtin.view.hex_editor.select.select"_lang) || + (ImGui::IsWindowFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter)))) { + editor->setSelection(m_region.getStartAddress(), m_region.getEndAddress()); + editor->jumpToSelection(); + + if (!this->isPinned()) + editor->closePopup(); + } + } + ImGui::EndDisabled(); + + ImGui::EndTabBar(); + } + } + + UnlocalizedString PopupSelect::getTitle() const { + return "hex.builtin.view.hex_editor.menu.edit.select"; + } + + bool PopupSelect::canBePinned() const { + return true; + } + +} \ 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 42ca3017f..25bae3f25 100644 --- a/plugins/builtin/source/content/views/view_hex_editor.cpp +++ b/plugins/builtin/source/content/views/view_hex_editor.cpp @@ -28,6 +28,17 @@ #include #include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -40,552 +51,6 @@ using namespace wolv::literals; namespace hex::plugin::builtin { - /* Popups */ - - class PopupGoto : public ViewHexEditor::Popup { - public: - void draw(ViewHexEditor *editor) override { - bool updateAddress = false; - if (ImGui::BeginTabBar("goto_tabs")) { - if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.absolute"_lang)) { - m_mode = Mode::Absolute; - updateAddress = true; - ImGui::EndTabItem(); - } - - ImGui::BeginDisabled(!editor->isSelectionValid()); - if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.relative"_lang)) { - m_mode = Mode::Relative; - updateAddress = true; - ImGui::EndTabItem(); - } - ImGui::EndDisabled(); - - if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.begin"_lang)) { - m_mode = Mode::Begin; - updateAddress = true; - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.end"_lang)) { - m_mode = Mode::End; - updateAddress = true; - ImGui::EndTabItem(); - } - - if (m_requestFocus){ - ImGui::SetKeyboardFocusHere(); - m_requestFocus = false; - } - - if (ImGuiExt::InputTextIcon("##input", ICON_VS_SYMBOL_OPERATOR, m_input)) { - updateAddress = true; - } - - if (updateAddress) { - if (auto result = m_evaluator.evaluate(m_input); result.has_value()) { - const auto inputResult = u64(result.value()); - auto provider = ImHexApi::Provider::get(); - - switch (m_mode) { - case Mode::Absolute: { - m_newAddress = inputResult; - } - break; - case Mode::Relative: { - const auto selection = editor->getSelection(); - m_newAddress = selection.getStartAddress() + inputResult; - } - break; - case Mode::Begin: { - m_newAddress = provider->getBaseAddress() + provider->getCurrentPageAddress() + inputResult; - } - break; - case Mode::End: { - m_newAddress = provider->getActualSize() - inputResult; - } - break; - } - } else { - m_newAddress.reset(); - } - } - - bool isOffsetValid = m_newAddress <= ImHexApi::Provider::get()->getActualSize(); - - bool executeGoto = false; - - if (ImGui::IsWindowFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { - executeGoto = true; - } - - ImGui::BeginDisabled(!m_newAddress.has_value() || !isOffsetValid); - { - const auto label = fmt::format("{} {}", "hex.builtin.view.hex_editor.menu.file.goto"_lang, m_newAddress.has_value() ? fmt::format("0x{:08X}", *m_newAddress) : "???"); - const auto buttonWidth = ImGui::GetWindowWidth() - ImGui::GetStyle().WindowPadding.x * 2; - if (ImGuiExt::DimmedButton(label.c_str(), ImVec2(buttonWidth, 0))) { - executeGoto = true; - } - } - ImGui::EndDisabled(); - - if (executeGoto && m_newAddress.has_value()) { - editor->setSelection(*m_newAddress, *m_newAddress); - editor->jumpToSelection(); - - if (!this->isPinned()) - editor->closePopup(); - } - - ImGui::EndTabBar(); - } - } - - [[nodiscard]] UnlocalizedString getTitle() const override { - return "hex.builtin.view.hex_editor.menu.file.goto"; - } - - bool canBePinned() const override { - return true; - } - - private: - enum class Mode : u8 { - Absolute, - Relative, - Begin, - End - }; - - Mode m_mode = Mode::Absolute; - std::optional m_newAddress; - - bool m_requestFocus = true; - std::string m_input = "0x"; - wolv::math_eval::MathEvaluator m_evaluator; - }; - - class PopupSelect : public ViewHexEditor::Popup { - public: - - PopupSelect(u64 address, size_t size): m_region({address, size}) {} - - void draw(ViewHexEditor *editor) override { - if (ImGui::BeginTabBar("select_tabs")) { - if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.select.offset.region"_lang)) { - u64 inputA = m_region.getStartAddress(); - u64 inputB = m_region.getEndAddress(); - - if (m_justOpened) { - ImGui::SetKeyboardFocusHere(); - m_justOpened = false; - } - ImGuiExt::InputHexadecimal("hex.builtin.view.hex_editor.select.offset.begin"_lang, &inputA, ImGuiInputTextFlags_AutoSelectAll); - ImGuiExt::InputHexadecimal("hex.builtin.view.hex_editor.select.offset.end"_lang, &inputB, ImGuiInputTextFlags_AutoSelectAll); - - if (inputB < inputA) - inputB = inputA; - - m_region = { inputA, (inputB - inputA) + 1 }; - - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.select.offset.size"_lang)) { - u64 inputA = m_region.getStartAddress(); - u64 inputB = m_region.getSize(); - - if (m_justOpened) { - ImGui::SetKeyboardFocusHere(); - m_justOpened = false; - } - ImGuiExt::InputHexadecimal("hex.builtin.view.hex_editor.select.offset.begin"_lang, &inputA, ImGuiInputTextFlags_AutoSelectAll); - ImGuiExt::InputHexadecimal("hex.builtin.view.hex_editor.select.offset.size"_lang, &inputB, ImGuiInputTextFlags_AutoSelectAll); - - if (inputB <= 0) - inputB = 1; - - m_region = { inputA, inputB }; - ImGui::EndTabItem(); - } - - const auto provider = ImHexApi::Provider::get(); - bool isOffsetValid = m_region.getStartAddress() <= m_region.getEndAddress() && - m_region.getEndAddress() < provider->getActualSize(); - ImGui::BeginDisabled(!isOffsetValid); - { - if (ImGui::Button("hex.builtin.view.hex_editor.select.select"_lang) || - (ImGui::IsWindowFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter)))) { - editor->setSelection(m_region.getStartAddress(), m_region.getEndAddress()); - editor->jumpToSelection(); - - if (!this->isPinned()) - editor->closePopup(); - } - } - ImGui::EndDisabled(); - - ImGui::EndTabBar(); - } - } - - [[nodiscard]] UnlocalizedString getTitle() const override { - return "hex.builtin.view.hex_editor.menu.edit.select"; - } - - [[nodiscard]] bool canBePinned() const override { - return true; - } - - private: - Region m_region = { 0, 1 }; - bool m_justOpened = true; - }; - - class PopupBaseAddress : public ViewHexEditor::Popup { - public: - explicit PopupBaseAddress(u64 baseAddress) : m_baseAddress(baseAddress) { } - - void draw(ViewHexEditor *editor) override { - ImGuiExt::InputHexadecimal("##base_address", &m_baseAddress); - if (ImGui::IsItemFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { - setBaseAddress(m_baseAddress); - editor->closePopup(); - } - - ImGuiExt::ConfirmButtons("hex.ui.common.set"_lang, "hex.ui.common.cancel"_lang, - [&, this]{ - setBaseAddress(m_baseAddress); - editor->closePopup(); - }, - [&]{ - editor->closePopup(); - } - ); - } - - [[nodiscard]] UnlocalizedString getTitle() const override { - return "hex.builtin.view.hex_editor.menu.edit.set_base"; - } - - private: - static void setBaseAddress(u64 baseAddress) { - if (ImHexApi::Provider::isValid()) - ImHexApi::Provider::get()->setBaseAddress(baseAddress); - } - - private: - u64 m_baseAddress; - }; - - class PopupPageSize : public ViewHexEditor::Popup { - public: - explicit PopupPageSize(u64 pageSize) : m_pageSize(pageSize) { } - - void draw(ViewHexEditor *editor) override { - ImGuiExt::InputHexadecimal("##page_size", &m_pageSize); - if (ImGui::IsItemFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { - setPageSize(m_pageSize); - editor->closePopup(); - } - - ImGuiExt::ConfirmButtons("hex.ui.common.set"_lang, "hex.ui.common.cancel"_lang, - [&, this]{ - setPageSize(m_pageSize); - editor->closePopup(); - }, - [&]{ - editor->closePopup(); - } - ); - } - - [[nodiscard]] UnlocalizedString getTitle() const override { - return "hex.builtin.view.hex_editor.menu.edit.set_page_size"; - } - - private: - static void setPageSize(u64 pageSize) { - if (ImHexApi::Provider::isValid()) { - auto provider = ImHexApi::Provider::get(); - - provider->setPageSize(pageSize); - provider->setCurrentPage(0); - } - } - - private: - u64 m_pageSize; - }; - - class PopupResize : public ViewHexEditor::Popup { - public: - explicit PopupResize(u64 currSize) : m_size(currSize) {} - - void draw(ViewHexEditor *editor) override { - ImGuiExt::InputHexadecimal("##resize", &m_size); - if (ImGui::IsItemFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { - this->resize(m_size); - editor->closePopup(); - } - - ImGuiExt::ConfirmButtons("hex.ui.common.set"_lang, "hex.ui.common.cancel"_lang, - [&, this]{ - this->resize(m_size); - editor->closePopup(); - }, - [&]{ - editor->closePopup(); - }); - } - - [[nodiscard]] UnlocalizedString getTitle() const override { - return "hex.builtin.view.hex_editor.menu.edit.resize"; - } - - private: - static void resize(size_t newSize) { - if (ImHexApi::Provider::isValid()) - ImHexApi::Provider::get()->resize(newSize); - } - - private: - u64 m_size; - }; - - class PopupInsert : public ViewHexEditor::Popup { - public: - PopupInsert(u64 address, size_t size) : m_address(address), m_size(size) {} - - void draw(ViewHexEditor *editor) override { - ImGuiExt::InputHexadecimal("hex.ui.common.address"_lang, &m_address); - ImGuiExt::InputHexadecimal("hex.ui.common.size"_lang, &m_size); - - ImGuiExt::ConfirmButtons("hex.ui.common.set"_lang, "hex.ui.common.cancel"_lang, - [&, this]{ - insert(m_address, m_size); - editor->closePopup(); - }, - [&]{ - editor->closePopup(); - }); - - if (ImGui::IsWindowFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { - insert(m_address, m_size); - editor->closePopup(); - } - } - - [[nodiscard]] UnlocalizedString getTitle() const override { - return "hex.builtin.view.hex_editor.menu.edit.insert"; - } - - private: - static void insert(u64 address, size_t size) { - if (ImHexApi::Provider::isValid()) - ImHexApi::Provider::get()->insert(address, size); - } - - private: - u64 m_address; - u64 m_size; - }; - - class PopupRemove : public ViewHexEditor::Popup { - public: - PopupRemove(u64 address, size_t size) : m_address(address), m_size(size) {} - - void draw(ViewHexEditor *editor) override { - ImGuiExt::InputHexadecimal("hex.ui.common.address"_lang, &m_address); - ImGuiExt::InputHexadecimal("hex.ui.common.size"_lang, &m_size); - - ImGuiExt::ConfirmButtons("hex.ui.common.set"_lang, "hex.ui.common.cancel"_lang, - [&, this]{ - remove(m_address, m_size); - editor->closePopup(); - }, - [&]{ - editor->closePopup(); - }); - - if (ImGui::IsWindowFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { - remove(m_address, m_size); - editor->closePopup(); - } - } - - [[nodiscard]] UnlocalizedString getTitle() const override { - return "hex.builtin.view.hex_editor.menu.edit.remove"; - } - - private: - static void remove(u64 address, size_t size) { - if (ImHexApi::Provider::isValid()) - ImHexApi::Provider::get()->remove(address, size); - } - - private: - u64 m_address; - u64 m_size; - }; - - class PopupFill : public ViewHexEditor::Popup { - public: - PopupFill(u64 address, size_t size) : m_address(address), m_size(size) {} - - void draw(ViewHexEditor *editor) override { - ImGuiExt::InputHexadecimal("hex.ui.common.address"_lang, &m_address); - ImGuiExt::InputHexadecimal("hex.ui.common.size"_lang, &m_size); - - ImGui::Separator(); - - ImGuiExt::InputTextIcon("hex.ui.common.bytes"_lang, ICON_VS_SYMBOL_NAMESPACE, m_input); - - ImGuiExt::ConfirmButtons("hex.ui.common.set"_lang, "hex.ui.common.cancel"_lang, - [&, this] { - fill(m_address, m_size, m_input); - editor->closePopup(); - }, - [&] { - editor->closePopup(); - }); - - if (ImGui::IsWindowFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) { - fill(m_address, m_size, m_input); - editor->closePopup(); - } - } - - [[nodiscard]] UnlocalizedString getTitle() const override { - return "hex.builtin.view.hex_editor.menu.edit.fill"; - } - - private: - static void fill(u64 address, size_t size, std::string input) { - if (!ImHexApi::Provider::isValid()) - return; - - std::erase(input, ' '); - - auto bytes = crypt::decode16(input); - if (bytes.empty()) - return; - - auto provider = ImHexApi::Provider::get(); - u32 patchCount = 0; - - // Group the fill pattern into a larger chunk - constexpr static auto BatchFillSize = 1_MiB; - std::vector batchData; - if (bytes.size() < BatchFillSize) { - batchData.resize(std::min(alignTo(BatchFillSize, bytes.size()), size)); - for (u64 i = 0; i < batchData.size(); i += bytes.size()) { - auto remainingSize = std::min(batchData.size() - i, bytes.size()); - std::copy_n(bytes.begin(), remainingSize, batchData.begin() + i); - } - } else { - batchData = std::move(bytes); - } - - const auto startAddress = provider->getBaseAddress() + address; - for (u64 i = 0; i < size; i += batchData.size()) { - auto remainingSize = std::min(size - i, batchData.size()); - provider->write(startAddress + i, batchData.data(), remainingSize); - patchCount += 1; - } - provider->getUndoStack().groupOperations(patchCount, "hex.builtin.undo_operation.fill"); - - AchievementManager::unlockAchievement("hex.builtin.achievement.hex_editor", "hex.builtin.achievement.hex_editor.fill.name"); - } - - private: - u64 m_address; - u64 m_size; - - std::string m_input; - }; - - class PopupPasteBehaviour final : public ViewHexEditor::Popup { - public: - explicit PopupPasteBehaviour(const Region &selection, const auto &pasteCallback) : m_selection(), m_pasteCallback(pasteCallback) { - m_selection = Region { .address=selection.getStartAddress(), .size=selection.getSize() }; - } - - void draw(ViewHexEditor *editor) override { - const auto width = ImGui::GetWindowWidth(); - - ImGui::TextWrapped("%s", "hex.builtin.view.hex_editor.menu.edit.paste.popup.description"_lang.get()); - ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.edit.paste.popup.hint"_lang); - - ImGui::Separator(); - - if (ImGui::Button("hex.builtin.view.hex_editor.menu.edit.paste.popup.button.selection"_lang, ImVec2(width / 4, 0))) { - m_pasteCallback(m_selection, true); - editor->closePopup(); - } - - ImGui::SameLine(); - if (ImGui::Button("hex.builtin.view.hex_editor.menu.edit.paste.popup.button.everything"_lang, ImVec2(width / 4, 0))) { - m_pasteCallback(m_selection, false); - editor->closePopup(); - } - - ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - (width / 6)); - if (ImGui::Button("hex.ui.common.cancel"_lang, ImVec2(width / 6, 0))) { - // Cancel the action, without updating settings nor pasting. - editor->closePopup(); - } - } - - [[nodiscard]] UnlocalizedString getTitle() const override { - return "hex.builtin.view.hex_editor.menu.edit.paste.popup.title"; - } - - private: - Region m_selection; - std::function m_pasteCallback; - }; - - class PopupDecodedString final : public ViewHexEditor::Popup { - public: - explicit PopupDecodedString(std::string decodedString) : m_decodedString(std::move(decodedString)) { - } - - void draw(ViewHexEditor *) override { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2()); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F); - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4()); - - auto text = wolv::util::trim(wolv::util::wrapMonospacedString( - m_decodedString, - ImGui::CalcTextSize("M").x, - std::max(100_scaled, ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ScrollbarSize - ImGui::GetStyle().FrameBorderSize) - )); - - ImGui::InputTextMultiline( - "##", - text.data(), - text.size() + 1, - ImGui::GetContentRegionAvail(), - ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoHorizontalScroll - ); - - ImGui::PopStyleColor(); - ImGui::PopStyleVar(2); - } - - [[nodiscard]] UnlocalizedString getTitle() const override { - return "hex.builtin.view.hex_editor.menu.edit.decoded_string.popup.title"; - } - - [[nodiscard]] bool canBePinned() const override { return true; } - [[nodiscard]] ImGuiWindowFlags getFlags() const override { return ImGuiWindowFlags_None; } - - private: - std::string m_decodedString; - ui::TextEditor m_editor; - }; - /* Hex Editor */ ViewHexEditor::ViewHexEditor() : View::Window("hex.builtin.view.hex_editor.name", ICON_VS_FILE_BINARY) {