refactor: move hex editor popups to their own files (#2536)

This commit is contained in:
iTrooz
2025-12-05 12:04:49 +01:00
committed by GitHub
parent 7374a06faa
commit df3200f936
23 changed files with 833 additions and 549 deletions

View File

@@ -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

View File

@@ -0,0 +1,18 @@
#pragma once
#include <content/views/view_hex_editor.hpp>
#include <hex/api/localization_manager.hpp>
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;
};
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include <content/views/view_hex_editor.hpp>
#include <hex/api/localization_manager.hpp>
#include <ui/text_editor.hpp>
#include <string>
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;
};
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <content/views/view_hex_editor.hpp>
#include <hex/api/localization_manager.hpp>
#include <string>
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;
};
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <content/views/view_hex_editor.hpp>
#include <hex/api/localization_manager.hpp>
#include <wolv/math_eval/math_evaluator.hpp>
#include <optional>
#include <string>
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<u64> m_newAddress;
bool m_requestFocus = true;
std::string m_input = "0x";
wolv::math_eval::MathEvaluator<i128> m_evaluator;
};
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <content/views/view_hex_editor.hpp>
#include <hex/api/localization_manager.hpp>
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;
};
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include <content/views/view_hex_editor.hpp>
#include <hex/api/localization_manager.hpp>
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;
};
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include <content/views/view_hex_editor.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/helpers/utils.hpp>
#include <functional>
namespace hex::plugin::builtin {
class PopupPasteBehaviour final : public ViewHexEditor::Popup {
public:
explicit PopupPasteBehaviour(const Region &selection, const std::function<void(const Region &selection, bool selectionCheck)> &pasteCallback);
void draw(ViewHexEditor *editor) override;
[[nodiscard]] UnlocalizedString getTitle() const override;
private:
Region m_selection;
std::function<void(const Region &selection, bool selectionCheck)> m_pasteCallback;
};
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <content/views/view_hex_editor.hpp>
#include <hex/api/localization_manager.hpp>
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;
};
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include <content/views/view_hex_editor.hpp>
#include <hex/api/localization_manager.hpp>
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;
};
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include <content/views/view_hex_editor.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/helpers/utils.hpp>
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;
};
}

View File

@@ -0,0 +1,39 @@
#include "content/popups/hex_editor/popup_hex_editor_base_address.hpp"
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry/settings.hpp>
#include <hex/api/content_registry/user_interface.hpp>
#include <content/differing_byte_searcher.hpp>
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);
}
}

View File

@@ -0,0 +1,48 @@
#include "content/popups/hex_editor/popup_hex_editor_decoded_string.hpp"
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry/settings.hpp>
#include <hex/api/content_registry/pattern_language.hpp>
#include <content/differing_byte_searcher.hpp>
#include <popups/popup_file_chooser.hpp>
#include <ui/text_editor.hpp>
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; }
}

View File

@@ -0,0 +1,89 @@
#include "content/popups/hex_editor/popup_hex_editor_fill.hpp"
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry/settings.hpp>
#include <hex/api/content_registry/user_interface.hpp>
#include <hex/api/content_registry/pattern_language.hpp>
#include <hex/api/achievement_manager.hpp>
#include <content/differing_byte_searcher.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/crypto.hpp>
#include <fonts/vscode_icons.hpp>
#include <ui/text_editor.hpp>
#include <wolv/literals.hpp>
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<u8> batchData;
if (bytes.size() < BatchFillSize) {
batchData.resize(std::min<u64>(alignTo<u64>(BatchFillSize, bytes.size()), size));
for (u64 i = 0; i < batchData.size(); i += bytes.size()) {
auto remainingSize = std::min<size_t>(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_t>(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");
}
}

View File

@@ -1,15 +1,24 @@
#include "content/popups/hex_editor/popup_hex_editor_find.hpp"
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry/views.hpp>
#include <hex/helpers/crypto.hpp>
#include <hex/api/content_registry/settings.hpp>
#include <hex/api/content_registry/user_interface.hpp>
#include <hex/api/content_registry/pattern_language.hpp>
#include <hex/api/achievement_manager.hpp>
#include <content/differing_byte_searcher.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/crypto.hpp>
#include <hex/providers/buffered_reader.hpp>
#include <fonts/vscode_icons.hpp>
#include <bit>
#include <popups/popup_file_chooser.hpp>
#include <ui/text_editor.hpp>
namespace hex::plugin::builtin {

View File

@@ -0,0 +1,122 @@
#include "content/popups/hex_editor/popup_hex_editor_goto.hpp"
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry/settings.hpp>
#include <hex/api/content_registry/user_interface.hpp>
#include <content/differing_byte_searcher.hpp>
#include <toasts/toast_notification.hpp>
#include <fonts/vscode_icons.hpp>
#include <pl/patterns/pattern.hpp>
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;
}
}

View File

@@ -0,0 +1,41 @@
#include "content/popups/hex_editor/popup_hex_editor_insert.hpp"
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry/settings.hpp>
#include <hex/api/content_registry/user_interface.hpp>
#include <content/differing_byte_searcher.hpp>
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);
}
}

