From b751f98e915e465fd5e66b688b131d1652c68d9d Mon Sep 17 00:00:00 2001 From: WerWolv Date: Fri, 27 May 2022 20:42:07 +0200 Subject: [PATCH] ui/ux: Rewrite of the entire hex editor view to make it more flexible (#512) * ui/ux: Initial recreation of the hex editor view * ui/ux: Added back support for editing cells * ux: Make scrolling and selecting bytes feel nice again * ui/ux: Improved byte selecting, added footer * sys: Make math evaluator more generic to support integer only calculations * patterns: Moved value formatting into pattern language * ui/ux: Added Goto and Search popups, improved selection * ui: Added better tooltips for bookmarks and patterns * sys: Use worse hex search algorithm on macOS Sadly it still doesn't support `std::boyer_moore_horsepool_searcher` * ui: Added back missing events, menu items and shortcuts * fix: Bookmark highlighting being rendered off by one * fix: Various macOS build errors * fix: size_t is not u64 on macos * fix: std::fmod and std::pow not working with integer types on macos * fix: Missing semicolons * sys: Added proper integer pow function * ui: Added back support for custom encodings * fix: Editor not jumping to selection when selection gets changed * ui: Turn Hexii setting into a data visualizer * sys: Added back remaining shortcuts * sys: Remove old hex editor files * sys: Moved more legacy things away from the hex editor view, updated localization * fix: Hex editor scrolling behaving weirdly and inconsistently * sys: Cleaned up Hex editor code * sys: Added selection color setting, localized all new settings * fix: Search feature not working correctly * ui: Replace custom ImGui::Disabled function with native ImGui ones * ui: Fix bookmark tooltip rendering issues * fix: Another size_t not being 64 bit issue on MacOS --- .idea/vcs.xml | 3 - .../imgui/include/imgui_memory_editor.h | 805 ------ lib/external/pattern_language | 2 +- lib/libimhex/include/hex.hpp | 16 + .../include/hex/api/content_registry.hpp | 43 + lib/libimhex/include/hex/api/event.hpp | 2 +- lib/libimhex/include/hex/api/imhex_api.hpp | 58 +- lib/libimhex/include/hex/helpers/utils.hpp | 25 + .../include/hex/providers/buffered_reader.hpp | 223 ++ .../include/hex/ui/imgui_imhex_extensions.h | 5 +- lib/libimhex/source/api/content_registry.cpp | 46 + lib/libimhex/source/api/imhex_api.cpp | 141 +- lib/libimhex/source/helpers/encoding_file.cpp | 4 +- .../source/ui/imgui_imhex_extensions.cpp | 47 +- main/include/window.hpp | 2 + main/source/init/splash_window.cpp | 2 +- main/source/init/tasks.cpp | 7 +- main/source/window/window.cpp | 24 +- plugins/builtin/CMakeLists.txt | 2 + .../include/content/views/view_hex_editor.hpp | 183 +- .../content/views/view_pattern_editor.hpp | 4 + .../include/content/views/view_yara.hpp | 2 + plugins/builtin/include/math_evaluator.hpp | 155 +- .../content/command_palette_commands.cpp | 10 +- .../builtin/source/content/data_inspector.cpp | 22 +- .../source/content/data_visualizers.cpp | 272 ++ plugins/builtin/source/content/events.cpp | 91 + .../source/content/main_menu_items.cpp | 284 +- .../source/content/settings_entries.cpp | 61 +- .../builtin/source/content/tools_entries.cpp | 4 +- plugins/builtin/source/content/ui_items.cpp | 74 +- .../source/content/views/view_bookmarks.cpp | 77 +- .../source/content/views/view_diff.cpp | 2 +- .../content/views/view_disassembler.cpp | 7 +- .../source/content/views/view_hex_editor.cpp | 2356 +++++++++-------- .../source/content/views/view_information.cpp | 7 +- .../content/views/view_pattern_editor.cpp | 93 +- .../source/content/views/view_strings.cpp | 7 +- .../source/content/views/view_yara.cpp | 23 +- .../builtin/source/content/welcome_screen.cpp | 24 +- plugins/builtin/source/lang/de_DE.cpp | 118 +- plugins/builtin/source/lang/en_US.cpp | 116 +- plugins/builtin/source/lang/it_IT.cpp | 116 +- plugins/builtin/source/lang/ja_JP.cpp | 116 +- plugins/builtin/source/lang/zh_CN.cpp | 115 +- plugins/builtin/source/math_evaluator.cpp | 224 +- plugins/builtin/source/pattern_drawer.cpp | 104 +- plugins/builtin/source/plugin_builtin.cpp | 11 +- 48 files changed, 3403 insertions(+), 2732 deletions(-) delete mode 100644 lib/external/imgui/include/imgui_memory_editor.h create mode 100644 lib/libimhex/include/hex/providers/buffered_reader.hpp create mode 100644 plugins/builtin/source/content/data_visualizers.cpp create mode 100644 plugins/builtin/source/content/events.cpp diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 996dfdeae..eeaf094c6 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,9 +2,6 @@ - - - diff --git a/lib/external/imgui/include/imgui_memory_editor.h b/lib/external/imgui/include/imgui_memory_editor.h deleted file mode 100644 index a2517567b..000000000 --- a/lib/external/imgui/include/imgui_memory_editor.h +++ /dev/null @@ -1,805 +0,0 @@ -// Mini memory editor for Dear ImGui (to embed in your game/tools) -// Get latest version at http://www.github.com/ocornut/imgui_club -// -// Right-click anywhere to access the Options menu! -// You can adjust the keyboard repeat delay/rate in ImGuiIO. -// The code assume a mono-space font for simplicity! -// If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before caling this. -// -// Usage: -// // Create a window and draw memory editor inside it: -// static MemoryEditor mem_edit_1; -// static char data[0x10000]; -// size_t data_size = 0x10000; -// mem_edit_1.DrawWindow("Memory Editor", data, data_size); -// -// Usage: -// // If you already have a window, use DrawContents() instead: -// static MemoryEditor mem_edit_2; -// ImGui::Begin("MyWindow") -// mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this); -// ImGui::End(); -// -// Changelog: -// - v0.10: initial version -// - v0.23 (2017/08/17): added to github. fixed right-arrow triggering a byte write. -// - v0.24 (2018/06/02): changed DragInt("Rows" to use a %d data format (which is desirable since imgui 1.61). -// - v0.25 (2018/07/11): fixed wording: all occurrences of "Rows" renamed to "Columns". -// - v0.26 (2018/08/02): fixed clicking on hex region -// - v0.30 (2018/08/02): added data preview for common data types -// - v0.31 (2018/10/10): added OptUpperCaseHex option to select lower/upper casing display [@samhocevar] -// - v0.32 (2018/10/10): changed signatures to use void* instead of unsigned char* -// - v0.33 (2018/10/10): added OptShowOptions option to hide all the interactive option setting. -// - v0.34 (2019/05/07): binary preview now applies endianness setting [@nicolasnoble] -// - v0.35 (2020/01/29): using ImGuiDataType available since Dear ImGui 1.69. -// - v0.36 (2020/05/05): minor tweaks, minor refactor. -// - v0.40 (2020/10/04): fix misuse of ImGuiListClipper API, broke with Dear ImGui 1.79. made cursor position appears on left-side of edit box. option popup appears on mouse release. fix MSVC warnings where _CRT_SECURE_NO_WARNINGS wasn't working in recent versions. -// - v0.41 (2020/10/05): fix when using with keyboard/gamepad navigation enabled. -// - v0.42 (2020/10/14): fix for . character in ASCII view always being greyed out. -// -// Todo/Bugs: -// - This is generally old code, it should work but please don't use this as reference! -// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame. -// - Using InputText() is awkward and maybe overkill here, consider implementing something custom. - -#pragma once - -#include // sprintf, scanf -#include // uint8_t, etc. - -#include -#include -#include -#include - -#include - -#ifdef _MSC_VER -#define _PRISizeT "I" -#define ImSnprintf _snprintf -#else -#define _PRISizeT "z" -#define ImSnprintf snprintf -#endif - -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe. -#endif - -ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b); - -struct MemoryEditor -{ - enum DataFormat - { - DataFormat_Bin = 0, - DataFormat_Dec = 1, - DataFormat_Hex = 2, - DataFormat_COUNT - }; - - struct DecodeData { - std::string data; - size_t advance; - ImColor color; - }; - - // Settings - bool ReadOnly; // = false // disable any editing. - int Cols; // = 16 // number of columns to display. - bool OptShowOptions; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them. - bool OptShowHexII; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X". - bool OptShowAscii; // = true // display ASCII representation on the right side. - bool OptShowAdvancedDecoding; // = true // display advanced decoding data on the right side. - bool OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color. - bool OptUpperCaseHex; // = true // display hexadecimal values as "FF" instead of "ff". - bool OptShowExtraInfo; // = true // display extra information about size of data and current selection - int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between every mid-cols. - int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr). - ImU32 HighlightColor; // // background color of highlighted bytes. - ImU8 (*ReadFn)(const ImU8* data, size_t off); // = 0 // optional handler to read bytes. - void (*WriteFn)(ImU8* data, size_t off, ImU8 d); // = 0 // optional handler to write bytes. - bool (*HighlightFn)(const ImU8* data, size_t off, bool next);//= 0 // optional handler to return Highlight property (to support non-contiguous highlighting). - void (*HoverFn)(const ImU8 *data, size_t off); - DecodeData (*DecodeFn)(const ImU8 *data, size_t off); - - // [Internal State] - bool ContentsWidthChanged; - size_t DataPreviewAddr; - size_t DataPreviewAddrOld; - size_t DataPreviewAddrEnd; - size_t DataPreviewAddrEndOld; - size_t DataEditingAddr; - bool DataEditingTakeFocus; - char DataInputBuf[32]; - char AddrInputBuf[32]; - size_t GotoAddr; - size_t HighlightMin, HighlightMax; - int PreviewEndianess; - ImGuiDataType PreviewDataType; - - MemoryEditor() - { - // Settings - ReadOnly = false; - Cols = 16; - OptShowOptions = true; - OptShowHexII = false; - OptShowAscii = true; - OptShowAdvancedDecoding = true; - OptGreyOutZeroes = true; - OptUpperCaseHex = true; - OptMidColsCount = 8; - OptAddrDigitsCount = 0; - HighlightColor = IM_COL32(255, 255, 255, 50); - ReadFn = NULL; - WriteFn = NULL; - HighlightFn = NULL; - HoverFn = NULL; - DecodeFn = NULL; - - // State/Internals - ContentsWidthChanged = false; - DataPreviewAddr = DataEditingAddr = DataPreviewAddrEnd = (size_t)-1; - DataPreviewAddrOld = DataPreviewAddrEndOld = (size_t)-1; - DataEditingTakeFocus = false; - memset(DataInputBuf, 0, sizeof(DataInputBuf)); - memset(AddrInputBuf, 0, sizeof(AddrInputBuf)); - GotoAddr = (size_t)-1; - HighlightMin = HighlightMax = (size_t)-1; - PreviewEndianess = 0; - PreviewDataType = ImGuiDataType_S32; - } - - void GotoAddrAndHighlight(size_t addr_min, size_t addr_max) - { - GotoAddr = addr_min; - HighlightMin = addr_min; - HighlightMax = addr_max; - } - - void GotoAddrAndSelect(size_t addr_min, size_t addr_max) - { - GotoAddr = addr_min; - DataPreviewAddr = addr_min; - DataPreviewAddrEnd = addr_max; - DataPreviewAddrOld = addr_min; - DataPreviewAddrEndOld = addr_max; - } - - struct Sizes - { - int AddrDigitsCount; - float LineHeight; - float GlyphWidth; - float HexCellWidth; - float SpacingBetweenMidCols; - float PosHexStart; - float PosHexEnd; - float PosAsciiStart; - float PosAsciiEnd; - float PosDecodingStart; - float PosDecodingEnd; - float WindowWidth; - - Sizes() { memset(this, 0, sizeof(*this)); } - }; - - void CalcSizes(Sizes& s, size_t mem_size, size_t base_display_addr) - { - ImGuiStyle& style = ImGui::GetStyle(); - s.AddrDigitsCount = OptAddrDigitsCount; - if (s.AddrDigitsCount == 0) - for (size_t n = base_display_addr + mem_size - 1; n > 0; n >>= 4) - s.AddrDigitsCount++; - s.LineHeight = ImGui::GetTextLineHeight(); - s.GlyphWidth = ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space - s.HexCellWidth = (float)(int)(s.GlyphWidth * 2.5f); // "FF " we include trailing space in the width to easily catch clicks everywhere - s.SpacingBetweenMidCols = (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns we add a bit of extra spacing - s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth; - s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols); - s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd; - - if (OptShowAscii && OptShowAdvancedDecoding) { - s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1; - if (OptMidColsCount > 0) - s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols; - s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth; - - s.PosDecodingStart = s.PosAsciiEnd + s.GlyphWidth * 1; - if (OptMidColsCount > 0) - s.PosDecodingStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols; - s.PosDecodingEnd = s.PosDecodingStart + Cols * s.GlyphWidth; - } else if (OptShowAscii) { - s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1; - if (OptMidColsCount > 0) - s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols; - s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth; - } else if (OptShowAdvancedDecoding) { - s.PosDecodingStart = s.PosHexEnd + s.GlyphWidth * 1; - if (OptMidColsCount > 0) - s.PosDecodingStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols; - s.PosDecodingEnd = s.PosDecodingStart + Cols * s.GlyphWidth; - } - s.WindowWidth = s.PosAsciiEnd + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth; - } - - // Standalone Memory Editor window - void DrawWindow(const char* title, bool *p_open, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000) - { - Sizes s; - CalcSizes(s, mem_size, base_display_addr); - - if (ImGui::Begin(title, p_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoNavInputs)) - { - if (DataPreviewAddr != DataPreviewAddrOld || DataPreviewAddrEnd != DataPreviewAddrEndOld) { - hex::Region selectionRegion = { std::min(DataPreviewAddr, DataPreviewAddrEnd) + base_display_addr, (std::max(DataPreviewAddr, DataPreviewAddrEnd) - std::min(DataPreviewAddr, DataPreviewAddrEnd)) + 1 }; - hex::EventManager::post(selectionRegion); - } - - DataPreviewAddrOld = DataPreviewAddr; - DataPreviewAddrEndOld = DataPreviewAddrEnd; - - if (mem_size > 0) - DrawContents(mem_data, mem_size, base_display_addr); - - if (ContentsWidthChanged) - { - CalcSizes(s, mem_size, base_display_addr); - ImGui::SetWindowSize(ImVec2(s.WindowWidth, ImGui::GetWindowSize().y)); - } - } - ImGui::End(); - - } - - // Memory Editor contents only - void DrawContents(void* mem_data_void, size_t mem_size, size_t base_display_addr = 0x0000) - { - if (Cols < 1) - Cols = 1; - - ImU8* mem_data = (ImU8*)mem_data_void; - Sizes s; - CalcSizes(s, mem_size, base_display_addr); - ImGuiStyle& style = ImGui::GetStyle(); - - // We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window. - // This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move. - const float height_separator = style.ItemSpacing.y; - float footer_height = 0; - if (OptShowOptions) - footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 2; - - ImGui::BeginChild("offset", ImVec2(0, s.LineHeight), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav); - ImGui::Text("%*c ", s.AddrDigitsCount, ' '); - for (int i = 0; i < Cols; i++) { - float byte_pos_x = s.PosHexStart + s.HexCellWidth * i; - if (OptMidColsCount > 0) - byte_pos_x += (float)(i / OptMidColsCount) * s.SpacingBetweenMidCols; - ImGui::SameLine(byte_pos_x); - ImGui::TextFormatted("{:02X}", i + (base_display_addr % Cols)); - } - ImGui::EndChild(); - - ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav); - - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - - - - // We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function. - ImGuiListClipper clipper; - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - - const int line_total_count = (int)((mem_size + Cols - 1) / Cols); - clipper.Begin(line_total_count, s.LineHeight); - const size_t visible_start_addr = clipper.DisplayStart * Cols; - const size_t visible_end_addr = clipper.DisplayEnd * Cols; - const size_t visible_count = visible_end_addr - visible_start_addr; - - bool data_next = false; - - if (DataEditingAddr >= mem_size) - DataEditingAddr = (size_t)-1; - if (DataPreviewAddr >= mem_size) - DataPreviewAddr = (size_t)-1; - if (DataPreviewAddrEnd >= mem_size) - DataPreviewAddrEnd = (size_t)-1; - - size_t data_editing_addr_backup = DataEditingAddr; - size_t data_preview_addr_backup = DataPreviewAddr; - size_t data_editing_addr_next = (size_t)-1; - size_t data_preview_addr_next = (size_t)-1; - - if (ImGui::IsWindowFocused()) { - // Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered) - if (DataEditingAddr != (size_t)-1) { - if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && DataEditingAddr >= (size_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; DataEditingTakeFocus = true; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && DataEditingAddr < mem_size - Cols) { data_editing_addr_next = DataEditingAddr + Cols; DataEditingTakeFocus = true; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && DataEditingAddr > 0) { data_editing_addr_next = DataEditingAddr - 1; DataEditingTakeFocus = true; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && DataEditingAddr < mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; DataEditingTakeFocus = true; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp)) && DataEditingAddr > 0) { data_editing_addr_next = std::max(0, DataEditingAddr - visible_count); DataEditingTakeFocus = true; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown)) && DataEditingAddr < mem_size - 1) { data_editing_addr_next = std::min(mem_size - 1, DataEditingAddr + visible_count); DataEditingTakeFocus = true; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)) && DataEditingAddr > 0) { data_editing_addr_next = 0; DataEditingTakeFocus = true; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)) && DataEditingAddr < mem_size - 1) { data_editing_addr_next = mem_size - 1; DataEditingTakeFocus = true; } - } else if (DataPreviewAddr != (size_t)-1) { - if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)) && DataPreviewAddr >= (size_t)Cols) { DataPreviewAddr = data_preview_addr_next = DataPreviewAddr - Cols; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)) && DataPreviewAddr < mem_size - Cols) { DataPreviewAddr = data_preview_addr_next = DataPreviewAddr + Cols; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)) && DataPreviewAddr > 0) { DataPreviewAddr = data_preview_addr_next = DataPreviewAddr - 1; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)) && DataPreviewAddr < mem_size - 1) { DataPreviewAddr = data_preview_addr_next = DataPreviewAddr + 1; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp)) && DataPreviewAddr > 0) { DataPreviewAddr = data_preview_addr_next = std::max(0, DataPreviewAddr - visible_count); if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown)) && DataPreviewAddr < mem_size - 1) { DataPreviewAddr = data_preview_addr_next = std::min(mem_size - 1, DataPreviewAddr + visible_count); if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)) && DataPreviewAddr > 0) { DataPreviewAddr = data_preview_addr_next = 0; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; } - else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)) && DataPreviewAddr < mem_size - 1) { DataPreviewAddr = data_preview_addr_next = mem_size - 1; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; } - } - } else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape))) { - DataPreviewAddr = DataPreviewAddrOld = DataPreviewAddrEnd = DataPreviewAddrEndOld = data_preview_addr_next = (size_t)-1; - HighlightMin = HighlightMax = (size_t)-1; - - hex::EventManager::post(hex::Region{ (size_t)-1, 0 }); - } - - if (data_preview_addr_next != (size_t)-1 && (data_preview_addr_next / Cols) != (data_preview_addr_backup / Cols)) - { - // Track cursor movements - const int scroll_offset = ((int)(data_preview_addr_next / Cols) - (int)(data_preview_addr_backup / Cols)); - const bool scroll_desired = (scroll_offset < 0 && data_preview_addr_next < visible_start_addr + Cols * 2) || (scroll_offset > 0 && data_preview_addr_next > visible_end_addr - Cols * 2); - if (scroll_desired) - ImGui::SetScrollY(ImGui::GetScrollY() + scroll_offset * s.LineHeight); - } - if (data_editing_addr_next != (size_t)-1 && (data_editing_addr_next / Cols) != (data_editing_addr_backup / Cols)) - { - // Track cursor movements - const int scroll_offset = ((int)(data_editing_addr_next / Cols) - (int)(data_editing_addr_backup / Cols)); - const bool scroll_desired = (scroll_offset < 0 && data_editing_addr_next < visible_start_addr + Cols * 2) || (scroll_offset > 0 && data_editing_addr_next > visible_end_addr - Cols * 2); - if (scroll_desired) - ImGui::SetScrollY(ImGui::GetScrollY() + scroll_offset * s.LineHeight); - } - - // Draw vertical separator - ImVec2 window_pos = ImGui::GetWindowPos(); - float scrollX = ImGui::GetScrollX(); - - if (OptShowAscii) - draw_list->AddLine(ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth - scrollX, window_pos.y), ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth - scrollX, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border)); - if (OptShowAdvancedDecoding) - draw_list->AddLine(ImVec2(window_pos.x + s.PosDecodingStart - s.GlyphWidth - scrollX, window_pos.y), ImVec2(window_pos.x + s.PosDecodingStart - s.GlyphWidth - scrollX, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border)); - - const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text); - const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text; - - const char* format_address = OptUpperCaseHex ? "%0*" _PRISizeT "X: " : "%0*" _PRISizeT "x: "; - const char* format_data = OptUpperCaseHex ? "%0*" _PRISizeT "X" : "%0*" _PRISizeT "x"; - const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x"; - const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x "; - - bool tooltipShown = false; - while (clipper.Step()) - { - for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines - { - size_t addr = (size_t)(line_i * Cols); - ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr); - - // Draw Hexadecimal - for (int n = 0; n < Cols && addr < mem_size; n++, addr++) - { - float byte_pos_x = s.PosHexStart + s.HexCellWidth * n; - if (OptMidColsCount > 0) - byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols; - ImGui::SameLine(byte_pos_x); - - // Draw highlight - bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax); - bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false)); - bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr); - if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview) - { - ImVec2 pos = ImGui::GetCursorScreenPos() - ImVec2(ImGui::GetStyle().CellPadding.x / 2, 0); - float highlight_width = s.GlyphWidth * 2 + ImGui::GetStyle().CellPadding.x / 2; - bool is_next_byte_highlighted = (addr + 1 < mem_size) && - ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || - (HighlightFn && HighlightFn(mem_data, addr + 1, true)) || - ((addr + 1) >= DataPreviewAddr && (addr + 1) <= DataPreviewAddrEnd) || ((addr + 1) >= DataPreviewAddrEnd && (addr + 1) <= DataPreviewAddr)); - if (is_next_byte_highlighted) - { - highlight_width = s.HexCellWidth; - if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0) - highlight_width += s.SpacingBetweenMidCols; - } - - ImU32 color = HighlightColor; - if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1) - color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000; - - draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), color); - - if (is_highlight_from_preview) { - size_t min = std::min(DataPreviewAddr, DataPreviewAddrEnd); - size_t max = std::max(DataPreviewAddr, DataPreviewAddrEnd); - - // Draw vertical line at the left of first byte and the start of the line - if (n == 0 || addr == min) - draw_list->AddLine(pos, pos + ImVec2(0, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F); - - // Draw vertical line at the right of the last byte and the end of the line - if (n == Cols - 1 || addr == max) { - draw_list->AddRectFilled(pos + ImVec2(highlight_width, 0), pos + ImVec2(highlight_width + 1, s.LineHeight), color); - draw_list->AddLine(pos + ImVec2(highlight_width + 1, -1), pos + ImVec2(highlight_width + 1, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F); - } - - // Draw horizontal line at the top of the bytes - if ((addr - Cols) < min) - draw_list->AddLine(pos, pos + ImVec2(highlight_width + 1, 0), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F); - - // Draw horizontal line at the bottom of the bytes - if ((addr + Cols) == (max + 1) && OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 1) - draw_list->AddLine(pos + ImVec2(-s.SpacingBetweenMidCols, s.LineHeight), pos + ImVec2(highlight_width + 1, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F); - else if ((addr + Cols) > max) - draw_list->AddLine(pos + ImVec2(0, s.LineHeight), pos + ImVec2(highlight_width + 1, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F); - } - } - - if (DataEditingAddr == addr) - { - // Display text input on current byte - bool data_write = false; - ImGui::PushID((void*)addr); - if (DataEditingTakeFocus) - { - ImGui::SetKeyboardFocusHere(); - ImGui::CaptureKeyboardFromApp(true); - sprintf(AddrInputBuf, format_data, s.AddrDigitsCount, base_display_addr + addr); - sprintf(DataInputBuf, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]); - } - ImGui::PushItemWidth(s.GlyphWidth * 2); - struct UserData - { - // FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here. - static int Callback(ImGuiInputTextCallbackData* data) - { - UserData* user_data = (UserData*)data->UserData; - if (!data->HasSelection()) - user_data->CursorPos = data->CursorPos; - if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen) - { - // When not editing a byte, always rewrite its content (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there) - data->DeleteChars(0, data->BufTextLen); - data->InsertChars(0, user_data->CurrentBufOverwrite); - data->SelectionStart = 0; - data->SelectionEnd = 2; - data->CursorPos = 0; - } - return 0; - } - char CurrentBufOverwrite[3]; // Input - int CursorPos; // Output - }; - UserData user_data; - user_data.CursorPos = -1; - sprintf(user_data.CurrentBufOverwrite, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]); - ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_AlwaysInsertMode | ImGuiInputTextFlags_CallbackAlways; - if (ImGui::InputText("##data", DataInputBuf, 32, flags, UserData::Callback, &user_data)) - data_write = data_next = true; - else if (!DataEditingTakeFocus && !ImGui::IsItemActive()) - DataEditingAddr = data_editing_addr_next = (size_t)-1; - DataEditingTakeFocus = false; - ImGui::PopItemWidth(); - if (user_data.CursorPos >= 2) - data_write = data_next = true; - if (data_editing_addr_next != (size_t)-1) - data_write = data_next = false; - unsigned int data_input_value = 0; - if (data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1) - { - if (WriteFn) - WriteFn(mem_data, addr, (ImU8)data_input_value); - else - mem_data[addr] = (ImU8)data_input_value; - } - ImGui::PopID(); - } - else - { - // NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on. - ImU8 b = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]; - - if (OptShowHexII) - { - if ((b >= 32 && b < 128)) - ImGui::Text(".%c ", b); - else if (b == 0xFF && OptGreyOutZeroes) - ImGui::TextDisabled("## "); - else if (b == 0x00) - ImGui::Text(" "); - else - ImGui::Text(format_byte_space, b); - } - else - { - if (b == 0 && OptGreyOutZeroes) - ImGui::TextDisabled("00 "); - else - ImGui::Text(format_byte_space, b); - } - if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift) - { - if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) { - DataEditingTakeFocus = true; - data_editing_addr_next = addr; - } - - DataPreviewAddr = addr; - DataPreviewAddrEnd = addr; - } - if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) { - DataPreviewAddrEnd = addr; - } - if (ImGui::IsItemHovered() && !tooltipShown) { - if (HoverFn) { - HoverFn(mem_data, addr); - tooltipShown = true; - } - } - } - } - - if (OptShowAscii) - { - // Draw ASCII values - ImGui::SameLine(s.PosAsciiStart); - ImVec2 pos = ImGui::GetCursorScreenPos(); - addr = line_i * Cols; - - ImGui::PushID(-1); - ImGui::SameLine(); - ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight)); - - ImGui::PopID(); - - for (int n = 0; n < Cols && addr < mem_size; n++, addr++) - { - if (addr == DataEditingAddr) - { - draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg)); - draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg)); - } - unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]; - char display_c = (c < 32 || c >= 128) ? '.' : c; - draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1); - - // Draw highlight - bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax); - bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false)); - bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr); - if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview) - { - ImU32 color = HighlightColor; - if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1) - color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000; - - draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), color); - } - - - ImGui::PushID(line_i * Cols + n); - ImGui::SameLine(); - ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight)); - - ImGui::PopID(); - - if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift) - { - if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) { - DataEditingTakeFocus = true; - data_editing_addr_next = addr; - } - - DataPreviewAddr = addr; - DataPreviewAddrEnd = addr; - - } - if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) { - DataPreviewAddrEnd = addr; - } - - pos.x += s.GlyphWidth; - } - - ImGui::PushID(-1); - ImGui::SameLine(); - ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight)); - - ImGui::PopID(); - } - - if (OptShowAdvancedDecoding && DecodeFn) { - // Draw decoded bytes - ImGui::SameLine(s.PosDecodingStart); - ImVec2 pos = ImGui::GetCursorScreenPos(); - addr = line_i * Cols; - - ImGui::PushID(-1); - ImGui::SameLine(); - ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight)); - - ImGui::PopID(); - - for (int n = 0; n < Cols && addr < mem_size;) - { - auto decodedData = DecodeFn(mem_data, addr); - - auto displayData = decodedData.data; - auto glyphWidth = ImGui::CalcTextSize(displayData.c_str()).x + 1; - - if (addr == DataEditingAddr) - { - draw_list->AddRectFilled(pos, ImVec2(pos.x + glyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg)); - draw_list->AddRectFilled(pos, ImVec2(pos.x + glyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg)); - } - - draw_list->AddText(pos, decodedData.color, displayData.c_str(), displayData.c_str() + displayData.length()); - - // Draw highlight - bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax); - bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false)); - bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr); - if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview) - { - ImU32 color = HighlightColor; - if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1) - color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000; - - draw_list->AddRectFilled(pos, ImVec2(pos.x + glyphWidth, pos.y + s.LineHeight), color); - } - - - ImGui::PushID(line_i * Cols + n); - ImGui::SameLine(); - ImGui::Dummy(ImVec2(glyphWidth, s.LineHeight)); - - ImGui::PopID(); - - if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift) - { - if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) { - DataEditingTakeFocus = true; - data_editing_addr_next = addr; - } - - DataPreviewAddr = addr; - DataPreviewAddrEnd = addr; - - } - if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) { - DataPreviewAddrEnd = addr; - } - - pos.x += glyphWidth; - - if (addr <= 1) { - n++; - addr++; - } else { - n += decodedData.advance; - addr += decodedData.advance; - } - } - } - } - } - - ImGui::PopStyleVar(2); - ImGui::EndChild(); - - if (data_next && DataEditingAddr < mem_size) - { - DataEditingAddr = DataPreviewAddr = DataPreviewAddrEnd = DataEditingAddr + 1; - DataEditingTakeFocus = true; - } - else if (data_editing_addr_next != (size_t)-1) - { - DataEditingAddr = DataPreviewAddr = DataPreviewAddrEnd = data_editing_addr_next; - } - - if (OptShowOptions) - { - ImGui::Separator(); - DrawOptionsLine(s, mem_data, mem_size, base_display_addr); - } - - // Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child) - ImGui::SetCursorPosX(s.WindowWidth); - } - - void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr) - { - IM_UNUSED(mem_data); - const char* format_range = OptUpperCaseHex ? "Range 0x%0*" _PRISizeT "X..0x%0*" _PRISizeT "X" : "Range 0x%0*" _PRISizeT "x..0x%0*" _PRISizeT "x"; - const char* format_selection = OptUpperCaseHex ? "Selection 0x%0*" _PRISizeT "X..0x%0*" _PRISizeT "X (%ld [0x%lX] %s)" : "Range 0x%0*" _PRISizeT "x..0x%0*" _PRISizeT "x (%ld [0x%lX] %s)"; - - if (this->OptShowExtraInfo) { - ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1); - if (DataPreviewAddr != (size_t)-1 && DataPreviewAddrEnd != (size_t)-1) { - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - - auto selectionStart = std::min(DataPreviewAddr, DataPreviewAddrEnd); - auto selectionEnd = std::max(DataPreviewAddr, DataPreviewAddrEnd); - - size_t regionSize = (selectionEnd - selectionStart) + 1; - ImGui::Text(format_selection, s.AddrDigitsCount, base_display_addr + selectionStart, s.AddrDigitsCount, base_display_addr + selectionEnd, regionSize, regionSize, regionSize == 1 ? "byte" : "bytes"); - } - } - - if (GotoAddr != (size_t)-1) - { - if (GotoAddr < mem_size) - { - ImGui::BeginChild("##scrolling"); - ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + (GotoAddr / Cols) * ImGui::GetTextLineHeight()); - ImGui::EndChild(); - } - GotoAddr = (size_t)-1; - } - } - - static bool IsBigEndian() - { - uint16_t x = 1; - char c[2]; - memcpy(c, &x, 2); - return c[0] != 0; - } - - static void* EndianessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian) - { - if (is_little_endian) - { - uint8_t* dst = (uint8_t*)_dst; - uint8_t* src = (uint8_t*)_src + s - 1; - for (int i = 0, n = (int)s; i < n; ++i) - memcpy(dst++, src--, 1); - return _dst; - } - else - { - return memcpy(_dst, _src, s); - } - } - - static void* EndianessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian) - { - if (is_little_endian) - { - return memcpy(_dst, _src, s); - } - else - { - uint8_t* dst = (uint8_t*)_dst; - uint8_t* src = (uint8_t*)_src + s - 1; - for (int i = 0, n = (int)s; i < n; ++i) - memcpy(dst++, src--, 1); - return _dst; - } - } - - void* EndianessCopy(void* dst, void* src, size_t size) const - { - static void* (*fp)(void*, void*, size_t, int) = NULL; - if (fp == NULL) - fp = IsBigEndian() ? EndianessCopyBigEndian : EndianessCopyLittleEndian; - return fp(dst, src, size, PreviewEndianess); - } -}; - -#undef _PRISizeT -#undef ImSnprintf - -#ifdef _MSC_VER -#pragma warning (pop) -#endif \ No newline at end of file diff --git a/lib/external/pattern_language b/lib/external/pattern_language index 0ad67f199..99f3be2cc 160000 --- a/lib/external/pattern_language +++ b/lib/external/pattern_language @@ -1 +1 @@ -Subproject commit 0ad67f199db0ca65c9b25935553409480d1cdfd8 +Subproject commit 99f3be2cc26632fcfb68f53d2fcd3baf0b8c6387 diff --git a/lib/libimhex/include/hex.hpp b/lib/libimhex/include/hex.hpp index 8e3294bc6..0906bf357 100644 --- a/lib/libimhex/include/hex.hpp +++ b/lib/libimhex/include/hex.hpp @@ -27,6 +27,22 @@ namespace hex { struct Region { u64 address; size_t size; + + [[nodiscard]] constexpr bool isWithin(const Region &other) const { + return (this->address >= other.address) && ((this->address + this->size) <= (other.address + other.size)); + } + + [[nodiscard]] constexpr bool overlaps(const Region &other) const { + return ((this->address + this->size) >= other.address) && (this->address < (other.address + other.size)); + } + + [[nodiscard]] constexpr u64 getStartAddress() const { + return this->address; + } + + [[nodiscard]] constexpr u64 getEndAddress() const { + return this->address + this->size - 1; + } }; } diff --git a/lib/libimhex/include/hex/api/content_registry.hpp b/lib/libimhex/include/hex/api/content_registry.hpp index 16ea207d2..0425c3cbc 100644 --- a/lib/libimhex/include/hex/api/content_registry.hpp +++ b/lib/libimhex/include/hex/api/content_registry.hpp @@ -10,12 +10,16 @@ #include #include +#include #include #include #include #include +using ImGuiDataType = int; +using ImGuiInputTextFlags = int; + namespace pl { class Evaluator; } @@ -378,6 +382,45 @@ namespace hex { std::vector &getEntries(); } + + namespace HexEditor { + + class DataVisualizer { + public: + DataVisualizer(u16 bytesPerCell, u16 maxCharsPerCell) + : m_bytesPerCell(bytesPerCell), m_maxCharsPerCell(maxCharsPerCell) {} + + virtual ~DataVisualizer() = default; + + virtual void draw(u64 address, const u8 *data, size_t size, bool upperCase) = 0; + virtual bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) = 0; + + [[nodiscard]] u16 getBytesPerCell() const { return this->m_bytesPerCell; } + [[nodiscard]] u16 getMaxCharsPerCell() const { return this->m_maxCharsPerCell; } + + protected: + const static int TextInputFlags; + + bool drawDefaultEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const; + private: + u16 m_bytesPerCell; + u16 m_maxCharsPerCell; + }; + + namespace impl { + + void addDataVisualizer(const std::string &unlocalizedName, DataVisualizer *visualizer); + + std::map &getVisualizers(); + + } + + template T, typename... Args> + void addDataVisualizer(const std::string &unlocalizedName, Args &&...args) { + return impl::addDataVisualizer(unlocalizedName, new T(std::forward(args)...)); + } + + } }; } diff --git a/lib/libimhex/include/hex/api/event.hpp b/lib/libimhex/include/hex/api/event.hpp index 261716dda..c451025e8 100644 --- a/lib/libimhex/include/hex/api/event.hpp +++ b/lib/libimhex/include/hex/api/event.hpp @@ -129,6 +129,6 @@ namespace hex { EVENT_DEF(RequestOpenPopup, std::string); EVENT_DEF(RequestCreateProvider, std::string, hex::prv::Provider **); - EVENT_DEF(QuerySelection, Region &); + EVENT_DEF(QuerySelection, std::optional &); } \ No newline at end of file diff --git a/lib/libimhex/include/hex/api/imhex_api.hpp b/lib/libimhex/include/hex/api/imhex_api.hpp index 66bc6f878..d50c97fb5 100644 --- a/lib/libimhex/include/hex/api/imhex_api.hpp +++ b/lib/libimhex/include/hex/api/imhex_api.hpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -31,37 +33,69 @@ namespace hex { namespace HexEditor { + using TooltipFunction = std::function; + class Highlighting { public: Highlighting() = default; - Highlighting(Region region, color_t color, std::string tooltip = ""); + Highlighting(Region region, color_t color); [[nodiscard]] const Region &getRegion() const { return this->m_region; } [[nodiscard]] const color_t &getColor() const { return this->m_color; } - [[nodiscard]] const std::string &getTooltip() const { return this->m_tooltip; } private: Region m_region = {}; color_t m_color = 0x00; - std::string m_tooltip; + }; + + class Tooltip { + public: + Tooltip() = default; + Tooltip(Region region, std::string value, color_t color); + + [[nodiscard]] const Region &getRegion() const { return this->m_region; } + [[nodiscard]] const color_t &getColor() const { return this->m_color; } + [[nodiscard]] const std::string &getValue() const { return this->m_value; } + + private: + Region m_region = {}; + std::string m_value; + color_t m_color = 0x00; }; namespace impl { - using HighlightingFunction = std::function(u64)>; + using HighlightingFunction = std::function(u64, const u8*, size_t)>; - std::map &getHighlights(); - std::map &getHighlightingFunctions(); + std::map &getBackgroundHighlights(); + std::map &getBackgroundHighlightingFunctions(); + std::map &getForegroundHighlights(); + std::map &getForegroundHighlightingFunctions(); + std::map &getTooltips(); + std::map &getTooltipFunctions(); } - u32 addHighlight(const Region ®ion, color_t color, const std::string &tooltip = ""); - void removeHighlight(u32 id); + u32 addBackgroundHighlight(const Region ®ion, color_t color); + void removeBackgroundHighlight(u32 id); - u32 addHighlightingProvider(const impl::HighlightingFunction &function); - void removeHighlightingProvider(u32 id); + u32 addForegroundHighlight(const Region ®ion, color_t color); + void removeForegroundHighlight(u32 id); - Region getSelection(); + u32 addTooltip(Region region, std::string value, color_t color); + void removeTooltip(u32 id); + + u32 addTooltipProvider(TooltipFunction function); + void removeTooltipProvider(u32 id); + + u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function); + void removeBackgroundHighlightingProvider(u32 id); + + u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function); + void removeForegroundHighlightingProvider(u32 id); + + bool isSelectionValid(); + std::optional getSelection(); void setSelection(const Region ®ion); void setSelection(u64 address, size_t size); @@ -76,8 +110,6 @@ namespace hex { std::string comment; u32 color; bool locked; - - u32 highlightId; }; void add(u64 address, size_t size, const std::string &name, const std::string &comment, color_t color = 0x00000000); diff --git a/lib/libimhex/include/hex/helpers/utils.hpp b/lib/libimhex/include/hex/helpers/utils.hpp index 2b129567f..6e9a17bc3 100644 --- a/lib/libimhex/include/hex/helpers/utils.hpp +++ b/lib/libimhex/include/hex/helpers/utils.hpp @@ -188,6 +188,24 @@ namespace hex { return T(1) << bit_width(T(x - 1)); } + template + auto powi(T base, U exp) { + using ResultType = decltype(T{} * U{}); + + if (exp < 0) + return ResultType(0); + + ResultType result = 1; + + while (exp != 0) { + if ((exp & 0b1) == 0b1) + result *= base; + exp >>= 1; + base *= base; + } + return result; + } + template void moveToVector(std::vector &buffer, T &&first, Args &&...rest) { buffer.push_back(std::move(first)); @@ -287,6 +305,13 @@ namespace hex { return *value; } + template + T alignTo(T value, T alignment) { + T remainder = value % alignment; + + return remainder != 0 ? value + (alignment - remainder) : value; + } + bool isProcessElevated(); std::optional getEnvironmentVariable(const std::string &env); diff --git a/lib/libimhex/include/hex/providers/buffered_reader.hpp b/lib/libimhex/include/hex/providers/buffered_reader.hpp new file mode 100644 index 000000000..41e543473 --- /dev/null +++ b/lib/libimhex/include/hex/providers/buffered_reader.hpp @@ -0,0 +1,223 @@ +#pragma once + +#include +#include + +#include + +namespace hex::prv { + + class BufferedReader { + public: + explicit BufferedReader(Provider *provider, size_t bufferSize = 0xFF'FFFF) : m_provider(provider), m_maxBufferSize(bufferSize), m_buffer(bufferSize) { } + + void seek(u64 address) { + this->m_baseAddress = address; + } + + [[nodiscard]] std::vector read(u64 address, size_t size) { + if (size > this->m_buffer.size()) { + std::vector result; + result.resize(size); + + this->m_provider->read(address, result.data(), result.size()); + + return result; + } + + this->updateBuffer(address, size); + + auto result = &this->m_buffer[address - this->m_baseAddress]; + + return { result, result + std::min(size, this->m_buffer.size()) }; + } + + [[nodiscard]] std::vector readReverse(u64 address, size_t size) { + if (size > this->m_buffer.size()) { + std::vector result; + result.resize(size); + + this->m_provider->read(address, result.data(), result.size()); + + return result; + } + + this->updateBuffer(address - std::min(address, this->m_buffer.size()), size); + + auto result = &this->m_buffer[address - this->m_baseAddress]; + + return { result, result + std::min(size, this->m_buffer.size()) }; + } + + class Iterator { + public: + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = u8; + using pointer = const value_type*; + using reference = const value_type&; + + Iterator(BufferedReader *reader, u64 address) : m_reader(reader), m_address(address) {} + + Iterator& operator++() { + this->m_address++; + + return *this; + } + + Iterator operator++(int) { + auto copy = *this; + this->m_address++; + + return copy; + } + + Iterator& operator+=(i64 offset) { + this->m_address += offset; + + return *this; + } + + Iterator& operator-=(i64 offset) { + this->m_address -= offset; + + return *this; + } + + value_type operator*() const { + return (*this)[0]; + } + + [[nodiscard]] u64 getAddress() const { + return this->m_address; + } + + difference_type operator-(const Iterator &other) const { + return this->m_address - other.m_address; + } + + Iterator operator+(i64 offset) const { + return { this->m_reader, this->m_address + offset }; + } + + value_type operator[](i64 offset) const { + auto result = this->m_reader->read(this->m_address + offset, 1); + if (result.empty()) + return 0x00; + + return result[0]; + } + + friend bool operator== (const Iterator& left, const Iterator& right) { return left.m_address == right.m_address; }; + friend bool operator!= (const Iterator& left, const Iterator& right) { return left.m_address != right.m_address; }; + private: + BufferedReader *m_reader; + u64 m_address; + }; + + class ReverseIterator { + public: + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = u8; + using pointer = const value_type*; + using reference = const value_type&; + + ReverseIterator(BufferedReader *reader, u64 address) : m_reader(reader), m_address(address) {} + + ReverseIterator& operator++() { + this->m_address--; + + return *this; + } + + ReverseIterator operator++(int) { + auto copy = *this; + this->m_address--; + + return copy; + } + + ReverseIterator& operator+=(i64 offset) { + this->m_address -= offset; + + return *this; + } + + ReverseIterator& operator-=(i64 offset) { + this->m_address += offset; + + return *this; + } + + value_type operator*() const { + return (*this)[0]; + } + + [[nodiscard]] u64 getAddress() const { + return this->m_address; + } + + difference_type operator-(const ReverseIterator &other) const { + return other.m_address - this->m_address; + } + + ReverseIterator operator+(i64 offset) const { + return { this->m_reader, this->m_address - offset }; + } + + value_type operator[](i64 offset) const { + auto result = this->m_reader->readReverse(this->m_address + offset, 1); + if (result.empty()) + return 0x00; + + return result[0]; + } + + friend bool operator== (const ReverseIterator& left, const ReverseIterator& right) { return left.m_address == right.m_address; }; + friend bool operator!= (const ReverseIterator& left, const ReverseIterator& right) { return left.m_address != right.m_address; }; + + private: + BufferedReader *m_reader; + u64 m_address; + }; + + Iterator begin() { + return { this, this->m_baseAddress }; + } + + Iterator end() { + return { this, this->m_baseAddress + this->m_provider->getActualSize() }; + } + + ReverseIterator rbegin() { + return { this, this->m_baseAddress }; + } + + ReverseIterator rend() { + return { this, std::numeric_limits::max() }; + } + + private: + void updateBuffer(u64 address, size_t size) { + if (!this->m_bufferValid || address < this->m_baseAddress || address + size > (this->m_baseAddress + this->m_buffer.size())) { + const auto remainingBytes = this->m_provider->getActualSize() - address; + if (remainingBytes < this->m_maxBufferSize) + this->m_buffer.resize(remainingBytes); + + this->m_provider->read(address, this->m_buffer.data(), this->m_buffer.size()); + this->m_baseAddress = address; + this->m_bufferValid = true; + } + } + + private: + Provider *m_provider; + + size_t m_maxBufferSize; + bool m_bufferValid = false; + u64 m_baseAddress = 0x00; + std::vector m_buffer; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h index 2123f1082..1a096bf40 100644 --- a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h +++ b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h @@ -63,7 +63,6 @@ namespace ImGui { void UnderlinedText(const char *label, ImColor color = ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg = ImVec2(0, 0)); - void Disabled(const std::function &widgets, bool disabled); void TextSpinner(const char *label); void Header(const char *label, bool firstEntry = false); @@ -131,4 +130,8 @@ namespace ImGui { bool InputText(const char* label, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None); bool InputTextMultiline(const char* label, std::string &buffer, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = ImGuiInputTextFlags_None); + + bool InputScalarCallback(const char* label, ImGuiDataType data_type, void* p_data, const char* format, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data); + + void HideTooltip(); } \ No newline at end of file diff --git a/lib/libimhex/source/api/content_registry.cpp b/lib/libimhex/source/api/content_registry.cpp index 44a64612a..ac84153e9 100644 --- a/lib/libimhex/source/api/content_registry.cpp +++ b/lib/libimhex/source/api/content_registry.cpp @@ -536,4 +536,50 @@ namespace hex { } + namespace ContentRegistry::HexEditor { + + const int DataVisualizer::TextInputFlags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_EnterReturnsTrue; + + bool DataVisualizer::drawDefaultEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const { + struct UserData { + u8 *data; + i32 maxChars; + + bool editingDone; + }; + + UserData userData = { + .data = data, + .maxChars = this->getMaxCharsPerCell(), + + .editingDone = false + }; + + ImGui::PushID(reinterpret_cast(address)); + ImGui::InputScalarCallback("##editing_input", dataType, data, format, flags | TextInputFlags | ImGuiInputTextFlags_CallbackEdit, [](ImGuiInputTextCallbackData *data) -> int { + auto &userData = *reinterpret_cast(data->UserData); + + if (data->BufTextLen >= userData.maxChars) + userData.editingDone = true; + + return 0; + }, &userData); + ImGui::PopID(); + + return userData.editingDone || ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Enter); + } + + void impl::addDataVisualizer(const std::string &unlocalizedName, DataVisualizer *visualizer) { + getVisualizers().insert({ unlocalizedName, visualizer }); + + } + + std::map &impl::getVisualizers() { + static std::map visualizers; + + return visualizers; + } + + } + } diff --git a/lib/libimhex/source/api/imhex_api.cpp b/lib/libimhex/source/api/imhex_api.cpp index 7b2df29fb..50770c7b2 100644 --- a/lib/libimhex/source/api/imhex_api.cpp +++ b/lib/libimhex/source/api/imhex_api.cpp @@ -29,32 +29,55 @@ namespace hex { namespace ImHexApi::HexEditor { - Highlighting::Highlighting(Region region, color_t color, std::string tooltip) - : m_region(region), m_color(color), m_tooltip(std::move(tooltip)) { + Highlighting::Highlighting(Region region, color_t color) + : m_region(region), m_color(color) { + } + + Tooltip::Tooltip(Region region, std::string value, color_t color) : m_region(region), m_value(std::move(value)), m_color(color) { + } namespace impl { - static std::map s_highlights; - std::map &getHighlights() { - return s_highlights; + static std::map s_backgroundHighlights; + std::map &getBackgroundHighlights() { + return s_backgroundHighlights; } - static std::map s_highlightingFunctions; - std::map &getHighlightingFunctions() { - return s_highlightingFunctions; + static std::map s_backgroundHighlightingFunctions; + std::map &getBackgroundHighlightingFunctions() { + return s_backgroundHighlightingFunctions; + } + + static std::map s_foregroundHighlights; + std::map &getForegroundHighlights() { + return s_foregroundHighlights; + } + + static std::map s_foregroundHighlightingFunctions; + std::map &getForegroundHighlightingFunctions() { + return s_foregroundHighlightingFunctions; + } + + static std::map s_tooltips; + std::map &getTooltips() { + return s_tooltips; + } + + static std::map s_tooltipFunctions; + std::map &getTooltipFunctions() { + return s_tooltipFunctions; } } - u32 addHighlight(const Region ®ion, color_t color, const std::string &tooltip) { - auto &highlights = impl::getHighlights(); - static u64 id = 0; + u32 addBackgroundHighlight(const Region ®ion, color_t color) { + static u32 id = 0; id++; - highlights.insert({ - id, Highlighting {region, color, tooltip} + impl::getBackgroundHighlights().insert({ + id, Highlighting {region, color} }); EventManager::post(); @@ -62,37 +85,101 @@ namespace hex { return id; } - void removeHighlight(u32 id) { - impl::getHighlights().erase(id); + void removeBackgroundHighlight(u32 id) { + impl::getBackgroundHighlights().erase(id); EventManager::post(); } - u32 addHighlightingProvider(const impl::HighlightingFunction &function) { - auto &highlightFuncs = impl::getHighlightingFunctions(); + u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function) { + static u32 id = 0; - auto id = highlightFuncs.size(); + id++; - highlightFuncs.insert({ id, function }); + impl::getBackgroundHighlightingFunctions().insert({ id, function }); EventManager::post(); return id; } - void removeHighlightingProvider(u32 id) { - impl::getHighlightingFunctions().erase(id); + void removeBackgroundHighlightingProvider(u32 id) { + impl::getBackgroundHighlightingFunctions().erase(id); EventManager::post(); } - Region getSelection() { - static Region selectedRegion; - EventManager::subscribe([](const Region ®ion) { - selectedRegion = region; + u32 addForegroundHighlight(const Region ®ion, color_t color) { + static u32 id = 0; + + id++; + + impl::getForegroundHighlights().insert({ + id, Highlighting {region, color} }); - return selectedRegion; + EventManager::post(); + + return id; + } + + void removeForegroundHighlight(u32 id) { + impl::getForegroundHighlights().erase(id); + + EventManager::post(); + } + + u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function) { + static u32 id = 0; + + id++; + + impl::getForegroundHighlightingFunctions().insert({ id, function }); + + EventManager::post(); + + return id; + } + + void removeForegroundHighlightingProvider(u32 id) { + impl::getForegroundHighlightingFunctions().erase(id); + + EventManager::post(); + } + + static u32 tooltipId = 0; + u32 addTooltip(Region region, std::string value, color_t color) { + tooltipId++; + impl::getTooltips().insert({ tooltipId, { region, std::move(value), color } }); + + return tooltipId; + } + + void removeTooltip(u32 id) { + impl::getTooltips().erase(id); + } + + static u32 tooltipFunctionId; + u32 addTooltipProvider(TooltipFunction function) { + tooltipFunctionId++; + impl::getTooltipFunctions().insert({ tooltipFunctionId, std::move(function) }); + + return tooltipFunctionId; + } + + void removeTooltipProvider(u32 id) { + impl::getTooltipFunctions().erase(id); + } + + bool isSelectionValid() { + return getSelection().has_value(); + } + + std::optional getSelection() { + std::optional selection; + EventManager::post(selection); + + return selection; } void setSelection(const Region ®ion) { @@ -210,7 +297,7 @@ namespace hex { } - static float s_globalScale; + static float s_globalScale = 1.0; void setGlobalScale(float scale) { s_globalScale = scale; } diff --git a/lib/libimhex/source/helpers/encoding_file.cpp b/lib/libimhex/source/helpers/encoding_file.cpp index be01984cc..410908e7c 100644 --- a/lib/libimhex/source/helpers/encoding_file.cpp +++ b/lib/libimhex/source/helpers/encoding_file.cpp @@ -40,9 +40,7 @@ namespace hex { if (delimiterPos == std::string::npos) continue; - if (delimiterPos >= from.length()) - continue; - if (delimiterPos >= to.length()) + if (delimiterPos >= line.length()) continue; from = line.substr(0, delimiterPos); diff --git a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp index 112ad269d..c5406c669 100644 --- a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp +++ b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp @@ -191,16 +191,6 @@ namespace ImGui { PopStyleColor(); } - void Disabled(const std::function &widgets, bool disabled) { - if (disabled) { - BeginDisabled(); - widgets(); - EndDisabled(); - } else { - widgets(); - } - } - void TextSpinner(const char *label) { ImGui::Text("[%c] %s", "|/-\\"[ImU32(ImGui::GetTime() * 20) % 4], label); } @@ -563,4 +553,41 @@ namespace ImGui { return ImGui::InputTextMultiline(label, buffer.data(), buffer.size() + 1, size, ImGuiInputTextFlags_CallbackResize | flags, ImGui::UpdateStringSizeCallback, &buffer); } + bool InputScalarCallback(const char* label, ImGuiDataType data_type, void* p_data, const char* format, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + + if (format == NULL) + format = DataTypeGetInfo(data_type)->PrintFmt; + + char buf[64]; + DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format); + + bool value_changed = false; + if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) + flags |= ImGuiInputTextFlags_CharsDecimal; + flags |= ImGuiInputTextFlags_AutoSelectAll; + flags |= ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. + + if (InputText(label, buf, IM_ARRAYSIZE(buf), flags, callback, user_data)) + value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format); + + if (value_changed) + MarkItemEdited(g.LastItemData.ID); + + return value_changed; + } + + void HideTooltip() { + char window_name[16]; + ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", GImGui->TooltipOverrideCount); + if (ImGuiWindow* window = FindWindowByName(window_name); window != nullptr) { + if (window->Active) + window->Hidden = true; + } + } + } \ No newline at end of file diff --git a/main/include/window.hpp b/main/include/window.hpp index 666eaad0d..8eccf3865 100644 --- a/main/include/window.hpp +++ b/main/include/window.hpp @@ -54,6 +54,8 @@ namespace hex { std::vector m_pressedKeys; std::fs::path m_imguiSettingsPath; + + bool m_mouseButtonDown = false; }; } \ No newline at end of file diff --git a/main/source/init/splash_window.cpp b/main/source/init/splash_window.cpp index 3506f74a4..a113d6190 100644 --- a/main/source/init/splash_window.cpp +++ b/main/source/init/splash_window.cpp @@ -180,7 +180,7 @@ namespace hex::init { meanScale /= 2; #endif - if (meanScale <= 0) { + if (meanScale <= 0.0) { meanScale = 1.0; } diff --git a/main/source/init/tasks.cpp b/main/source/init/tasks.cpp index a6d892b7c..85a495523 100644 --- a/main/source/init/tasks.cpp +++ b/main/source/init/tasks.cpp @@ -172,8 +172,11 @@ namespace hex::init { bool deleteSharedData() { ImHexApi::System::getInitArguments().clear(); ImHexApi::Tasks::getDeferredCalls().clear(); - ImHexApi::HexEditor::impl::getHighlights().clear(); - ImHexApi::HexEditor::impl::getHighlightingFunctions().clear(); + ImHexApi::HexEditor::impl::getBackgroundHighlights().clear(); + ImHexApi::HexEditor::impl::getForegroundHighlights().clear(); + ImHexApi::HexEditor::impl::getBackgroundHighlightingFunctions().clear(); + ImHexApi::HexEditor::impl::getForegroundHighlightingFunctions().clear(); + ImHexApi::HexEditor::impl::getTooltips().clear(); while (ImHexApi::Provider::isValid()) ImHexApi::Provider::remove(ImHexApi::Provider::get()); diff --git a/main/source/window/window.cpp b/main/source/window/window.cpp index 9af40056d..079427621 100644 --- a/main/source/window/window.cpp +++ b/main/source/window/window.cpp @@ -177,9 +177,10 @@ namespace hex { glfwWaitEvents(); } else { - double timeout = (1.0 / 5.0) - (glfwGetTime() - this->m_lastFrameTime); - timeout = timeout > 0 ? timeout : 0; - glfwWaitEventsTimeout(ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId) || Task::getRunningTaskCount() > 0 ? 0 : timeout); + const bool frameRateThrottled = !(ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId) || Task::getRunningTaskCount() > 0 || this->m_mouseButtonDown); + const double timeout = std::max(0.0, (1.0 / 5.0) - (glfwGetTime() - this->m_lastFrameTime)); + + glfwWaitEventsTimeout(frameRateThrottled ? timeout : 0); } @@ -568,23 +569,32 @@ namespace hex { win->frameEnd(); }); + glfwSetMouseButtonCallback(this->m_window, [](GLFWwindow *window, int button, int action, int mods) { + hex::unused(button, mods); + + auto win = static_cast(glfwGetWindowUserPointer(window)); + + if (action == GLFW_PRESS) + win->m_mouseButtonDown = true; + else if (action == GLFW_RELEASE) + win->m_mouseButtonDown = false; + }); + glfwSetKeyCallback(this->m_window, [](GLFWwindow *window, int key, int scancode, int action, int mods) { auto keyName = glfwGetKeyName(key, scancode); if (keyName != nullptr) key = std::toupper(keyName[0]); auto win = static_cast(glfwGetWindowUserPointer(window)); + auto &io = ImGui::GetIO(); - if (action == GLFW_PRESS) { - auto &io = ImGui::GetIO(); - + if (action == GLFW_PRESS || action == GLFW_REPEAT) { win->m_pressedKeys.push_back(key); io.KeysDown[key] = true; io.KeyCtrl = (mods & GLFW_MOD_CONTROL) != 0; io.KeyShift = (mods & GLFW_MOD_SHIFT) != 0; io.KeyAlt = (mods & GLFW_MOD_ALT) != 0; } else if (action == GLFW_RELEASE) { - auto &io = ImGui::GetIO(); io.KeysDown[key] = false; io.KeyCtrl = (mods & GLFW_MOD_CONTROL) != 0; io.KeyShift = (mods & GLFW_MOD_SHIFT) != 0; diff --git a/plugins/builtin/CMakeLists.txt b/plugins/builtin/CMakeLists.txt index f30a6e9d4..4572c01a6 100644 --- a/plugins/builtin/CMakeLists.txt +++ b/plugins/builtin/CMakeLists.txt @@ -20,6 +20,8 @@ add_library(${PROJECT_NAME} SHARED source/content/layouts.cpp source/content/main_menu_items.cpp source/content/welcome_screen.cpp + source/content/data_visualizers.cpp + source/content/events.cpp source/content/providers/file_provider.cpp source/content/providers/gdb_provider.cpp diff --git a/plugins/builtin/include/content/views/view_hex_editor.hpp b/plugins/builtin/include/content/views/view_hex_editor.hpp index 3d8ef7cb2..c225b1d9e 100644 --- a/plugins/builtin/include/content/views/view_hex_editor.hpp +++ b/plugins/builtin/include/content/views/view_hex_editor.hpp @@ -1,96 +1,131 @@ #pragma once +#include + #include +#include #include -#include - -#include -#include -#include -#include - -namespace hex::prv { - class Provider; -} +#include +#include namespace hex::plugin::builtin { - using SearchFunction = std::vector> (*)(prv::Provider *&provider, std::string string); - - struct HighlightBlock { - struct Highlight { - color_t color; - std::vector tooltips; - }; - - constexpr static size_t Size = 0x2000; - - u64 base = 0x00; - std::array highlight; - }; - class ViewHexEditor : public View { public: ViewHexEditor(); - ~ViewHexEditor() override; void drawContent() override; - void drawAlwaysVisible() override; private: - MemoryEditor m_memoryEditor; - - std::vector m_searchStringBuffer; - std::vector m_searchHexBuffer; - SearchFunction m_searchFunction = nullptr; - std::vector> *m_lastSearchBuffer = nullptr; - bool m_searchRequested = false; - - i64 m_lastSearchIndex = 0; - std::vector> m_lastStringSearch; - std::vector> m_lastHexSearch; - - std::string m_gotoAddressInput; - bool m_gotoRequested = false; - bool m_evaluateGoto = false; - - u64 m_baseAddress = 0; - u64 m_resizeSize = 0; - - std::vector m_dataToSave; - std::set m_highlightedPatterns; - - std::string m_loaderScriptScriptPath; - std::string m_loaderScriptFilePath; - - hex::EncodingFile m_currEncodingFile; - u8 m_highlightAlpha = 0x80; - - std::list m_highlights; - - bool m_processingImportExport = false; - bool m_advancedDecodingEnabled = false; - - void drawSearchPopup(); - void drawSearchInput(std::vector *currBuffer, ImGuiInputTextFlags flags); - void performSearch(const char *buffer); - void performSearchNext(); - void performSearchPrevious(); - static int inputCallback(ImGuiInputTextCallbackData *data); - - void drawGotoPopup(); - void drawEditPopup(); + constexpr static auto InvalidSelection = std::numeric_limits::max(); void openFile(const std::fs::path &path); - - void copyBytes() const; - void pasteBytes() const; - void copyString() const; - - void registerEvents(); void registerShortcuts(); + void registerEvents(); void registerMenuItems(); + + void drawCell(u64 address, u8 *data, size_t size, bool hovered); + void drawPopup(); + void drawSelectionFrame(u32 x, u32 y, u64 byteAddress, u16 bytesPerCell, const ImVec2 &cellPos, const ImVec2 &cellSize); + + public: + void setSelection(const Region ®ion) { this->setSelection(region.getStartAddress(), region.getEndAddress()); } + void setSelection(i128 start, i128 end) { + if (!ImHexApi::Provider::isValid()) return; + + const size_t maxAddress = ImHexApi::Provider::get()->getActualSize() - 1; + + this->m_selectionChanged = this->m_selectionStart != start || this->m_selectionEnd != end; + + this->m_selectionStart = std::clamp(start, 0, maxAddress); + this->m_selectionEnd = std::clamp(end, 0, maxAddress); + + if (this->m_selectionChanged) { + EventManager::post(this->getSelection()); + } + } + + [[nodiscard]] Region getSelection() const { + const auto start = std::min(this->m_selectionStart, this->m_selectionEnd); + const auto end = std::max(this->m_selectionStart, this->m_selectionEnd); + const size_t size = end - start + 1; + + return { start, size }; + } + + [[nodiscard]] bool isSelectionValid() const { + return this->m_selectionStart != InvalidSelection && this->m_selectionEnd != InvalidSelection; + } + + void jumpToSelection() { + this->m_shouldJumpToSelection = true; + } + + void scrollToSelection() { + this->m_shouldScrollToSelection = true; + } + + public: + class Popup { + public: + virtual ~Popup() = default; + virtual void draw(ViewHexEditor *editor) = 0; + }; + + [[nodiscard]] bool isAnyPopupOpen() const { + return this->m_currPopup != nullptr; + } + + template T> + [[nodiscard]] bool isPopupOpen() const { + return dynamic_cast(this->m_currPopup.get()) != nullptr; + } + + template T, typename ... Args> + void openPopup(Args && ...args) { + this->m_currPopup = std::make_unique(std::forward(args)...); + this->m_shouldOpenPopup = true; + } + + void closePopup() { + this->m_currPopup.reset(); + } + + private: + void drawEditor(const ImVec2 &size); + void drawFooter(const ImVec2 &size); + + void handleSelection(u64 address, u32 bytesPerCell, const u8 *data, bool cellHovered); + + private: + u16 m_bytesPerRow = 16; + + ContentRegistry::HexEditor::DataVisualizer *m_currDataVisualizer; + + bool m_shouldJumpToSelection = false; + bool m_shouldScrollToSelection = false; + + bool m_selectionChanged = false; + u64 m_selectionStart = InvalidSelection; + u64 m_selectionEnd = InvalidSelection; + + u16 m_visibleRowCount = 0; + + std::optional m_editingAddress; + bool m_shouldModifyValue = false; + bool m_enteredEditingMode = false; + std::vector m_editingBytes; + + color_t m_selectionColor = 0x00; + bool m_upperCaseHex = true; + bool m_grayOutZero = true; + bool m_showAscii = true; + + bool m_shouldOpenPopup = false; + std::unique_ptr m_currPopup; + + std::optional m_currCustomEncoding; }; } \ No newline at end of file diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index f37cb29ba..dba427e25 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -12,6 +12,8 @@ #include +namespace pl { class Pattern; } + namespace hex::plugin::builtin { class ViewPatternEditor : public View { @@ -91,6 +93,8 @@ namespace hex::plugin::builtin { void drawEnvVars(ImVec2 size); void drawVariableSettings(ImVec2 size); + void drawPatternTooltip(pl::Pattern *pattern); + void loadPatternFile(const std::fs::path &path); void clearPatterns(); diff --git a/plugins/builtin/include/content/views/view_yara.hpp b/plugins/builtin/include/content/views/view_yara.hpp index ba4c2819f..4fa039d8a 100644 --- a/plugins/builtin/include/content/views/view_yara.hpp +++ b/plugins/builtin/include/content/views/view_yara.hpp @@ -21,7 +21,9 @@ namespace hex::plugin::builtin { u64 address; size_t size; bool wholeDataMatch; + u32 highlightId; + u32 tooltipId; }; std::vector> m_rules; diff --git a/plugins/builtin/include/math_evaluator.hpp b/plugins/builtin/include/math_evaluator.hpp index 8bfca4ea1..82674522b 100644 --- a/plugins/builtin/include/math_evaluator.hpp +++ b/plugins/builtin/include/math_evaluator.hpp @@ -11,83 +11,108 @@ namespace hex { - enum class TokenType - { - Number, - Variable, - Function, - Operator, - Bracket - }; - - enum class Operator : u16 - { - Invalid = 0x000, - Assign = 0x010, - Or = 0x020, - Xor = 0x030, - And = 0x040, - BitwiseOr = 0x050, - BitwiseXor = 0x060, - BitwiseAnd = 0x070, - Equals = 0x080, - NotEquals = 0x081, - GreaterThan = 0x090, - LessThan = 0x091, - GreaterThanOrEquals = 0x092, - LessThanOrEquals = 0x093, - ShiftLeft = 0x0A0, - ShiftRight = 0x0A1, - Addition = 0x0B0, - Subtraction = 0x0B1, - Multiplication = 0x0C0, - Division = 0x0C1, - Modulus = 0x0C2, - Exponentiation = 0x1D0, - Combine = 0x0E0, - BitwiseNot = 0x0F0, - Not = 0x0F1 - }; - - enum class BracketType : std::uint8_t - { - Left, - Right - }; - - struct Token { - TokenType type; - - union { - long double number; - Operator op; - BracketType bracketType; - }; - - std::string name; - std::vector arguments; - }; - + template class MathEvaluator { public: MathEvaluator() = default; - std::optional evaluate(const std::string &input); + std::optional evaluate(const std::string &input); void registerStandardVariables(); void registerStandardFunctions(); - void setVariable(const std::string &name, long double value); - void setFunction(const std::string &name, const std::function(std::vector)> &function, size_t minNumArgs, size_t maxNumArgs); + void setVariable(const std::string &name, T value); + void setFunction(const std::string &name, const std::function(std::vector)> &function, size_t minNumArgs, size_t maxNumArgs); - std::unordered_map &getVariables() { return this->m_variables; } + std::unordered_map &getVariables() { return this->m_variables; } + + [[nodiscard]] bool hasError() const { + return this->m_lastError.has_value(); + } + + [[nodiscard]] std::optional getLastError() const { + return this->m_lastError; + } private: - std::queue parseInput(std::string input); - std::optional evaluate(std::queue postfixTokens); + void setError(const std::string &error) { + this->m_lastError = error; + } - std::unordered_map m_variables; - std::unordered_map(std::vector)>> m_functions; + private: + enum class TokenType + { + Number, + Variable, + Function, + Operator, + Bracket + }; + + enum class Operator : u16 + { + Invalid = 0x000, + Assign = 0x010, + Or = 0x020, + Xor = 0x030, + And = 0x040, + BitwiseOr = 0x050, + BitwiseXor = 0x060, + BitwiseAnd = 0x070, + Equals = 0x080, + NotEquals = 0x081, + GreaterThan = 0x090, + LessThan = 0x091, + GreaterThanOrEquals = 0x092, + LessThanOrEquals = 0x093, + ShiftLeft = 0x0A0, + ShiftRight = 0x0A1, + Addition = 0x0B0, + Subtraction = 0x0B1, + Multiplication = 0x0C0, + Division = 0x0C1, + Modulus = 0x0C2, + Exponentiation = 0x1D0, + Combine = 0x0E0, + BitwiseNot = 0x0F0, + Not = 0x0F1 + }; + + enum class BracketType : std::uint8_t + { + Left, + Right + }; + + struct Token { + TokenType type; + + union { + T number; + Operator op; + BracketType bracketType; + }; + + std::string name; + std::vector arguments; + }; + + static i16 comparePrecedence(const Operator &a, const Operator &b); + static bool isLeftAssociative(const Operator op); + static std::pair toOperator(const std::string &input); + + private: + std::optional> parseInput(std::string input); + std::optional> toPostfix(std::queue inputQueue); + std::optional evaluate(std::queue postfixTokens); + + std::unordered_map m_variables; + std::unordered_map(std::vector)>> m_functions; + + std::optional m_lastError; }; + extern template class MathEvaluator; + extern template class MathEvaluator; + } \ No newline at end of file diff --git a/plugins/builtin/source/content/command_palette_commands.cpp b/plugins/builtin/source/content/command_palette_commands.cpp index 83997c0d2..bd6cd7c94 100644 --- a/plugins/builtin/source/content/command_palette_commands.cpp +++ b/plugins/builtin/source/content/command_palette_commands.cpp @@ -16,19 +16,17 @@ namespace hex::plugin::builtin { "#", "hex.builtin.command.calc.desc", [](auto input) { - hex::MathEvaluator evaluator; + hex::MathEvaluator evaluator; evaluator.registerStandardVariables(); evaluator.registerStandardFunctions(); std::optional result; - try { - result = evaluator.evaluate(input); - } catch (std::exception &e) { } - - + result = evaluator.evaluate(input); if (result.has_value()) return hex::format("#{0} = {1}", input.data(), result.value()); + else if (evaluator.hasError()) + return hex::format("Error: {}", *evaluator.getLastError()); else return std::string("???"); }); diff --git a/plugins/builtin/source/content/data_inspector.cpp b/plugins/builtin/source/content/data_inspector.cpp index c5cea8423..e7ffd0d2e 100644 --- a/plugins/builtin/source/content/data_inspector.cpp +++ b/plugins/builtin/source/content/data_inspector.cpp @@ -323,19 +323,25 @@ namespace hex::plugin::builtin { [](auto buffer, auto endian, auto style) { hex::unused(endian, style); - Region currSelection = { 0, 0 }; - EventManager::post(currSelection); + auto currSelection = ImHexApi::HexEditor::getSelection(); constexpr static auto MaxStringLength = 32; - std::vector stringBuffer(std::min(MaxStringLength, currSelection.size), 0x00); - ImHexApi::Provider::get()->read(currSelection.address, stringBuffer.data(), stringBuffer.size()); + std::string value, copyValue; - auto value = hex::encodeByteString(stringBuffer); - auto copyValue = hex::encodeByteString(buffer); + if (currSelection.has_value()) { + std::vector stringBuffer(std::min(MaxStringLength, currSelection->size), 0x00); + ImHexApi::Provider::get()->read(currSelection->address, stringBuffer.data(), stringBuffer.size()); - if (currSelection.size > MaxStringLength) - value += "..."; + value = hex::encodeByteString(stringBuffer); + copyValue = hex::encodeByteString(buffer); + + if (currSelection->size > MaxStringLength) + value += "..."; + } else { + value = ""; + copyValue = ""; + } return [value, copyValue] { ImGui::TextFormatted("\"{0}\"", value.c_str()); return copyValue; }; }, diff --git a/plugins/builtin/source/content/data_visualizers.cpp b/plugins/builtin/source/content/data_visualizers.cpp new file mode 100644 index 000000000..0826af324 --- /dev/null +++ b/plugins/builtin/source/content/data_visualizers.cpp @@ -0,0 +1,272 @@ +#include +#include + +#include +#include +#include + +namespace hex::plugin::builtin { + + template + constexpr ImGuiDataType getImGuiDataType() { + if constexpr (std::same_as) return ImGuiDataType_U8; + else if constexpr (std::same_as) return ImGuiDataType_U16; + else if constexpr (std::same_as) return ImGuiDataType_U32; + else if constexpr (std::same_as) return ImGuiDataType_U64; + else if constexpr (std::same_as) return ImGuiDataType_S8; + else if constexpr (std::same_as) return ImGuiDataType_S16; + else if constexpr (std::same_as) return ImGuiDataType_S32; + else if constexpr (std::same_as) return ImGuiDataType_S64; + else if constexpr (std::same_as) return ImGuiDataType_Float; + else if constexpr (std::same_as) return ImGuiDataType_Double; + else static_assert(hex::always_false::value, "Invalid data type!"); + } + + template + class DataVisualizerHexadecimal : public hex::ContentRegistry::HexEditor::DataVisualizer { + public: + DataVisualizerHexadecimal() : DataVisualizer(ByteCount, CharCount) { } + + void draw(u64 address, const u8 *data, size_t size, bool upperCase) override { + hex::unused(address); + + if (size == ByteCount) + ImGui::Text(getFormatString(upperCase), *reinterpret_cast(data)); + else + ImGui::TextFormatted("{: {}s}", CharCount); + } + + bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) override { + hex::unused(address, startedEditing); + + if (size == ByteCount) { + return drawDefaultEditingTextBox(address, getFormatString(upperCase), getImGuiDataType(), data, ImGuiInputTextFlags_CharsHexadecimal); + } + else + return false; + } + + private: + constexpr static inline auto ByteCount = sizeof(T); + constexpr static inline auto CharCount = ByteCount * 2; + + const static inline auto FormattingUpperCase = hex::format("%0{}X", CharCount); + const static inline auto FormattingLowerCase = hex::format("%0{}x", CharCount); + + const char *getFormatString(bool upperCase) { + if (upperCase) + return FormattingUpperCase.c_str(); + else + return FormattingLowerCase.c_str(); + } + }; + + class DataVisualizerHexii : public hex::ContentRegistry::HexEditor::DataVisualizer { + public: + DataVisualizerHexii() : DataVisualizer(ByteCount, CharCount) { } + + void draw(u64 address, const u8 *data, size_t size, bool upperCase) override { + hex::unused(address); + + if (size == ByteCount) { + const u8 c = data[0]; + switch (c) { + case 0x00: + ImGui::Text(" "); + break; + case 0xFF: + ImGui::TextDisabled("##"); + break; + case ' ' ... '~': + ImGui::Text(".%c", c); + break; + default: + ImGui::Text(getFormatString(upperCase), c); + break; + } + } + else + ImGui::TextFormatted("{: {}s}", CharCount); + } + + bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) override { + hex::unused(address, startedEditing); + + if (size == ByteCount) { + return drawDefaultEditingTextBox(address, getFormatString(upperCase), getImGuiDataType(), data, ImGuiInputTextFlags_CharsHexadecimal); + } + else + return false; + } + + private: + constexpr static inline auto ByteCount = 1; + constexpr static inline auto CharCount = ByteCount * 2; + + const static inline auto FormattingUpperCase = hex::format("%0{}X", CharCount); + const static inline auto FormattingLowerCase = hex::format("%0{}x", CharCount); + + const char *getFormatString(bool upperCase) { + if (upperCase) + return FormattingUpperCase.c_str(); + else + return FormattingLowerCase.c_str(); + } + }; + + template + class DataVisualizerDecimal : public hex::ContentRegistry::HexEditor::DataVisualizer { + public: + DataVisualizerDecimal() : DataVisualizer(ByteCount, CharCount) { } + + void draw(u64 address, const u8 *data, size_t size, bool upperCase) override { + hex::unused(address, upperCase); + + if (size == ByteCount) { + if (hex::is_signed::value) + ImGui::Text(getFormatString(), static_cast(*reinterpret_cast(data))); + else + ImGui::Text(getFormatString(), static_cast(*reinterpret_cast(data))); + } + else + ImGui::TextFormatted("{: {}s}", CharCount); + } + + bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) override { + hex::unused(address, upperCase, startedEditing); + + if (size == ByteCount) { + return ImGui::InputScalar( + "##hex_input", + getImGuiDataType(), + data, + nullptr, + nullptr, + nullptr, + DataVisualizer::TextInputFlags); + } + else + return false; + } + + private: + constexpr static inline auto ByteCount = sizeof(T); + constexpr static inline auto CharCount = std::numeric_limits::digits10 + 2; + + const static inline auto FormatString = hex::format("%{}{}", CharCount, hex::is_signed::value ? "lld" : "llu"); + + const char *getFormatString() { + return FormatString.c_str(); + } + }; + + template + class DataVisualizerFloatingPoint : public hex::ContentRegistry::HexEditor::DataVisualizer { + public: + DataVisualizerFloatingPoint() : DataVisualizer(ByteCount, CharCount) { } + + void draw(u64 address, const u8 *data, size_t size, bool upperCase) override { + hex::unused(address); + + if (size == ByteCount) + ImGui::Text(getFormatString(upperCase), *reinterpret_cast(data)); + else + ImGui::TextFormatted("{: {}s}", CharCount); + } + + bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) override { + hex::unused(address, upperCase, startedEditing); + + if (size == ByteCount) { + return ImGui::InputScalar( + "##hex_input", + getImGuiDataType(), + data, + nullptr, + nullptr, + nullptr, + DataVisualizer::TextInputFlags | ImGuiInputTextFlags_CharsScientific); + } + else + return false; + } + + private: + constexpr static inline auto ByteCount = sizeof(T); + constexpr static inline auto CharCount = 14; + + const static inline auto FormatStringUpperCase = hex::format("%{}E", CharCount); + const static inline auto FormatStringLowerCase = hex::format("%{}e", CharCount); + + const char *getFormatString(bool upperCase) { + if (upperCase) + return FormatStringUpperCase.c_str(); + else + return FormatStringLowerCase.c_str(); + } + }; + + class DataVisualizerRGBA8 : public hex::ContentRegistry::HexEditor::DataVisualizer { + public: + DataVisualizerRGBA8() : DataVisualizer(4, 2) { } + + void draw(u64 address, const u8 *data, size_t size, bool upperCase) override { + hex::unused(address, upperCase); + + if (size == 4) + ImGui::ColorButton("##color", ImColor(data[0], data[1], data[2], data[3]), ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); + else + ImGui::ColorButton("##color", ImColor(0, 0, 0, 0xFF), ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); + } + + bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) override { + hex::unused(address, data, size, upperCase); + + if (startedEditing) { + this->m_currColor = { float(data[0]) / 0xFF, float(data[1]) / 0xFF, float(data[2]) / 0xFF, float(data[3]) / 0xFF }; + ImGui::OpenPopup("##color_popup"); + } + + ImGui::ColorButton("##color", ImColor(this->m_currColor[0], this->m_currColor[1], this->m_currColor[2], this->m_currColor[3]), ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); + + if (ImGui::BeginPopup("##color_popup")) { + if (ImGui::ColorPicker4("##picker", this->m_currColor.data(), ImGuiColorEditFlags_AlphaBar)) { + for (u8 i = 0; i < 4; i++) + data[i] = this->m_currColor[i] * 0xFF; + } + ImGui::EndPopup(); + } else { + return true; + } + + return false; + } + + std::array m_currColor; + + }; + + void registerDataVisualizers() { + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.hexadecimal.8bit"); + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.hexadecimal.16bit"); + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.hexadecimal.32bit"); + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.hexadecimal.64bit"); + + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.decimal.unsigned.8bit"); + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.decimal.unsigned.16bit"); + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.decimal.unsigned.32bit"); + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.decimal.unsigned.64bit"); + + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.decimal.signed.8bit"); + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.decimal.signed.16bit"); + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.decimal.signed.32bit"); + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.decimal.signed.64bit"); + + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.floating_point.32bit"); + ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.floating_point.64bit"); + + ContentRegistry::HexEditor::addDataVisualizer("hex.builtin.visualizer.rgba8"); + ContentRegistry::HexEditor::addDataVisualizer("hex.builtin.visualizer.hexii"); + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/events.cpp b/plugins/builtin/source/content/events.cpp new file mode 100644 index 000000000..deab7f3a2 --- /dev/null +++ b/plugins/builtin/source/content/events.cpp @@ -0,0 +1,91 @@ +#include + +#include +#include +#include +#include + +#include + +#include "content/providers/file_provider.hpp" + +namespace hex::plugin::builtin { + + static void openFile(const std::fs::path &path) { + hex::prv::Provider *provider = nullptr; + EventManager::post("hex.builtin.provider.file", &provider); + + if (auto fileProvider = dynamic_cast(provider)) { + fileProvider->setPath(path); + if (!fileProvider->open()) { + View::showErrorPopup("hex.builtin.popup.error.open"_lang); + ImHexApi::Provider::remove(provider); + + return; + } + } + + if (!provider->isWritable()) { + View::showErrorPopup("hex.builtin.popup.error.read_only"_lang); + } + + if (!provider->isAvailable()) { + View::showErrorPopup("hex.builtin.popup.error.open"_lang); + ImHexApi::Provider::remove(provider); + + return; + } + + ProjectFile::setFilePath(path); + + EventManager::post(path); + EventManager::post(); + EventManager::post(); + } + + void registerEventHandlers() { + EventManager::subscribe([]() { + EventManager::post(ProjectFile::getFilePath()); + }); + + EventManager::subscribe([](GLFWwindow *window) { + if (ProjectFile::hasUnsavedChanges()) { + glfwSetWindowShouldClose(window, GLFW_FALSE); + ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.popup.exit_application.title"_lang); }); + } + }); + + EventManager::subscribe(openFile); + + EventManager::subscribe([](const std::string &name) { + if (name == "Create File") { + fs::openFileBrowser(fs::DialogMode::Save, {}, [](const auto &path) { + fs::File file(path, fs::File::Mode::Create); + + if (!file.isValid()) { + View::showErrorPopup("hex.builtin.popup.error.create"_lang); + return; + } + + file.setSize(1); + + EventManager::post(path); + }); + } else if (name == "Open File") { + fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) { + EventManager::post(path); + }); + } else if (name == "Open Project") { + fs::openFileBrowser(fs::DialogMode::Open, { {"Project File", "hexproj"} }, + [](const auto &path) { + ProjectFile::load(path); + }); + } + }); + + EventManager::subscribe([](auto, auto) { + EventManager::post(); + }); + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/main_menu_items.cpp b/plugins/builtin/source/content/main_menu_items.cpp index 8a231f53a..a4c877067 100644 --- a/plugins/builtin/source/content/main_menu_items.cpp +++ b/plugins/builtin/source/content/main_menu_items.cpp @@ -4,18 +4,267 @@ #include #include +#include +#include +#include + +#include namespace hex::plugin::builtin { static bool g_demoWindowOpen = false; - void registerMainMenuEntries() { - + static void createFileMenu() { ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.file", 1000); + + ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1050, [&] { + if (ImGui::MenuItem("hex.builtin.menu.file.open_file"_lang, "CTRL + O")) { + + fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) { + EventManager::post(path); + }); + } + + if (ImGui::BeginMenu("hex.builtin.menu.file.open_other"_lang)) { + + for (const auto &unlocalizedProviderName : ContentRegistry::Provider::getEntries()) { + if (ImGui::MenuItem(LangEntry(unlocalizedProviderName))) { + EventManager::post(unlocalizedProviderName, nullptr); + } + } + + ImGui::EndMenu(); + } + }); + + /* File open, quit imhex */ + ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1150, [&] { + bool providerValid = ImHexApi::Provider::isValid(); + + if (ImGui::MenuItem("hex.builtin.menu.file.close"_lang, "", false, providerValid)) { + EventManager::post(); + ImHexApi::Provider::remove(ImHexApi::Provider::get()); + } + + if (ImGui::MenuItem("hex.builtin.menu.file.quit"_lang, "", false)) { + ImHexApi::Common::closeImHex(); + } + }); + + /* Project open / save */ + ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1250, [&] { + auto provider = ImHexApi::Provider::get(); + bool providerValid = ImHexApi::Provider::isValid(); + + if (ImGui::MenuItem("hex.builtin.menu.file.open_project"_lang, "")) { + fs::openFileBrowser(fs::DialogMode::Open, { {"Project File", "hexproj"} + }, + [](const auto &path) { + ProjectFile::load(path); + }); + } + + if (ImGui::MenuItem("hex.builtin.menu.file.save_project"_lang, "", false, providerValid && provider->isWritable())) { + if (ProjectFile::getProjectFilePath() == "") { + fs::openFileBrowser(fs::DialogMode::Save, { {"Project File", "hexproj"} + }, + [](std::fs::path path) { + if (path.extension() != ".hexproj") { + path.replace_extension(".hexproj"); + } + + ProjectFile::store(path); + }); + } else + ProjectFile::store(); + } + }); + + /* Import / Export */ + ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1300, [&] { + auto provider = ImHexApi::Provider::get(); + bool providerValid = ImHexApi::Provider::isValid(); + + /* Import */ + if (ImGui::BeginMenu("hex.builtin.menu.file.import"_lang)) { + if (ImGui::MenuItem("hex.builtin.menu.file.import.base64"_lang)) { + + fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) { + fs::File inputFile(path, fs::File::Mode::Read); + if (!inputFile.isValid()) { + View::showErrorPopup("hex.builtin.menu.file.import.base64.popup.open_error"_lang); + return; + } + + auto base64 = inputFile.readBytes(); + + if (!base64.empty()) { + auto data = crypt::decode64(base64); + + if (data.empty()) + View::showErrorPopup("hex.builtin.menu.file.import.base64.popup.import_error"_lang); + else { + fs::openFileBrowser(fs::DialogMode::Save, {}, [&data](const std::fs::path &path) { + fs::File outputFile(path, fs::File::Mode::Create); + + if (!outputFile.isValid()) + View::showErrorPopup("hex.builtin.menu.file.import.base64.popup.import_error"_lang); + + outputFile.write(data); + }); + } + } else { + View::showErrorPopup("hex.builtin.popup.file_open_error"_lang); + } + }); + } + + ImGui::Separator(); + + if (ImGui::MenuItem("hex.builtin.menu.file.import.ips"_lang, nullptr, false)) { + + fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) { + std::thread([path] { + auto task = ImHexApi::Tasks::createTask("hex.common.processing", 0); + + auto patchData = fs::File(path, fs::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(); + }).detach(); + }); + } + + if (ImGui::MenuItem("hex.builtin.menu.file.import.ips32"_lang, nullptr, false)) { + fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) { + std::thread([path] { + auto task = ImHexApi::Tasks::createTask("hex.common.processing", 0); + + auto patchData = fs::File(path, fs::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(); + }).detach(); + }); + } + + ImGui::EndMenu(); + } + + + /* Export */ + if (ImGui::BeginMenu("hex.builtin.menu.file.export"_lang, providerValid && provider->isWritable())) { + if (ImGui::MenuItem("hex.builtin.menu.file.export.ips"_lang, nullptr, false)) { + Patches patches = provider->getPatches(); + if (!patches.contains(0x00454F45) && patches.contains(0x00454F46)) { + u8 value = 0; + provider->read(0x00454F45, &value, sizeof(u8)); + patches[0x00454F45] = value; + } + + std::thread([patches] { + auto task = ImHexApi::Tasks::createTask("hex.common.processing", 0); + + auto data = generateIPSPatch(patches); + + ImHexApi::Tasks::doLater([data] { + fs::openFileBrowser(fs::DialogMode::Save, {}, [&data](const auto &path) { + auto file = fs::File(path, fs::File::Mode::Create); + if (!file.isValid()) { + View::showErrorPopup("hex.builtin.menu.file.export.base64.popup.export_error"_lang); + return; + } + + file.write(data); + }); + }); + }).detach(); + } + + if (ImGui::MenuItem("hex.builtin.menu.file.export.ips32"_lang, nullptr, false)) { + Patches patches = provider->getPatches(); + if (!patches.contains(0x00454F45) && patches.contains(0x45454F46)) { + u8 value = 0; + provider->read(0x45454F45, &value, sizeof(u8)); + patches[0x45454F45] = value; + } + + std::thread([patches] { + auto task = ImHexApi::Tasks::createTask("hex.common.processing", 0); + + auto data = generateIPS32Patch(patches); + + ImHexApi::Tasks::doLater([data] { + fs::openFileBrowser(fs::DialogMode::Save, {}, [&data](const auto &path) { + auto file = fs::File(path, fs::File::Mode::Create); + if (!file.isValid()) { + View::showErrorPopup("hex.builtin.menu.file.export.popup.create"_lang); + return; + } + + file.write(data); + }); + }); + }).detach(); + } + + ImGui::EndMenu(); + } + }); + } + + static void createEditMenu() { ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.edit", 2000); + + /* Provider Undo / Redo */ + ContentRegistry::Interface::addMenuItem("hex.builtin.menu.edit", 1000, [&] { + auto provider = ImHexApi::Provider::get(); + bool providerValid = ImHexApi::Provider::isValid(); + + if (ImGui::MenuItem("hex.builtin.menu.edit.undo"_lang, "CTRL + Z", false, providerValid && provider->canUndo())) + provider->undo(); + if (ImGui::MenuItem("hex.builtin.menu.edit.redo"_lang, "CTRL + Y", false, providerValid && provider->canRedo())) + provider->redo(); + }); + + ContentRegistry::Interface::addMenuItem("hex.builtin.menu.edit", 1050, [&] { + auto provider = ImHexApi::Provider::get(); + bool providerValid = ImHexApi::Provider::isValid(); + auto selection = ImHexApi::HexEditor::getSelection(); + + if (ImGui::MenuItem("hex.builtin.menu.edit.bookmark"_lang, nullptr, false, selection.has_value() && providerValid)) { + auto base = provider->getBaseAddress(); + + ImHexApi::Bookmarks::add(base + selection->getStartAddress(), selection->getEndAddress(), {}, {}); + } + }); + + } + + static void createViewMenu() { ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.view", 3000); - ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.layout", 4000); - ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.help", 5000); ContentRegistry::Interface::addMenuItem("hex.builtin.menu.view", 1000, [] { for (auto &[name, view] : ContentRegistry::Views::getEntries()) { @@ -24,11 +273,15 @@ namespace hex::plugin::builtin { } }); -#if defined(DEBUG) - ContentRegistry::Interface::addMenuItem("hex.builtin.menu.view", 2000, [] { - ImGui::MenuItem("hex.builtin.menu.view.demo"_lang, "", &g_demoWindowOpen); - }); -#endif + #if defined(DEBUG) + ContentRegistry::Interface::addMenuItem("hex.builtin.menu.view", 2000, [] { + ImGui::MenuItem("hex.builtin.menu.view.demo"_lang, "", &g_demoWindowOpen); + }); + #endif + } + + static void createLayoutMenu() { + ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.layout", 4000); ContentRegistry::Interface::addMenuItem("hex.builtin.menu.layout", 1000, [] { for (auto &[layoutName, func] : ContentRegistry::Interface::getLayouts()) { @@ -46,7 +299,20 @@ namespace hex::plugin::builtin { } } }); + } + static void createHelpMenu() { + ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.help", 5000); + + } + + + void registerMainMenuEntries() { + createFileMenu(); + createEditMenu(); + createViewMenu(); + createLayoutMenu(); + createHelpMenu(); (void)EventManager::subscribe([] { if (g_demoWindowOpen) { diff --git a/plugins/builtin/source/content/settings_entries.cpp b/plugins/builtin/source/content/settings_entries.cpp index 6ac7f1945..4a95384cc 100644 --- a/plugins/builtin/source/content/settings_entries.cpp +++ b/plugins/builtin/source/content/settings_entries.cpp @@ -148,18 +148,32 @@ namespace hex::plugin::builtin { return false; }); - ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.highlight_alpha", 0x80, [](auto name, nlohmann::json &setting) { - static int alpha = static_cast(setting); + ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.highlight_color", 0x60C08080, [](auto name, nlohmann::json &setting) { + static auto color = static_cast(setting); + + std::array colorArray = { + ((color >> 0) & 0x000000FF) / float(0xFF), + ((color >> 8) & 0x000000FF) / float(0xFF), + ((color >> 16) & 0x000000FF) / float(0xFF), + ((color >> 24) & 0x000000FF) / float(0xFF) + }; + + if (ImGui::ColorEdit4(name.data(), colorArray.data(), ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoDragDrop | ImGuiColorEditFlags_NoInputs)) { + color = + (color_t(colorArray[0] * 0xFF) << 0) | + (color_t(colorArray[1] * 0xFF) << 8) | + (color_t(colorArray[2] * 0xFF) << 16) | + (color_t(colorArray[3] * 0xFF) << 24); + + setting = color; - if (ImGui::SliderInt(name.data(), &alpha, 0x00, 0xFF)) { - setting = alpha; return true; } return false; }); - ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.column_count", 16, [](auto name, nlohmann::json &setting) { + ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.bytes_per_row", 16, [](auto name, nlohmann::json &setting) { static int columns = static_cast(setting); if (ImGui::SliderInt(name.data(), &columns, 1, 32)) { @@ -170,17 +184,6 @@ namespace hex::plugin::builtin { return false; }); - ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.hexii", 0, [](auto name, nlohmann::json &setting) { - static bool hexii = static_cast(setting); - - if (ImGui::Checkbox(name.data(), &hexii)) { - setting = static_cast(hexii); - return true; - } - - return false; - }); - ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.ascii", 1, [](auto name, nlohmann::json &setting) { static bool ascii = static_cast(setting); @@ -225,18 +228,30 @@ namespace hex::plugin::builtin { return false; }); - ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.extra_info", 1, [](auto name, nlohmann::json &setting) { - static bool extraInfos = static_cast(setting); + ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.visualizer", "hex.builtin.visualizer.hexadecimal.8bit", [](auto name, nlohmann::json &setting) { + auto &visualizers = ContentRegistry::HexEditor::impl::getVisualizers(); - if (ImGui::Checkbox(name.data(), &extraInfos)) { - setting = static_cast(extraInfos); - return true; + auto selectedVisualizer = setting; + + bool result = false; + if (ImGui::BeginCombo(name.data(), LangEntry(selectedVisualizer))) { + + for (const auto &[unlocalizedName, visualizer] : visualizers) { + if (ImGui::Selectable(LangEntry(unlocalizedName))) { + setting = unlocalizedName; + result = true; + } + } + + ImGui::EndCombo(); } - return false; + return result; }); + /* Fonts */ + static std::string fontPath; ContentRegistry::Settings::add( "hex.builtin.setting.font", "hex.builtin.setting.font.font_path", "", [](auto name, nlohmann::json &setting) { @@ -282,6 +297,8 @@ namespace hex::plugin::builtin { true); + /* Folders */ + static const std::string dirsSetting { "hex.builtin.setting.folders" }; ContentRegistry::Settings::addCategoryDescription(dirsSetting, "hex.builtin.setting.folders.description"); diff --git a/plugins/builtin/source/content/tools_entries.cpp b/plugins/builtin/source/content/tools_entries.cpp index 94e27cf3e..a38377337 100644 --- a/plugins/builtin/source/content/tools_entries.cpp +++ b/plugins/builtin/source/content/tools_entries.cpp @@ -134,8 +134,8 @@ namespace hex::plugin::builtin { static std::string mathInput; bool evaluate = false; - static MathEvaluator mathEvaluator = [&] { - MathEvaluator evaluator; + static MathEvaluator mathEvaluator = [&] { + MathEvaluator evaluator; evaluator.registerStandardVariables(); evaluator.registerStandardFunctions(); diff --git a/plugins/builtin/source/content/ui_items.cpp b/plugins/builtin/source/content/ui_items.cpp index c3c4247a1..fa17a1cce 100644 --- a/plugins/builtin/source/content/ui_items.cpp +++ b/plugins/builtin/source/content/ui_items.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -15,6 +16,28 @@ namespace hex::plugin::builtin { + static void drawGlobalPopups() { + + // "Are you sure you want to exit?" Popup + if (ImGui::BeginPopupModal("hex.builtin.popup.exit_application.title"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::NewLine(); + ImGui::TextUnformatted("hex.builtin.popup.exit_application.desc"_lang); + ImGui::NewLine(); + + View::confirmButtons( + "hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [] { ImHexApi::Common::closeImHex(true); }, [] { ImGui::CloseCurrentPopup(); }); + + if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape))) + ImGui::CloseCurrentPopup(); + + ImGui::EndPopup(); + } + } + + void addGlobalUIItems() { + EventManager::subscribe(drawGlobalPopups); + } + void addFooterItems() { if (hex::isProcessElevated()) { @@ -68,19 +91,20 @@ namespace hex::plugin::builtin { bool providerValid = provider != nullptr; // Undo - ImGui::Disabled([&provider] { + ImGui::BeginDisabled(!providerValid || !provider->canUndo()); + { if (ImGui::ToolBarButton(ICON_VS_DISCARD, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue))) provider->undo(); - }, - !providerValid || !provider->canUndo()); + } + ImGui::EndDisabled(); // Redo - ImGui::Disabled([&provider] { + ImGui::BeginDisabled(!providerValid || !provider->canRedo()); + { if (ImGui::ToolBarButton(ICON_VS_REDO, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue))) provider->redo(); - }, - !providerValid || !provider->canRedo()); - + } + ImGui::EndDisabled(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); @@ -96,42 +120,45 @@ namespace hex::plugin::builtin { ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); // Save file - ImGui::Disabled([&provider] { + ImGui::BeginDisabled(!providerValid || !provider->isWritable() || !provider->isSavable()); + { if (ImGui::ToolBarButton(ICON_VS_SAVE, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue))) provider->save(); - }, - !providerValid || !provider->isWritable() || !provider->isSavable()); + } + ImGui::EndDisabled(); // Save file as - ImGui::Disabled([&provider] { + ImGui::BeginDisabled(!providerValid || !provider->isSavable()); + { if (ImGui::ToolBarButton(ICON_VS_SAVE_AS, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue))) fs::openFileBrowser(fs::DialogMode::Save, {}, [&provider](auto path) { provider->saveAs(path); }); - }, - !providerValid || !provider->isSavable()); + } + ImGui::EndDisabled(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); // Create bookmark - ImGui::Disabled([] { + ImGui::BeginDisabled(!providerValid || !provider->isReadable() || !ImHexApi::HexEditor::isSelectionValid()); + { if (ImGui::ToolBarButton(ICON_VS_BOOKMARK, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen))) { - Region region = { }; - EventManager::post(region); + auto region = ImHexApi::HexEditor::getSelection(); - ImHexApi::Bookmarks::add(region.address, region.size, {}, {}); + if (region.has_value()) + ImHexApi::Bookmarks::add(region->address, region->size, {}, {}); } - }, - !providerValid || !provider->isReadable() || ImHexApi::HexEditor::getSelection().size == 0); - + } + ImGui::EndDisabled(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::Spacing(); // Provider switcher - ImGui::Disabled([] { + ImGui::BeginDisabled(!providerValid); + { auto &providers = ImHexApi::Provider::getProviders(); std::string preview; @@ -149,9 +176,10 @@ namespace hex::plugin::builtin { ImGui::EndCombo(); } - }, - !providerValid); + } + ImGui::EndDisabled(); }); + } } \ No newline at end of file diff --git a/plugins/builtin/source/content/views/view_bookmarks.cpp b/plugins/builtin/source/content/views/view_bookmarks.cpp index 57327f2e3..8edd14c4e 100644 --- a/plugins/builtin/source/content/views/view_bookmarks.cpp +++ b/plugins/builtin/source/content/views/view_bookmarks.cpp @@ -23,9 +23,8 @@ namespace hex::plugin::builtin { name, std::move(comment), color, - false, - - ImHexApi::HexEditor::addHighlight(region, color, name) }); + false + }); ProjectFile::markDirty(); }); @@ -41,6 +40,75 @@ namespace hex::plugin::builtin { EventManager::subscribe(this, [this] { this->m_bookmarks.clear(); }); + + + ImHexApi::HexEditor::addBackgroundHighlightingProvider([this](u64 address, const u8* data, size_t size) -> std::optional { + hex::unused(data); + + for (const auto &bookmark : this->m_bookmarks) { + if (Region { address, size }.isWithin(bookmark.region)) + return bookmark.color; + } + + return std::nullopt; + }); + + ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8 *data, size_t size) { + hex::unused(data); + for (const auto &bookmark : this->m_bookmarks) { + if (!Region { address, size }.isWithin(bookmark.region)) + continue; + + ImGui::BeginTooltip(); + + if (ImGui::BeginTable("##tooltips", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_NoClip)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImGui::PushID(&bookmark); + { + ImGui::ColorButton("##color", ImColor(bookmark.color)); + ImGui::SameLine(0, 10); + ImGui::TextUnformatted(bookmark.name.c_str()); + + if (ImGui::GetIO().KeyShift) { + ImGui::Indent(); + if (ImGui::BeginTable("##extra_info", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_NoClip)) { + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextFormatted("Region: "); + ImGui::TableNextColumn(); + ImGui::TextFormatted("[ 0x{:08X} - 0x{:08X} ]", bookmark.region.getStartAddress(), bookmark.region.getEndAddress()); + + if (!bookmark.comment.empty() && bookmark.comment[0] != '\x00') { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextFormatted("Comment: "); + ImGui::TableNextColumn(); + ImGui::TextFormattedWrapped("\"{}\"", bookmark.comment); + } + + ImGui::EndTable(); + } + ImGui::Unindent(); + } + } + + ImGui::PopID(); + + ImGui::PushStyleColor(ImGuiCol_TableRowBg, bookmark.color); + ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, bookmark.color); + ImGui::EndTable(); + ImGui::PopStyleColor(2); + } + + ImGui::EndTooltip(); + } + }); } ViewBookmarks::~ViewBookmarks() { @@ -63,7 +131,7 @@ namespace hex::plugin::builtin { u32 id = 1; auto bookmarkToRemove = this->m_bookmarks.end(); for (auto iter = this->m_bookmarks.begin(); iter != this->m_bookmarks.end(); iter++) { - auto &[region, name, comment, color, locked, highlight] = *iter; + auto &[region, name, comment, color, locked] = *iter; auto headerColor = ImColor(color); auto hoverColor = ImColor(color); @@ -169,7 +237,6 @@ namespace hex::plugin::builtin { } if (bookmarkToRemove != this->m_bookmarks.end()) { - ImHexApi::HexEditor::removeHighlight(bookmarkToRemove->highlightId); this->m_bookmarks.erase(bookmarkToRemove); ProjectFile::markDirty(); } diff --git a/plugins/builtin/source/content/views/view_diff.cpp b/plugins/builtin/source/content/views/view_diff.cpp index 4dbf44328..c6e830a57 100644 --- a/plugins/builtin/source/content/views/view_diff.cpp +++ b/plugins/builtin/source/content/views/view_diff.cpp @@ -14,7 +14,7 @@ namespace hex::plugin::builtin { EventManager::subscribe(this, [this] { { - auto columnCount = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.column_count"); + auto columnCount = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.bytes_per_row"); if (columnCount.is_number()) this->m_columnCount = static_cast(columnCount); diff --git a/plugins/builtin/source/content/views/view_disassembler.cpp b/plugins/builtin/source/content/views/view_disassembler.cpp index d0f4f2b3b..2f4cde3be 100644 --- a/plugins/builtin/source/content/views/view_disassembler.cpp +++ b/plugins/builtin/source/content/views/view_disassembler.cpp @@ -322,11 +322,12 @@ namespace hex::plugin::builtin { } ImGui::EndChild(); - ImGui::Disabled([this] { + ImGui::BeginDisabled(this->m_disassembling); + { if (ImGui::Button("hex.builtin.view.disassembler.disassemble"_lang)) this->disassemble(); - }, - this->m_disassembling); + } + ImGui::EndDisabled(); if (this->m_disassembling) { ImGui::SameLine(); diff --git a/plugins/builtin/source/content/views/view_hex_editor.cpp b/plugins/builtin/source/content/views/view_hex_editor.cpp index a6ab870b0..ba507d50d 100644 --- a/plugins/builtin/source/content/views/view_hex_editor.cpp +++ b/plugins/builtin/source/content/views/view_hex_editor.cpp @@ -1,267 +1,974 @@ #include "content/views/view_hex_editor.hpp" -#include #include - -#include -#include -#include -#include -#include #include -#include - -#include +#include +#include +#include #include "math_evaluator.hpp" -#include - +#include #include #include -#include namespace hex::plugin::builtin { - ViewHexEditor::ViewHexEditor() : View("hex.builtin.view.hex_editor.name"_lang) { + class PopupGoto : public ViewHexEditor::Popup { + public: - this->m_searchStringBuffer = std::vector(0xFFF, 0x00); - this->m_searchHexBuffer = std::vector(0xFFF, 0x00); + void draw(ViewHexEditor *editor) override { + ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.file.goto"_lang); + if (ImGui::BeginTabBar("hex.builtin.view.hex_editor.goto.offset.absolute"_lang)) { + if (ImGui::BeginTabItem("Absolute")) { + this->m_mode = Mode::Absolute; + ImGui::EndTabItem(); + } - ContentRegistry::FileHandler::add({ ".hexproj" }, [](const auto &path) { - return ProjectFile::load(path); + ImGui::BeginDisabled(!editor->isSelectionValid()); + if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.relative"_lang)) { + this->m_mode = Mode::Relative; + ImGui::EndTabItem(); + } + ImGui::EndDisabled(); + + if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.begin"_lang)) { + this->m_mode = Mode::Begin; + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.end"_lang)) { + this->m_mode = Mode::End; + ImGui::EndTabItem(); + } + + ImGui::SetKeyboardFocusHere(); + ImGui::CaptureKeyboardFromApp(true); + if (ImGui::InputText("##input", this->m_input, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) { + if (auto result = this->m_evaluator.evaluate(this->m_input); result.has_value()) { + const auto inputResult = result.value(); + u64 newAddress = 0x00; + + auto provider = ImHexApi::Provider::get(); + + switch (this->m_mode) { + case Mode::Absolute: { + newAddress = inputResult; + } + break; + case Mode::Relative: { + const auto selection = editor->getSelection(); + newAddress = selection.getStartAddress() + inputResult; + } + break; + case Mode::Begin: { + newAddress = provider->getBaseAddress() + provider->getCurrentPageAddress() + inputResult; + } + break; + case Mode::End: { + newAddress = provider->getActualSize() - inputResult; + } + break; + } + + editor->setSelection(newAddress, newAddress); + editor->jumpToSelection(); + } + } + + ImGui::EndTabBar(); + } + } + + private: + enum class Mode : u8 { + Absolute, + Relative, + Begin, + End + }; + + Mode m_mode = Mode::Absolute; + + std::string m_input; + MathEvaluator m_evaluator; + }; + + class PopupFind : public ViewHexEditor::Popup { + public: + void draw(ViewHexEditor *editor) override { + std::vector searchSequence; + + ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.file.search"_lang); + if (ImGui::BeginTabBar("##find_tabs")) { + if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.search.hex"_lang)) { + if (ImGui::InputText("##input", this->m_input, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_CharsHexadecimal)) { + this->m_shouldSearch = true; + this->m_backwards = false; + } + + this->drawButtons(); + + if (this->m_shouldSearch) { + searchSequence = crypt::decode16(this->m_input); + } + + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.search.string"_lang)) { + if (ImGui::InputText("##input", this->m_input, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) { + this->m_shouldSearch = true; + this->m_backwards = false; + } + + this->drawButtons(); + + if (this->m_shouldSearch) { + searchSequence.clear(); + std::copy(this->m_input.begin(), this->m_input.end(), std::back_inserter(searchSequence)); + + // Remove null termination + searchSequence.pop_back(); + } + + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } + + if (!this->m_searchRunning && !searchSequence.empty() && this->m_shouldSearch) { + std::thread([this, searchSequence, editor]{ + this->m_searchTask = ImHexApi::Tasks::createTask("Searching", ImHexApi::Provider::get()->getSize()); + this->m_searchRunning = true; + + if (auto region = this->findSequence(editor, searchSequence, this->m_backwards); region.has_value()) { + ImHexApi::Tasks::doLater([editor, region]{ + editor->setSelection(region->getStartAddress(), region->getEndAddress()); + editor->jumpToSelection(); + }); + } + + this->m_shouldSearch = false; + this->m_requestFocus = true; + this->m_searchRunning = false; + }).detach(); + } + } + + private: + void drawButtons() { + const auto ButtonSize = ImVec2(ImGui::CalcTextSize(ICON_VS_SEARCH).x, ImGui::GetTextLineHeight()) + ImGui::GetStyle().CellPadding * 2; + const auto ButtonColor = ImGui::GetStyleColorVec4(ImGuiCol_Text); + + if (this->m_requestFocus) { + ImGui::SetKeyboardFocusHere(-1); + this->m_requestFocus = false; + } + + ImGui::BeginDisabled(this->m_searchRunning); + { + ImGui::SameLine(); + if (ImGui::IconButton(ICON_VS_SEARCH "##search", ButtonColor, ButtonSize)) { + this->m_shouldSearch = true; + this->m_backwards = false; + this->m_searchPosition.reset(); + } + + if (ImGui::IconButton(ICON_VS_ARROW_UP "##up", ButtonColor, ButtonSize)) { + this->m_shouldSearch = true; + this->m_backwards = true; + } + ImGui::SameLine(); + if (ImGui::IconButton(ICON_VS_ARROW_DOWN "##down", ButtonColor, ButtonSize)) { + this->m_shouldSearch = true; + this->m_backwards = false; + } + } + ImGui::EndDisabled(); + } + + std::optional findSequence(ViewHexEditor *editor, const std::vector &sequence, bool backwards) { + hex::prv::BufferedReader reader(ImHexApi::Provider::get()); + + if (!editor->isSelectionValid()) + reader.seek(this->m_searchPosition.value_or(0x00)); + else + reader.seek(this->m_searchPosition.value_or(editor->getSelection().getEndAddress())); + + constexpr static auto searchFunction = [](const auto &haystackBegin, const auto &haystackEnd, const auto &needleBegin, const auto &needleEnd) { + #if defined(OS_MACOS) + return std::search(haystackBegin, haystackEnd, needleBegin, needleEnd); + #else + return std::search(haystackBegin, haystackEnd, std::boyer_moore_horspool_searcher(needleBegin, needleEnd)); + #endif + }; + + if (!backwards) { + auto occurrence = searchFunction(reader.begin(), reader.end(), sequence.begin(), sequence.end()); + if (occurrence != reader.end()) { + this->m_searchPosition = occurrence.getAddress() + 1; + return Region { occurrence.getAddress(), sequence.size() }; + } + } else { + auto occurrence = searchFunction(reader.rbegin(), reader.rend(), sequence.begin(), sequence.end()); + if (occurrence != reader.rend()) { + this->m_searchPosition = occurrence.getAddress() - 1; + return Region { occurrence.getAddress(), sequence.size() }; + } + } + + return std::nullopt; + } + + std::string m_input; + std::optional m_searchPosition; + + bool m_requestFocus = true; + std::atomic m_shouldSearch = false; + std::atomic m_backwards = false; + + std::optional m_searchTask; + std::atomic m_searchRunning = false; + }; + + class PopupBaseAddress : public ViewHexEditor::Popup { + public: + explicit PopupBaseAddress(u64 baseAddress) : m_baseAddress(baseAddress) { + + } + + void draw(ViewHexEditor *editor) override { + ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.edit.set_base"_lang); + + if (ImGui::InputHexadecimal("##base_address", &this->m_baseAddress, ImGuiInputTextFlags_EnterReturnsTrue)) { + setBaseAddress(this->m_baseAddress); + editor->closePopup(); + } + + View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, + [&, this]{ + setBaseAddress(this->m_baseAddress); + editor->closePopup(); + }, + [&]{ + editor->closePopup(); + }); + } + + private: + static void setBaseAddress(u64 baseAddress) { + if (ImHexApi::Provider::isValid()) + ImHexApi::Provider::get()->setBaseAddress(baseAddress); + } + + private: + u64 m_baseAddress; + }; + + class PopupResize : public ViewHexEditor::Popup { + public: + explicit PopupResize(u64 currSize) : m_size(currSize) {} + + void draw(ViewHexEditor *editor) override { + ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.edit.resize"_lang); + + if (ImGui::InputHexadecimal("##resize", &this->m_size, ImGuiInputTextFlags_EnterReturnsTrue)) { + resize(static_cast(this->m_size)); + } + + View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, + [&, this]{ + resize(static_cast(this->m_size)); + editor->closePopup(); + }, + [&]{ + editor->closePopup(); + }); + } + + 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 { + ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.edit.insert"_lang); + + if (ImGui::InputHexadecimal("hex.common.address"_lang, &this->m_address, ImGuiInputTextFlags_EnterReturnsTrue)) { + insert(this->m_address, static_cast(this->m_size)); + } + if (ImGui::InputHexadecimal("hex.common.size"_lang, &this->m_size, ImGuiInputTextFlags_EnterReturnsTrue)) { + insert(this->m_address, static_cast(this->m_size)); + } + + View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, + [&, this]{ + insert(this->m_address, static_cast(this->m_size)); + editor->closePopup(); + }, + [&]{ + editor->closePopup(); + }); + } + + 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; + }; + + ViewHexEditor::ViewHexEditor() : View("hex.builtin.view.hex_editor.name") { + this->m_currDataVisualizer = ContentRegistry::HexEditor::impl::getVisualizers()["hex.builtin.visualizer.hexadecimal.8bit"]; + + this->registerShortcuts(); + this->registerEvents(); + this->registerMenuItems(); + + ImHexApi::HexEditor::addForegroundHighlightingProvider([this](u64 address, const u8 *data, size_t size) -> std::optional { + hex::unused(address); + + if (!this->m_grayOutZero) + return std::nullopt; + + for (u32 i = 0; i < size; i++) + if (data[i] != 0x00) + return std::nullopt; + + return ImGui::GetColorU32(ImGuiCol_TextDisabled); }); - ContentRegistry::FileHandler::add({ ".tbl" }, [this](const auto &path) { - this->m_currEncodingFile = EncodingFile(EncodingFile::Type::Thingy, path); - - return true; - }); - - this->m_memoryEditor.ReadFn = [](const ImU8 *, size_t off) -> ImU8 { - auto provider = ImHexApi::Provider::get(); - if (!provider->isAvailable() || !provider->isReadable()) - return 0x00; - - ImU8 byte; - provider->read(off + provider->getBaseAddress() + provider->getCurrentPageAddress(), &byte, sizeof(ImU8)); - - return byte; - }; - - this->m_memoryEditor.WriteFn = [](ImU8 *, size_t off, ImU8 d) -> void { - auto provider = ImHexApi::Provider::get(); - if (!provider->isAvailable() || !provider->isWritable()) - return; - - provider->write(off + provider->getBaseAddress() + provider->getCurrentPageAddress(), &d, sizeof(ImU8)); - EventManager::post(); - ProjectFile::markDirty(); - }; - - this->m_memoryEditor.HighlightFn = [](const ImU8 *data, size_t off, bool next) -> bool { - bool firstByte = off == 0x00; - - auto _this = (ViewHexEditor *)(data); - auto provider = ImHexApi::Provider::get(); - off += provider->getBaseAddress() + provider->getCurrentPageAddress(); - - u32 alpha = static_cast(_this->m_highlightAlpha) << 24; - - std::optional currColor, prevColor; - - for (auto &highlightBlock : _this->m_highlights) { - if (off >= highlightBlock.base && off < (highlightBlock.base + HighlightBlock::Size)) { - currColor = highlightBlock.highlight[off - highlightBlock.base].color; - } - if ((off - 1) >= highlightBlock.base && (off - 1) < (highlightBlock.base + HighlightBlock::Size)) { - prevColor = highlightBlock.highlight[(off - 1) - highlightBlock.base].color; - } - - if (currColor && prevColor) break; - - if (firstByte) - prevColor = 0x00; - } - - if (!currColor || !prevColor) { - if (_this->m_highlights.size() > 0x100) - _this->m_highlights.pop_front(); - - auto blockStartOffset = off - (off % HighlightBlock::Size); - HighlightBlock newBlock = { - blockStartOffset, - {} - }; - - for (u32 i = 0; i < HighlightBlock::Size; i++) { - std::optional highlightColor; - std::string highlightTooltip; - - for (const auto &[id, highlight] : ImHexApi::HexEditor::impl::getHighlights()) { - auto ®ion = highlight.getRegion(); - auto &color = highlight.getColor(); - auto &tooltip = highlight.getTooltip(); - - if (blockStartOffset + i >= region.address && blockStartOffset + i < (region.address + region.size)) { - highlightColor = (color & 0x00FFFFFF) | alpha; - highlightTooltip = tooltip; - } - } - - for (const auto &[id, function] : ImHexApi::HexEditor::impl::getHighlightingFunctions()) { - auto highlight = function(blockStartOffset + i); - if (highlight.has_value()) { - highlightColor = highlightColor.has_value() ? ImAlphaBlendColors(highlight->getColor(), highlightColor.value()) : highlight->getColor(); - highlightTooltip = highlight->getTooltip(); - } - } - - auto &currHighlight = newBlock.highlight[i]; - currHighlight.color = highlightColor.value_or(0x00); - if (!highlightTooltip.empty()) - currHighlight.tooltips.push_back(highlightTooltip); - } - - _this->m_highlights.push_back(std::move(newBlock)); - } - - if (next && prevColor != currColor) { - return false; - } - - if (currColor.has_value() && (currColor.value() & 0x00FFFFFF) != 0x00) { - _this->m_memoryEditor.HighlightColor = (currColor.value() & 0x00FFFFFF) | alpha; - return true; - } - - _this->m_memoryEditor.HighlightColor = 0x60C08080; - return false; - }; - - this->m_memoryEditor.HoverFn = [](const ImU8 *data, size_t off) { - auto _this = (ViewHexEditor *)(data); - - bool tooltipShown = false; - - auto provider = ImHexApi::Provider::get(); - off += provider->getBaseAddress() + provider->getCurrentPageAddress(); - - for (auto &highlightBlock : _this->m_highlights) { - if (off >= highlightBlock.base && off < (highlightBlock.base + HighlightBlock::Size)) { - auto &highlight = highlightBlock.highlight[off - highlightBlock.base]; - if (!tooltipShown && !highlight.tooltips.empty()) { - ImGui::BeginTooltip(); - tooltipShown = true; - } - - for (const auto &tooltip : highlight.tooltips) { - ImGui::ColorButton(tooltip.c_str(), ImColor(highlight.color).Value); - ImGui::SameLine(0, 10); - ImGui::TextUnformatted(tooltip.c_str()); - } - } - } - - if (tooltipShown) - ImGui::EndTooltip(); - }; - - this->m_memoryEditor.DecodeFn = [](const ImU8 *data, size_t addr) -> MemoryEditor::DecodeData { - auto *_this = (ViewHexEditor *)data; - - if (_this->m_currEncodingFile.getLongestSequence() == 0) - return { ".", 1, 0xFFFF8000 }; - - auto provider = ImHexApi::Provider::get(); - size_t size = std::min(_this->m_currEncodingFile.getLongestSequence(), provider->getActualSize() - addr); - - std::vector 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(); - registerMenuItems(); } - ViewHexEditor::~ViewHexEditor() { - EventManager::unsubscribe(this); - EventManager::unsubscribe(this); - EventManager::unsubscribe(this); - EventManager::unsubscribe(this); - EventManager::unsubscribe(this); - EventManager::unsubscribe(this); - EventManager::unsubscribe(this); - EventManager::unsubscribe(this); + constexpr static u16 getByteColumnSeparatorCount(u16 columnCount) { + return (columnCount - 1) / 8; + } + + constexpr static bool isColumnSeparatorColumn(u16 currColumn, u16 columnCount) { + return currColumn > 0 && (currColumn) < columnCount && ((currColumn) % 8) == 0; + } + + static std::optional queryBackgroundColor(u64 address, const u8 *data, size_t size) { + for (const auto &[id, callback] : ImHexApi::HexEditor::impl::getBackgroundHighlightingFunctions()) { + if (auto color = callback(address, data, size); color.has_value()) + return color.value(); + } + + for (const auto &[id, highlighting] : ImHexApi::HexEditor::impl::getBackgroundHighlights()) { + if (highlighting.getRegion().overlaps({ address, size })) + return highlighting.getColor(); + } + + return std::nullopt; + } + + static std::optional queryForegroundColor(u64 address, const u8 *data, size_t size) { + for (const auto &[id, callback] : ImHexApi::HexEditor::impl::getForegroundHighlightingFunctions()) { + if (auto color = callback(address, data, size); color.has_value()) + return color.value(); + } + + for (const auto &[id, highlighting] : ImHexApi::HexEditor::impl::getForegroundHighlights()) { + if (highlighting.getRegion().overlaps({ address, size })) + return highlighting.getColor(); + } + + return std::nullopt; + } + + struct CustomEncodingData { + std::string displayValue; + size_t advance; + ImColor color; + }; + + static CustomEncodingData queryCustomEncodingData(const EncodingFile &encodingFile, u64 address) { + const auto longestSequence = encodingFile.getLongestSequence(); + + if (longestSequence == 0) + return { ".", 1, 0xFFFF8000 }; + + auto provider = ImHexApi::Provider::get(); + size_t size = std::min(longestSequence, provider->getActualSize() - address); + + std::vector buffer(size); + provider->read(address + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), size); + + const auto [decoded, advance] = encodingFile.getEncodingFor(buffer); + const ImColor color = [&decoded = decoded, &advance = advance]{ + if (decoded.length() == 1 && std::isalnum(decoded[0])) + return 0xFFFF8000; + else if (decoded.length() == 1 && advance == 1) + return 0xFF0000FF; + else if (decoded.length() > 1 && advance == 1) + return 0xFF00FFFF; + else if (advance > 1) + return 0xFFFFFFFF; + else + return 0xFFFF8000; + }(); + + return { std::string(decoded), advance, color }; + } + + static void drawTooltip(u64 address, const u8 *data, size_t size) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, scaled(ImVec2(5, 5))); + + for (const auto &[id, callback] : ImHexApi::HexEditor::impl::getTooltipFunctions()) { + callback(address, data, size); + } + + const auto &tooltips = ImHexApi::HexEditor::impl::getTooltips(); + if (!tooltips.empty()) { + ImGui::BeginTooltip(); + + for (const auto &[id, tooltip] : tooltips) { + if (ImGui::BeginTable("##tooltips", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_NoClip)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + if (tooltip.getRegion().overlaps({ address, size })) { + ImGui::ColorButton(tooltip.getValue().c_str(), ImColor(tooltip.getColor())); + ImGui::SameLine(0, 10); + ImGui::TextUnformatted(tooltip.getValue().c_str()); + } + + ImGui::PushStyleColor(ImGuiCol_TableRowBg, tooltip.getColor()); + ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, tooltip.getColor()); + ImGui::EndTable(); + ImGui::PopStyleColor(2); + } + } + + ImGui::EndTooltip(); + } + + ImGui::PopStyleVar(); + } + + void ViewHexEditor::drawCell(u64 address, u8 *data, size_t size, bool hovered) { + auto provider = ImHexApi::Provider::get(); + + if (this->m_editingAddress != address) { + this->m_currDataVisualizer->draw(address, data, size, this->m_upperCaseHex); + + if (hovered && provider->isWritable()) { + // Enter editing mode when double-clicking a cell + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { + this->m_editingAddress = address; + this->m_shouldModifyValue = false; + this->m_enteredEditingMode = true; + + this->m_editingBytes.resize(size); + std::memcpy(this->m_editingBytes.data(), data, size); + } + } + } + else { + ImGui::SetKeyboardFocusHere(); + ImGui::CaptureKeyboardFromApp(true); + + if (this->m_currDataVisualizer->drawEditing(address, this->m_editingBytes.data(), this->m_editingBytes.size(), this->m_upperCaseHex, this->m_enteredEditingMode) || this->m_shouldModifyValue) { + provider->write(address, this->m_editingBytes.data(), this->m_editingBytes.size()); + + if (!this->m_selectionChanged && !ImGui::IsMouseDown(ImGuiMouseButton_Left) && !ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { + auto nextEditingAddress = *this->m_editingAddress + this->m_currDataVisualizer->getBytesPerCell(); + this->setSelection(nextEditingAddress, nextEditingAddress); + + if (nextEditingAddress >= provider->getSize()) + this->m_editingAddress = std::nullopt; + else + this->m_editingAddress = nextEditingAddress; + } else { + this->m_editingAddress = std::nullopt; + } + + this->m_shouldModifyValue = false; + } + + this->m_enteredEditingMode = false; + } + } + + void ViewHexEditor::drawSelectionFrame(u32 x, u32 y, u64 byteAddress, u16 bytesPerCell, const ImVec2 &cellPos, const ImVec2 &cellSize) { + if (!this->isSelectionValid()) return; + + const auto selection = this->getSelection(); + if (!Region { byteAddress, 1 }.isWithin(selection)) + return; + + const color_t SelectionFrameColor = ImGui::GetColorU32(ImGuiCol_Text); + + auto drawList = ImGui::GetWindowDrawList(); + + // Draw vertical line at the left of first byte and the start of the line + if (x == 0 || byteAddress == selection.getStartAddress()) + drawList->AddLine(cellPos, cellPos + ImVec2(0, cellSize.y), ImColor(SelectionFrameColor), 1.0F); + + // Draw vertical line at the right of the last byte and the end of the line + if (x == u16((this->m_bytesPerRow / bytesPerCell) - 1) || (byteAddress + this->m_currDataVisualizer->getBytesPerCell()) > selection.getEndAddress()) + drawList->AddLine(cellPos + ImVec2(cellSize.x, -1), cellPos + cellSize, ImColor(SelectionFrameColor), 1.0F); + + // Draw horizontal line at the top of the bytes + if (y == 0 || (byteAddress - this->m_bytesPerRow) < selection.getStartAddress()) + drawList->AddLine(cellPos, cellPos + ImVec2(cellSize.x + 1, 0), ImColor(SelectionFrameColor), 1.0F); + + // Draw horizontal line at the bottom of the bytes + if ((byteAddress + this->m_bytesPerRow) > selection.getEndAddress()) + drawList->AddLine(cellPos + ImVec2(0, cellSize.y), cellPos + cellSize + ImVec2(1, 0), ImColor(SelectionFrameColor), 1.0F); + } + + void ViewHexEditor::drawPopup() { + // Popup windows + if (this->m_shouldOpenPopup) { + this->m_shouldOpenPopup = false; + ImGui::OpenPopup("##hex_editor_popup"); + } + + ImGui::SetNextWindowPos(ImGui::GetWindowPos() + ImGui::GetWindowContentRegionMin() - ImGui::GetStyle().WindowPadding, ImGuiCond_Appearing); + if (ImGui::BeginPopup("##hex_editor_popup", ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |ImGuiWindowFlags_NoTitleBar)) { + + if (this->m_currPopup != nullptr) + this->m_currPopup->draw(this); + else + ImGui::CloseCurrentPopup(); + + ImGui::EndPopup(); + } else { + this->closePopup(); + } + + // Right click menu + if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) + ImGui::OpenPopup("hex.builtin.menu.edit"_lang); + + if (ImGui::BeginPopup("hex.builtin.menu.edit"_lang)) { + + bool needsSeparator = false; + for (auto &[priority, menuItem] : ContentRegistry::Interface::getMenuItems()) { + if (menuItem.unlocalizedName != "hex.builtin.menu.edit") + continue; + + if (needsSeparator) + ImGui::Separator(); + + menuItem.callback(); + needsSeparator = true; + } + + ImGui::EndPopup(); + } + } + + void ViewHexEditor::drawEditor(const ImVec2 &size) { + const float SeparatorColumWidth = 6_scaled; + const auto CharacterSize = ImGui::CalcTextSize("0"); + + const auto bytesPerCell = this->m_currDataVisualizer->getBytesPerCell(); + const u16 columnCount = this->m_bytesPerRow / bytesPerCell; + const auto byteColumnCount = columnCount + getByteColumnSeparatorCount(columnCount); + + const auto selection = this->getSelection(); + const auto selectionMin = selection.getStartAddress(); + const auto selectionMax = selection.getEndAddress(); + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.5, 0)); + if (ImGui::BeginTable("##hex", 2 + byteColumnCount + 2 + 2 , ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoKeepColumnsVisible, size)) { + View::discardNavigationRequests(); + ImGui::TableSetupScrollFreeze(0, 2); + + // Row address column + ImGui::TableSetupColumn("hex.builtin.common.address"_lang); + ImGui::TableSetupColumn(""); + + // Byte columns + for (u16 i = 0; i < columnCount; i++) { + if (isColumnSeparatorColumn(i, columnCount)) + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, SeparatorColumWidth); + + ImGui::TableSetupColumn(hex::format(this->m_upperCaseHex ? "{:0{}X}" : "{:0{}x}", i * bytesPerCell, this->m_currDataVisualizer->getMaxCharsPerCell()).c_str(), ImGuiTableColumnFlags_WidthFixed, CharacterSize.x * this->m_currDataVisualizer->getMaxCharsPerCell() + 6); + } + + // ASCII column + ImGui::TableSetupColumn(""); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, CharacterSize.x * this->m_bytesPerRow); + + // Custom encoding column + ImGui::TableSetupColumn(""); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + for (i32 i = 0; i < ImGui::TableGetColumnCount(); i++) { + ImGui::TableNextColumn(); + ImGui::TextUnformatted(ImGui::TableGetColumnName(i)); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + CharacterSize.y / 2); + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + if (ImHexApi::Provider::isValid()) { + auto provider = ImHexApi::Provider::get(); + + ImGuiListClipper clipper(std::ceil(provider->getSize() / (long double)(this->m_bytesPerRow)), CharacterSize.y); + + while (clipper.Step()) { + this->m_visibleRowCount = clipper.DisplayEnd - clipper.DisplayStart; + + // Loop over rows + for (i128 y = clipper.DisplayStart; y < u64(clipper.DisplayEnd); y++) { + + // Draw address column + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextFormatted(this->m_upperCaseHex ? "{:08X}: " : "{:08x}: ", y * this->m_bytesPerRow + provider->getBaseAddress() + provider->getCurrentPageAddress()); + ImGui::TableNextColumn(); + + const u8 validBytes = std::min(this->m_bytesPerRow, provider->getSize() - y * this->m_bytesPerRow); + + std::vector bytes(validBytes); + provider->read(y * this->m_bytesPerRow + provider->getCurrentPageAddress(), bytes.data(), validBytes); + + std::vector, std::optional>> cellColors; + { + for (u64 x = 0; x < columnCount; x++) { + const u64 byteAddress = y * this->m_bytesPerRow + x * bytesPerCell + provider->getBaseAddress() + provider->getCurrentPageAddress(); + + const auto cellBytes = std::min(validBytes, bytesPerCell); + + // Query cell colors + const auto foregroundColor = queryForegroundColor(byteAddress, &bytes[x], cellBytes); + const auto backgroundColor = [&]{ + auto color = queryBackgroundColor(byteAddress, &bytes[x], cellBytes); + + if ((byteAddress >= selectionMin && byteAddress <= selectionMax)) { + if (color.has_value()) + color = ((ImAlphaBlendColors(color.value(), this->m_selectionColor)) & 0x00FFFFFF) | (this->m_selectionColor & 0xFF000000); + else + color = this->m_selectionColor; + } + + return color; + }(); + + cellColors.emplace_back( + foregroundColor, + backgroundColor + ); + } + } + + // Draw byte columns + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(3, 0)); + + for (u64 x = 0; x < columnCount; x++) { + const u64 byteAddress = y * this->m_bytesPerRow + x * bytesPerCell + provider->getBaseAddress() + provider->getCurrentPageAddress(); + + ImGui::TableNextColumn(); + if (isColumnSeparatorColumn(x, columnCount)) + ImGui::TableNextColumn(); + + if (x < validBytes) { + auto cellStartPos = (ImGui::GetWindowPos() + ImGui::GetCursorPos()) - ImGui::GetStyle().CellPadding - ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY()); + auto cellSize = (CharacterSize * ImVec2(this->m_currDataVisualizer->getMaxCharsPerCell(), 1) + (ImVec2(3, 2) * ImGui::GetStyle().CellPadding) - ImVec2(1, 0) * ImGui::GetStyle().CellPadding) + ImVec2(1, 0); + + auto [foregroundColor, backgroundColor] = cellColors[x]; + + if (isColumnSeparatorColumn(x + 1, columnCount) && selectionMax != x + y * columnCount) { + cellSize.x += SeparatorColumWidth + 1; + } + if (y == clipper.DisplayStart) + cellSize.y -= (ImGui::GetStyle().CellPadding.y + 1); + + // Draw highlights and selection + if (backgroundColor.has_value()) { + auto drawList = ImGui::GetWindowDrawList(); + + // Draw background color + drawList->AddRectFilled(cellStartPos, cellStartPos + cellSize, backgroundColor.value()); + + // Draw frame around mouse selection + this->drawSelectionFrame(x, y, byteAddress, bytesPerCell, cellStartPos, cellSize); + } + + const bool cellHovered = ImGui::IsMouseHoveringRect(cellStartPos, cellStartPos + cellSize, false); + + this->handleSelection(byteAddress, bytesPerCell, &bytes[x], cellHovered); + + // Get byte foreground color + if (foregroundColor.has_value()) + ImGui::PushStyleColor(ImGuiCol_Text, *foregroundColor); + + // Draw cell content + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushItemWidth((CharacterSize * this->m_currDataVisualizer->getMaxCharsPerCell()).x); + this->drawCell(byteAddress, &bytes[x], bytesPerCell, cellHovered); + ImGui::PopItemWidth(); + ImGui::PopStyleVar(); + + if (foregroundColor.has_value()) + ImGui::PopStyleColor(); + } + } + ImGui::PopStyleVar(); + + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + + // Draw ASCII column + if (this->m_showAscii) { + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0, 0)); + if (ImGui::BeginTable("##ascii_cell", this->m_bytesPerRow)) { + ImGui::TableNextRow(); + + for (u64 x = 0; x < this->m_bytesPerRow; x++) { + ImGui::TableNextColumn(); + + const u64 byteAddress = y * this->m_bytesPerRow + x + provider->getBaseAddress() + provider->getCurrentPageAddress(); + + const auto cellStartPos = (ImGui::GetWindowPos() + ImGui::GetCursorPos()) - ImGui::GetStyle().CellPadding - ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY()); + const auto cellSize = CharacterSize; + + const bool cellHovered = ImGui::IsMouseHoveringRect(cellStartPos, cellStartPos + cellSize, false); + + this->handleSelection(byteAddress, bytesPerCell, &bytes[x], cellHovered); + + auto [foregroundColor, backgroundColor] = cellColors[x / bytesPerCell]; + + // Draw highlights and selection + if (backgroundColor.has_value()) { + auto drawList = ImGui::GetWindowDrawList(); + + // Draw background color + drawList->AddRectFilled(cellStartPos, cellStartPos + cellSize, backgroundColor.value()); + + this->drawSelectionFrame(x, y, byteAddress, 1, cellStartPos, cellSize); + } + + if (x < validBytes) { + if (std::isprint(bytes[x])) + ImGui::TextFormatted("{:c}", bytes[x]); + else + ImGui::TextDisabled("."); + } + } + + ImGui::EndTable(); + } + ImGui::PopStyleVar(); + } + + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + + // Draw Custom encoding column + if (this->m_currCustomEncoding.has_value()) { + std::vector> encodingData; + u32 offset = 0; + do { + const u64 address = y * this->m_bytesPerRow + offset + provider->getBaseAddress() + provider->getCurrentPageAddress(); + + auto result = queryCustomEncodingData(*this->m_currCustomEncoding, address); + offset += std::max(1, result.advance); + + encodingData.emplace_back(address, result); + } while (offset < this->m_bytesPerRow); + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0, 0)); + if (ImGui::BeginTable("##encoding_cell", encodingData.size(), ImGuiTableFlags_SizingFixedFit)) { + ImGui::TableNextRow(); + + for (const auto &[address, data] : encodingData) { + ImGui::TableNextColumn(); + + const auto cellStartPos = (ImGui::GetWindowPos() + ImGui::GetCursorPos()) - ImGui::GetStyle().CellPadding - ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY()); + const auto cellSize = CharacterSize * data.advance; + const bool cellHovered = ImGui::IsMouseHoveringRect(cellStartPos, cellStartPos + cellSize, false); + + auto [foregroundColor, backgroundColor] = cellColors[address % this->m_bytesPerRow]; + + // Draw highlights and selection + if (backgroundColor.has_value()) { + auto drawList = ImGui::GetWindowDrawList(); + + // Draw background color + drawList->AddRectFilled(cellStartPos, cellStartPos + cellSize, backgroundColor.value()); + + this->drawSelectionFrame(address % this->m_bytesPerRow, y, address, 1, cellStartPos, cellSize); + } + + ImGui::TextFormattedColored(data.color, "{}", data.displayValue); + + this->handleSelection(address, data.advance, &bytes[address % this->m_bytesPerRow], cellHovered); + } + + ImGui::EndTable(); + } + ImGui::PopStyleVar(); + + } + + // Scroll to the cursor if it's either at the top or bottom edge of the screen + if (this->m_shouldScrollToSelection && (this->m_selectionEnd != InvalidSelection) && (this->m_selectionStart != InvalidSelection)) { + // Make sure simply clicking on a byte at the edge of the screen won't cause scrolling + if ((ImGui::IsMouseDown(ImGuiMouseButton_Left) && this->m_selectionStart != this->m_selectionEnd) || (!ImGui::IsMouseDown(ImGuiMouseButton_Left))) { + auto fractionPerLine = 1.0 / this->m_visibleRowCount; + + if (y == clipper.DisplayStart + 2) { + if (i128(this->m_selectionEnd - provider->getBaseAddress() - provider->getCurrentPageAddress()) <= (i64(clipper.DisplayStart + 2) * this->m_bytesPerRow)) { + this->m_shouldScrollToSelection = false; + ImGui::SetScrollHereY(fractionPerLine * 4); + + } + } else if (y == (clipper.DisplayEnd - 2)) { + if (i128(this->m_selectionEnd - provider->getBaseAddress() - provider->getCurrentPageAddress()) >= (i64(clipper.DisplayEnd - 2) * this->m_bytesPerRow)) { + this->m_shouldScrollToSelection = false; + ImGui::SetScrollHereY(fractionPerLine * (this->m_visibleRowCount - 1)); + } + } + } + + // If the cursor is off-screen, directly jump to the byte + { + const auto newSelection = this->getSelection(); + if (newSelection.getStartAddress() < u64(clipper.DisplayStart * this->m_bytesPerRow)) + this->jumpToSelection(); + if (newSelection.getEndAddress() > u64(clipper.DisplayEnd * this->m_bytesPerRow)) + this->jumpToSelection(); + + } + } + } + } + } else { + ImGui::TextFormattedCentered("hex.builtin.view.hex_editor.no_bytes"_lang); + } + + // Handle jumping to selection + if (this->m_shouldJumpToSelection) { + this->m_shouldJumpToSelection = false; + ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + (float(this->m_selectionStart) / this->m_bytesPerRow) * CharacterSize.y, 0.5); + } + + ImGui::EndTable(); + } + ImGui::PopStyleVar(); + } + + void ViewHexEditor::drawFooter(const ImVec2 &size) { + if (ImHexApi::Provider::isValid()) { + auto provider = ImHexApi::Provider::get(); + const auto pageCount = provider->getPageCount(); + constexpr static u32 MinPage = 1; + + const auto windowEndPos = ImGui::GetWindowPos() + ImGui::GetWindowSize() - ImGui::GetStyle().WindowPadding; + ImGui::GetWindowDrawList()->AddLine(windowEndPos - ImVec2(0, size.y - 1_scaled), windowEndPos - size + ImVec2(0, 1_scaled), ImGui::GetColorU32(ImGuiCol_Separator), 2.0_scaled); + + if (ImGui::BeginChild("##footer", size, false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { + if (ImGui::BeginTable("##footer_table", 2)) { + ImGui::TableNextRow(); + + // Page slider + ImGui::TableNextColumn(); + { + u32 page = provider->getCurrentPage() + 1; + + ImGui::TextFormatted("{}: ", "hex.builtin.view.hex_editor.page"_lang); + ImGui::SameLine(); + + ImGui::BeginDisabled(pageCount <= 1); + { + if (ImGui::SliderScalar("##page_selection", ImGuiDataType_U32, &page, &MinPage, &pageCount, hex::format("%d / {}", pageCount).c_str())) + provider->setCurrentPage(page - 1); + } + ImGui::EndDisabled(); + } + + // Page Address + ImGui::TableNextColumn(); + { + ImGui::TextFormatted("{0}: 0x{1:08X} - 0x{2:08X} ({1} - {2})", "hex.builtin.view.hex_editor.region"_lang, provider->getCurrentPageAddress(), provider->getSize()); + } + + ImGui::TableNextRow(); + + // Selection + ImGui::TableNextColumn(); + { + auto selection = this->getSelection(); + std::string value; + if (this->isSelectionValid()) + value = hex::format("0x{0:08X} - 0x{1:08X} ({0} - {1})", selection.getStartAddress(), selection.getEndAddress()); + else + value = std::string("hex.builtin.view.hex_editor.selection.none"_lang); + + ImGui::TextFormatted("{0}: {1}", "hex.builtin.view.hex_editor.selection"_lang, value); + } + + // Loaded data size + ImGui::TableNextColumn(); + { + ImGui::TextFormatted("{0}: 0x{1:08X} ({2})", "hex.builtin.view.hex_editor.data_size"_lang, provider->getActualSize(), hex::toByteString(provider->getActualSize())); + } + + ImGui::EndTable(); + } + + ImGui::EndChild(); + } + } + } + + void ViewHexEditor::handleSelection(u64 address, u32 bytesPerCell, const u8 *data, bool cellHovered) { + if (ImGui::IsWindowHovered() && cellHovered) { + drawTooltip(address, data, bytesPerCell); + + auto endAddress = address + bytesPerCell - 1; + + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { + this->setSelection(this->m_selectionStart, endAddress); + this->scrollToSelection(); + } + else if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) { + if (ImGui::GetIO().KeyShift) + this->setSelection(this->m_selectionStart, endAddress); + else + this->setSelection(address, endAddress); + + this->scrollToSelection(); + } + } } void ViewHexEditor::drawContent() { - auto provider = ImHexApi::Provider::get(); - size_t dataSize = (!ImHexApi::Provider::isValid() || !provider->isReadable()) ? 0x00 : provider->getSize(); + if (ImGui::Begin(View::toWindowName(this->getUnlocalizedName()).c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { + const auto FooterSize = ImVec2(ImGui::GetContentRegionAvailWidth(), ImGui::GetTextLineHeightWithSpacing() * 3); + const auto TableSize = ImGui::GetContentRegionAvail() - ImVec2(0, FooterSize.y); - this->m_memoryEditor.DrawWindow(View::toWindowName("hex.builtin.view.hex_editor.name").c_str(), &this->getWindowOpenState(), this, dataSize, dataSize == 0 ? 0x00 : provider->getBaseAddress() + provider->getCurrentPageAddress()); - - if (dataSize != 0x00) { - this->m_memoryEditor.OptShowAdvancedDecoding = this->m_advancedDecodingEnabled && this->m_currEncodingFile.valid(); - - if (ImGui::Begin(View::toWindowName("hex.builtin.view.hex_editor.name").c_str())) { - - if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) - ImGui::OpenPopup("hex.builtin.menu.edit"_lang); - - if (ImGui::BeginPopup("hex.builtin.menu.edit"_lang)) { - this->drawEditPopup(); - ImGui::EndPopup(); - } - - if (provider->getPageCount() > 1) { - ImGui::NewLine(); - - auto linePos = ImGui::GetCursorPosY() - 15_scaled; - - ImGui::SetCursorPosY(linePos); - - if (ImGui::ArrowButton("prevPage", ImGuiDir_Left)) { - provider->setCurrentPage(provider->getCurrentPage() - 1); - - EventManager::post(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(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.hex_editor.page"_lang, provider->getCurrentPage() + 1, provider->getPageCount()); - } - - this->drawSearchPopup(); - this->drawGotoPopup(); - } - ImGui::End(); + this->drawPopup(); + this->drawEditor(TableSize); + this->drawFooter(FooterSize); } + ImGui::End(); + + this->m_selectionChanged = false; } static void save() { @@ -274,154 +981,11 @@ namespace hex::plugin::builtin { }); } - void ViewHexEditor::drawAlwaysVisible() { + static void copyBytes(const Region &selection) { auto provider = ImHexApi::Provider::get(); - if (ImGui::BeginPopupModal("hex.builtin.view.hex_editor.exit_application.title"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::NewLine(); - ImGui::TextUnformatted("hex.builtin.view.hex_editor.exit_application.desc"_lang); - ImGui::NewLine(); - - confirmButtons( - "hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [] { ImHexApi::Common::closeImHex(true); }, [] { ImGui::CloseCurrentPopup(); }); - - if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape))) - ImGui::CloseCurrentPopup(); - - ImGui::EndPopup(); - } - - if (ImGui::BeginPopupModal("hex.builtin.view.hex_editor.script.title"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::SetCursorPosX(10); - ImGui::TextFormattedWrapped("{}", static_cast("hex.builtin.view.hex_editor.script.desc"_lang)); - - ImGui::NewLine(); - ImGui::InputText("##nolabel", this->m_loaderScriptScriptPath.data(), this->m_loaderScriptScriptPath.length(), ImGuiInputTextFlags_ReadOnly); - ImGui::SameLine(); - if (ImGui::Button("hex.builtin.view.hex_editor.script.script"_lang)) { - fs::openFileBrowser(fs::DialogMode::Open, { {"Python Script", "py"} }, - [this](const auto &path) { - this->m_loaderScriptScriptPath = path.string(); - }); - } - ImGui::InputText("##nolabel", this->m_loaderScriptFilePath.data(), this->m_loaderScriptFilePath.length(), ImGuiInputTextFlags_ReadOnly); - ImGui::SameLine(); - if (ImGui::Button("hex.builtin.view.hex_editor.script.file"_lang)) { - fs::openFileBrowser(fs::DialogMode::Open, {}, [this](const auto &path) { - this->m_loaderScriptFilePath = path.string(); - }); - } - if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape))) - ImGui::CloseCurrentPopup(); - - ImGui::NewLine(); - - confirmButtons( - "hex.builtin.common.load"_lang, "hex.builtin.common.cancel"_lang, [this, &provider] { - if (!this->m_loaderScriptScriptPath.empty() && !this->m_loaderScriptFilePath.empty()) { - EventManager::post(this->m_loaderScriptFilePath); - LoaderScript::setFilePath(this->m_loaderScriptFilePath); - LoaderScript::setDataProvider(provider); - LoaderScript::processFile(this->m_loaderScriptScriptPath); - ImGui::CloseCurrentPopup(); - } }, [] { ImGui::CloseCurrentPopup(); }); - - ImGui::EndPopup(); - } - - if (ImGui::BeginPopupModal("hex.builtin.view.hex_editor.menu.edit.set_base"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::InputHexadecimal("hex.builtin.common.address"_lang, &this->m_baseAddress, ImGuiInputTextFlags_CharsHexadecimal); - ImGui::NewLine(); - - confirmButtons( - "hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, [this, &provider] { - provider->setBaseAddress(this->m_baseAddress); - ImGui::CloseCurrentPopup(); }, [] { ImGui::CloseCurrentPopup(); }); - - if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape))) - ImGui::CloseCurrentPopup(); - - ImGui::EndPopup(); - } - - if (ImGui::BeginPopupModal("hex.builtin.view.hex_editor.menu.edit.resize"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::InputHexadecimal("hex.builtin.common.size"_lang, &this->m_resizeSize, ImGuiInputTextFlags_CharsHexadecimal); - ImGui::NewLine(); - - confirmButtons( - "hex.builtin.common.set"_lang, "hex.builtin.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(); - } - - if (ImGui::BeginPopupModal("hex.builtin.view.hex_editor.menu.edit.insert"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::InputHexadecimal("hex.builtin.common.size"_lang, &this->m_resizeSize, ImGuiInputTextFlags_CharsHexadecimal); - ImGui::NewLine(); - - confirmButtons( - "hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang, [this, &provider] { - provider->insert(std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd), this->m_resizeSize); - ImGui::CloseCurrentPopup(); }, [] { ImGui::CloseCurrentPopup(); }); - - if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape))) - ImGui::CloseCurrentPopup(); - - ImGui::EndPopup(); - } - } - - void ViewHexEditor::openFile(const std::fs::path &path) { - hex::prv::Provider *provider = nullptr; - EventManager::post("hex.builtin.provider.file", &provider); - - if (auto fileProvider = dynamic_cast(provider)) { - fileProvider->setPath(path); - if (!fileProvider->open()) { - View::showErrorPopup("hex.builtin.view.hex_editor.error.open"_lang); - ImHexApi::Provider::remove(provider); - - return; - } - } - - if (!provider->isWritable()) { - this->m_memoryEditor.ReadOnly = true; - View::showErrorPopup("hex.builtin.view.hex_editor.error.read_only"_lang); - } else { - this->m_memoryEditor.ReadOnly = false; - } - - if (!provider->isAvailable()) { - View::showErrorPopup("hex.builtin.view.hex_editor.error.open"_lang); - ImHexApi::Provider::remove(provider); - - return; - } - - ProjectFile::setFilePath(path); - - this->getWindowOpenState() = true; - - EventManager::post(path); - EventManager::post(); - EventManager::post(); - } - - 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; - - std::vector buffer(copySize, 0x00); - provider->read(start + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), buffer.size()); + std::vector buffer(selection.size, 0x00); + provider->read(selection.getStartAddress() + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), buffer.size()); std::string str; for (const auto &byte : buffer) @@ -431,12 +995,9 @@ namespace hex::plugin::builtin { ImGui::SetClipboardText(str.c_str()); } - void ViewHexEditor::pasteBytes() const { + static void pasteBytes(const Region &selection) { 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); - std::string clipboard = ImGui::GetClipboardText(); // Check for non-hex characters @@ -453,621 +1014,230 @@ namespace hex::plugin::builtin { if (clipboard.length() % 2 != 0) return; // Convert hex string to bytes - std::vector 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; - - stringIndex++; - } - } + std::vector buffer = hex::decodeByteString(clipboard); // Write bytes - provider->write(start + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), std::min(end - start + 1, buffer.size())); + provider->write(selection.getStartAddress() + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), std::min(selection.size, buffer.size())); } - void ViewHexEditor::copyString() const { + static void copyString(const Region &selection) { 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; - - std::string buffer(copySize, 0x00); - buffer.reserve(copySize); - provider->read(start + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), copySize); + std::string buffer(selection.size, 0x00); + buffer.reserve(selection.size); + provider->read(selection.getStartAddress() + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), selection.size); ImGui::SetClipboardText(buffer.c_str()); } - static std::vector> findString(hex::prv::Provider *&provider, std::string string) { - std::vector> results; - - if (string.empty()) - return {}; - - u32 foundCharacters = 0; - - std::vector buffer(1024, 0x00); - size_t dataSize = provider->getSize(); - for (u64 offset = 0; offset < dataSize; offset += 1024) { - size_t usedBufferSize = std::min(buffer.size(), dataSize - offset); - provider->read(offset + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), usedBufferSize); - - 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); - foundCharacters = 0; - } - } - } - - return results; - } - - static std::vector> findHex(hex::prv::Provider *&provider, std::string string) { - std::vector> results; - - if ((string.size() % 2) == 1) - string = "0" + string; - - if (string.empty()) - return {}; - - std::vector 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 buffer(1024, 0x00); - size_t dataSize = provider->getSize(); - for (u64 offset = 0; offset < dataSize; offset += 1024) { - size_t usedBufferSize = std::min(buffer.size(), dataSize - offset); - provider->read(offset + provider->getBaseAddress() + provider->getCurrentPageAddress(), buffer.data(), usedBufferSize); - - 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); - foundCharacters = 0; - } - } - } - - return results; - } - - void ViewHexEditor::drawSearchInput(std::vector *currBuffer, ImGuiInputTextFlags flags) { - if (this->m_searchRequested) { - ImGui::SetKeyboardFocusHere(); - this->m_searchRequested = false; - } - - if (ImGui::InputText("##nolabel", currBuffer->data(), currBuffer->size(), flags, ViewHexEditor::inputCallback, this)) { - this->m_searchRequested = true; - if (this->m_lastSearchBuffer == nullptr || this->m_lastSearchBuffer->empty()) - performSearch(currBuffer->data()); - else - performSearchNext(); - } - - ImGui::EndTabItem(); - } - - void ViewHexEditor::performSearch(const char *buffer) { - auto provider = ImHexApi::Provider::get(); - - *this->m_lastSearchBuffer = this->m_searchFunction(provider, buffer); - this->m_lastSearchIndex = 0; - - if (!this->m_lastSearchBuffer->empty()) - this->m_memoryEditor.GotoAddrAndSelect((*this->m_lastSearchBuffer)[0].first, (*this->m_lastSearchBuffer)[0].second); - } - - void ViewHexEditor::performSearchNext() { - if (this->m_lastSearchBuffer->empty()) { - return; - } - - ++this->m_lastSearchIndex %= this->m_lastSearchBuffer->size(); - this->m_memoryEditor.GotoAddrAndSelect((*this->m_lastSearchBuffer)[this->m_lastSearchIndex].first, - (*this->m_lastSearchBuffer)[this->m_lastSearchIndex].second); - } - - void ViewHexEditor::performSearchPrevious() { - if (this->m_lastSearchBuffer->empty()) { - return; - } - - this->m_lastSearchIndex--; - - 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, - (*this->m_lastSearchBuffer)[this->m_lastSearchIndex].second); - } - - int ViewHexEditor::inputCallback(ImGuiInputTextCallbackData *data) { - auto _this = static_cast(data->UserData); - auto provider = ImHexApi::Provider::get(); - - *_this->m_lastSearchBuffer = _this->m_searchFunction(provider, data->Buf); - _this->m_lastSearchIndex = 0; - - if (!_this->m_lastSearchBuffer->empty()) - _this->m_memoryEditor.GotoAddrAndSelect((*_this->m_lastSearchBuffer)[0].first, (*_this->m_lastSearchBuffer)[0].second); - - return 0; - } - - void ViewHexEditor::drawSearchPopup() { - ImGui::SetNextWindowPos(ImGui::GetWindowPos() + ImGui::GetWindowContentRegionMin() - ImGui::GetStyle().WindowPadding); - if (ImGui::BeginPopup("hex.builtin.view.hex_editor.menu.file.search"_lang)) { - if (ImGui::BeginTabBar("searchTabs")) { - std::vector *currBuffer = nullptr; - if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.search.string"_lang)) { - this->m_searchFunction = findString; - this->m_lastSearchBuffer = &this->m_lastStringSearch; - currBuffer = &this->m_searchStringBuffer; - - drawSearchInput(currBuffer, ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_EnterReturnsTrue); - } - - if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.search.hex"_lang)) { - this->m_searchFunction = findHex; - this->m_lastSearchBuffer = &this->m_lastHexSearch; - currBuffer = &this->m_searchHexBuffer; - - drawSearchInput(currBuffer, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_EnterReturnsTrue); - } - - if (currBuffer != nullptr) { - if (ImGui::Button("hex.builtin.view.hex_editor.search.find"_lang)) - performSearch(currBuffer->data()); - - if (!this->m_lastSearchBuffer->empty()) { - if ((ImGui::Button("hex.builtin.view.hex_editor.search.find_next"_lang))) - performSearchNext(); - - ImGui::SameLine(); - - if ((ImGui::Button("hex.builtin.view.hex_editor.search.find_prev"_lang))) - performSearchPrevious(); - } - } - - ImGui::EndTabBar(); - } - - if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape))) - ImGui::CloseCurrentPopup(); - - ImGui::EndPopup(); - } - } - - 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); - if (ImGui::BeginPopup("hex.builtin.view.hex_editor.menu.file.goto"_lang)) { - bool runGoto = this->m_evaluateGoto; - this->m_evaluateGoto = false; - - if (ImGui::BeginTabBar("gotoTabs")) { - u64 newOffset = 0; - if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.absolute"_lang)) { - if (this->m_gotoRequested) { - ImGui::SetKeyboardFocusHere(); - this->m_gotoRequested = false; - } - - runGoto = ImGui::InputText("##goto", this->m_gotoAddressInput.data(), this->m_gotoAddressInput.size(), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &this->m_gotoAddressInput) || runGoto; - if (runGoto) { - MathEvaluator evaluator; - auto result = evaluator.evaluate(this->m_gotoAddressInput); - if (result) { - if (*result < baseAddress || *result > baseAddress + dataSize) - newOffset = baseAddress; - else - newOffset = *result; - } else { - runGoto = false; - } - } - - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.begin"_lang)) { - if (this->m_gotoRequested) { - ImGui::SetKeyboardFocusHere(); - this->m_gotoRequested = false; - } - runGoto = ImGui::InputText("##goto", this->m_gotoAddressInput.data(), this->m_gotoAddressInput.size(), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &this->m_gotoAddressInput) || runGoto; - if (runGoto) { - MathEvaluator evaluator; - auto result = evaluator.evaluate(this->m_gotoAddressInput); - if (result) { - if (*result < 0 || *result > dataSize) - newOffset = 0; - else - newOffset = *result; - } else { - runGoto = false; - } - } - - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.current"_lang)) { - ImGui::SetKeyboardFocusHere(); - runGoto = ImGui::InputText("##goto", this->m_gotoAddressInput.data(), this->m_gotoAddressInput.size(), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &this->m_gotoAddressInput) || runGoto; - if (runGoto) { - MathEvaluator evaluator; - auto result = evaluator.evaluate(this->m_gotoAddressInput); - if (result) { - i64 currSelectionOffset = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd); - - newOffset = 0; - if (currSelectionOffset + *result < 0) - newOffset = -currSelectionOffset; - else if (currSelectionOffset + *result > dataSize) - newOffset = dataSize - currSelectionOffset; - else - newOffset = *result; - - newOffset = currSelectionOffset + *result + baseAddress; - } else { - runGoto = false; - } - } - - - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.end"_lang)) { - ImGui::SetKeyboardFocusHere(); - runGoto = ImGui::InputText("##goto", this->m_gotoAddressInput.data(), this->m_gotoAddressInput.size(), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &this->m_gotoAddressInput) || runGoto; - if (runGoto) { - MathEvaluator evaluator; - auto result = evaluator.evaluate(this->m_gotoAddressInput); - if (result) { - if (*result < 0 || *result > dataSize) - newOffset = 0; - - newOffset = (baseAddress + dataSize) - *result - 1; - } else { - runGoto = false; - } - } - - ImGui::EndTabItem(); - } - - if (ImGui::Button("hex.builtin.view.hex_editor.menu.file.goto"_lang)) { - this->m_evaluateGoto = true; - } - - if (runGoto) { - this->m_gotoRequested = true; - provider->setCurrentPage(std::floor(double(newOffset - baseAddress) / hex::prv::Provider::PageSize)); - ImHexApi::HexEditor::setSelection(newOffset, 1); - } - - ImGui::EndTabBar(); - } - - if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape))) - ImGui::CloseCurrentPopup(); - - ImGui::EndPopup(); - } - } - - void ViewHexEditor::drawEditPopup() { - auto provider = ImHexApi::Provider::get(); - bool providerValid = ImHexApi::Provider::isValid(); - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.undo"_lang, "CTRL + Z", false, providerValid && provider->canUndo())) - provider->undo(); - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.redo"_lang, "CTRL + Y", false, providerValid && provider->canRedo())) - provider->redo(); - - ImGui::Separator(); - - bool bytesSelected = this->m_memoryEditor.DataPreviewAddr != static_cast(-1) && this->m_memoryEditor.DataPreviewAddrEnd != static_cast(-1); - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.copy"_lang, "CTRL + C", false, bytesSelected)) - this->copyBytes(); - - if (ImGui::BeginMenu("hex.builtin.view.hex_editor.menu.edit.copy_as"_lang, bytesSelected)) { - if (ImGui::MenuItem("hex.builtin.view.hex_editor.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(); - } - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.paste"_lang, "CTRL + V", false, bytesSelected)) - this->pasteBytes(); - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.select_all"_lang, "CTRL + A", false, providerValid)) - ImHexApi::HexEditor::setSelection(provider->getBaseAddress(), provider->getActualSize()); - - ImGui::Separator(); - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.bookmark"_lang, nullptr, false, - this->m_memoryEditor.DataPreviewAddr != static_cast(-1) && this->m_memoryEditor.DataPreviewAddrEnd != static_cast(-1))) - { - auto base = provider->getBaseAddress(); - - 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); - - ImHexApi::Bookmarks::add(start, end - start + 1, {}, {}); - } - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.set_base"_lang, nullptr, false, providerValid && provider->isReadable())) { - this->m_baseAddress = provider->getBaseAddress(); - ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.view.hex_editor.menu.edit.set_base"_lang); }); - } - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.resize"_lang, nullptr, false, providerValid && provider->isResizable())) { - ImHexApi::Tasks::doLater([this] { - this->m_resizeSize = ImHexApi::Provider::get()->getActualSize(); - ImGui::OpenPopup("hex.builtin.view.hex_editor.menu.edit.resize"_lang); - }); - } - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.insert"_lang, nullptr, false, providerValid && provider->isResizable())) { - ImHexApi::Tasks::doLater([this] { - this->m_resizeSize = 0; - ImGui::OpenPopup("hex.builtin.view.hex_editor.menu.edit.insert"_lang); - }); - } - } - - void ViewHexEditor::registerEvents() { - EventManager::subscribe(this, [this](const auto &path) { - this->openFile(path); - this->getWindowOpenState() = true; - }); - - EventManager::subscribe(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(Region { this->m_memoryEditor.DataPreviewAddr, (this->m_memoryEditor.DataPreviewAddrEnd - this->m_memoryEditor.DataPreviewAddr) + 1 }); - }); - - EventManager::subscribe(this, []() { - EventManager::post(ProjectFile::getFilePath()); - }); - - EventManager::subscribe(this, [](GLFWwindow *window) { - if (ProjectFile::hasUnsavedChanges()) { - glfwSetWindowShouldClose(window, GLFW_FALSE); - ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.view.hex_editor.exit_application.title"_lang); }); - } - }); - - EventManager::subscribe(this, [this](const std::string &name) { - if (name == "Create File") { - fs::openFileBrowser(fs::DialogMode::Save, {}, [this](const auto &path) { - fs::File file(path, fs::File::Mode::Create); - - if (!file.isValid()) { - View::showErrorPopup("hex.builtin.view.hex_editor.error.create"_lang); - return; - } - - file.setSize(1); - - EventManager::post(path); - this->getWindowOpenState() = true; - }); - } else if (name == "Open File") { - fs::openFileBrowser(fs::DialogMode::Open, {}, [this](const auto &path) { - EventManager::post(path); - this->getWindowOpenState() = true; - }); - } else if (name == "Open Project") { - fs::openFileBrowser(fs::DialogMode::Open, { {"Project File", "hexproj"} }, - [this](const auto &path) { - ProjectFile::load(path); - this->getWindowOpenState() = true; - }); - } - }); - - EventManager::subscribe(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(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(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(ascii); - } - - { - auto advancedDecoding = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.advanced_decoding"); - - if (advancedDecoding.is_number()) - this->m_advancedDecodingEnabled = static_cast(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(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(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(showExtraInfo); - } - }); - - EventManager::subscribe(this, [this](auto ®ion) { - u64 address = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd); - size_t size = std::abs(i64(this->m_memoryEditor.DataPreviewAddrEnd) - i64(this->m_memoryEditor.DataPreviewAddr)) + 1; - - region = Region { address, size }; - }); - - EventManager::subscribe(this, [this] { - this->m_highlights.clear(); - }); - - EventManager::subscribe(this, [](auto, auto) { - EventManager::post(); - }); - } - void ViewHexEditor::registerShortcuts() { - ShortcutManager::addGlobalShortcut(CTRL + Keys::S, [] { + + // Save operations + ShortcutManager::addShortcut(this, CTRL + Keys::S, [] { save(); }); - - ShortcutManager::addGlobalShortcut(CTRL + SHIFT + Keys::S, [] { + ShortcutManager::addShortcut(this, CTRL + SHIFT + Keys::S, [] { saveAs(); }); - - ShortcutManager::addShortcut(this, CTRL + Keys::Z, [] { + // Select All + ShortcutManager::addShortcut(this, CTRL + Keys::A, [this] { if (ImHexApi::Provider::isValid()) - ImHexApi::Provider::get()->undo(); + this->setSelection(size_t(0), ImHexApi::Provider::get()->getActualSize()); }); - ShortcutManager::addShortcut(this, CTRL + Keys::Y, [] { - if (ImHexApi::Provider::isValid()) - ImHexApi::Provider::get()->redo(); + // Remove selection + ShortcutManager::addShortcut(this, Keys::Escape, [this] { + this->m_selectionStart = InvalidSelection; + this->m_selectionEnd = InvalidSelection; + EventManager::post(this->getSelection()); }); + // Move cursor around + ShortcutManager::addShortcut(this, Keys::Up, [this] { + if (this->m_selectionEnd >= this->m_bytesPerRow) { + auto pos = this->m_selectionEnd - this->m_bytesPerRow; + this->setSelection(pos, pos); + this->scrollToSelection(); + } + }); + ShortcutManager::addShortcut(this, Keys::Down, [this] { + auto pos = this->m_selectionEnd + this->m_bytesPerRow; + this->setSelection(pos, pos); + this->scrollToSelection(); + }); + ShortcutManager::addShortcut(this, Keys::Left, [this] { + if (this->m_selectionEnd > 0) { + auto pos = this->m_selectionEnd - 1; + this->setSelection(pos, pos); + this->scrollToSelection(); + } + }); + ShortcutManager::addShortcut(this, Keys::Right, [this] { + auto pos = this->m_selectionEnd + 1; + this->setSelection(pos, pos); + this->scrollToSelection(); + }); - ShortcutManager::addShortcut(this, CTRL + Keys::F, [this] { - this->m_searchRequested = true; - ImGui::OpenPopupInWindow(View::toWindowName("hex.builtin.view.hex_editor.name").c_str(), - "hex.builtin.view.hex_editor.menu.file.search"_lang); + ShortcutManager::addShortcut(this, Keys::PageUp, [this] { + u64 visibleByteCount = this->m_bytesPerRow * this->m_visibleRowCount; + if (this->m_selectionEnd >= visibleByteCount) { + auto pos = this->m_selectionEnd - visibleByteCount; + this->setSelection(pos, pos); + this->scrollToSelection(); + } + }); + ShortcutManager::addShortcut(this, Keys::PageDown, [this] { + auto pos = this->m_selectionEnd + (this->m_bytesPerRow * this->m_visibleRowCount); + this->setSelection(pos, pos); + this->scrollToSelection(); + }); + + // Move selection around + ShortcutManager::addShortcut(this, SHIFT + Keys::Up, [this] { + this->setSelection(this->m_selectionEnd - this->m_bytesPerRow, this->m_selectionEnd); + this->scrollToSelection(); + }); + ShortcutManager::addShortcut(this, SHIFT + Keys::Down, [this] { + this->setSelection(this->m_selectionEnd + this->m_bytesPerRow, this->m_selectionEnd); + this->scrollToSelection(); + + }); + ShortcutManager::addShortcut(this, SHIFT + Keys::Left, [this] { + this->setSelection(this->m_selectionEnd - 1, this->m_selectionEnd); + this->scrollToSelection(); + + }); + ShortcutManager::addShortcut(this, SHIFT + Keys::Right, [this] { + this->setSelection(this->m_selectionEnd + 1, this->m_selectionEnd); + this->scrollToSelection(); }); ShortcutManager::addShortcut(this, CTRL + Keys::G, [this] { - this->m_gotoRequested = true; - ImGui::OpenPopupInWindow(View::toWindowName("hex.builtin.view.hex_editor.name").c_str(), - "hex.builtin.view.hex_editor.menu.file.goto"_lang); + if (!ImHexApi::Provider::isValid()) return; + + this->openPopup(); + }); + ShortcutManager::addShortcut(this, CTRL + Keys::F, [this] { + if (!ImHexApi::Provider::isValid()) return; + + this->openPopup(); }); + // Copy + ShortcutManager::addShortcut(this, CTRL + Keys::C, [this] { + const auto selection = this->getSelection(); + copyBytes(selection); + }); + ShortcutManager::addShortcut(this, CTRL + SHIFT + Keys::C, [this] { + const auto selection = this->getSelection(); + copyString(selection); + }); + + // Paste + ShortcutManager::addShortcut(this, CTRL + Keys::V, [this] { + const auto selection = this->getSelection(); + pasteBytes(selection); + }); + + // Open file ShortcutManager::addShortcut(this, CTRL + Keys::O, [] { fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) { EventManager::post(path); }); }); + // Undo / Redo + 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::C, [this] { - this->copyBytes(); + void ViewHexEditor::registerEvents() { + EventManager::subscribe(this, [this](auto) { + this->m_shouldModifyValue = true; }); - 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, [] { + EventManager::subscribe(this, [this](Region region) { auto provider = ImHexApi::Provider::get(); - ImHexApi::HexEditor::setSelection(provider->getBaseAddress(), provider->getActualSize()); + auto page = provider->getPageOfAddress(region.getStartAddress()); + + if (!page.has_value()) + return; + + if (region.size != 0) { + provider->setCurrentPage(page.value()); + region.address -= provider->getBaseAddress() + provider->getCurrentPageAddress(); + this->setSelection(region); + this->jumpToSelection(); + } + }); + + EventManager::subscribe(this, [this](auto ®ion) { + if (this->isSelectionValid()) + region = this->getSelection(); + }); + + EventManager::subscribe(this, [this] { + { + auto bytesPerRow = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.bytes_per_row"); + + if (bytesPerRow.is_number()) + this->m_bytesPerRow = static_cast(bytesPerRow); + } + + { + auto ascii = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.ascii"); + + if (ascii.is_number()) + this->m_showAscii = static_cast(ascii); + } + + { + auto greyOutZeros = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.grey_zeros"); + + if (greyOutZeros.is_number()) + this->m_grayOutZero = static_cast(greyOutZeros); + } + + { + auto upperCaseHex = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.uppercase_hex"); + + if (upperCaseHex.is_number()) + this->m_upperCaseHex = static_cast(upperCaseHex); + } + + { + auto selectionColor = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.highlight_color"); + + if (selectionColor.is_number()) + this->m_selectionColor = static_cast(selectionColor); + } + + { + auto &visualizers = ContentRegistry::HexEditor::impl::getVisualizers(); + auto selectedVisualizer = ContentRegistry::Settings::getSetting("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.visualizer"); + + if (selectedVisualizer.is_string() && visualizers.contains(selectedVisualizer)) + this->m_currDataVisualizer = visualizers[selectedVisualizer]; + else + this->m_currDataVisualizer = visualizers["hex.builtin.visualizer.hexadecimal.8bit"]; + } + }); } void ViewHexEditor::registerMenuItems() { - - /* Basic operations */ - + // Basic operations ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1100, [&] { auto provider = ImHexApi::Provider::get(); bool providerValid = ImHexApi::Provider::isValid(); - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.save"_lang, "CTRL + S", false, providerValid && provider->isWritable())) { save(); } @@ -1075,47 +1245,13 @@ namespace hex::plugin::builtin { if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.save_as"_lang, "CTRL + SHIFT + S", false, providerValid && provider->isWritable())) { saveAs(); } - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.close"_lang, "", false, providerValid)) { - EventManager::post(); - ImHexApi::Provider::remove(ImHexApi::Provider::get()); - providerValid = false; - } - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.quit"_lang, "", false)) { - ImHexApi::Common::closeImHex(); - } }); - /* Metadata save/load */ + // Metadata save/load ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1200, [&, this] { - auto provider = ImHexApi::Provider::get(); bool providerValid = ImHexApi::Provider::isValid(); - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.open_project"_lang, "")) { - fs::openFileBrowser(fs::DialogMode::Open, { {"Project File", "hexproj"} - }, - [](const auto &path) { - ProjectFile::load(path); - }); - } - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.save_project"_lang, "", false, providerValid && provider->isWritable())) { - if (ProjectFile::getProjectFilePath() == "") { - fs::openFileBrowser(fs::DialogMode::Save, { {"Project File", "hexproj"} - }, - [](std::fs::path path) { - if (path.extension() != ".hexproj") { - path.replace_extension(".hexproj"); - } - - ProjectFile::store(path); - }); - } else - ProjectFile::store(); - } - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.load_encoding_file"_lang, nullptr, false, providerValid)) { std::vector paths; for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Encodings)) { @@ -1129,197 +1265,79 @@ namespace hex::plugin::builtin { View::showFileChooserPopup(paths, { {"Thingy Table File", "tbl"} }, [this](const auto &path) { - this->m_currEncodingFile = EncodingFile(EncodingFile::Type::Thingy, path); + this->m_currCustomEncoding = EncodingFile(EncodingFile::Type::Thingy, path); }); } }); - /* Import / Export */ - ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1300, [&, this] { - auto provider = ImHexApi::Provider::get(); - bool providerValid = ImHexApi::Provider::isValid(); - - /* Import */ - if (ImGui::BeginMenu("hex.builtin.view.hex_editor.menu.file.import"_lang)) { - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.import.base64"_lang)) { - - fs::openFileBrowser(fs::DialogMode::Open, {}, [this](const auto &path) { - fs::File file(path, fs::File::Mode::Read); - if (!file.isValid()) { - View::showErrorPopup("hex.builtin.view.hex_editor.error.open"_lang); - return; - } - - auto base64 = file.readBytes(); - - if (!base64.empty()) { - this->m_dataToSave = crypt::decode64(base64); - - if (this->m_dataToSave.empty()) - View::showErrorPopup("hex.builtin.view.hex_editor.base64.import_error"_lang); - else - ImGui::OpenPopup("hex.builtin.view.hex_editor.save_data"_lang); - this->getWindowOpenState() = true; - } else View::showErrorPopup("hex.builtin.view.hex_editor.file_open_error"_lang); - }); - } - - ImGui::Separator(); - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.import.ips"_lang, nullptr, false, !this->m_processingImportExport)) { - - fs::openFileBrowser(fs::DialogMode::Open, {}, [this](const auto &path) { - this->m_processingImportExport = true; - std::thread([this, path] { - auto task = ImHexApi::Tasks::createTask("hex.builtin.view.hex_editor.processing", 0); - - auto patchData = fs::File(path, fs::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(); - - this->getWindowOpenState() = true; - }); - } - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.import.ips32"_lang, nullptr, false, !this->m_processingImportExport)) { - fs::openFileBrowser(fs::DialogMode::Open, {}, [this](const auto &path) { - this->m_processingImportExport = true; - std::thread([this, path] { - auto task = ImHexApi::Tasks::createTask("hex.builtin.view.hex_editor.processing", 0); - - auto patchData = fs::File(path, fs::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(); - - this->getWindowOpenState() = true; - }); - } - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.import.script"_lang)) { - this->m_loaderScriptFilePath.clear(); - this->m_loaderScriptScriptPath.clear(); - ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.view.hex_editor.script.title"_lang); }); - } - - ImGui::EndMenu(); - } - - - /* Export */ - if (ImGui::BeginMenu("hex.builtin.view.hex_editor.menu.file.export"_lang, providerValid && provider->isWritable())) { - if (ImGui::MenuItem("hex.builtin.view.hex_editor.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.hex_editor.processing", 0); - - this->m_dataToSave = generateIPSPatch(patches); - this->m_processingImportExport = false; - - ImHexApi::Tasks::doLater([this] { - fs::openFileBrowser(fs::DialogMode::Save, {}, [this](const auto &path) { - auto file = fs::File(path, fs::File::Mode::Create); - if (!file.isValid()) { - View::showErrorPopup("hex.builtin.view.hex_editor.error.create"_lang); - return; - } - - file.write(this->m_dataToSave); - }); - }); - }).detach(); - } - - if (ImGui::MenuItem("hex.builtin.view.hex_editor.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.hex_editor.processing", 0); - - this->m_dataToSave = generateIPS32Patch(patches); - this->m_processingImportExport = false; - - ImHexApi::Tasks::doLater([this] { - fs::openFileBrowser(fs::DialogMode::Save, {}, [this](const auto &path) { - auto file = fs::File(path, fs::File::Mode::Create); - if (!file.isValid()) { - View::showErrorPopup("hex.builtin.view.hex_editor.error.create"_lang); - return; - } - - file.write(this->m_dataToSave); - }); - }); - }).detach(); - } - - ImGui::EndMenu(); - } - }); - - - /* Search / Goto */ + // Search / Goto ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1400, [&, this] { bool providerValid = ImHexApi::Provider::isValid(); if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.search"_lang, "CTRL + F", false, providerValid)) { - this->getWindowOpenState() = true; - this->m_searchRequested = true; - ImGui::OpenPopupInWindow(View::toWindowName("hex.builtin.view.hex_editor.name").c_str(), "hex.builtin.view.hex_editor.menu.file.search"_lang); + this->openPopup(); } if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.goto"_lang, "CTRL + G", false, providerValid)) { - this->getWindowOpenState() = true; - this->m_gotoRequested = true; - ImGui::OpenPopupInWindow(View::toWindowName("hex.builtin.view.hex_editor.name").c_str(), "hex.builtin.view.hex_editor.menu.file.goto"_lang); + this->openPopup(); } }); - /* Edit menu */ - ContentRegistry::Interface::addMenuItem("hex.builtin.menu.edit", 1000, [&, this] { - this->drawEditPopup(); + // Copy / Paste + ContentRegistry::Interface::addMenuItem("hex.builtin.menu.edit", 1100, [&] { + auto provider = ImHexApi::Provider::get(); + bool providerValid = ImHexApi::Provider::isValid(); + auto selection = ImHexApi::HexEditor::getSelection(); + + if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.copy"_lang, "CTRL + C", false, selection.has_value())) + copyBytes(*selection); + + if (ImGui::BeginMenu("hex.builtin.view.hex_editor.menu.edit.copy_as"_lang, selection.has_value() && providerValid)) { + if (ImGui::MenuItem("hex.builtin.view.hex_editor.copy.hex"_lang, "CTRL + SHIFT + C")) + copyString(*selection); + + ImGui::Separator(); + + for (const auto &[unlocalizedName, callback] : ContentRegistry::DataFormatter::getEntries()) { + if (ImGui::MenuItem(LangEntry(unlocalizedName))) { + ImGui::SetClipboardText( + callback( + provider, + selection->getStartAddress() + provider->getBaseAddress() + provider->getCurrentPageAddress(), + selection->size + ).c_str() + ); + } + } + + ImGui::EndMenu(); + } + + if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.paste"_lang, "CTRL + V", false, selection.has_value())) + pasteBytes(*selection); + + if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.select_all"_lang, "CTRL + A", false, selection.has_value() && providerValid)) + ImHexApi::HexEditor::setSelection(provider->getBaseAddress(), provider->getActualSize()); + }); + + // Popups + ContentRegistry::Interface::addMenuItem("hex.builtin.menu.edit", 1200, [&] { + auto provider = ImHexApi::Provider::get(); + bool providerValid = ImHexApi::Provider::isValid(); + + if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.set_base"_lang, nullptr, false, providerValid && provider->isReadable())) { + this->openPopup(provider->getBaseAddress()); + } + + if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.resize"_lang, nullptr, false, providerValid && provider->isResizable())) { + this->openPopup(provider->getActualSize()); + } + + if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.insert"_lang, nullptr, false, providerValid && provider->isResizable())) { + this->openPopup(this->getSelection().getStartAddress(), 0x00); + } }); } diff --git a/plugins/builtin/source/content/views/view_information.cpp b/plugins/builtin/source/content/views/view_information.cpp index 247b02153..ed82966c1 100644 --- a/plugins/builtin/source/content/views/view_information.cpp +++ b/plugins/builtin/source/content/views/view_information.cpp @@ -135,11 +135,12 @@ namespace hex::plugin::builtin { ImGui::TextUnformatted("hex.builtin.view.information.control"_lang); ImGui::Separator(); - ImGui::Disabled([this] { + ImGui::BeginDisabled(this->m_analyzing); + { if (ImGui::Button("hex.builtin.view.information.analyze"_lang)) this->analyze(); - }, - this->m_analyzing); + } + ImGui::EndDisabled(); if (this->m_analyzing) { ImGui::TextSpinner("hex.builtin.view.information.analyzing"_lang); diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 89effa06d..7e3409582 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -231,17 +231,44 @@ namespace hex::plugin::builtin { }); - ImHexApi::HexEditor::addHighlightingProvider([](u64 address) -> std::optional { + ImHexApi::HexEditor::addBackgroundHighlightingProvider([](u64 address, const u8 *data, size_t size) -> std::optional { + hex::unused(data, size); + const auto &patterns = ImHexApi::Provider::get()->getPatternLanguageRuntime().getPatterns(); for (const auto &pattern : patterns) { auto child = pattern->getPattern(address); if (child != nullptr) { - return ImHexApi::HexEditor::Highlighting(Region { address, 1 }, child->getColor(), child->getDisplayName()); + return child->getColor(); } } return std::nullopt; }); + + ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8 *data, size_t size) { + hex::unused(data, size); + + auto &patterns = ImHexApi::Provider::get()->getPatternLanguageRuntime().getPatterns(); + for (auto &pattern : patterns) { + auto child = pattern->getPattern(address); + if (child != nullptr) { + ImGui::BeginTooltip(); + + if (ImGui::BeginTable("##tooltips", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_NoClip)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + this->drawPatternTooltip(child); + + ImGui::PushStyleColor(ImGuiCol_TableRowBg, pattern->getColor()); + ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, pattern->getColor()); + ImGui::EndTable(); + ImGui::PopStyleColor(2); + } + ImGui::EndTooltip(); + } + } + }); } ViewPatternEditor::~ViewPatternEditor() { @@ -608,6 +635,68 @@ namespace hex::plugin::builtin { } + void ViewPatternEditor::drawPatternTooltip(pl::Pattern *pattern) { + ImGui::PushID(pattern); + { + ImGui::ColorButton(pattern->getVariableName().c_str(), ImColor(pattern->getColor())); + ImGui::SameLine(0, 10); + ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{}", pattern->getFormattedName()); + ImGui::SameLine(0, 5); + ImGui::TextFormatted("{}", pattern->getDisplayName()); + ImGui::SameLine(); + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + ImGui::SameLine(); + ImGui::TextFormatted("{}", pattern->getFormattedValue()); + + if (ImGui::GetIO().KeyShift) { + ImGui::Indent(); + if (ImGui::BeginTable("##extra_info", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_NoClip)) { + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextFormatted("Type: "); + ImGui::TableNextColumn(); + ImGui::TextFormatted("{}", pattern->getTypeName()); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextFormatted("Address: "); + ImGui::TableNextColumn(); + ImGui::TextFormatted("0x{:08X}", pattern->getOffset()); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextFormatted("Size: "); + ImGui::TableNextColumn(); + ImGui::TextFormatted("{} {}", pattern->getSize(), pattern->getSize() > 1 ? "Bytes" : "Byte"); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextFormatted("Endian: "); + ImGui::TableNextColumn(); + ImGui::TextFormatted("{}", pattern->getEndian() == std::endian::little ? "Little" : "Big"); + + if (auto comment = pattern->getComment(); comment.has_value()) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextFormatted("Comment: "); + ImGui::TableNextColumn(); + ImGui::TextWrapped("\"%s\"", pattern->getComment()->c_str()); + } + + ImGui::EndTable(); + } + ImGui::Unindent(); + } + } + + ImGui::PopID(); + } + + void ViewPatternEditor::loadPatternFile(const std::fs::path &path) { fs::File file(path, fs::File::Mode::Read); if (file.isValid()) { diff --git a/plugins/builtin/source/content/views/view_strings.cpp b/plugins/builtin/source/content/views/view_strings.cpp index 5278b1fae..9dd1d9922 100644 --- a/plugins/builtin/source/content/views/view_strings.cpp +++ b/plugins/builtin/source/content/views/view_strings.cpp @@ -105,7 +105,8 @@ namespace hex::plugin::builtin { if (ImGui::Begin(View::toWindowName("hex.builtin.view.strings.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { if (ImHexApi::Provider::isValid() && provider->isReadable()) { - ImGui::Disabled([this] { + ImGui::BeginDisabled(this->m_searching); + { if (ImGui::InputInt("hex.builtin.view.strings.min_length"_lang, &this->m_minimumLength, 1, 0)) this->m_foundStrings.clear(); @@ -158,8 +159,8 @@ namespace hex::plugin::builtin { if (ImGui::Button("hex.builtin.view.strings.extract"_lang)) this->searchStrings(); - }, - this->m_searching); + } + ImGui::EndDisabled(); if (this->m_searching) { ImGui::SameLine(); diff --git a/plugins/builtin/source/content/views/view_yara.cpp b/plugins/builtin/source/content/views/view_yara.cpp index 56c84d281..45cc25196 100644 --- a/plugins/builtin/source/content/views/view_yara.cpp +++ b/plugins/builtin/source/content/views/view_yara.cpp @@ -44,7 +44,8 @@ namespace hex::plugin::builtin { if (ImGui::Button("hex.builtin.view.yara.reload"_lang)) this->reloadRules(); } else { - ImGui::Disabled([this] { + ImGui::BeginDisabled(this->m_matching); + { if (ImGui::BeginCombo("hex.builtin.view.yara.header.rules"_lang, this->m_rules[this->m_selectedRule].first.c_str())) { for (u32 i = 0; i < this->m_rules.size(); i++) { const bool selected = (this->m_selectedRule == i); @@ -59,8 +60,8 @@ namespace hex::plugin::builtin { ImGui::SameLine(); if (ImGui::Button("hex.builtin.view.yara.reload"_lang)) this->reloadRules(); if (ImGui::Button("hex.builtin.view.yara.match"_lang)) this->applyRules(); - }, - this->m_matching); + } + ImGui::EndDisabled(); if (this->m_matching) { ImGui::SameLine(); @@ -91,7 +92,7 @@ namespace hex::plugin::builtin { while (clipper.Step()) { for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - auto &[identifier, variableName, address, size, wholeDataMatch, highlightId] = this->m_matches[i]; + auto &[identifier, variableName, address, size, wholeDataMatch, highlightId, tooltipId] = this->m_matches[i]; ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::PushID(i); @@ -142,8 +143,10 @@ namespace hex::plugin::builtin { } void ViewYara::clearResult() { - for (const auto &match : this->m_matches) - ImHexApi::HexEditor::removeHighlight(match.highlightId); + for (const auto &match : this->m_matches) { + ImHexApi::HexEditor::removeBackgroundHighlight(match.highlightId); + ImHexApi::HexEditor::removeTooltip(match.tooltipId); + } this->m_matches.clear(); this->m_consoleMessages.clear(); @@ -310,11 +313,11 @@ namespace hex::plugin::builtin { if (rule->strings != nullptr) { yr_rule_strings_foreach(rule, string) { yr_string_matches_foreach(context, string, match) { - results.newMatches.push_back({ rule->identifier, string->identifier, u64(match->offset), size_t(match->match_length), false, 0 }); + results.newMatches.push_back({ rule->identifier, string->identifier, u64(match->offset), size_t(match->match_length), false, 0, 0 }); } } } else { - results.newMatches.push_back({ rule->identifier, "", 0, 0, true, 0 }); + results.newMatches.push_back({ rule->identifier, "", 0, 0, true, 0, 0 }); } } break; @@ -337,8 +340,10 @@ namespace hex::plugin::builtin { this->m_matches = resultContext.newMatches; this->m_consoleMessages = resultContext.consoleMessages; + constexpr static color_t YaraColor = 0x70B4771F; for (auto &match : this->m_matches) { - match.highlightId = ImHexApi::HexEditor::addHighlight({ match.address, match.size }, 0x70B4771F, hex::format("{0} [{1}]", match.identifier, match.variable)); + match.highlightId = ImHexApi::HexEditor::addBackgroundHighlight({ match.address, match.size }, YaraColor); + match.tooltipId = ImHexApi::HexEditor::addTooltip({ match. address, match.size }, hex::format("{0} [{1}]", match.identifier, match.variable), YaraColor); } }); }).detach(); diff --git a/plugins/builtin/source/content/welcome_screen.cpp b/plugins/builtin/source/content/welcome_screen.cpp index a3c83f4f4..4a05effca 100644 --- a/plugins/builtin/source/content/welcome_screen.cpp +++ b/plugins/builtin/source/content/welcome_screen.cpp @@ -417,15 +417,8 @@ namespace hex::plugin::builtin { } }); - ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1050, [&] { - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.open_file"_lang, "CTRL + O")) { - - fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) { - EventManager::post(path); - }); - } - - if (ImGui::BeginMenu("hex.builtin.view.hex_editor.menu.file.open_recent"_lang, !s_recentFilePaths.empty())) { + ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1075, [&] { + if (ImGui::BeginMenu("hex.builtin.menu.file.open_recent"_lang, !s_recentFilePaths.empty())) { // Copy to avoid changing list while iteration std::list recentFilePaths = s_recentFilePaths; for (auto &path : recentFilePaths) { @@ -436,7 +429,7 @@ namespace hex::plugin::builtin { } ImGui::Separator(); - if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.clear_recent"_lang)) { + if (ImGui::MenuItem("hex.builtin.menu.file.clear_recent"_lang)) { s_recentFilePaths.clear(); ContentRegistry::Settings::write( "hex.builtin.setting.imhex", @@ -446,17 +439,6 @@ namespace hex::plugin::builtin { ImGui::EndMenu(); } - - if (ImGui::BeginMenu("hex.builtin.view.hex_editor.menu.file.open_other"_lang)) { - - for (const auto &unlocalizedProviderName : ContentRegistry::Provider::getEntries()) { - if (ImGui::MenuItem(LangEntry(unlocalizedProviderName))) { - EventManager::post(unlocalizedProviderName, nullptr); - } - } - - ImGui::EndMenu(); - } }); diff --git a/plugins/builtin/source/lang/de_DE.cpp b/plugins/builtin/source/lang/de_DE.cpp index 3658942a7..10c1da763 100644 --- a/plugins/builtin/source/lang/de_DE.cpp +++ b/plugins/builtin/source/lang/de_DE.cpp @@ -80,12 +80,40 @@ namespace hex::plugin::builtin { { "hex.builtin.common.open", "Öffnen" }, { "hex.builtin.common.browse", "Druchsuchen..." }, { "hex.builtin.common.choose_file", "Datei auswählen" }, + { "hex.builtin.common.processing", "Verarbeiten" }, - { "hex.builtin.message.file_handler_failed", "Datei konnte nicht mit registriertem Dateihandler geöffnet werden." }, - + { "hex.builtin.popup.exit_application.title", "Applikation verlassen?" }, + { "hex.builtin.popup.exit_application.desc", "Es wurden ungespeicherte Änderungen an diesem Projekt vorgenommen\nBist du sicher, dass du ImHex schliessen willst?" }, + { "hex.builtin.popup.error.read_only", "Schreibzugriff konnte nicht erlangt werden. Datei wurde im Lesemodus geöffnet." }, + { "hex.builtin.popup.error.open", "Öffnen der Datei fehlgeschlagen!" }, + { "hex.builtin.popup.error.create", "Erstellen der neuen Datei fehlgeschlagen!" }, { "hex.builtin.menu.file", "Datei" }, + { "hex.builtin.file.open_file", "Datei öffnen..." }, + { "hex.builtin.file.open_recent", "Kürzlich geöffnete Dateien" }, + { "hex.builtin.file.clear_recent", "Löschen" }, + { "hex.builtin.file.open_other", "Provider öffnen..." }, + { "hex.builtin.file.close", "Schliessen" }, + { "hex.builtin.file.quit", "ImHex Beenden" }, + { "hex.builtin.file.open_project", "Projekt öffnen..." }, + { "hex.builtin.file.save_project", "Projekt speichern..." }, + { "hex.builtin.menu.file.import", "Importieren..." }, + { "hex.builtin.menu.file.import.base64", "Base64 Datei" }, + { "hex.builtin.menu.file.import.base64.popup.import_error", "Datei ist nicht in einem korrekten Base64 Format!" }, + { "hex.builtin.menu.file.import.base64.popup.open_error", "Öffnen der Datei fehlgeschlagen!" }, + { "hex.builtin.menu.file.import.ips", "IPS Patch" }, + { "hex.builtin.menu.file.import.ips32", "IPS32 Patch" }, + { "hex.builtin.menu.file.export", "Exportieren..." }, + { "hex.builtin.menu.file.export.title", "Datei exportieren" }, + { "hex.builtin.menu.file.export.ips", "IPS Patch" }, + { "hex.builtin.menu.file.export.ips32", "IPS32 Patch" }, + { "hex.builtin.menu.file.export.base64.popup.export_error", "File is not in a valid Base64 format!" }, + { "hex.builtin.menu.edit", "Bearbeiten" }, + { "hex.builtin.menu.edit.undo", "Rückgängig" }, + { "hex.builtin.menu.edit.redo", "Wiederholen" }, + { "hex.builtin.menu.edit.bookmark", "Lesezeichen erstellen" }, + { "hex.builtin.menu.view", "Ansicht" }, { "hex.builtin.menu.layout", "Layout" }, { "hex.builtin.menu.view.fps", "FPS anzeigen" }, @@ -207,48 +235,14 @@ namespace hex::plugin::builtin { { "hex.builtin.view.help.calc_cheat_sheet", "Rechner Cheat Sheet" }, { "hex.builtin.view.hex_editor.name", "Hex editor" }, - { "hex.builtin.view.hex_editor.create_file", "Neu" }, - { "hex.builtin.view.hex_editor.open_file", "Öffnen" }, - { "hex.builtin.view.hex_editor.open_project", "Projekt öffnen" }, - { "hex.builtin.view.hex_editor.save_project", "Projekt speichern" }, - { "hex.builtin.view.hex_editor.save_data", "Daten speichern" }, - { "hex.builtin.view.hex_editor.open_base64", "Base64 Datei öffnen" }, - { "hex.builtin.view.hex_editor.load_enconding_file", "Custom encoding Datei laden" }, - { "hex.builtin.view.hex_editor.page", "Seite {0} / {1}" }, - { "hex.builtin.view.hex_editor.save_as", "Speichern unter" }, - { "hex.builtin.view.hex_editor.exit_application.title", "Applikation verlassen?" }, - { "hex.builtin.view.hex_editor.exit_application.desc", "Es wurden ungespeicherte Änderungen an diesem Projekt vorgenommen\nBist du sicher, dass du ImHex schliessen willst?" }, - { "hex.builtin.view.hex_editor.script.title", "Datei mit Loader Skript laden" }, - { "hex.builtin.view.hex_editor.script.desc", "Lade eine Datei mit Hilfe eines Python Skriptes" }, - { "hex.builtin.view.hex_editor.script.script", "Skript" }, - { "hex.builtin.view.hex_editor.script.script.title", "Loader Script: Skript öffnen" }, - { "hex.builtin.view.hex_editor.script.file", "Datei" }, - { "hex.builtin.view.hex_editor.script.file.title", "Loader Script: Datei öffnen" }, - { "hex.builtin.view.hex_editor.processing", "Importieren / Exportieren" }, + { "hex.builtin.view.hex_editor.page", "Seite" }, + { "hex.builtin.view.hex_editor.selection", "Auswahl" }, + { "hex.builtin.view.hex_editor.selection.none", "Keine" }, + { "hex.builtin.view.hex_editor.region", "Region" }, + { "hex.builtin.view.hex_editor.data_size", "Datengrösse" }, + { "hex.builtin.view.hex_editor.no_bytes", "Keine bytes verfügbar" }, - { "hex.builtin.view.hex_editor.menu.file.open_file", "Datei öffnen..." }, - { "hex.builtin.view.hex_editor.menu.file.open_recent", "Kürzlich geöffnete Dateien" }, - { "hex.builtin.view.hex_editor.menu.file.clear_recent", "Löschen" }, - { "hex.builtin.view.hex_editor.menu.file.open_other", "Provider öffnen..." }, - { "hex.builtin.view.hex_editor.menu.file.save", "Speichern" }, - { "hex.builtin.view.hex_editor.menu.file.save_as", "Speichern unter..." }, - { "hex.builtin.view.hex_editor.menu.file.close", "Schliessen" }, - { "hex.builtin.view.hex_editor.menu.file.quit", "ImHex Beenden" }, - { "hex.builtin.view.hex_editor.menu.file.open_project", "Projekt öffnen..." }, - { "hex.builtin.view.hex_editor.menu.file.save_project", "Projekt speichern..." }, { "hex.builtin.view.hex_editor.menu.file.load_encoding_file", "Custom encoding laden..." }, - { "hex.builtin.view.hex_editor.menu.file.import", "Importieren..." }, - { "hex.builtin.view.hex_editor.menu.file.import.base64", "Base64 Datei" }, - { "hex.builtin.view.hex_editor.base64.import_error", "Datei ist nicht in einem korrekten Base64 Format!" }, - { "hex.builtin.view.hex_editor.file_open_error", "Öffnen der Datei fehlgeschlagen!" }, - { "hex.builtin.view.hex_editor.menu.file.import.ips", "IPS Patch" }, - { "hex.builtin.view.hex_editor.menu.file.import.ips32", "IPS32 Patch" }, - { "hex.builtin.view.hex_editor.menu.file.import.script", "Datei mit Loader Script" }, - - { "hex.builtin.view.hex_editor.menu.file.export", "Exportieren..." }, - { "hex.builtin.view.hex_editor.menu.file.export.title", "Datei exportieren" }, - { "hex.builtin.view.hex_editor.menu.file.export.ips", "IPS Patch" }, - { "hex.builtin.view.hex_editor.menu.file.export.ips32", "IPS32 Patch" }, { "hex.builtin.view.hex_editor.menu.file.search", "Suchen" }, { "hex.builtin.view.hex_editor.search.string", "String" }, { "hex.builtin.view.hex_editor.search.hex", "Hex" }, @@ -260,11 +254,8 @@ namespace hex::plugin::builtin { { "hex.builtin.view.hex_editor.goto.offset.current", "Momentan" }, { "hex.builtin.view.hex_editor.goto.offset.begin", "Beginn" }, { "hex.builtin.view.hex_editor.goto.offset.end", "Ende" }, - { "hex.builtin.view.hex_editor.error.read_only", "Schreibzugriff konnte nicht erlangt werden. Datei wurde im Lesemodus geöffnet." }, - { "hex.builtin.view.hex_editor.error.open", "Öffnen der Datei fehlgeschlagen!" }, - { "hex.builtin.view.hex_editor.error.create", "Erstellen der neuen Datei fehlgeschlagen!" }, - { "hex.builtin.view.hex_editor.menu.edit.undo", "Rückgängig" }, - { "hex.builtin.view.hex_editor.menu.edit.redo", "Wiederholen" }, + { "hex.builtin.view.hex_editor.file.save", "Speichern" }, + { "hex.builtin.view.hex_editor.file.save_as", "Speichern unter..." }, { "hex.builtin.view.hex_editor.menu.edit.copy", "Kopieren" }, { "hex.builtin.view.hex_editor.menu.edit.copy_as", "Kopieren als..." }, { "hex.builtin.view.hex_editor.copy.hex", "String" }, @@ -279,7 +270,6 @@ namespace hex::plugin::builtin { { "hex.builtin.view.hex_editor.copy.html", "HTML" }, { "hex.builtin.view.hex_editor.menu.edit.paste", "Einfügen" }, { "hex.builtin.view.hex_editor.menu.edit.select_all", "Alles auswählen" }, - { "hex.builtin.view.hex_editor.menu.edit.bookmark", "Lesezeichen erstellen" }, { "hex.builtin.view.hex_editor.menu.edit.set_base", "Basisadresse setzen" }, { "hex.builtin.view.hex_editor.menu.edit.resize", "Grösse ändern..." }, { "hex.builtin.view.hex_editor.menu.edit.insert", "Einsetzen..." }, @@ -666,22 +656,21 @@ namespace hex::plugin::builtin { { "hex.builtin.setting.interface.scaling.x1_5", "x1.5" }, { "hex.builtin.setting.interface.scaling.x2_0", "x2.0" }, { "hex.builtin.setting.interface.language", "Sprache" }, - //{ "hex.builtin.setting.interface.wiki_explain_language", "Wikipedia Language" }, + { "hex.builtin.setting.interface.wiki_explain_language", "Wikipedia Sprache" }, { "hex.builtin.setting.interface.fps", "FPS Limite" }, { "hex.builtin.setting.interface.fps.unlocked", "Unbegrenzt" }, - { "hex.builtin.setting.interface.highlight_alpha", "Markierungssichtbarkeit" }, { "hex.builtin.setting.hex_editor", "Hex Editor" }, - { "hex.builtin.setting.hex_editor.column_count", "Anzahl Byte Spalten" }, - { "hex.builtin.setting.hex_editor.hexii", "HexII anstatt Bytes anzeigen" }, + { "hex.builtin.setting.hex_editor.highlight_color", "Auswahlfarbe" }, + { "hex.builtin.setting.hex_editor.bytes_per_row", "Bytes pro Zeile" }, { "hex.builtin.setting.hex_editor.ascii", "ASCII Spalte anzeigen" }, { "hex.builtin.setting.hex_editor.advanced_decoding", "Erweiterte Dekodierungsspalte anzeigen" }, { "hex.builtin.setting.hex_editor.grey_zeros", "Nullen ausgrauen" }, { "hex.builtin.setting.hex_editor.uppercase_hex", "Hex Zeichen als Grossbuchstaben" }, - { "hex.builtin.setting.hex_editor.extra_info", "Extra informationen anzeigen" }, + { "hex.builtin.setting.hex_editor.visualizer", "Data visualizer" }, { "hex.builtin.setting.folders", "Ordner" }, { "hex.builtin.setting.folders.description", "Gib zusätzliche Orderpfade an in welchen Pattern, Scripts, Yara Rules und anderes gesucht wird" }, - // { "hex.builtin.setting.folders.add_folder", "Add new folder" }, - // { "hex.builtin.setting.folders.remove_folder", "Remove currently selected folder from list" }, + { "hex.builtin.setting.folders.add_folder", "Neuer Ordner hinzufügen" }, + { "hex.builtin.setting.folders.remove_folder", "Ausgewählter Ordner von Liste entfernen" }, { "hex.builtin.setting.font", "Schriftart" }, { "hex.builtin.setting.font.font_path", "Eigene Schriftart" }, { "hex.builtin.setting.font.font_size", "Schriftgrösse" }, @@ -704,7 +693,24 @@ namespace hex::plugin::builtin { { "hex.builtin.provider.disk.sector_size", "Sektorgrösse" }, { "hex.builtin.provider.disk.reload", "Neu laden" }, - { "hex.builtin.layouts.default", "Standard" } + { "hex.builtin.layouts.default", "Standard" }, + + { "hex.builtin.visualizer.hexadecimal.8bit", "Hexadezimal (8 bits)" }, + { "hex.builtin.visualizer.hexadecimal.16bit", "Hexadezimal (16 bits)" }, + { "hex.builtin.visualizer.hexadecimal.32bit", "Hexadezimal (32 bits)" }, + { "hex.builtin.visualizer.hexadecimal.64bit", "Hexadezimal (64 bits)" }, + { "hex.builtin.visualizer.decimal.signed.8bit", "Dezimal Signed (8 bits)" }, + { "hex.builtin.visualizer.decimal.signed.16bit", "Dezimal Signed (16 bits)" }, + { "hex.builtin.visualizer.decimal.signed.32bit", "Dezimal Signed (32 bits)" }, + { "hex.builtin.visualizer.decimal.signed.64bit", "Dezimal Signed (64 bits)" }, + { "hex.builtin.visualizer.decimal.unsigned.8bit", "Dezimal Unsigned (8 bits)" }, + { "hex.builtin.visualizer.decimal.unsigned.16bit", "Dezimal Unsigned (16 bits)" }, + { "hex.builtin.visualizer.decimal.unsigned.32bit", "Dezimal Unsigned (32 bits)" }, + { "hex.builtin.visualizer.decimal.unsigned.64bit", "Dezimal Unsigned (64 bits)" }, + { "hex.builtin.visualizer.floating_point.32bit", "Floating Point (32 bits)" }, + { "hex.builtin.visualizer.floating_point.64bit", "Floating Point (64 bits)" }, + { "hex.builtin.visualizer.hexii", "HexII" }, + { "hex.builtin.visualizer.rgba8", "RGBA8 Farbe" }, }); } diff --git a/plugins/builtin/source/lang/en_US.cpp b/plugins/builtin/source/lang/en_US.cpp index 8e5e2aabe..b13c75405 100644 --- a/plugins/builtin/source/lang/en_US.cpp +++ b/plugins/builtin/source/lang/en_US.cpp @@ -81,11 +81,41 @@ namespace hex::plugin::builtin { { "hex.builtin.common.open", "Open" }, { "hex.builtin.common.browse", "Browse..." }, { "hex.builtin.common.choose_file", "Choose file" }, + { "hex.common.processing", "Processing" }, - { "hex.builtin.message.file_handler_failed", "Failed to open file with registered file handler." }, + { "hex.builtin.popup.exit_application.title", "Exit Application?" }, + { "hex.builtin.popup.exit_application.desc", "You have unsaved changes made to your Project.\nAre you sure you want to exit?" }, + { "hex.builtin.popup.error.read_only", "Couldn't get write access. File opened in read-only mode." }, + { "hex.builtin.popup.error.open", "Failed to open file!" }, + { "hex.builtin.popup.error.create", "Failed to create new file!" }, { "hex.builtin.menu.file", "File" }, + { "hex.builtin.menu.file.open_file", "Open File..." }, + { "hex.builtin.menu.file.open_recent", "Open Recent" }, + { "hex.builtin.menu.file.clear_recent", "Clear" }, + { "hex.builtin.menu.file.open_other", "Open Other..." }, + { "hex.builtin.menu.file.close", "Close" }, + { "hex.builtin.menu.file.quit", "Quit ImHex" }, + { "hex.builtin.menu.file.open_project", "Open Project..." }, + { "hex.builtin.menu.file.save_project", "Save Project..." }, + { "hex.builtin.menu.file.import", "Import..." }, + { "hex.builtin.menu.file.import.base64", "Base64 File" }, + { "hex.builtin.menu.file.import.base64.popup.import_error", "File is not in a valid Base64 format!" }, + { "hex.builtin.menu.file.import.base64.popup.open_error", "Failed to open file!" }, + { "hex.builtin.menu.file.import.ips", "IPS Patch" }, + { "hex.builtin.menu.file.import.ips32", "IPS32 Patch" }, + { "hex.builtin.menu.file.export", "Export..." }, + { "hex.builtin.menu.file.export.title", "Export File" }, + { "hex.builtin.menu.file.export.ips", "IPS Patch" }, + { "hex.builtin.menu.file.export.ips32", "IPS32 Patch" }, + { "hex.builtin.menu.file.export.base64.popup.export_error", "File is not in a valid Base64 format!" }, + { "hex.builtin.menu.file.export.popup.create", "Cannot export data. Failed to create file!" }, + { "hex.builtin.menu.edit", "Edit" }, + { "hex.builtin.menu.edit.undo", "Undo" }, + { "hex.builtin.menu.edit.redo", "Redo" }, + { "hex.builtin.menu.edit.bookmark", "Create bookmark" }, + { "hex.builtin.menu.view", "View" }, { "hex.builtin.menu.layout", "Layout" }, { "hex.builtin.menu.view.fps", "Display FPS" }, @@ -211,64 +241,25 @@ namespace hex::plugin::builtin { { "hex.builtin.view.help.calc_cheat_sheet", "Calculator Cheat Sheet" }, { "hex.builtin.view.hex_editor.name", "Hex editor" }, - { "hex.builtin.view.hex_editor.create_file", "New" }, - { "hex.builtin.view.hex_editor.open_file", "Open" }, - { "hex.builtin.view.hex_editor.open_project", "Open Project" }, - { "hex.builtin.view.hex_editor.save_project", "Save Project" }, - { "hex.builtin.view.hex_editor.save_data", "Save Data" }, - { "hex.builtin.view.hex_editor.open_base64", "Open Base64 File" }, - { "hex.builtin.view.hex_editor.load_enconding_file", "Load custom encoding File" }, - { "hex.builtin.view.hex_editor.page", "Page {0} / {1}" }, - { "hex.builtin.view.hex_editor.save_as", "Save As" }, - { "hex.builtin.view.hex_editor.exit_application.title", "Exit Application?" }, - { "hex.builtin.view.hex_editor.exit_application.desc", "You have unsaved changes made to your Project.\nAre you sure you want to exit?" }, - { "hex.builtin.view.hex_editor.script.title", "Load File with Loader Script" }, - { "hex.builtin.view.hex_editor.script.desc", "Load a file using a Python loader script." }, - { "hex.builtin.view.hex_editor.script.script", "Script" }, - { "hex.builtin.view.hex_editor.script.script.title", "Loader Script: Open Script" }, - { "hex.builtin.view.hex_editor.script.file", "File" }, - { "hex.builtin.view.hex_editor.script.file.title", "Loader Script: Open File" }, - { "hex.builtin.view.hex_editor.processing", "Importing / Exporting" }, + { "hex.builtin.view.hex_editor.page", "Page" }, + { "hex.builtin.view.hex_editor.selection", "Selection" }, + { "hex.builtin.view.hex_editor.selection.none", "None" }, + { "hex.builtin.view.hex_editor.region", "Region" }, + { "hex.builtin.view.hex_editor.data_size", "Data Size" }, + { "hex.builtin.view.hex_editor.no_bytes", "No bytes available" }, - { "hex.builtin.view.hex_editor.menu.file.open_file", "Open File..." }, - { "hex.builtin.view.hex_editor.menu.file.open_recent", "Open Recent" }, - { "hex.builtin.view.hex_editor.menu.file.clear_recent", "Clear" }, - { "hex.builtin.view.hex_editor.menu.file.open_other", "Open Other..." }, - { "hex.builtin.view.hex_editor.menu.file.save", "Save" }, - { "hex.builtin.view.hex_editor.menu.file.save_as", "Save As..." }, - { "hex.builtin.view.hex_editor.menu.file.close", "Close" }, - { "hex.builtin.view.hex_editor.menu.file.quit", "Quit ImHex" }, - { "hex.builtin.view.hex_editor.menu.file.open_project", "Open Project..." }, - { "hex.builtin.view.hex_editor.menu.file.save_project", "Save Project..." }, { "hex.builtin.view.hex_editor.menu.file.load_encoding_file", "Load custom encoding..." }, - { "hex.builtin.view.hex_editor.menu.file.import", "Import..." }, - { "hex.builtin.view.hex_editor.menu.file.import.base64", "Base64 File" }, - { "hex.builtin.view.hex_editor.base64.import_error", "File is not in a valid Base64 format!" }, - { "hex.builtin.view.hex_editor.file_open_error", "Failed to open file!" }, - { "hex.builtin.view.hex_editor.menu.file.import.ips", "IPS Patch" }, - { "hex.builtin.view.hex_editor.menu.file.import.ips32", "IPS32 Patch" }, - { "hex.builtin.view.hex_editor.menu.file.import.script", "File with Loader Script" }, - - { "hex.builtin.view.hex_editor.menu.file.export", "Export..." }, - { "hex.builtin.view.hex_editor.menu.file.export.title", "Export File" }, - { "hex.builtin.view.hex_editor.menu.file.export.ips", "IPS Patch" }, - { "hex.builtin.view.hex_editor.menu.file.export.ips32", "IPS32 Patch" }, { "hex.builtin.view.hex_editor.menu.file.search", "Search" }, { "hex.builtin.view.hex_editor.search.string", "String" }, { "hex.builtin.view.hex_editor.search.hex", "Hex" }, { "hex.builtin.view.hex_editor.search.find", "Find" }, - { "hex.builtin.view.hex_editor.search.find_next", "Find next" }, - { "hex.builtin.view.hex_editor.search.find_prev", "Find previous" }, { "hex.builtin.view.hex_editor.menu.file.goto", "Goto" }, { "hex.builtin.view.hex_editor.goto.offset.absolute", "Absolute" }, - { "hex.builtin.view.hex_editor.goto.offset.current", "Current" }, + { "hex.builtin.view.hex_editor.goto.offset.relative", "Relative" }, { "hex.builtin.view.hex_editor.goto.offset.begin", "Begin" }, { "hex.builtin.view.hex_editor.goto.offset.end", "End" }, - { "hex.builtin.view.hex_editor.error.read_only", "Couldn't get write access. File opened in read-only mode." }, - { "hex.builtin.view.hex_editor.error.open", "Failed to open file!" }, - { "hex.builtin.view.hex_editor.error.create", "Failed to create new file!" }, - { "hex.builtin.view.hex_editor.menu.edit.undo", "Undo" }, - { "hex.builtin.view.hex_editor.menu.edit.redo", "Redo" }, + { "hex.builtin.view.hex_editor.menu.file.save", "Save" }, + { "hex.builtin.view.hex_editor.menu.file.save_as", "Save As..." }, { "hex.builtin.view.hex_editor.menu.edit.copy", "Copy" }, { "hex.builtin.view.hex_editor.menu.edit.copy_as", "Copy as..." }, { "hex.builtin.view.hex_editor.copy.hex", "String" }, @@ -283,7 +274,6 @@ namespace hex::plugin::builtin { { "hex.builtin.view.hex_editor.copy.html", "HTML" }, { "hex.builtin.view.hex_editor.menu.edit.paste", "Paste" }, { "hex.builtin.view.hex_editor.menu.edit.select_all", "Select all" }, - { "hex.builtin.view.hex_editor.menu.edit.bookmark", "Create bookmark" }, { "hex.builtin.view.hex_editor.menu.edit.set_base", "Set base address" }, { "hex.builtin.view.hex_editor.menu.edit.resize", "Resize..." }, { "hex.builtin.view.hex_editor.menu.edit.insert", "Insert..." }, @@ -673,15 +663,14 @@ namespace hex::plugin::builtin { { "hex.builtin.setting.interface.wiki_explain_language", "Wikipedia Language" }, { "hex.builtin.setting.interface.fps", "FPS Limit" }, { "hex.builtin.setting.interface.fps.unlocked", "Unlocked" }, - { "hex.builtin.setting.interface.highlight_alpha", "Highlighting opacity" }, { "hex.builtin.setting.hex_editor", "Hex Editor" }, - { "hex.builtin.setting.hex_editor.column_count", "Byte column count" }, - { "hex.builtin.setting.hex_editor.hexii", "Display HexII instead of Bytes" }, + { "hex.builtin.setting.hex_editor.highlight_color", "Selection highlight color" }, + { "hex.builtin.setting.hex_editor.bytes_per_row", "Bytes per row" }, { "hex.builtin.setting.hex_editor.ascii", "Display ASCII column" }, { "hex.builtin.setting.hex_editor.advanced_decoding", "Display advanced decoding column" }, { "hex.builtin.setting.hex_editor.grey_zeros", "Grey out zeros" }, { "hex.builtin.setting.hex_editor.uppercase_hex", "Upper case Hex characters" }, - { "hex.builtin.setting.hex_editor.extra_info", "Display extra information" }, + { "hex.builtin.setting.hex_editor.visualizer", "Data visualizer" }, { "hex.builtin.setting.folders", "Folders" }, { "hex.builtin.setting.folders.description", "Specify additional search paths for patterns, scripts, Yara rules and more" }, { "hex.builtin.setting.folders.add_folder", "Add new folder" }, @@ -708,7 +697,24 @@ namespace hex::plugin::builtin { { "hex.builtin.provider.disk.sector_size", "Sector Size" }, { "hex.builtin.provider.disk.reload", "Reload" }, - { "hex.builtin.layouts.default", "Default" } + { "hex.builtin.layouts.default", "Default" }, + + { "hex.builtin.visualizer.hexadecimal.8bit", "Hexadecimal (8 bits)" }, + { "hex.builtin.visualizer.hexadecimal.16bit", "Hexadecimal (16 bits)" }, + { "hex.builtin.visualizer.hexadecimal.32bit", "Hexadecimal (32 bits)" }, + { "hex.builtin.visualizer.hexadecimal.64bit", "Hexadecimal (64 bits)" }, + { "hex.builtin.visualizer.decimal.signed.8bit", "Decimal Signed (8 bits)" }, + { "hex.builtin.visualizer.decimal.signed.16bit", "Decimal Signed (16 bits)" }, + { "hex.builtin.visualizer.decimal.signed.32bit", "Decimal Signed (32 bits)" }, + { "hex.builtin.visualizer.decimal.signed.64bit", "Decimal Signed (64 bits)" }, + { "hex.builtin.visualizer.decimal.unsigned.8bit", "Decimal Unsigned (8 bits)" }, + { "hex.builtin.visualizer.decimal.unsigned.16bit", "Decimal Unsigned (16 bits)" }, + { "hex.builtin.visualizer.decimal.unsigned.32bit", "Decimal Unsigned (32 bits)" }, + { "hex.builtin.visualizer.decimal.unsigned.64bit", "Decimal Unsigned (64 bits)" }, + { "hex.builtin.visualizer.floating_point.32bit", "Floating Point (32 bits)" }, + { "hex.builtin.visualizer.floating_point.64bit", "Floating Point (64 bits)" }, + { "hex.builtin.visualizer.hexii", "HexII" }, + { "hex.builtin.visualizer.rgba8", "RGBA8 Color" }, }); } diff --git a/plugins/builtin/source/lang/it_IT.cpp b/plugins/builtin/source/lang/it_IT.cpp index e0c25218a..26b39083b 100644 --- a/plugins/builtin/source/lang/it_IT.cpp +++ b/plugins/builtin/source/lang/it_IT.cpp @@ -80,11 +80,42 @@ namespace hex::plugin::builtin { { "hex.builtin.common.open", "Apri" }, { "hex.builtin.common.browse", "Esplora..." }, { "hex.builtin.common.choose_file", "Scegli file" }, + //{ "hex.common.processing", "Processing" }, - { "hex.builtin.message.file_handler_failed", "Impossibile aprire il file con il gestore di file registrato." }, + { "hex.builtin.popup.exit_application.title", "Uscire dall'applicazione?" }, + { "hex.builtin.popup.exit_application.desc", "Hai delle modifiche non salvate nel tuo progetto.\nSei sicuro di voler uscire?" }, + { "hex.builtin.popup.error.read_only", "Impossibile scrivere sul File. File aperto solo in modalità lettura" }, + { "hex.builtin.popup.error.open", "Impossibile aprire il File!" }, + { "hex.builtin.popup.error.create", "Impossibile creare il nuovo File!" }, { "hex.builtin.menu.file", "File" }, + { "hex.builtin.menu.file.open_file", "Apri File..." }, + { "hex.builtin.menu.file.open_recent", "File recenti" }, + { "hex.builtin.menu.file.clear_recent", "Pulisci" }, + { "hex.builtin.menu.file.open_other", "Apri altro..." }, + { "hex.builtin.menu.file.close", "Chiudi" }, + { "hex.builtin.menu.file.quit", "Uscita ImHex" }, + { "hex.builtin.menu.file.open_project", "Apri un Progetto..." }, + { "hex.builtin.menu.file.save_project", "Salva Progetto..." }, + { "hex.builtin.menu.file.import", "Importa..." }, + { "hex.builtin.menu.file.import.base64", "Base64 File" }, + //{ "hex.builtin.menu.file.import.base64.popup.import_error", "File is not in a valid Base64 format!" }, + //{ "hex.builtin.menu.file.import.base64.popup.open_error", "Failed to open file!" }, + { "hex.builtin.file_open_error", "Impossibile aprire il File!" }, + { "hex.builtin.menu.file.import.ips", "IPS Patch" }, + { "hex.builtin.menu.file.import.ips32", "IPS32 Patch" }, + { "hex.builtin.menu.file.export", "Esporta..." }, + { "hex.builtin.menu.file.export.title", "Esporta File" }, + { "hex.builtin.menu.file.export.ips", "IPS Patch" }, + { "hex.builtin.menu.file.export.ips32", "IPS32 Patch" }, + //{ "hex.builtin.menu.file.export.base64.popup.export_error", "File is not in a valid Base64 format!" }, + //{ "hex.builtin.menu.file.export.popup.create", "Cannot export data. Failed to create file!" }, + { "hex.builtin.menu.edit", "Modifica" }, + { "hex.builtin.menu.edit.undo", "Annulla" }, + { "hex.builtin.menu.edit.redo", "Ripeti" }, + { "hex.builtin.menu.edit.bookmark", "Crea segnalibro" }, + { "hex.builtin.menu.view", "Vista" }, { "hex.builtin.menu.layout", "Layout" }, { "hex.builtin.menu.view.fps", "Mostra FPS" }, @@ -206,48 +237,15 @@ namespace hex::plugin::builtin { { "hex.builtin.view.help.calc_cheat_sheet", "Calcolatrice Cheat Sheet" }, { "hex.builtin.view.hex_editor.name", "Hex editor" }, - { "hex.builtin.view.hex_editor.create_file", "Nuovo" }, - { "hex.builtin.view.hex_editor.open_file", "Apri" }, - { "hex.builtin.view.hex_editor.menu.file.open_recent", "File recenti" }, - { "hex.builtin.view.hex_editor.menu.file.clear_recent", "Pulisci" }, - { "hex.builtin.view.hex_editor.menu.file.open_other", "Apri altro..." }, - { "hex.builtin.view.hex_editor.open_project", "Apri i Progetti" }, - { "hex.builtin.view.hex_editor.save_project", "Salva i Progetti" }, - { "hex.builtin.view.hex_editor.save_data", "Salva i Dati" }, - { "hex.builtin.view.hex_editor.open_base64", "Apri Base64 File" }, - { "hex.builtin.view.hex_editor.load_enconding_file", "Carica un File di codfica personalizzato" }, - { "hex.builtin.view.hex_editor.page", "Pagina {0} / {1}" }, - { "hex.builtin.view.hex_editor.save_as", "Salva come" }, - { "hex.builtin.view.hex_editor.exit_application.title", "Uscire dall'applicazione?" }, - { "hex.builtin.view.hex_editor.exit_application.desc", "Hai delle modifiche non salvate nel tuo progetto.\nSei sicuro di voler uscire?" }, - { "hex.builtin.view.hex_editor.script.title", "Carica un File tramite il Caricatore di Script" }, - { "hex.builtin.view.hex_editor.script.desc", "Carica un file tramite il Caricatore di Script di Python." }, - { "hex.builtin.view.hex_editor.script.script", "Script" }, - { "hex.builtin.view.hex_editor.script.script.title", "Caricatore Script: Apri Script" }, - { "hex.builtin.view.hex_editor.script.file", "File" }, - { "hex.builtin.view.hex_editor.script.file.title", "Caricatore Script: Apri File" }, - { "hex.builtin.view.hex_editor.processing", "Importa / Esporta" }, - { "hex.builtin.view.hex_editor.menu.file.open_file", "Apri File..." }, - { "hex.builtin.view.hex_editor.menu.file.save", "Salva" }, - { "hex.builtin.view.hex_editor.menu.file.save_as", "Salva come..." }, - { "hex.builtin.view.hex_editor.menu.file.close", "Chiudi" }, - { "hex.builtin.view.hex_editor.menu.file.quit", "Uscita ImHex" }, - { "hex.builtin.view.hex_editor.menu.file.open_project", "Apri un Progetto..." }, - { "hex.builtin.view.hex_editor.menu.file.save_project", "Salva Progetto..." }, + //{ "hex.builtin.view.hex_editor.page", "Page" }, + //{ "hex.builtin.view.hex_editor.selection", "Selection" }, + //{ "hex.builtin.view.hex_editor.selection.none", "None" }, + //{ "hex.builtin.view.hex_editor.region", "Region" }, + //{ "hex.builtin.view.hex_editor.data_size", "Data Size" }, + //{ "hex.builtin.view.hex_editor.no_bytes", "No bytes available" }, + { "hex.builtin.view.hex_editor.menu.file.load_encoding_file", "Carica una codifica personalizzata..." }, - { "hex.builtin.view.hex_editor.menu.file.import", "Importa..." }, - { "hex.builtin.view.hex_editor.menu.file.import.base64", "Base64 File" }, - { "hex.builtin.view.hex_editor.base64.import_error", "Il file non è in un formato bas64 corretto!" }, - { "hex.builtin.view.hex_editor.file_open_error", "Impossibile aprire il File!" }, - { "hex.builtin.view.hex_editor.menu.file.import.ips", "IPS Patch" }, - { "hex.builtin.view.hex_editor.menu.file.import.ips32", "IPS32 Patch" }, - { "hex.builtin.view.hex_editor.menu.file.import.script", "File con il Caricatore di Script" }, - - { "hex.builtin.view.hex_editor.menu.file.export", "Esporta..." }, - { "hex.builtin.view.hex_editor.menu.file.export.title", "Esporta File" }, - { "hex.builtin.view.hex_editor.menu.file.export.ips", "IPS Patch" }, - { "hex.builtin.view.hex_editor.menu.file.export.ips32", "IPS32 Patch" }, { "hex.builtin.view.hex_editor.menu.file.search", "Cerca" }, { "hex.builtin.view.hex_editor.search.string", "Stringa" }, { "hex.builtin.view.hex_editor.search.hex", "Hex" }, @@ -256,14 +254,11 @@ namespace hex::plugin::builtin { { "hex.builtin.view.hex_editor.search.find_prev", "Cerca il precedente" }, { "hex.builtin.view.hex_editor.menu.file.goto", "Vai a" }, { "hex.builtin.view.hex_editor.goto.offset.absolute", "Assoluto" }, - { "hex.builtin.view.hex_editor.goto.offset.current", "Corrente" }, + //{ "hex.builtin.view.hex_editor.goto.offset.current", "Relative" }, { "hex.builtin.view.hex_editor.goto.offset.begin", "Inizo" }, { "hex.builtin.view.hex_editor.goto.offset.end", "Fine" }, - { "hex.builtin.view.hex_editor.error.read_only", "Impossibile scrivere sul File. File aperto solo in modalità lettura" }, - { "hex.builtin.view.hex_editor.error.open", "Impossibile aprire il File!" }, - { "hex.builtin.view.hex_editor.error.create", "Impossibile creare il nuovo File!" }, - { "hex.builtin.view.hex_editor.menu.edit.undo", "Annulla" }, - { "hex.builtin.view.hex_editor.menu.edit.redo", "Ripeti" }, + { "hex.builtin.view.hex_editor.menu.file.save", "Salva" }, + { "hex.builtin.view.hex_editor.menu.file.save_as", "Salva come..." }, { "hex.builtin.view.hex_editor.menu.edit.copy", "Copia" }, { "hex.builtin.view.hex_editor.menu.edit.copy_as", "Copia come..." }, { "hex.builtin.view.hex_editor.copy.hex", "Stringa" }, @@ -278,7 +273,6 @@ namespace hex::plugin::builtin { { "hex.builtin.view.hex_editor.copy.html", "HTML" }, { "hex.builtin.view.hex_editor.menu.edit.paste", "Incolla" }, { "hex.builtin.view.hex_editor.menu.edit.select_all", "Seleziona tutti" }, - { "hex.builtin.view.hex_editor.menu.edit.bookmark", "Crea segnalibro" }, { "hex.builtin.view.hex_editor.menu.edit.set_base", "Imposta indirizzo di base" }, { "hex.builtin.view.hex_editor.menu.edit.resize", "Ridimensiona..." }, { "hex.builtin.view.hex_editor.menu.edit.insert", "Inserisci..." }, @@ -671,15 +665,14 @@ namespace hex::plugin::builtin { { "hex.builtin.setting.interface.scaling.x2_0", "x2.0" }, { "hex.builtin.setting.interface.fps", "Limite FPS" }, { "hex.builtin.setting.interface.fps.unlocked", "Unblocca" }, - { "hex.builtin.setting.interface.highlight_alpha", "Evidenziazione dell'opacità" }, { "hex.builtin.setting.hex_editor", "Hex Editor" }, - { "hex.builtin.setting.hex_editor.column_count", "Conteggio della colonna dei byte" }, - { "hex.builtin.setting.hex_editor.hexii", "Mostra HexII invece dei byte" }, + //{ "hex.builtin.setting.hex_editor.highlight_color", "Selection highlight color" }, + //{ "hex.builtin.setting.hex_editor.bytes_per_row", "Bytes per row" }, { "hex.builtin.setting.hex_editor.ascii", "Mostra la colonna ASCII" }, { "hex.builtin.setting.hex_editor.advanced_decoding", "Mostra la colonna di decodifica avanzata" }, { "hex.builtin.setting.hex_editor.grey_zeros", "Taglia fuori gli zeri" }, { "hex.builtin.setting.hex_editor.uppercase_hex", "Caratteri esadecimali maiuscoli" }, - { "hex.builtin.setting.hex_editor.extra_info", "Mostra informazioni extra" }, + //{ "hex.builtin.setting.hex_editor.visualizer", "Data visualizer" }, //{ "hex.builtin.setting.folders", "Folders" }, //{ "hex.builtin.setting.folders.description", "Specify additional search paths for patterns, scripts, rules and more" }, // { "hex.builtin.setting.folders.add_folder", "Add new folder" }, @@ -706,7 +699,24 @@ namespace hex::plugin::builtin { { "hex.builtin.provider.disk.sector_size", "Dimensione settore" }, { "hex.builtin.provider.disk.reload", "Ricarica" }, - { "hex.builtin.layouts.default", "Default" } + { "hex.builtin.layouts.default", "Default" }, + + //{ "hex.builtin.visualizer.hexadecimal.8bit", "Hexadecimal (8 bits)" }, + //{ "hex.builtin.visualizer.hexadecimal.16bit", "Hexadecimal (16 bits)" }, + //{ "hex.builtin.visualizer.hexadecimal.32bit", "Hexadecimal (32 bits)" }, + //{ "hex.builtin.visualizer.hexadecimal.64bit", "Hexadecimal (64 bits)" }, + //{ "hex.builtin.visualizer.decimal.signed.8bit", "Decimal Signed (8 bits)" }, + //{ "hex.builtin.visualizer.decimal.signed.16bit", "Decimal Signed (16 bits)" }, + //{ "hex.builtin.visualizer.decimal.signed.32bit", "Decimal Signed (32 bits)" }, + //{ "hex.builtin.visualizer.decimal.signed.64bit", "Decimal Signed (64 bits)" }, + //{ "hex.builtin.visualizer.decimal.unsigned.8bit", "Decimal Unsigned (8 bits)" }, + //{ "hex.builtin.visualizer.decimal.unsigned.16bit", "Decimal Unsigned (16 bits)" }, + //{ "hex.builtin.visualizer.decimal.unsigned.32bit", "Decimal Unsigned (32 bits)" }, + //{ "hex.builtin.visualizer.decimal.unsigned.64bit", "Decimal Unsigned (64 bits)" }, + //{ "hex.builtin.visualizer.floating_point.32bit", "Floating Point (32 bits)" }, + //{ "hex.builtin.visualizer.floating_point.64bit", "Floating Point (64 bits)" }, + //{ "hex.builtin.visualizer.hexii", "HexII" }, + //{ "hex.builtin.visualizer.rgba8", "RGBA8 Color" }, }); } diff --git a/plugins/builtin/source/lang/ja_JP.cpp b/plugins/builtin/source/lang/ja_JP.cpp index f110c9358..d07862534 100644 --- a/plugins/builtin/source/lang/ja_JP.cpp +++ b/plugins/builtin/source/lang/ja_JP.cpp @@ -80,11 +80,42 @@ namespace hex::plugin::builtin { { "hex.builtin.common.open", "開く" }, { "hex.builtin.common.browse", "ファイルを参照..." }, { "hex.builtin.common.choose_file", "ファイルを選択" }, + // { "hex.common.processing", "Processing" }, - { "hex.builtin.message.file_handler_failed", "登録されたファイルハンドラでファイルを開くのに失敗しました。" }, + { "hex.builtin.popup.exit_application.title", "アプリケーションを終了しますか?" }, + { "hex.builtin.popup.exit_application.desc", "プロジェクトに保存されていない変更があります。\n終了してもよろしいですか?" }, + { "hex.builtin.popup.error.read_only", "書き込み権限を取得できませんでした。ファイルが読み取り専用で開かれました。" }, + { "hex.builtin.popup.error.open", "ファイルを開けませんでした!" }, + { "hex.builtin.popup.error.create", "新しいファイルを作成できませんでした!" }, { "hex.builtin.menu.file", "ファイル" }, + { "hex.builtin.menu.file.open_file", "ファイルを開く..." }, + { "hex.builtin.menu.file.open_recent", "最近使用したファイルを開く" }, + { "hex.builtin.menu.file.clear_recent", "消去" }, + { "hex.builtin.menu.file.open_other", "その他の開くオプション..." }, + { "hex.builtin.menu.file.close", "ファイルを閉じる" }, + { "hex.builtin.menu.file.quit", "ImHexを終了" }, + { "hex.builtin.menu.file.open_project", "プロジェクトを開く..." }, + { "hex.builtin.menu.file.save_project", "プロジェクトを保存..." }, + { "hex.builtin.menu.file.load_encoding_file", "カスタムエンコードを読み込み..." }, + { "hex.builtin.menu.file.import", "インポート..." }, + { "hex.builtin.menu.file.import.base64", "Base64ファイル" }, + { "hex.builtin.menu.file.import.base64.popup.import_error", "ファイルが有効なBase64形式ではありません!" }, + { "hex.builtin.menu.file.import.base64.popup.open_error", "ファイルを開けませんでした!" }, + { "hex.builtin.view.hex_editor.menu.file.import.ips", "IPSパッチ" }, + { "hex.builtin.view.hex_editor.menu.file.import.ips32", "IPS32パッチ" }, + { "hex.builtin.view.hex_editor.menu.file.export", "エクスポート..." }, + { "hex.builtin.view.hex_editor.menu.file.export.title", "ファイルをエクスポート" }, + { "hex.builtin.view.hex_editor.menu.file.export.ips", "IPSパッチ" }, + { "hex.builtin.view.hex_editor.menu.file.export.ips32", "IPS32パッチ" }, + { "hex.builtin.menu.file.import.base64.popup.export_error", "ファイルが有効なBase64形式ではありません!" }, + //{ "hex.builtin.menu.file.export.popup.create", "Cannot export data. Failed to create file!" }, + { "hex.builtin.menu.edit", "編集" }, + { "hex.builtin.menu.edit.undo", "もとに戻す" }, + { "hex.builtin.menu.edit.redo", "やり直す" }, + { "hex.builtin.menu.edit.bookmark", "ブックマークを作成" }, + { "hex.builtin.menu.view", "表示" }, { "hex.builtin.menu.layout", "レイアウト" }, { "hex.builtin.menu.view.fps", "FPSを表示" }, @@ -210,48 +241,13 @@ namespace hex::plugin::builtin { { "hex.builtin.view.help.calc_cheat_sheet", "計算機チートシート" }, { "hex.builtin.view.hex_editor.name", "Hexエディタ" }, - { "hex.builtin.view.hex_editor.create_file", "新規" }, - { "hex.builtin.view.hex_editor.open_file", "開く" }, - { "hex.builtin.view.hex_editor.open_project", "プロジェクトを開く" }, - { "hex.builtin.view.hex_editor.save_project", "プロジェクトを保存" }, - { "hex.builtin.view.hex_editor.save_data", "データを保存" }, - { "hex.builtin.view.hex_editor.open_base64", "Base64ファイルを開く" }, - { "hex.builtin.view.hex_editor.load_enconding_file", "カスタムエンコードファイルの読み込み" }, //? - { "hex.builtin.view.hex_editor.page", "ページ {0} / {1}" }, - { "hex.builtin.view.hex_editor.save_as", "名前をつけて保存" }, - { "hex.builtin.view.hex_editor.exit_application.title", "アプリケーションを終了しますか?" }, - { "hex.builtin.view.hex_editor.exit_application.desc", "プロジェクトに保存されていない変更があります。\n終了してもよろしいですか?" }, - { "hex.builtin.view.hex_editor.script.title", "ローダースクリプトでファイルを読み込む" }, - { "hex.builtin.view.hex_editor.script.desc", "Pythonのローダースクリプトを使用してファイルを読み込みます。" }, - { "hex.builtin.view.hex_editor.script.script", "スクリプト" }, - { "hex.builtin.view.hex_editor.script.script.title", "ローダースクリプト: スクリプトを開く" }, - { "hex.builtin.view.hex_editor.script.file", "ファイル" }, - { "hex.builtin.view.hex_editor.script.file.title", "ローダースクリプト: ファイルを開く" }, - { "hex.builtin.view.hex_editor.processing", "インポート / エクスポート" }, + //{ "hex.builtin.view.hex_editor.page", "Page" }, + //{ "hex.builtin.view.hex_editor.selection", "Selection" }, + //{ "hex.builtin.view.hex_editor.selection.none", "None" }, + //{ "hex.builtin.view.hex_editor.region", "Region" }, + //{ "hex.builtin.view.hex_editor.data_size", "Data Size" }, + //{ "hex.builtin.view.hex_editor.no_bytes", "No bytes available" }, - { "hex.builtin.view.hex_editor.menu.file.open_file", "ファイルを開く..." }, - { "hex.builtin.view.hex_editor.menu.file.open_recent", "最近使用したファイルを開く" }, - { "hex.builtin.view.hex_editor.menu.file.clear_recent", "消去" }, - { "hex.builtin.view.hex_editor.menu.file.open_other", "その他の開くオプション..." }, - { "hex.builtin.view.hex_editor.menu.file.save", "保存" }, - { "hex.builtin.view.hex_editor.menu.file.save_as", "名前をつけて保存..." }, - { "hex.builtin.view.hex_editor.menu.file.close", "ファイルを閉じる" }, - { "hex.builtin.view.hex_editor.menu.file.quit", "ImHexを終了" }, - { "hex.builtin.view.hex_editor.menu.file.open_project", "プロジェクトを開く..." }, - { "hex.builtin.view.hex_editor.menu.file.save_project", "プロジェクトを保存..." }, - { "hex.builtin.view.hex_editor.menu.file.load_encoding_file", "カスタムエンコードを読み込み..." }, - { "hex.builtin.view.hex_editor.menu.file.import", "インポート..." }, - { "hex.builtin.view.hex_editor.menu.file.import.base64", "Base64ファイル" }, - { "hex.builtin.view.hex_editor.base64.import_error", "ファイルが有効なBase64形式ではありません!" }, - { "hex.builtin.view.hex_editor.file_open_error", "ファイルを開けませんでした!" }, - { "hex.builtin.view.hex_editor.menu.file.import.ips", "IPSパッチ" }, - { "hex.builtin.view.hex_editor.menu.file.import.ips32", "IPS32パッチ" }, - { "hex.builtin.view.hex_editor.menu.file.import.script", "ローダースクリプト付きのファイル" }, - - { "hex.builtin.view.hex_editor.menu.file.export", "エクスポート..." }, - { "hex.builtin.view.hex_editor.menu.file.export.title", "ファイルをエクスポート" }, - { "hex.builtin.view.hex_editor.menu.file.export.ips", "IPSパッチ" }, - { "hex.builtin.view.hex_editor.menu.file.export.ips32", "IPS32パッチ" }, { "hex.builtin.view.hex_editor.menu.file.search", "検索" }, { "hex.builtin.view.hex_editor.search.string", "文字列" }, { "hex.builtin.view.hex_editor.search.hex", "16進" }, @@ -260,14 +256,11 @@ namespace hex::plugin::builtin { { "hex.builtin.view.hex_editor.search.find_prev", "前を検索" }, { "hex.builtin.view.hex_editor.menu.file.goto", "移動" }, { "hex.builtin.view.hex_editor.goto.offset.absolute", "絶対値" }, //? - { "hex.builtin.view.hex_editor.goto.offset.current", "現在" }, //? + //{ "hex.builtin.view.hex_editor.goto.offset.relative", "Relative" }, //? { "hex.builtin.view.hex_editor.goto.offset.begin", "開始" }, //? { "hex.builtin.view.hex_editor.goto.offset.end", "終了" }, //? - { "hex.builtin.view.hex_editor.error.read_only", "書き込み権限を取得できませんでした。ファイルが読み取り専用で開かれました。" }, - { "hex.builtin.view.hex_editor.error.open", "ファイルを開けませんでした!" }, - { "hex.builtin.view.hex_editor.error.create", "新しいファイルを作成できませんでした!" }, - { "hex.builtin.view.hex_editor.menu.edit.undo", "もとに戻す" }, - { "hex.builtin.view.hex_editor.menu.edit.redo", "やり直す" }, + { "hex.builtin.view.hex_editor.menu.file.save", "保存" }, + { "hex.builtin.view.hex_editor.menu.file.save_as", "名前をつけて保存..." }, { "hex.builtin.view.hex_editor.menu.edit.copy", "コピー" }, { "hex.builtin.view.hex_editor.menu.edit.copy_as", "〜としてコピー..." }, { "hex.builtin.view.hex_editor.copy.hex", "文字列" }, @@ -671,15 +664,15 @@ namespace hex::plugin::builtin { //{ "hex.builtin.setting.interface.wiki_explain_language", "Wikipedia Language" }, { "hex.builtin.setting.interface.fps", "FPS制限" }, { "hex.builtin.setting.interface.fps.unlocked", "無制限" }, - { "hex.builtin.setting.interface.highlight_alpha", "ハイライトの不透明度" }, { "hex.builtin.setting.hex_editor", "Hexエディタ" }, - { "hex.builtin.setting.hex_editor.column_count", "バイトのカラム数" }, - { "hex.builtin.setting.hex_editor.hexii", "Bytesの代わりにHexIIを表示する" }, + //{ "hex.builtin.setting.hex_editor.highlight_color", "Selection highlight color" }, + //{ "hex.builtin.setting.hex_editor.bytes_per_row", "Bytes per row" }, { "hex.builtin.setting.hex_editor.ascii", "ASCIIカラムを表示" }, { "hex.builtin.setting.hex_editor.advanced_decoding", "高度なデコードカラムを表示" }, { "hex.builtin.setting.hex_editor.grey_zeros", "ゼロをグレーアウト" }, { "hex.builtin.setting.hex_editor.uppercase_hex", "16進数を大文字表記" }, - { "hex.builtin.setting.hex_editor.extra_info", "追加情報を表示" }, + //{ "hex.builtin.setting.hex_editor.visualizer", "Data visualizer" }, + //{ "hex.builtin.setting.folders", "Folders" }, //{ "hex.builtin.setting.folders.description", "Specify additional search paths for patterns, scripts, rules and more" }, // { "hex.builtin.setting.folders.add_folder", "Add new folder" }, @@ -706,7 +699,24 @@ namespace hex::plugin::builtin { { "hex.builtin.provider.disk.sector_size", "セクタサイズ" }, { "hex.builtin.provider.disk.reload", "リロード" }, - { "hex.builtin.layouts.default", "標準" } + { "hex.builtin.layouts.default", "標準" }, + + //{ "hex.builtin.visualizer.hexadecimal.8bit", "Hexadecimal (8 bits)" }, + //{ "hex.builtin.visualizer.hexadecimal.16bit", "Hexadecimal (16 bits)" }, + //{ "hex.builtin.visualizer.hexadecimal.32bit", "Hexadecimal (32 bits)" }, + //{ "hex.builtin.visualizer.hexadecimal.64bit", "Hexadecimal (64 bits)" }, + //{ "hex.builtin.visualizer.decimal.signed.8bit", "Decimal Signed (8 bits)" }, + //{ "hex.builtin.visualizer.decimal.signed.16bit", "Decimal Signed (16 bits)" }, + //{ "hex.builtin.visualizer.decimal.signed.32bit", "Decimal Signed (32 bits)" }, + //{ "hex.builtin.visualizer.decimal.signed.64bit", "Decimal Signed (64 bits)" }, + //{ "hex.builtin.visualizer.decimal.unsigned.8bit", "Decimal Unsigned (8 bits)" }, + //{ "hex.builtin.visualizer.decimal.unsigned.16bit", "Decimal Unsigned (16 bits)" }, + //{ "hex.builtin.visualizer.decimal.unsigned.32bit", "Decimal Unsigned (32 bits)" }, + //{ "hex.builtin.visualizer.decimal.unsigned.64bit", "Decimal Unsigned (64 bits)" }, + //{ "hex.builtin.visualizer.floating_point.32bit", "Floating Point (32 bits)" }, + //{ "hex.builtin.visualizer.floating_point.64bit", "Floating Point (64 bits)" }, + //{ "hex.builtin.visualizer.hexii", "HexII" }, + //{ "hex.builtin.visualizer.rgba8", "RGBA8 Color" }, }); } diff --git a/plugins/builtin/source/lang/zh_CN.cpp b/plugins/builtin/source/lang/zh_CN.cpp index e8894eef3..2ac78a8d5 100644 --- a/plugins/builtin/source/lang/zh_CN.cpp +++ b/plugins/builtin/source/lang/zh_CN.cpp @@ -80,11 +80,41 @@ namespace hex::plugin::builtin { { "hex.builtin.common.open", "打开" }, { "hex.builtin.common.browse", "浏览..." }, { "hex.builtin.common.choose_file", "选择文件" }, + //{ "hex.common.processing", "Processing" }, - { "hex.builtin.message.file_handler_failed", "通过注册的文件处理器打开文件失败。" }, + { "hex.builtin.popup.exit_application.title", "退出?" }, + { "hex.builtin.popup.exit_application.desc", "工程还有为保存的更改。\n确定要退出吗?" }, + { "hex.builtin.popup.error.read_only", "无法获得写权限,文件以只读方式打开。" }, + { "hex.builtin.popup.error.open", "打开文件失败!" }, + { "hex.builtin.popup.error.create", "创建新文件失败!" }, { "hex.builtin.menu.file", "文件" }, + { "hex.builtin.menu.file.open_file", "打开文件..." }, + { "hex.builtin.menu.file.open_recent", "打开最近" }, + { "hex.builtin.menu.file.clear_recent", "清除" }, + { "hex.builtin.menu.file.open_other", "打开其他..." }, + { "hex.builtin.menu.file.close", "关闭" }, + { "hex.builtin.menu.file.quit", "退出ImHex" }, + { "hex.builtin.menu.file.open_project", "打开项目..." }, + { "hex.builtin.menu.file.save_project", "保存项目..." }, + { "hex.builtin.menu.file.import", "导入..." }, + { "hex.builtin.menu.file.import.base64", "Base64文件" }, + { "hex.builtin.menu.file.import.base64.popup.import_error", "文件不是有效的Base64格式!" }, + { "hex.builtin.menu.file.import.base64.popup.open_error", "打开文件失败!" }, + { "hex.builtin.view.hex_editor.menu.file.import.ips", "IPS补丁" }, + { "hex.builtin.view.hex_editor.menu.file.import.ips32", "IPS32补丁" }, + { "hex.builtin.view.hex_editor.menu.file.export", "导出..." }, + { "hex.builtin.view.hex_editor.menu.file.export.title", "导出文件" }, + { "hex.builtin.view.hex_editor.menu.file.export.ips", "IPS补丁" }, + { "hex.builtin.view.hex_editor.menu.file.export.ips32", "IPS32补丁" }, + //{ "hex.builtin.menu.file.export.base64.popup.export_error", "File is not in a valid Base64 format!" }, + //{ "hex.builtin.menu.file.export.popup.create", "Cannot export data. Failed to create file!" }, + { "hex.builtin.menu.edit", "编辑" }, + { "hex.builtin.view.hex_editor.menu.edit.undo", "撤销" }, + { "hex.builtin.view.hex_editor.menu.edit.redo", "重做" }, + { "hex.builtin.view.hex_editor.menu.edit.bookmark", "添加书签" }, + { "hex.builtin.menu.view", "视图" }, { "hex.builtin.menu.layout", "布局" }, { "hex.builtin.menu.view.fps", "显示FPS" }, @@ -206,48 +236,14 @@ namespace hex::plugin::builtin { { "hex.builtin.view.help.calc_cheat_sheet", "计算器帮助" }, { "hex.builtin.view.hex_editor.name", "Hex编辑器" }, - { "hex.builtin.view.hex_editor.create_file", "新建" }, - { "hex.builtin.view.hex_editor.open_file", "打开" }, - { "hex.builtin.view.hex_editor.open_project", "打开项目" }, - { "hex.builtin.view.hex_editor.save_project", "保存项目" }, - { "hex.builtin.view.hex_editor.save_data", "保存数据" }, - { "hex.builtin.view.hex_editor.open_base64", "打开Base64文件" }, - { "hex.builtin.view.hex_editor.load_enconding_file", "加载自定义编码定义文件" }, - { "hex.builtin.view.hex_editor.page", "页 {0} / {1}" }, - { "hex.builtin.view.hex_editor.save_as", "另存为" }, - { "hex.builtin.view.hex_editor.exit_application.title", "退出?" }, - { "hex.builtin.view.hex_editor.exit_application.desc", "工程还有为保存的更改。\n确定要退出吗?" }, - { "hex.builtin.view.hex_editor.script.title", "通过加载器脚本加载文件" }, - { "hex.builtin.view.hex_editor.script.desc", "通过Python加载器脚本加载文件。" }, - { "hex.builtin.view.hex_editor.script.script", "脚本" }, - { "hex.builtin.view.hex_editor.script.script.title", "加载器脚本:打开脚本" }, - { "hex.builtin.view.hex_editor.script.file", "文件" }, - { "hex.builtin.view.hex_editor.script.file.title", "加载器脚本:打开文件" }, - { "hex.builtin.view.hex_editor.processing", "导入 / 导出" }, + //{ "hex.builtin.view.hex_editor.page", "Page" }, + //{ "hex.builtin.view.hex_editor.selection", "Selection" }, + //{ "hex.builtin.view.hex_editor.selection.none", "None" }, + //{ "hex.builtin.view.hex_editor.region", "Region" }, + //{ "hex.builtin.view.hex_editor.data_size", "Data Size" }, + //{ "hex.builtin.view.hex_editor.no_bytes", "No bytes available" }, - { "hex.builtin.view.hex_editor.menu.file.open_file", "打开文件..." }, - { "hex.builtin.view.hex_editor.menu.file.open_recent", "打开最近" }, - { "hex.builtin.view.hex_editor.menu.file.clear_recent", "清除" }, - { "hex.builtin.view.hex_editor.menu.file.open_other", "打开其他..." }, - { "hex.builtin.view.hex_editor.menu.file.save", "保存" }, - { "hex.builtin.view.hex_editor.menu.file.save_as", "另存为..." }, - { "hex.builtin.view.hex_editor.menu.file.close", "关闭" }, - { "hex.builtin.view.hex_editor.menu.file.quit", "退出ImHex" }, - { "hex.builtin.view.hex_editor.menu.file.open_project", "打开项目..." }, - { "hex.builtin.view.hex_editor.menu.file.save_project", "保存项目..." }, { "hex.builtin.view.hex_editor.menu.file.load_encoding_file", "加载自定义编码..." }, - { "hex.builtin.view.hex_editor.menu.file.import", "导入..." }, - { "hex.builtin.view.hex_editor.menu.file.import.base64", "Base64文件" }, - { "hex.builtin.view.hex_editor.base64.import_error", "文件不是有效的Base64格式!" }, - { "hex.builtin.view.hex_editor.file_open_error", "打开文件失败!" }, - { "hex.builtin.view.hex_editor.menu.file.import.ips", "IPS补丁" }, - { "hex.builtin.view.hex_editor.menu.file.import.ips32", "IPS32补丁" }, - { "hex.builtin.view.hex_editor.menu.file.import.script", "带有加载器脚本的文件" }, - - { "hex.builtin.view.hex_editor.menu.file.export", "导出..." }, - { "hex.builtin.view.hex_editor.menu.file.export.title", "导出文件" }, - { "hex.builtin.view.hex_editor.menu.file.export.ips", "IPS补丁" }, - { "hex.builtin.view.hex_editor.menu.file.export.ips32", "IPS32补丁" }, { "hex.builtin.view.hex_editor.menu.file.search", "搜索" }, { "hex.builtin.view.hex_editor.search.string", "字符串" }, { "hex.builtin.view.hex_editor.search.hex", "Hex" }, @@ -256,14 +252,11 @@ namespace hex::plugin::builtin { { "hex.builtin.view.hex_editor.search.find_prev", "查找上一个" }, { "hex.builtin.view.hex_editor.menu.file.goto", "转到" }, { "hex.builtin.view.hex_editor.goto.offset.absolute", "绝对" }, - { "hex.builtin.view.hex_editor.goto.offset.current", "当前" }, + //{ "hex.builtin.view.hex_editor.goto.offset.relative", "Relative" }, { "hex.builtin.view.hex_editor.goto.offset.begin", "起始" }, { "hex.builtin.view.hex_editor.goto.offset.end", "末尾" }, - { "hex.builtin.view.hex_editor.error.read_only", "无法获得写权限,文件以只读方式打开。" }, - { "hex.builtin.view.hex_editor.error.open", "打开文件失败!" }, - { "hex.builtin.view.hex_editor.error.create", "创建新文件失败!" }, - { "hex.builtin.view.hex_editor.menu.edit.undo", "撤销" }, - { "hex.builtin.view.hex_editor.menu.edit.redo", "重做" }, + { "hex.builtin.view.hex_editor.menu.file.save", "保存" }, + { "hex.builtin.view.hex_editor.menu.file.save_as", "另存为..." }, { "hex.builtin.view.hex_editor.menu.edit.copy", "复制" }, { "hex.builtin.view.hex_editor.menu.edit.copy_as", "复制为..." }, { "hex.builtin.view.hex_editor.copy.hex", "字符串" }, @@ -278,7 +271,6 @@ namespace hex::plugin::builtin { { "hex.builtin.view.hex_editor.copy.html", "HTML" }, { "hex.builtin.view.hex_editor.menu.edit.paste", "粘贴" }, { "hex.builtin.view.hex_editor.menu.edit.select_all", "全选" }, - { "hex.builtin.view.hex_editor.menu.edit.bookmark", "添加书签" }, { "hex.builtin.view.hex_editor.menu.edit.set_base", "设置基地址" }, { "hex.builtin.view.hex_editor.menu.edit.resize", "修改大小..." }, { "hex.builtin.view.hex_editor.menu.edit.insert", "插入..." }, @@ -665,16 +657,14 @@ namespace hex::plugin::builtin { { "hex.builtin.setting.interface.wiki_explain_language", "维基百科语种" }, { "hex.builtin.setting.interface.fps", "FPS限制" }, { "hex.builtin.setting.interface.fps.unlocked", "无限制" }, - { "hex.builtin.setting.interface.highlight_alpha", "高亮不透明度" }, { "hex.builtin.setting.hex_editor", "Hex编辑器" }, - { "hex.builtin.setting.hex_editor.column_count", "字节列数" }, - { "hex.builtin.setting.hex_editor.hexii", "显示HexII替代字节" }, + //{ "hex.builtin.setting.hex_editor.highlight_color", "Selection highlight color" }, + //{ "hex.builtin.setting.hex_editor.bytes_per_row", "Bytes per row" }, { "hex.builtin.setting.hex_editor.ascii", "显示ASCII栏" }, { "hex.builtin.setting.hex_editor.advanced_decoding", "显示高级解码栏" }, { "hex.builtin.setting.hex_editor.grey_zeros", "显示零字节为灰色" }, { "hex.builtin.setting.hex_editor.uppercase_hex", "大写Hex字符" }, - { "hex.builtin.setting.hex_editor.extra_info", "显示额外信息" }, - + //{ "hex.builtin.setting.hex_editor.visualizer", "Data visualizer" }, { "hex.builtin.setting.folders", "扩展搜索路径" }, { "hex.builtin.setting.folders.description", "为模式、脚本和规则等指定额外的搜索路径" }, { "hex.builtin.setting.folders.add_folder", "添加新的目录" }, @@ -701,7 +691,24 @@ namespace hex::plugin::builtin { { "hex.builtin.provider.disk.sector_size", "扇区大小" }, { "hex.builtin.provider.disk.reload", "刷新" }, - { "hex.builtin.layouts.default", "默认" } + { "hex.builtin.layouts.default", "默认" }, + + //{ "hex.builtin.visualizer.hexadecimal.8bit", "Hexadecimal (8 bits)" }, + //{ "hex.builtin.visualizer.hexadecimal.16bit", "Hexadecimal (16 bits)" }, + //{ "hex.builtin.visualizer.hexadecimal.32bit", "Hexadecimal (32 bits)" }, + //{ "hex.builtin.visualizer.hexadecimal.64bit", "Hexadecimal (64 bits)" }, + //{ "hex.builtin.visualizer.decimal.signed.8bit", "Decimal Signed (8 bits)" }, + //{ "hex.builtin.visualizer.decimal.signed.16bit", "Decimal Signed (16 bits)" }, + //{ "hex.builtin.visualizer.decimal.signed.32bit", "Decimal Signed (32 bits)" }, + //{ "hex.builtin.visualizer.decimal.signed.64bit", "Decimal Signed (64 bits)" }, + //{ "hex.builtin.visualizer.decimal.unsigned.8bit", "Decimal Unsigned (8 bits)" }, + //{ "hex.builtin.visualizer.decimal.unsigned.16bit", "Decimal Unsigned (16 bits)" }, + //{ "hex.builtin.visualizer.decimal.unsigned.32bit", "Decimal Unsigned (32 bits)" }, + //{ "hex.builtin.visualizer.decimal.unsigned.64bit", "Decimal Unsigned (64 bits)" }, + //{ "hex.builtin.visualizer.floating_point.32bit", "Floating Point (32 bits)" }, + //{ "hex.builtin.visualizer.floating_point.64bit", "Floating Point (64 bits)" }, + //{ "hex.builtin.visualizer.hexii", "HexII" }, + //{ "hex.builtin.visualizer.rgba8", "RGBA8 Color" }, }); } diff --git a/plugins/builtin/source/math_evaluator.cpp b/plugins/builtin/source/math_evaluator.cpp index 522ba588f..0a035a23c 100644 --- a/plugins/builtin/source/math_evaluator.cpp +++ b/plugins/builtin/source/math_evaluator.cpp @@ -1,5 +1,8 @@ #include "math_evaluator.hpp" +#include +#include + #include #include #include @@ -10,15 +13,18 @@ namespace hex { - i16 comparePrecedence(const Operator &a, const Operator &b) { + template + i16 MathEvaluator::comparePrecedence(const Operator &a, const Operator &b) { return static_cast((static_cast(a) & 0x0F0) - (static_cast(b) & 0x0F0)); } - bool isLeftAssociative(const Operator op) { + template + bool MathEvaluator::isLeftAssociative(const Operator op) { return (static_cast(op) & 0xF00) == 0; } - std::pair toOperator(const std::string &input) { + template + std::pair::Operator, size_t> MathEvaluator::toOperator(const std::string &input) { if (input.starts_with("##")) return { Operator::Combine, 2 }; if (input.starts_with("==")) return { Operator::Equals, 2 }; if (input.starts_with("!=")) return { Operator::NotEquals, 2 }; @@ -47,7 +53,8 @@ namespace hex { return { Operator::Invalid, 0 }; } - static std::queue toPostfix(std::queue inputQueue) { + template + std::optional::Token>> MathEvaluator::toPostfix(std::queue inputQueue) { std::queue outputQueue; std::stack operatorStack; @@ -67,12 +74,16 @@ namespace hex { if (currToken.bracketType == BracketType::Left) operatorStack.push(currToken); else { - if (operatorStack.empty()) - throw std::invalid_argument("Mismatching parenthesis!"); + if (operatorStack.empty()) { + this->setError("Mismatching parenthesis!"); + return std::nullopt; + } while (operatorStack.top().type != TokenType::Bracket || (operatorStack.top().type == TokenType::Bracket && operatorStack.top().bracketType != BracketType::Left)) { - if (operatorStack.empty()) - throw std::invalid_argument("Mismatching parenthesis!"); + if (operatorStack.empty()) { + this->setError("Mismatching parenthesis!"); + return std::nullopt; + } outputQueue.push(operatorStack.top()); operatorStack.pop(); @@ -86,8 +97,10 @@ namespace hex { while (!operatorStack.empty()) { auto top = operatorStack.top(); - if (top.type == TokenType::Bracket) - throw std::invalid_argument("Mismatching parenthesis!"); + if (top.type == TokenType::Bracket) { + this->setError("Mismatching parenthesis!"); + return std::nullopt; + } outputQueue.push(top); operatorStack.pop(); @@ -96,13 +109,23 @@ namespace hex { return outputQueue; } - std::queue MathEvaluator::parseInput(std::string input) { + template + std::optional::Token>> MathEvaluator::parseInput(std::string input) { std::queue inputQueue; char *prevPos = input.data(); for (char *pos = prevPos; *pos != 0x00;) { if (std::isdigit(*pos) || *pos == '.') { - auto number = std::strtold(pos, &pos); + auto number = [&] { + if constexpr (hex::floating_point) + return std::strtold(pos, &pos); + else if constexpr (hex::signed_integral) + return std::strtoll(pos, &pos, 10); + else if constexpr (hex::unsigned_integral) + return std::strtoull(pos, &pos, 10); + else + static_assert(hex::always_false::value, "Can't parse literal of this type"); + }(); if (*pos == 'x') { pos--; @@ -159,17 +182,26 @@ namespace hex { pos++; for (const auto &expression : expressions) { - if (expression.empty() && expressions.size() > 1) - throw std::invalid_argument("Invalid function call syntax!"); + if (expression.empty() && expressions.size() > 1) { + this->setError("Invalid function call syntax!"); + return std::nullopt; + } else if (expression.empty()) break; auto newInputQueue = parseInput(expression); - auto postfixTokens = toPostfix(newInputQueue); - auto result = evaluate(postfixTokens); + if (!newInputQueue.has_value()) + return std::nullopt; - if (!result.has_value()) - throw std::invalid_argument("Invalid argument for function!"); + auto postfixTokens = toPostfix(*newInputQueue); + if (!postfixTokens.has_value()) + return std::nullopt; + + auto result = evaluate(*postfixTokens); + if (!result.has_value()) { + this->setError("Invalid argument for function!"); + return std::nullopt; + } token.arguments.push_back(result.value()); } @@ -184,8 +216,10 @@ namespace hex { } } - if (prevPos == pos) - throw std::invalid_argument("Invalid syntax!"); + if (prevPos == pos) { + this->setError("Invalid syntax!"); + return std::nullopt; + } prevPos = pos; } @@ -193,8 +227,9 @@ namespace hex { return inputQueue; } - std::optional MathEvaluator::evaluate(std::queue postfixTokens) { - std::stack evaluationStack; + template + std::optional MathEvaluator::evaluate(std::queue postfixTokens) { + std::stack evaluationStack; while (!postfixTokens.empty()) { auto front = postfixTokens.front(); @@ -203,13 +238,16 @@ namespace hex { if (front.type == TokenType::Number) evaluationStack.push(front.number); else if (front.type == TokenType::Operator) { - long double rightOperand, leftOperand; + T rightOperand, leftOperand; if (evaluationStack.size() < 2) { if ((front.op == Operator::Addition || front.op == Operator::Subtraction || front.op == Operator::Not || front.op == Operator::BitwiseNot) && evaluationStack.size() == 1) { rightOperand = evaluationStack.top(); evaluationStack.pop(); leftOperand = 0; - } else throw std::invalid_argument("Not enough operands for operator!"); + } else { + this->setError("Not enough operands for operator!"); + return std::nullopt; + } } else { rightOperand = evaluationStack.top(); evaluationStack.pop(); @@ -217,11 +255,17 @@ namespace hex { evaluationStack.pop(); } - long double result = std::numeric_limits::quiet_NaN(); + T result = [] { + if constexpr (std::numeric_limits::has_quiet_NaN) + return std::numeric_limits::quiet_NaN(); + else + return 0; + }(); switch (front.op) { default: case Operator::Invalid: - throw std::invalid_argument("Invalid operator!"); + this->setError("Invalid operator!"); + return std::nullopt; case Operator::And: result = static_cast(leftOperand) && static_cast(rightOperand); break; @@ -283,10 +327,16 @@ namespace hex { result = leftOperand / rightOperand; break; case Operator::Modulus: - result = std::fmod(leftOperand, rightOperand); + if constexpr (std::floating_point) + result = std::fmod(leftOperand, rightOperand); + else + result = leftOperand % rightOperand; break; case Operator::Exponentiation: - result = std::pow(leftOperand, rightOperand); + if constexpr (std::floating_point) + result = std::pow(leftOperand, rightOperand); + else + result = hex::powi(leftOperand, rightOperand); break; case Operator::Combine: result = (static_cast(leftOperand) << (64 - __builtin_clzll(static_cast(rightOperand)))) | static_cast(rightOperand); @@ -297,51 +347,66 @@ namespace hex { } else if (front.type == TokenType::Variable) { if (this->m_variables.contains(front.name)) evaluationStack.push(this->m_variables.at(front.name)); - else - throw std::invalid_argument("Unknown variable!"); + else { + this->setError("Unknown variable!"); + return std::nullopt; + } } else if (front.type == TokenType::Function) { - if (!this->m_functions[front.name]) - throw std::invalid_argument("Unknown function called!"); + if (!this->m_functions[front.name]) { + this->setError("Unknown function called!"); + return std::nullopt; + } auto result = this->m_functions[front.name](front.arguments); if (result.has_value()) evaluationStack.push(result.value()); - } else - throw std::invalid_argument("Parenthesis in postfix expression!"); + } else { + this->setError("Parenthesis in postfix expression!"); + return std::nullopt; + } } - if (evaluationStack.empty()) + if (evaluationStack.empty()) { return std::nullopt; - else if (evaluationStack.size() > 1) - throw std::invalid_argument("Undigested input left!"); - else + } + else if (evaluationStack.size() > 1) { + this->setError("Undigested input left!"); + return std::nullopt; + } + else { return evaluationStack.top(); + } } - std::optional MathEvaluator::evaluate(const std::string &input) { + template + std::optional MathEvaluator::evaluate(const std::string &input) { auto inputQueue = parseInput(input); + if (!inputQueue.has_value()) + return std::nullopt; std::string resultVariable = "ans"; { - std::queue queueCopy = inputQueue; + auto queueCopy = *inputQueue; if (queueCopy.front().type == TokenType::Variable) { resultVariable = queueCopy.front().name; queueCopy.pop(); if (queueCopy.front().type != TokenType::Operator || queueCopy.front().op != Operator::Assign) resultVariable = "ans"; else { - inputQueue.pop(); - inputQueue.pop(); + inputQueue->pop(); + inputQueue->pop(); } } } - auto postfixTokens = toPostfix(inputQueue); + auto postfixTokens = toPostfix(*inputQueue); + if (!postfixTokens.has_value()) + return std::nullopt; - auto result = evaluate(postfixTokens); + auto result = evaluate(*postfixTokens); if (result.has_value()) { this->setVariable(resultVariable, result.value()); @@ -350,48 +415,59 @@ namespace hex { return result; } - void MathEvaluator::setVariable(const std::string &name, long double value) { + template + void MathEvaluator::setVariable(const std::string &name, T value) { this->m_variables[name] = value; } - void MathEvaluator::setFunction(const std::string &name, const std::function(std::vector)> &function, size_t minNumArgs, size_t maxNumArgs) { - this->m_functions[name] = [minNumArgs, maxNumArgs, function](auto args) { - if (args.size() < minNumArgs || args.size() > maxNumArgs) - throw std::invalid_argument("Invalid number of function arguments!"); + template + void MathEvaluator::setFunction(const std::string &name, const std::function(std::vector)> &function, size_t minNumArgs, size_t maxNumArgs) { + this->m_functions[name] = [this, minNumArgs, maxNumArgs, function](auto args) -> std::optional { + if (args.size() < minNumArgs || args.size() > maxNumArgs) { + this->setError("Invalid number of function arguments!"); + return std::nullopt; + } return function(args); }; } - void MathEvaluator::registerStandardVariables() { + template + void MathEvaluator::registerStandardVariables() { this->setVariable("ans", 0); } - void MathEvaluator::registerStandardFunctions() { - this->setFunction( - "sin", [](auto args) { return std::sin(args[0]); }, 1, 1); - this->setFunction( - "cos", [](auto args) { return std::cos(args[0]); }, 1, 1); - this->setFunction( - "tan", [](auto args) { return std::tan(args[0]); }, 1, 1); - this->setFunction( - "sqrt", [](auto args) { return std::sqrt(args[0]); }, 1, 1); - this->setFunction( - "ceil", [](auto args) { return std::ceil(args[0]); }, 1, 1); - this->setFunction( - "floor", [](auto args) { return std::floor(args[0]); }, 1, 1); - this->setFunction( - "sign", [](auto args) { return (args[0] > 0) ? 1 : (args[0] == 0) ? 0 - : -1; }, 1, 1); - this->setFunction( - "abs", [](auto args) { return std::abs(args[0]); }, 1, 1); - this->setFunction( - "ln", [](auto args) { return std::log(args[0]); }, 1, 1); - this->setFunction( - "lb", [](auto args) { return std::log2(args[0]); }, 1, 1); - this->setFunction( - "log", [](auto args) { return args.size() == 1 ? std::log10(args[0]) : std::log(args[1]) / std::log(args[0]); }, 1, 2); + template + void MathEvaluator::registerStandardFunctions() { + if constexpr (hex::floating_point) { + this->setFunction( + "sin", [](auto args) { return std::sin(args[0]); }, 1, 1); + this->setFunction( + "cos", [](auto args) { return std::cos(args[0]); }, 1, 1); + this->setFunction( + "tan", [](auto args) { return std::tan(args[0]); }, 1, 1); + this->setFunction( + "sqrt", [](auto args) { return std::sqrt(args[0]); }, 1, 1); + this->setFunction( + "ceil", [](auto args) { return std::ceil(args[0]); }, 1, 1); + this->setFunction( + "floor", [](auto args) { return std::floor(args[0]); }, 1, 1); + this->setFunction( + "sign", [](auto args) { return (args[0] > 0) ? 1 : (args[0] == 0) ? 0 + : -1; }, 1, 1); + this->setFunction( + "abs", [](auto args) { return std::abs(args[0]); }, 1, 1); + this->setFunction( + "ln", [](auto args) { return std::log(args[0]); }, 1, 1); + this->setFunction( + "lb", [](auto args) { return std::log2(args[0]); }, 1, 1); + this->setFunction( + "log", [](auto args) { return args.size() == 1 ? std::log10(args[0]) : std::log(args[1]) / std::log(args[0]); }, 1, 2); + } } + template class MathEvaluator; + template class MathEvaluator; + } diff --git a/plugins/builtin/source/pattern_drawer.cpp b/plugins/builtin/source/pattern_drawer.cpp index 44e2f9e17..6d5bdb929 100644 --- a/plugins/builtin/source/pattern_drawer.cpp +++ b/plugins/builtin/source/pattern_drawer.cpp @@ -65,13 +65,10 @@ namespace hex { ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "bits"); ImGui::TableNextColumn(); - u64 extractedValue = pattern.getValue(); - ImGui::TextFormatted("{}", pattern.formatDisplayValue(hex::format("{0} (0x{1:X})", extractedValue, extractedValue), &pattern)); + ImGui::TextFormatted("{}", pattern.getFormattedValue()); } void PatternDrawer::visit(pl::PatternBitfield& pattern) { - std::vector value = pattern.getValue(); - bool open = true; if (!pattern.isInlined()) { ImGui::TableNextRow(); @@ -85,12 +82,7 @@ namespace hex { drawSizeColumn(pattern); drawTypenameColumn(pattern, "bitfield"); - std::string valueString = "{ "; - for (auto i : value) - valueString += hex::format("{0:02X} ", i); - valueString += "}"; - - ImGui::TextFormatted("{}", pattern.formatDisplayValue(valueString, &pattern)); + ImGui::TextFormatted("{}", pattern.getFormattedValue()); } else { ImGui::SameLine(); ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); @@ -106,50 +98,14 @@ namespace hex { } void PatternDrawer::visit(pl::PatternBoolean& pattern) { - u8 boolean = pattern.getValue(); - - if (boolean == 0) - this->createDefaultEntry(pattern, "false", false); - else if (boolean == 1) - this->createDefaultEntry(pattern, "true", true); - else - this->createDefaultEntry(pattern, "true*", true); + this->createDefaultEntry(pattern, pattern.getFormattedValue(), static_cast(pattern.getValue())); } void PatternDrawer::visit(pl::PatternCharacter& pattern) { - char character = pattern.getValue(); - this->createDefaultEntry(pattern, hex::format("'{0}'", character), character); + this->createDefaultEntry(pattern, pattern.getFormattedValue(), pattern.getValue()); } void PatternDrawer::visit(pl::PatternEnum& pattern) { - u64 value = pattern.getValue(); - - std::string valueString = pattern.getTypeName() + "::"; - - bool foundValue = false; - for (auto &[entryValueLiteral, entryName] : pattern.getEnumValues()) { - auto visitor = overloaded { - [&, name = entryName](auto &entryValue) { - if (static_cast(value) == entryValue) { - valueString += name; - foundValue = true; - return true; - } - - return false; - }, - [](const std::string &) { return false; }, - [](pl::Pattern *) { return false; }, - }; - - bool matches = std::visit(visitor, entryValueLiteral); - if (matches) - break; - } - - if (!foundValue) - valueString += "???"; - ImGui::TableNextRow(); createLeafNode(pattern); drawCommentTooltip(pattern); @@ -161,20 +117,14 @@ namespace hex { drawOffsetColumn(pattern); drawSizeColumn(pattern); drawTypenameColumn(pattern, "enum"); - ImGui::TextFormatted("{}", pattern.formatDisplayValue(hex::format("{} (0x{:0{}X})", valueString.c_str(), value, pattern.getSize() * 2), &pattern)); + ImGui::TextFormatted("{}", pattern.getFormattedValue()); } void PatternDrawer::visit(pl::PatternFloat& pattern) { if (pattern.getSize() == 4) { - float f32 = static_cast(pattern.getValue()); - u32 integerResult = 0; - std::memcpy(&integerResult, &f32, sizeof(float)); - this->createDefaultEntry(pattern, hex::format("{:e} (0x{:0{}X})", f32, integerResult, pattern.getSize() * 2), f32); + this->createDefaultEntry(pattern, pattern.getFormattedValue(), static_cast(pattern.getValue())); } else if (pattern.getSize() == 8) { - double f64 = pattern.getValue(); - u64 integerResult = 0; - std::memcpy(&integerResult, &f64, sizeof(double)); - this->createDefaultEntry(pattern, hex::format("{:e} (0x{:0{}X})", f64, integerResult, pattern.getSize() * 2), f64); + this->createDefaultEntry(pattern, pattern.getFormattedValue(), static_cast(pattern.getValue())); } } @@ -184,8 +134,6 @@ namespace hex { } void PatternDrawer::visit(pl::PatternPointer& pattern) { - u64 data = pattern.getValue(); - bool open = true; if (!pattern.isInlined()) { @@ -201,7 +149,7 @@ namespace hex { drawSizeColumn(pattern); ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{}", pattern.getFormattedName()); ImGui::TableNextColumn(); - ImGui::TextFormatted("{}", pattern.formatDisplayValue(hex::format("*(0x{0:X})", data), u128(data))); + ImGui::TextFormatted("{}", pattern.getFormattedValue()); } else { ImGui::SameLine(); ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); @@ -215,18 +163,11 @@ namespace hex { } void PatternDrawer::visit(pl::PatternSigned& pattern) { - i128 data = pattern.getValue(); - this->createDefaultEntry(pattern, hex::format("{:d} (0x{:0{}X})", data, data, 1 * 2), data); + this->createDefaultEntry(pattern, pattern.getFormattedValue(), pattern.getValue()); } void PatternDrawer::visit(pl::PatternString& pattern) { - auto size = std::min(pattern.getSize(), 0x7F); - - if (size == 0) - return; - - std::string displayString = pattern.getValue(size); - this->createDefaultEntry(pattern, hex::format("\"{0}\" {1}", displayString, size > pattern.getSize() ? "(truncated)" : ""), displayString); + this->createDefaultEntry(pattern, pattern.getFormattedValue(), pattern.getValue()); } void PatternDrawer::visit(pl::PatternStruct& pattern) { @@ -243,7 +184,7 @@ namespace hex { drawOffsetColumn(pattern); drawSizeColumn(pattern); drawTypenameColumn(pattern, "struct"); - ImGui::TextFormatted("{}", pattern.formatDisplayValue("{ ... }", &pattern)); + ImGui::TextFormatted("{}", pattern.getFormattedValue()); } else { ImGui::SameLine(); ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); @@ -272,7 +213,7 @@ namespace hex { drawOffsetColumn(pattern); drawSizeColumn(pattern); drawTypenameColumn(pattern, "union"); - ImGui::TextFormatted("{}", pattern.formatDisplayValue("{ ... }", &pattern)); + ImGui::TextFormatted("{}", pattern.getFormattedValue()); } else { ImGui::SameLine(); ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); @@ -288,26 +229,17 @@ namespace hex { } void PatternDrawer::visit(pl::PatternUnsigned& pattern) { - u128 data = pattern.getValue(); - this->createDefaultEntry(pattern, hex::format("{:d} (0x{:0{}X})", data, data, pattern.getSize() * 2), data); + this->createDefaultEntry(pattern, pattern.getFormattedValue(), pattern.getValue()); } void PatternDrawer::visit(pl::PatternWideCharacter& pattern) { - char16_t character = pattern.getValue(); - u128 literal = character; - auto str = std::wstring_convert, char16_t> {}.to_bytes(character); - this->createDefaultEntry(pattern, hex::format("'{0}'", str), literal); + this->createDefaultEntry(pattern, pattern.getFormattedValue(), u128(pattern.getValue())); } void PatternDrawer::visit(pl::PatternWideString& pattern) { - auto size = std::min(pattern.getSize(), 0x100); + std::string utf8String = pattern.getValue(); - if (size == 0) - return; - - std::string utf8String = pattern.getValue(size); - - this->createDefaultEntry(pattern, hex::format("\"{0}\" {1}", utf8String, size > pattern.getSize() ? "(truncated)" : ""), utf8String); + this->createDefaultEntry(pattern, pattern.getFormattedValue(), utf8String); } void PatternDrawer::createDefaultEntry(const pl::Pattern &pattern, const std::string &value, pl::Token::Literal &&literal) const { @@ -323,7 +255,7 @@ namespace hex { drawColorColumn(pattern); drawOffsetColumn(pattern); drawSizeColumn(pattern); - ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{}", pattern.getTypeName().empty() ? pattern.getFormattedName() : pattern.getTypeName()); + ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{}", pattern.getFormattedName().empty() ? pattern.getTypeName() : pattern.getFormattedName()); ImGui::TableNextColumn(); ImGui::TextFormatted("{}", pattern.formatDisplayValue(value, literal)); } @@ -380,7 +312,7 @@ namespace hex { ImGui::TextUnformatted("]"); ImGui::TableNextColumn(); - ImGui::TextFormatted("{}", pattern.formatDisplayValue("{ ... }", &pattern)); + ImGui::TextFormatted("{}", pattern.getFormattedValue()); } else { ImGui::SameLine(); ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); diff --git a/plugins/builtin/source/plugin_builtin.cpp b/plugins/builtin/source/plugin_builtin.cpp index 6cf9c3745..2f3cf7470 100644 --- a/plugins/builtin/source/plugin_builtin.cpp +++ b/plugins/builtin/source/plugin_builtin.cpp @@ -2,7 +2,8 @@ namespace hex::plugin::builtin { - void registerViews(); + void registerEventHandlers(); + void registerDataVisualizers(); void registerDataInspectorEntries(); void registerToolEntries(); void registerPatternLanguageFunctions(); @@ -15,9 +16,11 @@ namespace hex::plugin::builtin { void registerLayouts(); void registerMainMenuEntries(); void createWelcomeScreen(); + void registerViews(); void addFooterItems(); void addToolbarItems(); + void addGlobalUIItems(); void registerLanguageEnUS(); void registerLanguageDeDE(); @@ -37,7 +40,8 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") { registerLanguageJaJP(); registerLanguageZhCN(); - registerViews(); + registerEventHandlers(); + registerDataVisualizers(); registerDataInspectorEntries(); registerToolEntries(); registerPatternLanguageFunctions(); @@ -48,9 +52,12 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") { registerProviders(); registerDataFormatters(); createWelcomeScreen(); + registerViews(); addFooterItems(); addToolbarItems(); + addGlobalUIItems(); + registerLayouts(); registerMainMenuEntries(); }