1
0
mirror of synced 2024-11-16 03:53:22 +01:00
ImHex/plugins/builtin/source/content/views/view_hexeditor.cpp

1224 lines
51 KiB
C++
Raw Normal View History

2021-12-07 22:47:41 +01:00
#include "content/views/view_hexeditor.hpp"
2020-11-10 21:31:04 +01:00
2021-01-20 20:16:24 +01:00
#include <hex/api/imhex_api.hpp>
#include <hex/providers/provider.hpp>
#include <hex/helpers/crypto.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/paths.hpp>
2021-12-07 22:47:41 +01:00
#include <hex/helpers/patches.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/helpers/loader_script_handler.hpp>
#include <hex/pattern_language/pattern_data.hpp>
#include <content/providers/file_provider.hpp>
#include <GLFW/glfw3.h>
#include <nlohmann/json.hpp>
#undef __STRICT_ANSI__
#include <cstdio>
#include <thread>
#include <filesystem>
#if defined(OS_WINDOWS)
#include <windows.h>
#else
#include <fcntl.h>
#include <unistd.h>
#endif
2021-12-07 22:47:41 +01:00
namespace hex::plugin::builtin {
2020-11-10 21:31:04 +01:00
2021-12-07 22:47:41 +01:00
ViewHexEditor::ViewHexEditor() : View("hex.builtin.view.hexeditor.name"_lang) {
2020-11-10 21:31:04 +01:00
2021-02-17 14:47:25 +01:00
this->m_searchStringBuffer.resize(0xFFF, 0x00);
this->m_searchHexBuffer.resize(0xFFF, 0x00);
ContentRegistry::FileHandler::add({ ".hexproj" }, [](const auto &path) {
return ProjectFile::load(path.string());
});
2020-11-10 21:31:04 +01:00
this->m_memoryEditor.ReadFn = [](const ImU8 *data, size_t off) -> ImU8 {
auto provider = ImHexApi::Provider::get();
if (!provider->isAvailable() || !provider->isReadable())
2020-11-10 21:31:04 +01:00
return 0x00;
ImU8 byte;
provider->read(off + provider->getBaseAddress() + provider->getCurrentPageAddress(), &byte, sizeof(ImU8));
2020-11-10 21:31:04 +01:00
return byte;
};
this->m_memoryEditor.WriteFn = [](ImU8 *data, size_t off, ImU8 d) -> void {
auto provider = ImHexApi::Provider::get();
if (!provider->isAvailable() || !provider->isWritable())
2020-11-10 21:31:04 +01:00
return;
provider->write(off + provider->getBaseAddress() + provider->getCurrentPageAddress(), &d, sizeof(ImU8));
EventManager::post<EventDataChanged>();
2020-11-30 00:03:12 +01:00
ProjectFile::markDirty();
2020-11-10 21:31:04 +01:00
};
this->m_memoryEditor.HighlightFn = [](const ImU8 *data, size_t off, bool next) -> bool {
ViewHexEditor *_this = (ViewHexEditor *) data;
std::optional<u32> currColor, prevColor;
auto provider = ImHexApi::Provider::get();
off += provider->getBaseAddress() + provider->getCurrentPageAddress();
2021-02-17 14:57:32 +01:00
u32 alpha = static_cast<u32>(_this->m_highlightAlpha) << 24;
for (const auto &[region, name, comment, color, locked] : ImHexApi::Bookmarks::getEntries()) {
if (off >= region.address && off < (region.address + region.size))
currColor = (color & 0x00FFFFFF) | alpha;
if ((off - 1) >= region.address && (off - 1) < (region.address + region.size))
prevColor = (color & 0x00FFFFFF) | alpha;
}
{
for (const auto &pattern : SharedData::patternData) {
auto child = pattern->getPattern(off);
if (child != nullptr) {
auto color = (child->getColor() & 0x00FFFFFF) | alpha;
currColor = currColor.has_value() ? ImAlphaBlendColors(color, currColor.value()) : color;
break;
}
}
for (const auto &pattern : SharedData::patternData) {
auto child = pattern->getPattern(off - 1);
if (child != nullptr) {
auto color = (child->getColor() & 0x00FFFFFF) | alpha;
prevColor = prevColor.has_value() ? ImAlphaBlendColors(color, currColor.value()) : color;
break;
}
}
2021-01-20 18:10:40 +01:00
}
if (next && prevColor != currColor) {
return false;
}
2021-01-20 18:10:40 +01:00
if (currColor.has_value() && (currColor.value() & 0x00FFFFFF) != 0x00) {
_this->m_memoryEditor.HighlightColor = (currColor.value() & 0x00FFFFFF) | alpha;
return true;
2020-11-10 21:31:04 +01:00
}
_this->m_memoryEditor.HighlightColor = 0x60C08080;
2020-11-10 21:31:04 +01:00
return false;
};
2020-11-17 13:58:50 +01:00
2021-02-17 14:57:32 +01:00
this->m_memoryEditor.HoverFn = [](const ImU8 *data, size_t off) {
bool tooltipShown = false;
off += ImHexApi::Provider::get()->getBaseAddress();
2021-02-17 14:57:32 +01:00
for (const auto &[region, name, comment, color, locked] : ImHexApi::Bookmarks::getEntries()) {
2021-02-17 14:57:32 +01:00
if (off >= region.address && off < (region.address + region.size)) {
if (!tooltipShown) {
ImGui::BeginTooltip();
tooltipShown = true;
}
ImGui::ColorButton(name.data(), ImColor(color).Value);
ImGui::SameLine(0, 10);
ImGui::TextUnformatted(name.data());
}
}
if (tooltipShown)
ImGui::EndTooltip();
};
this->m_memoryEditor.DecodeFn = [](const ImU8 *data, size_t addr) -> MemoryEditor::DecodeData {
ViewHexEditor *_this = (ViewHexEditor *) data;
if (_this->m_currEncodingFile.getLongestSequence() == 0)
return { ".", 1, 0xFFFF8000 };
auto provider = ImHexApi::Provider::get();
size_t size = std::min<size_t>(_this->m_currEncodingFile.getLongestSequence(), provider->getActualSize() - addr);
std::vector<u8> buffer(size);
provider->read(addr + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), size);
auto [decoded, advance] = _this->m_currEncodingFile.getEncodingFor(buffer);
ImColor color;
if (decoded.length() == 1 && std::isalnum(decoded[0])) color = 0xFFFF8000;
else if (decoded.length() == 1 && advance == 1) color = 0xFF0000FF;
else if (decoded.length() > 1 && advance == 1) color = 0xFF00FFFF;
else if (advance > 1) color = 0xFFFFFFFF;
else color = 0xFFFF8000;
return { std::string(decoded), advance, color };
};
registerEvents();
registerShortcuts();
2020-11-10 21:31:04 +01:00
}
ViewHexEditor::~ViewHexEditor() {
EventManager::unsubscribe<RequestOpenFile>(this);
EventManager::unsubscribe<RequestSelectionChange>(this);
EventManager::unsubscribe<EventProjectFileLoad>(this);
EventManager::unsubscribe<EventWindowClosing>(this);
EventManager::unsubscribe<RequestOpenWindow>(this);
EventManager::unsubscribe<EventSettingsChanged>(this);
}
2020-11-10 21:31:04 +01:00
void ViewHexEditor::drawContent() {
auto provider = ImHexApi::Provider::get();
size_t dataSize = (!ImHexApi::Provider::isValid() || !provider->isReadable()) ? 0x00 : provider->getSize();
this->m_memoryEditor.DrawWindow(View::toWindowName("hex.builtin.view.hexeditor.name").c_str(), &this->getWindowOpenState(), this, dataSize, dataSize == 0 ? 0x00 : provider->getBaseAddress() + provider->getCurrentPageAddress());
if (dataSize != 0x00) {
2021-12-07 22:47:41 +01:00
if (ImGui::Begin(View::toWindowName("hex.builtin.view.hexeditor.name").c_str())) {
if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows))
ImGui::OpenPopup("hex.menu.edit"_lang);
if (ImGui::BeginPopup("hex.menu.edit"_lang)) {
this->drawEditPopup();
ImGui::EndPopup();
}
if (provider->getPageCount() > 1) {
ImGui::NewLine();
2021-12-07 22:47:41 +01:00
auto linePos = ImGui::GetCursorPosY() - 15_scaled;
ImGui::SetCursorPosY(linePos);
if (ImGui::ArrowButton("prevPage", ImGuiDir_Left)) {
provider->setCurrentPage(provider->getCurrentPage() - 1);
EventManager::post<EventRegionSelected>(Region { std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd), 1 });
}
ImGui::SameLine();
if (ImGui::ArrowButton("nextPage", ImGuiDir_Right)) {
provider->setCurrentPage(provider->getCurrentPage() + 1);
EventManager::post<EventRegionSelected>(Region { std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd), 1 });
}
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
ImGui::SetCursorPosY(linePos);
ImGui::TextFormatted("hex.builtin.view.hexeditor.page"_lang, provider->getCurrentPage() + 1, provider->getPageCount());
}
this->drawSearchPopup();
this->drawGotoPopup();
}
ImGui::End();
}
}
static void save() {
ImHexApi::Provider::get()->save();
}
static void saveAs() {
2021-12-07 22:47:41 +01:00
hex::openFileBrowser("hex.builtin.view.hexeditor.save_as"_lang, DialogMode::Save, { }, [](auto path) {
ImHexApi::Provider::get()->saveAs(path);
});
}
void ViewHexEditor::drawAlwaysVisible() {
auto provider = ImHexApi::Provider::get();
2021-12-07 22:47:41 +01:00
if (ImGui::BeginPopupModal("hex.builtin.view.hexeditor.exit_application.title"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
2020-11-30 00:03:12 +01:00
ImGui::NewLine();
2021-12-07 22:47:41 +01:00
ImGui::TextUnformatted("hex.builtin.view.hexeditor.exit_application.desc"_lang);
Proper DPI scaling and basic custom font (#85) * add glm to arch deps After running got `None of the required 'glm' found`. This fixes that * dist/fedora: Include file magic headers Due to differences in package names between Deb based systems, Arch Linux, and RPM based systems the package containing the development headers for file were missing from the Fedora dependencies script. This includes the package `file-devel`, which is the package which resolves the issue. In Fedora, one can identify the package providing a specific file using the verb "whatprovides" with the command dnf, e.g.: [~]$ dnf whatprovides /usr/include/magic.h Last metadata expiration check: 4 days, 0:23:05 ago on Fri 04 Dec 2020 09:06:53 AM PST. file-devel-5.39-3.fc33.i686 : Libraries and header files for file development Repo : fedora Matched from: Filename : /usr/include/magic.h file-devel-5.39-3.fc33.x86_64 : Libraries and header files for file development Repo : @System Matched from: Filename : /usr/include/magic.h file-devel-5.39-3.fc33.x86_64 : Libraries and header files for file development Repo : fedora Matched from: Filename : /usr/include/magic.h If one is unsure of the specific path, globbing may be used (but must be quoted): dnf whatprovides "*/magic.h" Resolves #48 * dist: Prevent already installed packages in ArchLinux and MSYS2. Use --needed option with pacman to prevent it. * Add script to install dependencies on Debian/Ubuntu. Tested with Xubuntu 20.04 and Debian testing (in today's Docker image bitnami/minideb). Update README.md. * ci: rework (#31) * Support non standard LLVM library names (#86) This fix openSUSE and Gentoo issue mentioned in https://github.com/WerWolv/ImHex/issues/37#issuecomment-739503138. (tested on openSUSE tumbleweed via Docker) I also took the liberty of renaming llvm_lib to llvm_demangle_lib to be more specific in the ``CMakeLists.txt``. * Implement proper DPI handling * Implement basic custom font support * Fix building on windows * Hopefully fix fonts on Windows * Fix several scaling issues * Replace font renderer with freetype * Updated CI and dependency scripts * Rebuild default font atlas * Correct platform detection macro for mingw * Fixed PKGBUILD Co-authored-by: brockelmore <31553173+brockelmore@users.noreply.github.com> Co-authored-by: Brian 'Redbeard' Harrington <redbeard@dead-city.org> Co-authored-by: Biswapriyo Nath <nathbappai@gmail.com> Co-authored-by: Stéphane Gourichon <stephane.gourichon@fidergo.fr> Co-authored-by: umarcor <38422348+umarcor@users.noreply.github.com> Co-authored-by: Mary <me@thog.eu> Co-authored-by: WerWolv <werwolv98@gmail.com>
2020-12-11 14:24:42 +01:00
ImGui::NewLine();
confirmButtons("hex.common.yes"_lang, "hex.common.no"_lang, [] {
ImHexApi::Common::closeImHex(true);
},
[] {
ImGui::CloseCurrentPopup();
});
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
ImGui::CloseCurrentPopup();
2020-11-30 00:03:12 +01:00
ImGui::EndPopup();
}
2021-12-07 22:47:41 +01:00
if (ImGui::BeginPopupModal("hex.builtin.view.hexeditor.script.title"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
Proper DPI scaling and basic custom font (#85) * add glm to arch deps After running got `None of the required 'glm' found`. This fixes that * dist/fedora: Include file magic headers Due to differences in package names between Deb based systems, Arch Linux, and RPM based systems the package containing the development headers for file were missing from the Fedora dependencies script. This includes the package `file-devel`, which is the package which resolves the issue. In Fedora, one can identify the package providing a specific file using the verb "whatprovides" with the command dnf, e.g.: [~]$ dnf whatprovides /usr/include/magic.h Last metadata expiration check: 4 days, 0:23:05 ago on Fri 04 Dec 2020 09:06:53 AM PST. file-devel-5.39-3.fc33.i686 : Libraries and header files for file development Repo : fedora Matched from: Filename : /usr/include/magic.h file-devel-5.39-3.fc33.x86_64 : Libraries and header files for file development Repo : @System Matched from: Filename : /usr/include/magic.h file-devel-5.39-3.fc33.x86_64 : Libraries and header files for file development Repo : fedora Matched from: Filename : /usr/include/magic.h If one is unsure of the specific path, globbing may be used (but must be quoted): dnf whatprovides "*/magic.h" Resolves #48 * dist: Prevent already installed packages in ArchLinux and MSYS2. Use --needed option with pacman to prevent it. * Add script to install dependencies on Debian/Ubuntu. Tested with Xubuntu 20.04 and Debian testing (in today's Docker image bitnami/minideb). Update README.md. * ci: rework (#31) * Support non standard LLVM library names (#86) This fix openSUSE and Gentoo issue mentioned in https://github.com/WerWolv/ImHex/issues/37#issuecomment-739503138. (tested on openSUSE tumbleweed via Docker) I also took the liberty of renaming llvm_lib to llvm_demangle_lib to be more specific in the ``CMakeLists.txt``. * Implement proper DPI handling * Implement basic custom font support * Fix building on windows * Hopefully fix fonts on Windows * Fix several scaling issues * Replace font renderer with freetype * Updated CI and dependency scripts * Rebuild default font atlas * Correct platform detection macro for mingw * Fixed PKGBUILD Co-authored-by: brockelmore <31553173+brockelmore@users.noreply.github.com> Co-authored-by: Brian 'Redbeard' Harrington <redbeard@dead-city.org> Co-authored-by: Biswapriyo Nath <nathbappai@gmail.com> Co-authored-by: Stéphane Gourichon <stephane.gourichon@fidergo.fr> Co-authored-by: umarcor <38422348+umarcor@users.noreply.github.com> Co-authored-by: Mary <me@thog.eu> Co-authored-by: WerWolv <werwolv98@gmail.com>
2020-12-11 14:24:42 +01:00
ImGui::SetCursorPosX(10);
2022-01-15 14:14:53 +01:00
ImGui::TextFormattedWrapped("{}", static_cast<const char *>("hex.builtin.view.hexeditor.script.desc"_lang));
2020-12-01 02:21:40 +01:00
ImGui::NewLine();
ImGui::InputText("##nolabel", this->m_loaderScriptScriptPath.data(), this->m_loaderScriptScriptPath.length(), ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
2021-12-07 22:47:41 +01:00
if (ImGui::Button("hex.builtin.view.hexeditor.script.script"_lang)) {
hex::openFileBrowser("hex.builtin.view.hexeditor.script.script.title"_lang, DialogMode::Open, { { "Python Script", "py" } }, [this](auto path) {
2021-01-27 00:44:10 +01:00
this->m_loaderScriptScriptPath = path;
});
}
2020-12-01 02:21:40 +01:00
ImGui::InputText("##nolabel", this->m_loaderScriptFilePath.data(), this->m_loaderScriptFilePath.length(), ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
2021-12-07 22:47:41 +01:00
if (ImGui::Button("hex.builtin.view.hexeditor.script.file"_lang)) {
hex::openFileBrowser("hex.builtin.view.hexeditor.script.file.title"_lang, DialogMode::Open, { }, [this](auto path) {
2021-01-27 00:44:10 +01:00
this->m_loaderScriptFilePath = path;
});
}
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
ImGui::CloseCurrentPopup();
2020-12-01 02:21:40 +01:00
ImGui::NewLine();
confirmButtons("hex.common.load"_lang, "hex.common.cancel"_lang,
2021-04-13 08:41:59 +02:00
[this, &provider] {
if (!this->m_loaderScriptScriptPath.empty() && !this->m_loaderScriptFilePath.empty()) {
EventManager::post<RequestOpenFile>(this->m_loaderScriptFilePath);
2021-04-13 08:41:59 +02:00
LoaderScript::setFilePath(this->m_loaderScriptFilePath);
LoaderScript::setDataProvider(provider);
LoaderScript::processFile(this->m_loaderScriptScriptPath);
ImGui::CloseCurrentPopup();
}
},
[] {
ImGui::CloseCurrentPopup();
}
Proper DPI scaling and basic custom font (#85) * add glm to arch deps After running got `None of the required 'glm' found`. This fixes that * dist/fedora: Include file magic headers Due to differences in package names between Deb based systems, Arch Linux, and RPM based systems the package containing the development headers for file were missing from the Fedora dependencies script. This includes the package `file-devel`, which is the package which resolves the issue. In Fedora, one can identify the package providing a specific file using the verb "whatprovides" with the command dnf, e.g.: [~]$ dnf whatprovides /usr/include/magic.h Last metadata expiration check: 4 days, 0:23:05 ago on Fri 04 Dec 2020 09:06:53 AM PST. file-devel-5.39-3.fc33.i686 : Libraries and header files for file development Repo : fedora Matched from: Filename : /usr/include/magic.h file-devel-5.39-3.fc33.x86_64 : Libraries and header files for file development Repo : @System Matched from: Filename : /usr/include/magic.h file-devel-5.39-3.fc33.x86_64 : Libraries and header files for file development Repo : fedora Matched from: Filename : /usr/include/magic.h If one is unsure of the specific path, globbing may be used (but must be quoted): dnf whatprovides "*/magic.h" Resolves #48 * dist: Prevent already installed packages in ArchLinux and MSYS2. Use --needed option with pacman to prevent it. * Add script to install dependencies on Debian/Ubuntu. Tested with Xubuntu 20.04 and Debian testing (in today's Docker image bitnami/minideb). Update README.md. * ci: rework (#31) * Support non standard LLVM library names (#86) This fix openSUSE and Gentoo issue mentioned in https://github.com/WerWolv/ImHex/issues/37#issuecomment-739503138. (tested on openSUSE tumbleweed via Docker) I also took the liberty of renaming llvm_lib to llvm_demangle_lib to be more specific in the ``CMakeLists.txt``. * Implement proper DPI handling * Implement basic custom font support * Fix building on windows * Hopefully fix fonts on Windows * Fix several scaling issues * Replace font renderer with freetype * Updated CI and dependency scripts * Rebuild default font atlas * Correct platform detection macro for mingw * Fixed PKGBUILD Co-authored-by: brockelmore <31553173+brockelmore@users.noreply.github.com> Co-authored-by: Brian 'Redbeard' Harrington <redbeard@dead-city.org> Co-authored-by: Biswapriyo Nath <nathbappai@gmail.com> Co-authored-by: Stéphane Gourichon <stephane.gourichon@fidergo.fr> Co-authored-by: umarcor <38422348+umarcor@users.noreply.github.com> Co-authored-by: Mary <me@thog.eu> Co-authored-by: WerWolv <werwolv98@gmail.com>
2020-12-11 14:24:42 +01:00
);
2020-12-01 02:21:40 +01:00
ImGui::EndPopup();
Proper DPI scaling and basic custom font (#85) * add glm to arch deps After running got `None of the required 'glm' found`. This fixes that * dist/fedora: Include file magic headers Due to differences in package names between Deb based systems, Arch Linux, and RPM based systems the package containing the development headers for file were missing from the Fedora dependencies script. This includes the package `file-devel`, which is the package which resolves the issue. In Fedora, one can identify the package providing a specific file using the verb "whatprovides" with the command dnf, e.g.: [~]$ dnf whatprovides /usr/include/magic.h Last metadata expiration check: 4 days, 0:23:05 ago on Fri 04 Dec 2020 09:06:53 AM PST. file-devel-5.39-3.fc33.i686 : Libraries and header files for file development Repo : fedora Matched from: Filename : /usr/include/magic.h file-devel-5.39-3.fc33.x86_64 : Libraries and header files for file development Repo : @System Matched from: Filename : /usr/include/magic.h file-devel-5.39-3.fc33.x86_64 : Libraries and header files for file development Repo : fedora Matched from: Filename : /usr/include/magic.h If one is unsure of the specific path, globbing may be used (but must be quoted): dnf whatprovides "*/magic.h" Resolves #48 * dist: Prevent already installed packages in ArchLinux and MSYS2. Use --needed option with pacman to prevent it. * Add script to install dependencies on Debian/Ubuntu. Tested with Xubuntu 20.04 and Debian testing (in today's Docker image bitnami/minideb). Update README.md. * ci: rework (#31) * Support non standard LLVM library names (#86) This fix openSUSE and Gentoo issue mentioned in https://github.com/WerWolv/ImHex/issues/37#issuecomment-739503138. (tested on openSUSE tumbleweed via Docker) I also took the liberty of renaming llvm_lib to llvm_demangle_lib to be more specific in the ``CMakeLists.txt``. * Implement proper DPI handling * Implement basic custom font support * Fix building on windows * Hopefully fix fonts on Windows * Fix several scaling issues * Replace font renderer with freetype * Updated CI and dependency scripts * Rebuild default font atlas * Correct platform detection macro for mingw * Fixed PKGBUILD Co-authored-by: brockelmore <31553173+brockelmore@users.noreply.github.com> Co-authored-by: Brian 'Redbeard' Harrington <redbeard@dead-city.org> Co-authored-by: Biswapriyo Nath <nathbappai@gmail.com> Co-authored-by: Stéphane Gourichon <stephane.gourichon@fidergo.fr> Co-authored-by: umarcor <38422348+umarcor@users.noreply.github.com> Co-authored-by: Mary <me@thog.eu> Co-authored-by: WerWolv <werwolv98@gmail.com>
2020-12-11 14:24:42 +01:00
2020-12-01 02:21:40 +01:00
}
2021-12-07 22:47:41 +01:00
if (ImGui::BeginPopupModal("hex.builtin.view.hexeditor.menu.edit.set_base"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::InputText("hex.common.address"_lang, this->m_baseAddressBuffer, 16, ImGuiInputTextFlags_CharsHexadecimal);
ImGui::NewLine();
confirmButtons("hex.common.set"_lang, "hex.common.cancel"_lang,
[this, &provider]{
provider->setBaseAddress(strtoull(this->m_baseAddressBuffer, nullptr, 16));
ImGui::CloseCurrentPopup();
}, []{
ImGui::CloseCurrentPopup();
});
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
2021-12-07 22:47:41 +01:00
if (ImGui::BeginPopupModal("hex.builtin.view.hexeditor.menu.edit.resize"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::InputScalar("hex.common.size"_lang, ImGuiDataType_U64, &this->m_resizeSize, nullptr, nullptr, "0x%016llx", ImGuiInputTextFlags_CharsHexadecimal);
ImGui::NewLine();
confirmButtons("hex.common.set"_lang, "hex.common.cancel"_lang,
[this, &provider]{
provider->resize(this->m_resizeSize);
ImGui::CloseCurrentPopup();
}, []{
ImGui::CloseCurrentPopup();
});
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
2021-01-27 00:44:10 +01:00
}
void ViewHexEditor::drawMenu() {
auto provider = ImHexApi::Provider::get();
bool providerValid = ImHexApi::Provider::isValid();
if (ImGui::BeginMenu("hex.menu.file"_lang)) {
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.open_file"_lang, "CTRL + O")) {
2021-01-27 00:44:10 +01:00
2021-12-07 22:47:41 +01:00
hex::openFileBrowser("hex.builtin.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
EventManager::post<RequestOpenFile>(path);
2021-01-27 00:44:10 +01:00
});
}
2021-12-07 22:47:41 +01:00
if (ImGui::BeginMenu("hex.builtin.view.hexeditor.menu.file.open_recent"_lang, !SharedData::recentFilePaths.empty())) {
2021-04-13 08:41:59 +02:00
for (auto &path : SharedData::recentFilePaths) {
if (ImGui::MenuItem(fs::path(path).filename().string().c_str())) {
EventManager::post<RequestOpenFile>(path);
2021-04-13 08:41:59 +02:00
}
}
ImGui::Separator();
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.clear_recent"_lang)) {
SharedData::recentFilePaths.clear();
ContentRegistry::Settings::write(
"hex.builtin.setting.imhex",
"hex.builtin.setting.imhex.recent_files",
std::vector<std::string>{});
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("hex.builtin.view.hexeditor.menu.file.open_other"_lang)) {
for (const auto &unlocalizedProviderName : ContentRegistry::Provider::getEntries()) {
if (ImGui::MenuItem(LangEntry(unlocalizedProviderName))) {
EventManager::post<RequestCreateProvider>(unlocalizedProviderName, nullptr);
}
2021-04-13 08:41:59 +02:00
}
ImGui::EndMenu();
}
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.save"_lang, "CTRL + S", false, providerValid && provider->isWritable())) {
2021-01-27 00:44:10 +01:00
save();
}
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.save_as"_lang, "CTRL + SHIFT + S", false, providerValid && provider->isWritable())) {
2021-01-27 00:44:10 +01:00
saveAs();
}
2021-12-12 21:46:48 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.close"_lang, "", false, providerValid)) {
2021-03-29 22:44:35 +02:00
EventManager::post<EventFileUnloaded>();
ImHexApi::Provider::remove(ImHexApi::Provider::get());
2021-09-22 12:58:49 +02:00
providerValid = false;
2021-03-29 22:44:35 +02:00
}
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.quit"_lang, "", false)) {
ImHexApi::Common::closeImHex();
}
2020-11-30 00:03:12 +01:00
ImGui::Separator();
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.open_project"_lang, "")) {
hex::openFileBrowser("hex.builtin.view.hexeditor.menu.file.open_project"_lang, DialogMode::Open, { { "Project File", "hexproj" } }, [this](auto path) {
2021-01-27 00:44:10 +01:00
ProjectFile::load(path);
});
2020-11-30 00:03:12 +01:00
}
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.save_project"_lang, "", false, providerValid && provider->isWritable())) {
2021-01-27 00:44:10 +01:00
if (ProjectFile::getProjectFilePath() == "") {
2021-12-07 22:47:41 +01:00
hex::openFileBrowser("hex.builtin.view.hexeditor.save_project"_lang, DialogMode::Save, { { "Project File", "hexproj" } }, [](auto path) {
if (path.ends_with(".hexproj")) {
ProjectFile::store(path);
}
else {
ProjectFile::store(path + ".hexproj");
}
2021-01-27 00:44:10 +01:00
});
}
2020-11-30 00:03:12 +01:00
else
ProjectFile::store();
}
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.load_encoding_file"_lang)) {
hex::openFileBrowser("hex.builtin.view.hexeditor.load_enconding_file"_lang, DialogMode::Open, { }, [this](auto path) {
this->m_currEncodingFile = EncodingFile(EncodingFile::Type::Thingy, path);
});
}
2020-11-30 00:03:12 +01:00
ImGui::Separator();
2021-12-07 22:47:41 +01:00
if (ImGui::BeginMenu("hex.builtin.view.hexeditor.menu.file.import"_lang)) {
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.import.base64"_lang)) {
2021-01-27 00:44:10 +01:00
2021-12-07 22:47:41 +01:00
hex::openFileBrowser("hex.builtin.view.hexeditor.menu.file.import.base64"_lang, DialogMode::Open, { }, [this](auto path) {
2021-01-27 00:44:10 +01:00
std::vector<u8> base64;
this->loadFromFile(path, base64);
if (!base64.empty()) {
this->m_dataToSave = crypt::decode64(base64);
2021-01-27 00:44:10 +01:00
if (this->m_dataToSave.empty())
2021-12-07 22:47:41 +01:00
View::showErrorPopup("hex.builtin.view.hexeditor.base64.import_error"_lang);
2021-01-27 00:44:10 +01:00
else
2021-12-07 22:47:41 +01:00
ImGui::OpenPopup("hex.builtin.view.hexeditor.save_data"_lang);
2021-01-27 00:44:10 +01:00
this->getWindowOpenState() = true;
2021-12-07 22:47:41 +01:00
} else View::showErrorPopup("hex.builtin.view.hexeditor.file_open_error"_lang);
2021-01-27 00:44:10 +01:00
});
}
ImGui::Separator();
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.import.ips"_lang, nullptr, false, !this->m_processingImportExport)) {
2021-01-27 00:44:10 +01:00
2021-12-07 22:47:41 +01:00
hex::openFileBrowser("hex.builtin.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
this->m_processingImportExport = true;
std::thread([this, path] {
auto task = ImHexApi::Tasks::createTask("hex.builtin.view.hexeditor.processing", 0);
auto patchData = File(path, File::Mode::Read).readBytes();
auto patch = hex::loadIPSPatch(patchData);
task.setMaxValue(patch.size());
auto provider = ImHexApi::Provider::get();
u64 progress = 0;
for (auto &[address, value] : patch) {
provider->addPatch(address, &value, 1);
progress++;
task.update(progress);
}
provider->createUndoPoint();
this->m_processingImportExport = false;
}).detach();
2021-01-27 00:44:10 +01:00
this->getWindowOpenState() = true;
});
}
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.import.ips32"_lang, nullptr, false, !this->m_processingImportExport)) {
2021-12-07 22:47:41 +01:00
hex::openFileBrowser("hex.builtin.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
this->m_processingImportExport = true;
std::thread([this, path] {
auto task = ImHexApi::Tasks::createTask("hex.builtin.view.hexeditor.processing", 0);
auto patchData = File(path, File::Mode::Read).readBytes();
auto patch = hex::loadIPS32Patch(patchData);
task.setMaxValue(patch.size());
auto provider = ImHexApi::Provider::get();
u64 progress = 0;
for (auto &[address, value] : patch) {
provider->addPatch(address, &value, 1);
progress++;
task.update(progress);
}
provider->createUndoPoint();
this->m_processingImportExport = false;
}).detach();
2021-01-27 00:44:10 +01:00
this->getWindowOpenState() = true;
});
}
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.import.script"_lang)) {
2020-12-01 02:21:40 +01:00
this->m_loaderScriptFilePath.clear();
this->m_loaderScriptScriptPath.clear();
2021-12-07 22:47:41 +01:00
View::doLater([]{ ImGui::OpenPopup("hex.builtin.view.hexeditor.script.title"_lang); });
2020-12-01 02:21:40 +01:00
}
ImGui::EndMenu();
}
2021-12-07 22:47:41 +01:00
if (ImGui::BeginMenu("hex.builtin.view.hexeditor.menu.file.export"_lang, providerValid && provider->isWritable())) {
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.export.ips"_lang, nullptr, false, !this->m_processingImportExport)) {
Patches patches = provider->getPatches();
if (!patches.contains(0x00454F45) && patches.contains(0x00454F46)) {
u8 value = 0;
provider->read(0x00454F45, &value, sizeof(u8));
patches[0x00454F45] = value;
}
this->m_processingImportExport = true;
std::thread([this, patches]{
auto task = ImHexApi::Tasks::createTask("hex.builtin.view.hexeditor.processing", 0);
this->m_dataToSave = generateIPSPatch(patches);
this->m_processingImportExport = false;
View::doLater([this]{
hex::openFileBrowser("hex.builtin.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) {
this->saveToFile(path, this->m_dataToSave);
});
});
}).detach();
}
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.export.ips32"_lang, nullptr, false, !this->m_processingImportExport)) {
Patches patches = provider->getPatches();
if (!patches.contains(0x00454F45) && patches.contains(0x45454F46)) {
u8 value = 0;
provider->read(0x45454F45, &value, sizeof(u8));
patches[0x45454F45] = value;
}
this->m_processingImportExport = true;
std::thread([this, patches]{
auto task = ImHexApi::Tasks::createTask("hex.builtin.view.hexeditor.processing", 0);
this->m_dataToSave = generateIPS32Patch(patches);
this->m_processingImportExport = false;
View::doLater([this]{
hex::openFileBrowser("hex.builtin.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) {
this->saveToFile(path, this->m_dataToSave);
});
});
}).detach();
}
ImGui::EndMenu();
}
ImGui::Separator();
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.search"_lang, "CTRL + F")) {
this->getWindowOpenState() = true;
2021-12-07 22:47:41 +01:00
ImGui::OpenPopupInWindow(View::toWindowName("hex.builtin.view.hexeditor.name").c_str(), "hex.builtin.view.hexeditor.menu.file.search"_lang);
}
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.file.goto"_lang, "CTRL + G")) {
this->getWindowOpenState() = true;
2021-12-07 22:47:41 +01:00
ImGui::OpenPopupInWindow(View::toWindowName("hex.builtin.view.hexeditor.name").c_str(), "hex.builtin.view.hexeditor.menu.file.goto"_lang);
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("hex.menu.edit"_lang)) {
this->drawEditPopup();
ImGui::EndMenu();
}
}
bool ViewHexEditor::createFile(const std::string &path) {
#if defined(OS_WINDOWS)
std::wstring widePath;
{
auto length = path.length() + 1;
auto wideLength = MultiByteToWideChar(CP_UTF8, 0, path.data(), length, 0, 0);
auto buffer = new wchar_t[wideLength];
MultiByteToWideChar(CP_UTF8, 0, path.data(), length, buffer, wideLength);
widePath = buffer;
delete[] buffer;
}
auto handle = ::CreateFileW(widePath.data(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return false;
::SetFilePointer(handle, 1, nullptr, FILE_BEGIN);
::SetEndOfFile(handle);
::CloseHandle(handle);
#else
auto handle = ::open(path.data(), O_RDWR | O_CREAT, 0644);
if (handle == -1)
return false;
lseek(handle, 0, SEEK_SET);
write(handle, "", 1);
close(handle);
#endif
return true;
}
void ViewHexEditor::openFile(const std::string &path) {
2021-12-07 22:47:41 +01:00
hex::prv::Provider *provider = nullptr;
EventManager::post<RequestCreateProvider>("hex.builtin.provider.file", &provider);
if (auto fileProvider = dynamic_cast<prv::FileProvider*>(provider)) {
fileProvider->setPath(path);
if (!fileProvider->open()) {
View::showErrorPopup("hex.builtin.view.hexeditor.error.open"_lang);
2021-12-12 21:46:48 +01:00
ImHexApi::Provider::remove(provider);
return;
}
}
if (!provider->isWritable()) {
this->m_memoryEditor.ReadOnly = true;
2021-12-07 22:47:41 +01:00
View::showErrorPopup("hex.builtin.view.hexeditor.error.read_only"_lang);
} else {
this->m_memoryEditor.ReadOnly = false;
}
if (!provider->isAvailable()) {
2021-12-07 22:47:41 +01:00
View::showErrorPopup("hex.builtin.view.hexeditor.error.open"_lang);
ImHexApi::Provider::remove(provider);
return;
}
2020-11-30 00:03:12 +01:00
ProjectFile::setFilePath(path);
2020-11-30 00:03:12 +01:00
this->getWindowOpenState() = true;
EventManager::post<EventFileLoaded>(path);
EventManager::post<EventDataChanged>();
{
std::vector<pl::PatternData*> patterns;
EventManager::post<EventPatternChanged>(patterns);
}
2020-11-17 13:58:50 +01:00
}
bool ViewHexEditor::saveToFile(const std::string &path, const std::vector<u8>& data) {
File(path, File::Mode::Create).write(data);
return true;
}
bool ViewHexEditor::loadFromFile(const std::string &path, std::vector<u8>& data) {
data = File(path, File::Mode::Read).readBytes();
return true;
}
void ViewHexEditor::copyBytes() const {
auto provider = ImHexApi::Provider::get();
size_t start = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
size_t end = std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
size_t copySize = (end - start) + 1;
2020-11-16 00:07:42 +01:00
std::vector<u8> buffer(copySize, 0x00);
provider->read(start + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), buffer.size());
2020-11-16 00:07:42 +01:00
std::string str;
for (const auto &byte : buffer)
str += hex::format("{0:02X} ", byte);
2020-11-16 00:07:42 +01:00
str.pop_back();
ImGui::SetClipboardText(str.c_str());
}
void ViewHexEditor::pasteBytes() const {
auto provider = ImHexApi::Provider::get();
2021-04-16 17:01:01 +02:00
size_t start = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
size_t end = std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
std::string clipboard = ImGui::GetClipboardText();
// Check for non-hex characters
bool isValidHexString = std::find_if(clipboard.begin(), clipboard.end(), [](char c) {
return !std::isxdigit(c) && !std::isspace(c);
}) == clipboard.end();
if (!isValidHexString) return;
// Remove all whitespace
std::erase_if(clipboard, [](char c) { return std::isspace(c); });
// Only paste whole bytes
if (clipboard.length() % 2 != 0) return;
// Convert hex string to bytes
std::vector<u8> buffer(clipboard.length() / 2, 0x00);
u32 stringIndex = 0;
for (u8 &byte : buffer) {
for (u8 i = 0; i < 2; i++) {
byte <<= 4;
char c = clipboard[stringIndex];
if (c >= '0' && c <= '9') byte |= (c - '0');
else if (c >= 'a' && c <= 'f') byte |= (c - 'a') + 0xA;
else if (c >= 'A' && c <= 'F') byte |= (c - 'A') + 0xA;
2021-04-16 19:44:22 +02:00
stringIndex++;
2021-04-16 17:01:01 +02:00
}
}
// Write bytes
provider->read(start + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), std::min(end - start + 1, buffer.size()));
2021-04-16 17:01:01 +02:00
}
void ViewHexEditor::copyString() const {
auto provider = ImHexApi::Provider::get();
size_t start = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
size_t end = std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
size_t copySize = (end - start) + 1;
2020-11-16 00:07:42 +01:00
std::string buffer(copySize, 0x00);
buffer.reserve(copySize);
provider->read(start + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), copySize);
2020-11-16 00:07:42 +01:00
ImGui::SetClipboardText(buffer.c_str());
}
2021-12-07 22:47:41 +01:00
static std::vector<std::pair<u64, u64>> findString(hex::prv::Provider* &provider, std::string string) {
2020-11-15 23:04:46 +01:00
std::vector<std::pair<u64, u64>> results;
u32 foundCharacters = 0;
std::vector<u8> buffer(1024, 0x00);
size_t dataSize = provider->getSize();
for (u64 offset = 0; offset < dataSize; offset += 1024) {
size_t usedBufferSize = std::min(u64(buffer.size()), dataSize - offset);
provider->read(offset + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), usedBufferSize);
2020-11-15 23:04:46 +01:00
for (u64 i = 0; i < usedBufferSize; i++) {
if (buffer[i] == string[foundCharacters])
foundCharacters++;
else
foundCharacters = 0;
if (foundCharacters == string.size()) {
results.emplace_back(offset + i - foundCharacters + 1, offset + i);
2020-11-15 23:04:46 +01:00
foundCharacters = 0;
}
}
}
return results;
}
2021-12-07 22:47:41 +01:00
static std::vector<std::pair<u64, u64>> findHex(hex::prv::Provider* &provider, std::string string) {
2020-11-15 23:04:46 +01:00
std::vector<std::pair<u64, u64>> results;
if ((string.size() % 2) == 1)
string = "0" + string;
std::vector<u8> hex;
hex.reserve(string.size() / 2);
for (u32 i = 0; i < string.size(); i += 2) {
char byte[3] = { string[i], string[i + 1], 0 };
hex.push_back(strtoul(byte, nullptr, 16));
}
u32 foundCharacters = 0;
std::vector<u8> buffer(1024, 0x00);
size_t dataSize = provider->getSize();
for (u64 offset = 0; offset < dataSize; offset += 1024) {
size_t usedBufferSize = std::min(u64(buffer.size()), dataSize - offset);
provider->read(offset + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), usedBufferSize);
2020-11-15 23:04:46 +01:00
for (u64 i = 0; i < usedBufferSize; i++) {
if (buffer[i] == hex[foundCharacters])
foundCharacters++;
else
foundCharacters = 0;
if (foundCharacters == hex.size()) {
results.emplace_back(offset + i - foundCharacters + 1, offset + i);
2020-11-15 23:04:46 +01:00
foundCharacters = 0;
}
}
}
return results;
}
void ViewHexEditor::drawSearchPopup() {
2020-11-15 23:04:46 +01:00
static auto InputCallback = [](ImGuiInputTextCallbackData* data) -> int {
auto _this = static_cast<ViewHexEditor*>(data->UserData);
auto provider = ImHexApi::Provider::get();
*_this->m_lastSearchBuffer = _this->m_searchFunction(provider, data->Buf);
2020-11-15 23:04:46 +01:00
_this->m_lastSearchIndex = 0;
2020-11-11 14:42:01 +01:00
if (!_this->m_lastSearchBuffer->empty())
_this->m_memoryEditor.GotoAddrAndSelect((*_this->m_lastSearchBuffer)[0].first, (*_this->m_lastSearchBuffer)[0].second);
2020-11-11 14:42:01 +01:00
2020-11-15 23:04:46 +01:00
return 0;
};
2020-11-11 14:42:01 +01:00
2020-11-15 23:04:46 +01:00
static auto Find = [this](char *buffer) {
auto provider = ImHexApi::Provider::get();
*this->m_lastSearchBuffer = this->m_searchFunction(provider, buffer);
2020-11-15 23:04:46 +01:00
this->m_lastSearchIndex = 0;
2020-11-11 14:42:01 +01:00
if (!this->m_lastSearchBuffer->empty())
this->m_memoryEditor.GotoAddrAndSelect((*this->m_lastSearchBuffer)[0].first, (*this->m_lastSearchBuffer)[0].second);
2020-11-15 23:04:46 +01:00
};
2020-11-11 14:42:01 +01:00
2020-11-15 23:04:46 +01:00
static auto FindNext = [this]() {
if (!this->m_lastSearchBuffer->empty()) {
2020-11-15 23:04:46 +01:00
++this->m_lastSearchIndex %= this->m_lastSearchBuffer->size();
this->m_memoryEditor.GotoAddrAndSelect((*this->m_lastSearchBuffer)[this->m_lastSearchIndex].first,
2020-11-15 23:04:46 +01:00
(*this->m_lastSearchBuffer)[this->m_lastSearchIndex].second);
2020-11-11 14:42:01 +01:00
}
2020-11-15 23:04:46 +01:00
};
2020-11-11 14:42:01 +01:00
2020-11-15 23:04:46 +01:00
static auto FindPrevious = [this]() {
if (!this->m_lastSearchBuffer->empty()) {
2020-11-15 23:04:46 +01:00
this->m_lastSearchIndex--;
2020-11-11 14:42:01 +01:00
2020-11-15 23:04:46 +01:00
if (this->m_lastSearchIndex < 0)
this->m_lastSearchIndex = this->m_lastSearchBuffer->size() - 1;
this->m_lastSearchIndex %= this->m_lastSearchBuffer->size();
this->m_memoryEditor.GotoAddrAndSelect((*this->m_lastSearchBuffer)[this->m_lastSearchIndex].first,
2020-11-15 23:04:46 +01:00
(*this->m_lastSearchBuffer)[this->m_lastSearchIndex].second);
}
};
ImGui::SetNextWindowPos(ImGui::GetWindowPos() + ImGui::GetWindowContentRegionMin() - ImGui::GetStyle().WindowPadding);
2021-12-07 22:47:41 +01:00
if (ImGui::BeginPopup("hex.builtin.view.hexeditor.menu.file.search"_lang)) {
2020-11-15 23:04:46 +01:00
if (ImGui::BeginTabBar("searchTabs")) {
2021-02-17 14:47:25 +01:00
std::vector<char> *currBuffer = nullptr;
2021-12-07 22:47:41 +01:00
if (ImGui::BeginTabItem("hex.builtin.view.hexeditor.search.string"_lang)) {
2020-11-15 23:04:46 +01:00
this->m_searchFunction = findString;
this->m_lastSearchBuffer = &this->m_lastStringSearch;
2021-02-17 14:47:25 +01:00
currBuffer = &this->m_searchStringBuffer;
2020-11-15 23:04:46 +01:00
2021-02-17 14:47:25 +01:00
ImGui::InputText("##nolabel", currBuffer->data(), currBuffer->size(), ImGuiInputTextFlags_CallbackCompletion,
2020-11-15 23:04:46 +01:00
InputCallback, this);
ImGui::EndTabItem();
2020-11-11 14:42:01 +01:00
}
2021-12-07 22:47:41 +01:00
if (ImGui::BeginTabItem("hex.builtin.view.hexeditor.search.hex"_lang)) {
2020-11-15 23:04:46 +01:00
this->m_searchFunction = findHex;
this->m_lastSearchBuffer = &this->m_lastHexSearch;
2021-02-17 14:47:25 +01:00
currBuffer = &this->m_searchHexBuffer;
2020-11-15 23:04:46 +01:00
2021-02-17 14:47:25 +01:00
ImGui::InputText("##nolabel", currBuffer->data(), currBuffer->size(),
2020-11-15 23:04:46 +01:00
ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CallbackCompletion,
InputCallback, this);
ImGui::EndTabItem();
}
2020-11-11 14:42:01 +01:00
2021-02-17 14:47:25 +01:00
if (currBuffer != nullptr) {
2021-12-07 22:47:41 +01:00
if (ImGui::Button("hex.builtin.view.hexeditor.search.find"_lang))
2021-02-17 14:47:25 +01:00
Find(currBuffer->data());
2020-11-11 14:42:01 +01:00
if (!this->m_lastSearchBuffer->empty()) {
2021-12-07 22:47:41 +01:00
if ((ImGui::Button("hex.builtin.view.hexeditor.search.find_next"_lang)))
2021-02-17 14:47:25 +01:00
FindNext();
2020-11-11 14:42:01 +01:00
2021-02-17 14:47:25 +01:00
ImGui::SameLine();
2020-11-11 14:42:01 +01:00
2021-12-07 22:47:41 +01:00
if ((ImGui::Button("hex.builtin.view.hexeditor.search.find_prev"_lang)))
2021-02-17 14:47:25 +01:00
FindPrevious();
}
2020-11-11 14:42:01 +01:00
}
2020-11-15 23:04:46 +01:00
ImGui::EndTabBar();
2020-11-11 14:42:01 +01:00
}
ImGui::EndPopup();
}
2020-11-10 21:31:04 +01:00
}
void ViewHexEditor::drawGotoPopup() {
auto provider = ImHexApi::Provider::get();
auto baseAddress = provider->getBaseAddress();
auto dataSize = provider->getActualSize();
ImGui::SetNextWindowPos(ImGui::GetWindowPos() + ImGui::GetWindowContentRegionMin() - ImGui::GetStyle().WindowPadding);
2021-12-07 22:47:41 +01:00
if (ImGui::BeginPopup("hex.builtin.view.hexeditor.menu.file.goto"_lang)) {
if (ImGui::BeginTabBar("gotoTabs")) {
u64 newOffset = 0;
2021-12-07 22:47:41 +01:00
if (ImGui::BeginTabItem("hex.builtin.view.hexeditor.goto.offset.absolute"_lang)) {
ImGui::InputScalar("hex", ImGuiDataType_U64, &this->m_gotoAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
2020-11-10 21:31:04 +01:00
if (this->m_gotoAddress < baseAddress || this->m_gotoAddress > baseAddress + dataSize)
this->m_gotoAddress = baseAddress;
2020-11-10 21:31:04 +01:00
newOffset = this->m_gotoAddress;
ImGui::EndTabItem();
}
2021-12-07 22:47:41 +01:00
if (ImGui::BeginTabItem("hex.builtin.view.hexeditor.goto.offset.begin"_lang)) {
ImGui::InputScalar("hex", ImGuiDataType_U64, &this->m_gotoAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
if (this->m_gotoAddress < 0 || this->m_gotoAddress > dataSize)
this->m_gotoAddress = 0;
newOffset = this->m_gotoAddress + baseAddress;
ImGui::EndTabItem();
}
2021-12-07 22:47:41 +01:00
if (ImGui::BeginTabItem("hex.builtin.view.hexeditor.goto.offset.current"_lang)) {
ImGui::InputScalar("dec", ImGuiDataType_S64, &this->m_gotoAddress, nullptr, nullptr, "%lld", ImGuiInputTextFlags_CharsDecimal);
s64 currSelectionOffset = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
if (currSelectionOffset + this->m_gotoAddress < 0)
this->m_gotoAddress = -currSelectionOffset;
else if (currSelectionOffset + this->m_gotoAddress > dataSize)
this->m_gotoAddress = dataSize - currSelectionOffset;
newOffset = currSelectionOffset + this->m_gotoAddress + baseAddress;
ImGui::EndTabItem();
}
2021-12-07 22:47:41 +01:00
if (ImGui::BeginTabItem("hex.builtin.view.hexeditor.goto.offset.end"_lang)) {
ImGui::InputScalar("hex", ImGuiDataType_U64, &this->m_gotoAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
if (this->m_gotoAddress < 0 || this->m_gotoAddress > dataSize)
this->m_gotoAddress = 0;
newOffset = (baseAddress + dataSize) - this->m_gotoAddress - 1;
ImGui::EndTabItem();
}
2021-12-07 22:47:41 +01:00
if (ImGui::Button("hex.builtin.view.hexeditor.menu.file.goto"_lang)) {
provider->setCurrentPage(std::floor(double(newOffset - baseAddress) / hex::prv::Provider::PageSize));
EventManager::post<RequestSelectionChange>(Region { newOffset, 1 });
}
ImGui::EndTabBar();
2020-11-10 21:31:04 +01:00
}
ImGui::EndPopup();
2020-11-11 14:42:01 +01:00
}
}
void ViewHexEditor::drawEditPopup() {
auto provider = ImHexApi::Provider::get();
bool providerValid = ImHexApi::Provider::isValid();
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.edit.undo"_lang, "CTRL + Z", false, providerValid))
provider->undo();
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.edit.redo"_lang, "CTRL + Y", false, providerValid))
provider->redo();
2021-03-26 21:43:24 +01:00
2021-04-16 17:01:01 +02:00
ImGui::Separator();
bool bytesSelected = this->m_memoryEditor.DataPreviewAddr != -1 && this->m_memoryEditor.DataPreviewAddrEnd != -1;
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.edit.copy"_lang, "CTRL + C", false, bytesSelected))
2021-04-16 17:01:01 +02:00
this->copyBytes();
2021-12-07 22:47:41 +01:00
if (ImGui::BeginMenu("hex.builtin.view.hexeditor.menu.edit.copy_as"_lang, bytesSelected)) {
if (ImGui::MenuItem("hex.builtin.view.hexeditor.copy.hex"_lang, "CTRL + SHIFT + C"))
this->copyString();
ImGui::Separator();
for (const auto&[unlocalizedName, callback] : ContentRegistry::DataFormatter::getEntries()) {
if (ImGui::MenuItem(LangEntry(unlocalizedName))) {
size_t start = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
size_t end = std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
size_t copySize = (end - start) + 1;
ImGui::SetClipboardText(callback(provider, start + provider->getBaseAddress() + provider->getCurrentPageAddress(), copySize).c_str());
}
}
ImGui::EndMenu();
}
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.edit.paste"_lang, "CTRL + V", false, bytesSelected))
2021-04-16 17:01:01 +02:00
this->pasteBytes();
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.edit.select_all"_lang, "CTRL + A", false, providerValid))
EventManager::post<RequestSelectionChange>(Region { provider->getBaseAddress(), provider->getActualSize() });
2021-04-16 17:01:01 +02:00
ImGui::Separator();
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.edit.bookmark"_lang, nullptr, false, this->m_memoryEditor.DataPreviewAddr != -1 && this->m_memoryEditor.DataPreviewAddrEnd != -1)) {
auto base = ImHexApi::Provider::get()->getBaseAddress();
2021-02-17 14:57:32 +01:00
size_t start = base + std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
size_t end = base + std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
2021-01-20 20:16:24 +01:00
ImHexApi::Bookmarks::add(start, end - start + 1, { }, { });
}
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.edit.set_base"_lang, nullptr, false, providerValid && provider->isReadable())) {
2021-01-20 20:16:24 +01:00
std::memset(this->m_baseAddressBuffer, 0x00, sizeof(this->m_baseAddressBuffer));
2021-12-07 22:47:41 +01:00
View::doLater([]{ ImGui::OpenPopup("hex.builtin.view.hexeditor.menu.edit.set_base"_lang); });
}
2021-12-07 22:47:41 +01:00
if (ImGui::MenuItem("hex.builtin.view.hexeditor.menu.edit.resize"_lang, nullptr, false, providerValid && provider->isResizable())) {
2021-09-13 17:26:30 +02:00
View::doLater([this]{
this->m_resizeSize = ImHexApi::Provider::get()->getActualSize();
2021-12-07 22:47:41 +01:00
ImGui::OpenPopup("hex.builtin.view.hexeditor.menu.edit.resize"_lang);
});
}
}
void ViewHexEditor::registerEvents() {
EventManager::subscribe<RequestOpenFile>(this, [this](const std::string &filePath) {
this->openFile(filePath);
this->getWindowOpenState() = true;
});
EventManager::subscribe<RequestSelectionChange>(this, [this](Region region) {
auto provider = ImHexApi::Provider::get();
auto page = provider->getPageOfAddress(region.address);
if (!page.has_value())
return;
if (region.size != 0) {
provider->setCurrentPage(page.value());
u64 start = region.address - provider->getBaseAddress() - provider->getCurrentPageAddress();
this->m_memoryEditor.GotoAddrAndSelect(start, start + region.size - 1);
}
EventManager::post<EventRegionSelected>(Region { this->m_memoryEditor.DataPreviewAddr, (this->m_memoryEditor.DataPreviewAddrEnd - this->m_memoryEditor.DataPreviewAddr) + 1});
});
EventManager::subscribe<EventProjectFileLoad>(this, []() {
EventManager::post<RequestOpenFile>(ProjectFile::getFilePath());
});
EventManager::subscribe<EventWindowClosing>(this, [](GLFWwindow *window) {
if (ProjectFile::hasUnsavedChanges()) {
glfwSetWindowShouldClose(window, GLFW_FALSE);
View::doLater([] { ImGui::OpenPopup("hex.builtin.view.hexeditor.exit_application.title"_lang); });
}
});
EventManager::subscribe<RequestOpenWindow>(this, [this](std::string name) {
if (name == "Create File") {
hex::openFileBrowser("hex.builtin.view.hexeditor.create_file"_lang, DialogMode::Save, { }, [this](auto path) {
if (!this->createFile(path)) {
View::showErrorPopup("hex.builtin.view.hexeditor.error.create"_lang);
return;
}
EventManager::post<RequestOpenFile>(path);
this->getWindowOpenState() = true;
});
} else if (name == "Open File") {
hex::openFileBrowser("hex.builtin.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
EventManager::post<RequestOpenFile>(path);
this->getWindowOpenState() = true;
});
} else if (name == "Open Project") {
hex::openFileBrowser("hex.builtin.view.hexeditor.open_project"_lang, DialogMode::Open, { { "Project File", "hexproj" } }, [this](auto path) {
ProjectFile::load(path);
this->getWindowOpenState() = true;
});
}
});
EventManager::subscribe<EventSettingsChanged>(this, [this] {
{
auto alpha = ContentRegistry::Settings::getSetting("hex.builtin.setting.interface", "hex.builtin.setting.interface.highlight_alpha");
if (alpha.is_number())
this->m_highlightAlpha = alpha;
}
{
auto columnCount = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.column_count");
if (columnCount.is_number())
this->m_memoryEditor.Cols = static_cast<int>(columnCount);
}
{
auto hexii = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.hexii");
if (hexii.is_number())
this->m_memoryEditor.OptShowHexII = static_cast<int>(hexii);
}
{
auto ascii = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.ascii");
if (ascii.is_number())
this->m_memoryEditor.OptShowAscii = static_cast<int>(ascii);
}
{
auto advancedDecoding = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.advanced_decoding");
if (advancedDecoding.is_number())
this->m_memoryEditor.OptShowAdvancedDecoding = static_cast<int>(advancedDecoding);
}
{
auto greyOutZeros = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.grey_zeros");
if (greyOutZeros.is_number())
this->m_memoryEditor.OptGreyOutZeroes = static_cast<int>(greyOutZeros);
}
{
auto upperCaseHex = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.uppercase_hex");
if (upperCaseHex.is_number())
this->m_memoryEditor.OptUpperCaseHex = static_cast<int>(upperCaseHex);
}
{
auto showExtraInfo = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.extra_info");
if (showExtraInfo.is_number())
this->m_memoryEditor.OptShowExtraInfo = static_cast<int>(showExtraInfo);
}
});
EventManager::subscribe<QuerySelection>(this, [this](auto &region) {
u64 address = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
size_t size = std::abs(s64(this->m_memoryEditor.DataPreviewAddrEnd) - s64(this->m_memoryEditor.DataPreviewAddr)) + 1;
region = Region { address, size };
});
}
void ViewHexEditor::registerShortcuts() {
ShortcutManager::addGlobalShortcut(CTRL + Keys::S, [] {
save();
});
ShortcutManager::addGlobalShortcut(CTRL + SHIFT + Keys::S, [] {
saveAs();
});
ShortcutManager::addShortcut(this, CTRL + Keys::Z, [] {
if (ImHexApi::Provider::isValid())
ImHexApi::Provider::get()->undo();
});
ShortcutManager::addShortcut(this, CTRL + Keys::Y, [] {
if (ImHexApi::Provider::isValid())
ImHexApi::Provider::get()->redo();
});
ShortcutManager::addShortcut(this, CTRL + Keys::F, [] {
ImGui::OpenPopupInWindow(View::toWindowName("hex.builtin.view.hexeditor.name").c_str(), "hex.builtin.view.hexeditor.menu.file.search"_lang);
});
ShortcutManager::addShortcut(this, CTRL + Keys::G, [] {
ImGui::OpenPopupInWindow(View::toWindowName("hex.builtin.view.hexeditor.name").c_str(), "hex.builtin.view.hexeditor.menu.file.goto"_lang);
});
ShortcutManager::addShortcut(this, CTRL + Keys::O, [] {
hex::openFileBrowser("hex.builtin.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [](auto path) {
EventManager::post<RequestOpenFile>(path);
});
});
ShortcutManager::addShortcut(this, CTRL + Keys::C, [this] {
this->copyBytes();
});
ShortcutManager::addShortcut(this, CTRL + SHIFT + Keys::C, [this] {
this->copyString();
});
ShortcutManager::addShortcut(this, CTRL + Keys::V, [this] {
this->pasteBytes();
});
ShortcutManager::addShortcut(this, CTRL + Keys::A, [this] {
auto provider = ImHexApi::Provider::get();
EventManager::post<RequestSelectionChange>(Region { provider->getBaseAddress(), provider->getActualSize() });
});
}
2020-11-10 21:31:04 +01:00
}