View File

@@ -0,0 +1,44 @@
#include "content/popups/hex_editor/popup_hex_editor_page_size.hpp"
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry/settings.hpp>
#include <hex/api/content_registry/user_interface.hpp>
#include <content/differing_byte_searcher.hpp>
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);
}
}
}

View File

@@ -0,0 +1,45 @@
#include "content/popups/hex_editor/popup_hex_editor_paste_behaviour.hpp"
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry/settings.hpp>
#include <ui/text_editor.hpp>
namespace hex::plugin::builtin {
PopupPasteBehaviour::PopupPasteBehaviour(const Region &selection, const std::function<void(const Region &selection, bool selectionCheck)> &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";
}
}

View File

@@ -0,0 +1,43 @@
#include "content/popups/hex_editor/popup_hex_editor_remove.hpp"
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry/settings.hpp>
#include <hex/api/content_registry/user_interface.hpp>
#include <content/differing_byte_searcher.hpp>
#include <ui/text_editor.hpp>
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);
}
}

View File

@@ -0,0 +1,39 @@
#include "content/popups/hex_editor/popup_hex_editor_resize.hpp"
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry/settings.hpp>
#include <hex/api/content_registry/user_interface.hpp>
#include <content/differing_byte_searcher.hpp>
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);
}
}

View File

@@ -0,0 +1,80 @@
#include "content/popups/hex_editor/popup_hex_editor_select.hpp"
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry/settings.hpp>
#include <hex/api/content_registry/user_interface.hpp>
#include <content/differing_byte_searcher.hpp>
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;
}
}

View File

@@ -28,6 +28,17 @@
#include <fonts/vscode_icons.hpp>
#include <popups/popup_file_chooser.hpp>
#include <content/popups/hex_editor/popup_hex_editor_goto.hpp>
#include <content/popups/hex_editor/popup_hex_editor_select.hpp>
#include <content/popups/hex_editor/popup_hex_editor_base_address.hpp>
#include <content/popups/hex_editor/popup_hex_editor_page_size.hpp>
#include <content/popups/hex_editor/popup_hex_editor_resize.hpp>
#include <content/popups/hex_editor/popup_hex_editor_insert.hpp>
#include <content/popups/hex_editor/popup_hex_editor_remove.hpp>
#include <content/popups/hex_editor/popup_hex_editor_fill.hpp>
#include <content/popups/hex_editor/popup_hex_editor_paste_behaviour.hpp>
#include <content/popups/hex_editor/popup_hex_editor_decoded_string.hpp>
#include <content/popups/popup_blocking_task.hpp>
#include <content/popups/hex_editor/popup_hex_editor_find.hpp>
#include <pl/patterns/pattern.hpp>
@@ -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<u64> m_newAddress;
bool m_requestFocus = true;
std::string m_input = "0x";
wolv::math_eval::MathEvaluator<i128> 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<u8> batchData;
if (bytes.size() < BatchFillSize) {
batchData.resize(std::min<u64>(alignTo<u64>(BatchFillSize, bytes.size()), size));
for (u64 i = 0; i < batchData.size(); i += bytes.size()) {
auto remainingSize = std::min<size_t>(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_t>(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<void(const Region &selection, bool selectionCheck)> 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) {