From 59429fe2368e9d2d70d2f7d827dd3c9f12556310 Mon Sep 17 00:00:00 2001 From: Ignacio Sanchez Gines <863613+drhelius@users.noreply.github.com> Date: Sat, 14 Dec 2024 19:22:38 +0100 Subject: [PATCH] Upgrade mem editor from Geargrafx --- platforms/desktop-shared/gui_debug.cpp | 144 +++- .../desktop-shared/imgui/memory_editor.cpp | 648 +++++++++++++----- .../desktop-shared/imgui/memory_editor.h | 37 +- 3 files changed, 621 insertions(+), 208 deletions(-) diff --git a/platforms/desktop-shared/gui_debug.cpp b/platforms/desktop-shared/gui_debug.cpp index 5b56166..5c48c21 100644 --- a/platforms/desktop-shared/gui_debug.cpp +++ b/platforms/desktop-shared/gui_debug.cpp @@ -21,6 +21,7 @@ #include "imgui/imgui.h" #include "imgui/memory_editor.h" #include "imgui/colors.h" +#include "nfd/nfd.h" #include "config.h" #include "emu.h" #include "renderer.h" @@ -60,9 +61,11 @@ static bool goto_address_requested = false; static u16 goto_address_target = 0; static bool goto_back_requested = false; static int goto_back = 0; +static char set_value_buffer[5] = {0}; static void debug_window_processor(void); static void debug_window_memory(void); +static void memory_editor_menu(void); static void debug_window_disassembler(void); static void debug_window_vram_registers(void); static void debug_window_vram(void); @@ -197,61 +200,132 @@ void gui_debug_go_back(void) void gui_debug_copy_memory(void) { - int size = 0; - u8* data = NULL; - mem_edit[current_mem_edit].Copy(&data, &size); + mem_edit[current_mem_edit].Copy(); +} - if (IsValidPointer(data)) - { - std::string text; +void gui_debug_paste_memory(void) +{ + mem_edit[current_mem_edit].Paste(); +} + +static void memory_editor_menu(void) +{ + ImGui::BeginMenuBar(); - for (int i = 0; i < size; i++) + if (ImGui::BeginMenu("File")) + { + if (ImGui::MenuItem("Save Memory As...")) { - char byte[3]; - sprintf(byte, "%02X", data[i]); - if (i > 0) - text += " "; - text += byte; + nfdchar_t *outPath; + nfdfilteritem_t filterItem[1] = { { "Memory Dump Files", "txt" } }; + nfdresult_t result = NFD_SaveDialog(&outPath, filterItem, 1, NULL, NULL); + if (result == NFD_OKAY) + { + mem_edit[current_mem_edit].SaveToFile(outPath); + NFD_FreePath(outPath); + } + else if (result != NFD_CANCEL) + { + Log("Save Memory Dump Error: %s", NFD_GetError()); + } } - SDL_SetClipboardText(text.c_str()); + ImGui::EndMenu(); } -} -void gui_debug_paste_memory(void) -{ - char* clipboard = SDL_GetClipboardText(); - - if (IsValidPointer(clipboard)) + if (ImGui::BeginMenu("Edit")) { - std::string text(clipboard); + if (ImGui::MenuItem("Copy", "Ctrl+C")) + { + gui_debug_copy_memory(); + } - text.erase(std::remove(text.begin(), text.end(), ' '), text.end()); + if (ImGui::MenuItem("Paste", "Ctrl+V")) + { + gui_debug_paste_memory(); + } - size_t buffer_size = text.size() / 2; - u8* data = new u8[buffer_size]; + ImGui::EndMenu(); + } - for (size_t i = 0; i < buffer_size; i ++) + if (ImGui::BeginMenu("Selection")) + { + if (ImGui::MenuItem("Select All", "Ctrl+A")) + { + mem_edit[current_mem_edit].SelectAll(); + } + + if (ImGui::MenuItem("Clear Selection")) { - std::string byte = text.substr(i * 2, 2); + mem_edit[current_mem_edit].ClearSelection(); + } - try + if (ImGui::BeginMenu("Set value")) + { + ImGui::SetNextItemWidth(50); + if (ImGui::InputTextWithHint("##set_value", "XXXX", set_value_buffer, IM_ARRAYSIZE(set_value_buffer), ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) { - data[i] = (u8)std::stoul(byte, 0, 16); + try + { + mem_edit[current_mem_edit].SetValueToSelection((int)std::stoul(set_value_buffer, 0, 16)); + set_value_buffer[0] = 0; + } + catch(const std::invalid_argument&) + { + } } - catch(const std::invalid_argument&) + ImGui::SameLine(); + if (ImGui::Button("Set!", ImVec2(40, 0))) { - delete[] data; - return; + try + { + mem_edit[current_mem_edit].SetValueToSelection((int)std::stoul(set_value_buffer, 0, 16)); + set_value_buffer[0] = 0; + } + catch(const std::invalid_argument&) + { + } } + ImGui::EndMenu(); } - mem_edit[current_mem_edit].Paste(data, buffer_size); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Bookmarks")) + { + if (ImGui::MenuItem("Clear All")) + { + mem_edit[current_mem_edit].RemoveBookmarks(); + } - delete[] data; + if (ImGui::MenuItem("Add Bookmark")) + { + mem_edit[current_mem_edit].AddBookmark(); + } + + std::vector* bookmarks = mem_edit[current_mem_edit].GetBookmarks(); + + if (bookmarks->size() > 0) + ImGui::Separator(); + + for (long unsigned int i = 0; i < bookmarks->size(); i++) + { + MemEditor::Bookmark* bookmark = &(*bookmarks)[i]; + + char label[80]; + snprintf(label, 80, "$%04X: %s", bookmark->address, bookmark->name); + + if (ImGui::MenuItem(label)) + { + mem_edit[current_mem_edit].JumpToAddress(bookmark->address); + } + } + + ImGui::EndMenu(); } - SDL_free(clipboard); + ImGui::EndMenuBar(); } static void debug_window_memory(void) @@ -260,7 +334,9 @@ static void debug_window_memory(void) ImGui::SetNextWindowPos(ImVec2(567, 249), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(324, 308), ImGuiCond_FirstUseEver); - ImGui::Begin("Memory Editor", &config_debug.show_memory); + ImGui::Begin("Memory Editor", &config_debug.show_memory, ImGuiWindowFlags_MenuBar); + + memory_editor_menu(); GearcolecoCore* core = emu_get_core(); Memory* memory = core->GetMemory(); diff --git a/platforms/desktop-shared/imgui/memory_editor.cpp b/platforms/desktop-shared/imgui/memory_editor.cpp index 2382544..983fbc4 100644 --- a/platforms/desktop-shared/imgui/memory_editor.cpp +++ b/platforms/desktop-shared/imgui/memory_editor.cpp @@ -17,9 +17,11 @@ * */ -#include "memory_editor.h" #include #include +#include +#include +#include "memory_editor.h" #include "colors.h" MemEditor::MemEditor() @@ -40,6 +42,10 @@ MemEditor::MemEditor() m_mem_data = NULL; m_mem_size = 0; m_mem_base_addr = 0; + m_mem_word = 1; + m_goto_address[0] = 0; + m_add_bookmark = false; + m_draw_list = 0; } MemEditor::~MemEditor() @@ -47,11 +53,27 @@ MemEditor::~MemEditor() } -void MemEditor::Draw(uint8_t* mem_data, int mem_size, int base_display_addr) +void MemEditor::Draw(uint8_t* mem_data, int mem_size, int base_display_addr, int word, bool ascii, bool preview, bool options, bool cursors) { m_mem_data = mem_data; m_mem_size = mem_size; m_mem_base_addr = base_display_addr; + m_mem_word = word; + if (m_mem_word > 2) + m_mem_word = 2; + + if ((m_mem_word > 1) && ((m_preview_data_type < 2) || (m_preview_data_type > 3))) + m_preview_data_type = 2; + + uint8_t hex_digits = 1; + int size = m_mem_size - 1; + + while (size >>= 4) + { + hex_digits++; + } + + snprintf(m_hex_mem_format, 8, "%%0%dX", hex_digits); ImVec4 addr_color = cyan; ImVec4 ascii_color = magenta; @@ -60,36 +82,53 @@ void MemEditor::Draw(uint8_t* mem_data, int mem_size, int base_display_addr) ImVec4 highlight_color = orange; ImVec4 gray_color = mid_gray; - int total_rows = (mem_size + (m_bytes_per_row - 1)) / m_bytes_per_row; + int total_rows = (m_mem_size + (m_bytes_per_row - 1)) / m_bytes_per_row; int separator_count = (m_bytes_per_row - 1) / 4; int byte_column_count = 2 + m_bytes_per_row + separator_count + 2; int byte_cell_padding = 0; + int ascii_padding = 4; int character_cell_padding = 0; - int max_chars_per_cell = 2; + int max_chars_per_cell = 2 * m_mem_word; ImVec2 character_size = ImGui::CalcTextSize("0"); - float footer_height = (ImGui::GetFrameHeightWithSpacing() * 4) + 4; + float footer_height = 0; + + if (options) + footer_height += ImGui::GetFrameHeightWithSpacing(); + if (preview) + footer_height += ((character_size.y + 4) * 3) + 4; + if (cursors) + footer_height += ImGui::GetFrameHeightWithSpacing(); + char buf[32]; if (ImGui::BeginChild("##mem", ImVec2(ImGui::GetContentRegionAvail().x, -footer_height), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav)) { + m_draw_list = ImGui::GetWindowDrawList(); + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.5, 0)); if (ImGui::BeginTable("##header", byte_column_count, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoKeepColumnsVisible)) { - ImGui::TableSetupColumn("ADDR "); + char addr_spaces[32]; + int addr_padding = hex_digits - 2; + snprintf(addr_spaces, 32, "ADDR %*s", addr_padding, ""); + ImGui::TableSetupColumn(addr_spaces); ImGui::TableSetupColumn(""); for (int i = 0; i < m_bytes_per_row; i++) { if (IsColumnSeparator(i, m_bytes_per_row)) ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, m_separator_column_width); - sprintf(buf, "%02X", i); + snprintf(buf, 32, "%02X", i); ImGui::TableSetupColumn(buf, ImGuiTableColumnFlags_WidthFixed, character_size.x * max_chars_per_cell + (6 + byte_cell_padding) * 1); } - ImGui::TableSetupColumn(""); - ImGui::TableSetupColumn("ASCII", ImGuiTableColumnFlags_WidthFixed, (character_size.x + character_cell_padding * 1) * m_bytes_per_row); + if ((m_mem_word == 1) && ascii) + { + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, character_size.x * ascii_padding); + ImGui::TableSetupColumn("ASCII", ImGuiTableColumnFlags_WidthFixed, (character_size.x + character_cell_padding * 1) * m_bytes_per_row); + } ImGui::TableNextRow(); @@ -101,16 +140,19 @@ void MemEditor::Draw(uint8_t* mem_data, int mem_size, int base_display_addr) ImGui::TextColored(column_color, "%s", ImGui::TableGetColumnName(i)); } - ImGui::TableNextColumn(); - ImGui::TextColored(ascii_color, "%s", ImGui::TableGetColumnName(ImGui::TableGetColumnCount() - 1)); + if ((m_mem_word == 1) && ascii) + { + ImGui::TableNextColumn(); + ImGui::TextColored(ascii_color, "%s", ImGui::TableGetColumnName(ImGui::TableGetColumnCount() - 1)); + } ImGui::EndTable(); } if (ImGui::BeginTable("##hex", byte_column_count, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoKeepColumnsVisible | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) { - m_row_scroll_top = ImGui::GetScrollY() / character_size.y; - m_row_scroll_bottom = m_row_scroll_top + (ImGui::GetWindowHeight() / character_size.y); + m_row_scroll_top = (int)(ImGui::GetScrollY() / character_size.y); + m_row_scroll_bottom = m_row_scroll_top + (int)(ImGui::GetWindowHeight() / character_size.y); ImGui::TableSetupColumn("ADDR"); ImGui::TableSetupColumn(""); @@ -119,12 +161,10 @@ void MemEditor::Draw(uint8_t* mem_data, int mem_size, int base_display_addr) if (IsColumnSeparator(i, m_bytes_per_row)) ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, m_separator_column_width); - sprintf(buf, "%02X", i); - ImGui::TableSetupColumn(buf, ImGuiTableColumnFlags_WidthFixed, character_size.x * max_chars_per_cell + (6 + byte_cell_padding) * 1); } - ImGui::TableSetupColumn(" "); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, character_size.x * ascii_padding); ImGui::TableSetupColumn("ASCII", ImGuiTableColumnFlags_WidthFixed, (character_size.x + character_cell_padding * 1) * m_bytes_per_row); ImGuiListClipper clipper; @@ -138,7 +178,9 @@ void MemEditor::Draw(uint8_t* mem_data, int mem_size, int base_display_addr) int address = (row * m_bytes_per_row); ImGui::TableNextColumn(); - ImGui::Text("%04X: ", address + base_display_addr); + char single_addr[32]; + snprintf(single_addr, 32, "%s: ", m_hex_mem_format); + ImGui::Text(single_addr, address + m_mem_base_addr); ImGui::TableNextColumn(); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(2.75f, 0.0f)); @@ -151,8 +193,16 @@ void MemEditor::Draw(uint8_t* mem_data, int mem_size, int base_display_addr) ImGui::TableNextColumn(); ImVec2 cell_start_pos = ImGui::GetCursorScreenPos() - ImGui::GetStyle().CellPadding; - ImVec2 cell_size = (character_size * ImVec2(max_chars_per_cell, 1)) + (ImVec2(2, 2) * ImGui::GetStyle().CellPadding) + ImVec2(1 + byte_cell_padding, 0); - bool cell_hovered = ImGui::IsMouseHoveringRect(cell_start_pos, cell_start_pos + cell_size, false) && ImGui::IsWindowHovered(); + ImVec2 cell_size = (character_size * ImVec2((float)max_chars_per_cell, 1)) + (ImVec2(2, 2) * ImGui::GetStyle().CellPadding) + ImVec2((float)(1 + byte_cell_padding), 0); + + ImVec2 hover_cell_size = cell_size; + + if (IsColumnSeparator(x + 1, m_bytes_per_row)) + { + hover_cell_size.x += m_separator_column_width + 1; + } + + bool cell_hovered = ImGui::IsMouseHoveringRect(cell_start_pos, cell_start_pos + hover_cell_size, false) && ImGui::IsWindowHovered(); DrawSelectionBackground(x, byte_address, cell_start_pos, cell_size); @@ -165,8 +215,15 @@ void MemEditor::Draw(uint8_t* mem_data, int mem_size, int base_display_addr) if (m_editing_address == byte_address) { - ImGui::PushItemWidth((character_size).x *2); - sprintf(buf, "%02X", mem_data[byte_address]); + ImGui::PushItemWidth((character_size).x * (2 * m_mem_word)); + + if (m_mem_word == 1) + snprintf(buf, 32, "%02X", m_mem_data[byte_address]); + else if (m_mem_word == 2) + { + uint16_t* mem_data_16 = (uint16_t*)m_mem_data; + snprintf(buf, 32, "%04X", mem_data_16[byte_address]); + } if (m_set_keyboard_here) { @@ -174,16 +231,22 @@ void MemEditor::Draw(uint8_t* mem_data, int mem_size, int base_display_addr) m_set_keyboard_here = false; } - ImGui::PushStyleColor(ImGuiCol_Text, red); + ImGui::PushStyleColor(ImGuiCol_Text, yellow); ImGui::PushStyleColor(ImGuiCol_FrameBg, dark_cyan); - if (ImGui::InputText("##editing_input", buf, 3, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_AlwaysOverwrite)) + if (ImGui::InputText("##editing_input", buf, (m_mem_word == 1) ? 3 : 5, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_AlwaysOverwrite)) { try { - mem_data[byte_address] = (uint8_t)std::stoul(buf, 0, 16); + if (m_mem_word == 1) + m_mem_data[byte_address] = (uint8_t)std::stoul(buf, 0, 16); + else if (m_mem_word == 2) + { + uint16_t* mem_data_16 = (uint16_t*)m_mem_data; + mem_data_16[byte_address] = (uint16_t)std::stoul(buf, 0, 16); + } - if (byte_address < (mem_size - 1)) + if (byte_address < (m_mem_size - 1)) { m_editing_address = byte_address + 1; m_selection_end = m_selection_start = m_editing_address; @@ -205,17 +268,33 @@ void MemEditor::Draw(uint8_t* mem_data, int mem_size, int base_display_addr) { ImGui::PushItemWidth((character_size).x); - bool gray_out = m_gray_out_zeros && mem_data[byte_address] == 0; - bool highlight = (byte_address >= m_selection_start && byte_address < (m_selection_start + DataPreviewSize())); + uint16_t data = 0; + + if (m_mem_word == 1) + data = m_mem_data[byte_address]; + else if (m_mem_word == 2) + { + uint16_t* mem_data_16 = (uint16_t*)m_mem_data; + data = mem_data_16[byte_address]; + } + + bool gray_out = m_gray_out_zeros && (data== 0); + bool highlight = (byte_address >= m_selection_start && byte_address < (m_selection_start + (DataPreviewSize() / m_mem_word))); ImVec4 color = highlight ? highlight_color : (gray_out ? gray_color : normal_color); - ImGui::TextColored(color, m_uppercase_hex ? "%02X" : "%02x", mem_data[byte_address]); + if (m_mem_word == 1) + ImGui::TextColored(color, m_uppercase_hex ? "%02X" : "%02x", data); + else if (m_mem_word == 2) + ImGui::TextColored(color, m_uppercase_hex ? "%04X" : "%04x", data); if (cell_hovered && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { m_editing_address = byte_address; m_set_keyboard_here = true; } + + + DrawContexMenu(byte_address, cell_hovered); } ImGui::PopItemWidth(); @@ -226,51 +305,58 @@ void MemEditor::Draw(uint8_t* mem_data, int mem_size, int base_display_addr) ImGui::PopStyleVar(); - ImGui::TableNextColumn(); - ImGui::TableNextColumn(); - - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0, 0)); - if (ImGui::BeginTable("##ascii_column", m_bytes_per_row)) + if ((m_mem_word == 1) && ascii) { - for (int x = 0; x < m_bytes_per_row; x++) - { - sprintf(buf, "##ascii_cell%d", x); - ImGui::TableSetupColumn(buf, ImGuiTableColumnFlags_WidthFixed, character_size.x + character_cell_padding * 1); - } + ImGui::TableNextColumn(); + float column_x = ImGui::GetCursorPosX() + (ImGui::GetColumnWidth() / 2.0f); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + ImVec2 window_pos = ImGui::GetWindowPos(); + draw_list->AddLine(ImVec2(window_pos.x + column_x, window_pos.y), ImVec2(window_pos.x + column_x, window_pos.y + 9999), ImGui::GetColorU32(dark_magenta)); - ImGui::TableNextRow(); + ImGui::TableNextColumn(); - for (int x = 0; x < m_bytes_per_row; x++) + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0, 0)); + if (ImGui::BeginTable("##ascii_column", m_bytes_per_row)) { - ImGui::TableNextColumn(); + for (int x = 0; x < m_bytes_per_row; x++) + { + snprintf(buf, 32, "##ascii_cell%d", x); + ImGui::TableSetupColumn(buf, ImGuiTableColumnFlags_WidthFixed, character_size.x + character_cell_padding * 1); + } - int byte_address = address + x; - ImVec2 cell_start_pos = ImGui::GetCursorScreenPos() - ImGui::GetStyle().CellPadding; - ImVec2 cell_size = (character_size * ImVec2(1, 1)) + (ImVec2(2, 2) * ImGui::GetStyle().CellPadding) + ImVec2(1 + byte_cell_padding, 0); + ImGui::TableNextRow(); - DrawSelectionAsciiBackground(byte_address, cell_start_pos, cell_size); + for (int x = 0; x < m_bytes_per_row; x++) + { + ImGui::TableNextColumn(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (character_cell_padding * 1) / 2); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::PushItemWidth(character_size.x); + int byte_address = address + x; + ImVec2 cell_start_pos = ImGui::GetCursorScreenPos() - ImGui::GetStyle().CellPadding; + ImVec2 cell_size = (character_size * ImVec2(1, 1)) + (ImVec2(2, 2) * ImGui::GetStyle().CellPadding) + ImVec2((float)(1 + byte_cell_padding), 0); - unsigned char c = mem_data[byte_address]; + DrawSelectionAsciiBackground(byte_address, cell_start_pos, cell_size); - bool gray_out = m_gray_out_zeros && (c < 32 || c >= 128); - ImGui::TextColored(gray_out ? gray_color : normal_color, "%c", (c >= 32 && c < 128) ? c : '.'); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (character_cell_padding * 1) / 2); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushItemWidth(character_size.x); - ImGui::PopItemWidth(); - ImGui::PopStyleVar(); - } + unsigned char c = m_mem_data[byte_address]; - ImGui::EndTable(); - } + bool gray_out = m_gray_out_zeros && (c < 32 || c >= 128); + ImGui::TextColored(gray_out ? gray_color : normal_color, "%c", (c >= 32 && c < 128) ? c : '.'); - ImGui::PopStyleVar(); + ImGui::PopItemWidth(); + ImGui::PopStyleVar(); + } + + ImGui::EndTable(); + } + ImGui::PopStyleVar(); + } } } - if (m_jump_to_address >= 0 && m_jump_to_address < mem_size) + if (m_jump_to_address >= 0 && m_jump_to_address < m_mem_size) { ImGui::SetScrollY((m_jump_to_address / m_bytes_per_row) * character_size.y); m_selection_start = m_selection_end = m_jump_to_address; @@ -286,9 +372,14 @@ void MemEditor::Draw(uint8_t* mem_data, int mem_size, int base_display_addr) } ImGui::EndChild(); - DrawCursors(); - DrawDataPreview(m_selection_start); - DrawOptions(); + if (cursors) + DrawCursors(); + if (preview) + DrawDataPreview(m_selection_start); + if (options) + DrawOptions(); + + BookMarkPopup(); } bool MemEditor::IsColumnSeparator(int current_column, int column_count) @@ -298,7 +389,6 @@ bool MemEditor::IsColumnSeparator(int current_column, int column_count) void MemEditor::DrawSelectionBackground(int x, int address, ImVec2 cell_pos, ImVec2 cell_size) { - ImDrawList* drawList = ImGui::GetWindowDrawList(); ImVec4 background_color = dark_cyan; int start = m_selection_start <= m_selection_end ? m_selection_start : m_selection_end; int end = m_selection_end >= m_selection_start ? m_selection_end : m_selection_start; @@ -311,7 +401,7 @@ void MemEditor::DrawSelectionBackground(int x, int address, ImVec2 cell_pos, ImV cell_size.x += m_separator_column_width + 1; } - drawList->AddRectFilled(cell_pos + ImVec2(x == 0 ? 1 : 0, 0), cell_pos + cell_size, ImColor(background_color)); + m_draw_list->AddRectFilled(cell_pos, cell_pos + cell_size + ImVec2(1, 0), ImColor(background_color)); } void MemEditor::DrawSelectionAsciiBackground(int address, ImVec2 cell_pos, ImVec2 cell_size) @@ -328,10 +418,11 @@ void MemEditor::DrawSelectionAsciiBackground(int address, ImVec2 cell_pos, ImVec void MemEditor::DrawSelectionFrame(int x, int y, int address, ImVec2 cell_pos, ImVec2 cell_size) { - ImDrawList* drawList = ImGui::GetWindowDrawList(); + m_draw_list->Flags = ImDrawListFlags_None; ImVec4 frame_color = cyan; int start = m_selection_start <= m_selection_end ? m_selection_start : m_selection_end; int end = m_selection_end >= m_selection_start ? m_selection_end : m_selection_start; + bool multiline = (start / m_bytes_per_row) != (end / m_bytes_per_row); if (address < start || address > end) return; @@ -341,17 +432,20 @@ void MemEditor::DrawSelectionFrame(int x, int y, int address, ImVec2 cell_pos, I cell_size.x += m_separator_column_width + 1; } - if (x == 0 || address == start) - drawList->AddLine(cell_pos, cell_pos + ImVec2(0, cell_size.y), ImColor(frame_color), 1); + if ((x == 0) || (address == start)) + m_draw_list->AddLine(cell_pos + ImVec2(-1, -1), cell_pos + ImVec2(-1, cell_size.y), ImColor(frame_color), 1); + + if ((x == (m_bytes_per_row - 1)) || (address == end)) + m_draw_list->AddLine(cell_pos + ImVec2(cell_size.x, multiline && (address == end) && (x != (m_bytes_per_row - 1)) ? 0 : -1), cell_pos + ImVec2(cell_size.x, cell_size.y), ImColor(frame_color), 1); - if (x == (m_bytes_per_row - 1) || (address) == end) - drawList->AddLine(cell_pos + ImVec2(cell_size.x, 0), cell_pos + cell_size - ImVec2(0, 0), ImColor(frame_color), 1); + if ((y == 0) || ((address - m_bytes_per_row) < start)) + m_draw_list->AddLine(cell_pos + ImVec2(-1, -1), cell_pos + ImVec2(cell_size.x, -1), ImColor(frame_color), 1); - if (y == 0 || (address - m_bytes_per_row) < start) - drawList->AddLine(cell_pos - ImVec2(1, 0), cell_pos + ImVec2(cell_size.x + 1, 0), ImColor(frame_color), 1); + if ((address + m_bytes_per_row) > end) + m_draw_list->AddLine(cell_pos + ImVec2(-1, cell_size.y), cell_pos + ImVec2(cell_size.x, cell_size.y), ImColor(frame_color), 1); - if ((address + m_bytes_per_row) >= end) - drawList->AddLine(cell_pos + ImVec2(0, cell_size.y), cell_pos + cell_size + ImVec2(1, 0), ImColor(frame_color), 1); + if (multiline && (address == end) && (x != (m_bytes_per_row - 1))) + m_draw_list->AddLine(cell_pos + ImVec2(cell_size.x, 0), cell_pos + ImVec2(cell_size.x + cell_size.x, 0), ImColor(frame_color), 1); } void MemEditor::HandleSelection(int address, int row) @@ -372,7 +466,7 @@ void MemEditor::HandleSelection(int address, int row) } } } - else if (ImGui::IsMouseDown(ImGuiMouseButton_Left) || ImGui::IsMouseDown(ImGuiMouseButton_Right)) + else if (ImGui::IsMouseDown(ImGuiMouseButton_Left))// || ImGui::IsMouseDown(ImGuiMouseButton_Right)) { m_selection_start = address; m_selection_end = address; @@ -392,19 +486,59 @@ void MemEditor::HandleSelection(int address, int row) void MemEditor::DrawCursors() { + ImGui::PushItemWidth(55); + if (ImGui::InputTextWithHint("##gotoaddr", "XXXXXX", m_goto_address, IM_ARRAYSIZE(m_goto_address), ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) + { + try + { + JumpToAddress((int)std::stoul(m_goto_address, 0, 16)); + m_goto_address[0] = 0; + } + catch(const std::invalid_argument&) + { + } + } + ImGui::SameLine(); + if (ImGui::Button("Go!", ImVec2(30, 0))) + { + try + { + JumpToAddress((int)std::stoul(m_goto_address, 0, 16)); + m_goto_address[0] = 0; + } + catch(const std::invalid_argument&) + { + } + } + + ImGui::SameLine(); + + char range_addr[32]; + char region_text[32]; + char single_addr[32]; + char selection_text[32]; + char all_text[128]; + snprintf(range_addr, 32, "%s-%s", m_hex_mem_format, m_hex_mem_format); + snprintf(region_text, 32, range_addr, m_mem_base_addr, m_mem_base_addr + m_mem_size - 1); + snprintf(single_addr, 32, "%s", m_hex_mem_format); + if (m_selection_start == m_selection_end) + snprintf(selection_text, 32, single_addr, m_mem_base_addr + m_selection_start); + else + snprintf(selection_text, 32, range_addr, m_mem_base_addr + m_selection_start, m_mem_base_addr + m_selection_end); + snprintf(all_text, 128, "REGION: %s SELECTION: %s", region_text, selection_text); + + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetColumnWidth() - ImGui::CalcTextSize(all_text).x + - ImGui::GetScrollX() - 2 * ImGui::GetStyle().ItemSpacing.x); + ImVec4 color = ImVec4(0.1f,0.9f,0.9f,1.0f); ImGui::TextColored(color, "REGION:"); ImGui::SameLine(); - ImGui::Text("%04X-%04X", m_mem_base_addr, m_mem_base_addr + m_mem_size); + ImGui::Text("%s", region_text); ImGui::SameLine(); ImGui::TextColored(color, " SELECTION:"); ImGui::SameLine(); - if (m_selection_start == m_selection_end) - ImGui::Text("%04X", m_mem_base_addr + m_selection_start); - else - ImGui::Text("%04X-%04X", m_mem_base_addr + m_selection_start, m_mem_base_addr + m_selection_end); - ImGui::Separator(); + ImGui::Text("%s", selection_text); } void MemEditor::DrawOptions() @@ -431,140 +565,98 @@ void MemEditor::DrawOptions() ImGui::EndPopup(); } - - ImGui::SameLine(); - ImGui::Text("Go to:"); - ImGui::SameLine(); - ImGui::PushItemWidth(45); - char goto_address[5] = ""; - if (ImGui::InputTextWithHint("##gotoaddr", "XXXX", goto_address, IM_ARRAYSIZE(goto_address), ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) - { - try - { - JumpToAddress((int)std::stoul(goto_address, 0, 16)); - } - catch(const std::invalid_argument&) - { - } - goto_address[0] = 0; - } } void MemEditor::DrawDataPreview(int address) { + ImGui::Separator(); + if (address < 0 || address >= m_mem_size) return; + int data = 0; + int data_size = DataPreviewSize(); + int final_address = address * m_mem_word; + + for (int i = 0; i < data_size; i++) + { + if (m_preview_endianess == 0) + data |= m_mem_data[final_address + i] << (i * 8); + else + data |= m_mem_data[final_address + data_size - i - 1] << (i * 8); + } + ImVec4 color = orange; ImGui::TextColored(color, "Dec:"); ImGui::SameLine(); - DrawDataPreviewAsDec(address); + if (final_address + data_size <= (m_mem_size * m_mem_word)) + DrawDataPreviewAsDec(data); + else + ImGui::Text(" "); ImGui::TextColored(color, "Hex:"); ImGui::SameLine(); - DrawDataPreviewAsHex(address); + if (final_address + data_size <= (m_mem_size * m_mem_word)) + DrawDataPreviewAsHex(data); + else + ImGui::Text(" "); ImGui::TextColored(color, "Bin:"); ImGui::SameLine(); - DrawDataPreviewAsBin(address); + if (final_address + data_size <= (m_mem_size * m_mem_word)) + DrawDataPreviewAsBin(data); + else + ImGui::Text(" "); } -void MemEditor::DrawDataPreviewAsHex(int address) +void MemEditor::DrawDataPreviewAsHex(int data) { int data_size = DataPreviewSize(); - if (address + data_size > m_mem_size) - { - ImGui::Text(" "); - return; - } - - int data = 0; - - for (int i = 0; i < data_size; i++) - { - if (m_preview_endianess == 0) - data |= m_mem_data[address + i] << (i * 8); - else - data |= m_mem_data[address + data_size - i - 1] << (i * 8); - } - const char* format = ((data_size == 1) ? "%02X" : (data_size == 2 ? "%04X" : "%08X")); ImGui::Text(format, data); } -void MemEditor::DrawDataPreviewAsDec(int address) +void MemEditor::DrawDataPreviewAsDec(int data) { - int data_size = DataPreviewSize(); - if (address + data_size > m_mem_size) - { - ImGui::Text(" "); - return; - } - - int data = 0; - - for (int i = 0; i < data_size; i++) - { - if (m_preview_endianess == 0) - data |= m_mem_data[address + i] << (i * 8); - else - data |= m_mem_data[address + data_size - i - 1] << (i * 8); - } - switch (m_preview_data_type) { case 0: { - ImGui::Text("%u", (uint8_t)data); + ImGui::Text("%u (Uint8)", (uint8_t)data); break; } case 1: { - ImGui::Text("%d", (int8_t)data); + ImGui::Text("%d (Int8)", (int8_t)data); break; } case 2: { - ImGui::Text("%u", (uint16_t)data); + ImGui::Text("%u (Uint16)", (uint16_t)data); break; } case 3: { - ImGui::Text("%d", (int16_t)data); + ImGui::Text("%d (Int16)", (int16_t)data); break; } case 4: { - ImGui::Text("%u", (uint32_t)data); + ImGui::Text("%u (Uint32)", (uint32_t)data); break; } case 5: { - ImGui::Text("%d", (int32_t)data); + ImGui::Text("%d (Int32)", (int32_t)data); break; } } } -void MemEditor::DrawDataPreviewAsBin(int address) +void MemEditor::DrawDataPreviewAsBin(int data) { int data_size = DataPreviewSize(); - if (address + data_size > m_mem_size) - { - ImGui::Text(" "); - return; - }; - - int data = 0; - - for (int i = 0; i < data_size; i++) - { - if (m_preview_endianess == 0) - data |= m_mem_data[address + i] << (i * 8); - else - data |= m_mem_data[address + data_size - i - 1] << (i * 8); - } std::string bin = ""; for (int i = 0; i < data_size * 8; i++) @@ -595,28 +687,248 @@ int MemEditor::DataPreviewSize() } } -void MemEditor::Copy(uint8_t** data, int* size) +void MemEditor::DrawContexMenu(int address, bool cell_hovered) { - int start = m_selection_start; - int end = m_selection_end; + char id[16]; + snprintf(id, 16, "##context_%d", address); + + if (cell_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) + ImGui::OpenPopup(id); + + if (ImGui::BeginPopup(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings)) + { + if (m_gui_font != NULL) + ImGui::PushFont(m_gui_font); + + if ((address < m_selection_start) || (address > m_selection_end)) + m_selection_start = m_selection_end = address; + + if (ImGui::Selectable("Copy")) + { + Copy(); + } + + if (ImGui::Selectable("Paste")) + { + Paste(); + } + + if (ImGui::Selectable("Select All")) + { + SelectAll(); + } + + if (ImGui::Selectable("Add Bookmark...")) + { + m_add_bookmark = true; + } + + if (m_gui_font != NULL) + ImGui::PopFont(); - *size = end - start + 1; - *data = m_mem_data + start; + ImGui::EndPopup(); + } } -void MemEditor::Paste(uint8_t* data, int size) +void MemEditor::BookMarkPopup() { - int selection = m_selection_end - m_selection_start + 1; - int start = m_selection_start; - int end = m_selection_start + (size < selection ? size : selection); + if (m_add_bookmark) + { + ImGui::OpenPopup("Add Bookmark"); + m_add_bookmark = false; + } - for (int i = start; i < end; i++) + if (m_gui_font != NULL) + ImGui::PushFont(m_gui_font); + + if (ImGui::BeginPopupModal("Add Bookmark", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + static char address[9] = ""; + static char name[32] = ""; + int bookmark_address = m_selection_start; + + if (bookmark_address > 0) + snprintf(address, 9, "%06X", bookmark_address); + + ImGui::Text("Name:"); + ImGui::PushItemWidth(200);ImGui::SetItemDefaultFocus(); + ImGui::InputText("##name", name, IM_ARRAYSIZE(name)); + + ImGui::Text("Address:"); + ImGui::PushItemWidth(70); + ImGui::InputTextWithHint("##bookaddr", "XXXXXX", address, 7, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); + + ImGui::Separator(); + + if (ImGui::Button("OK", ImVec2(90, 0))) + { + try + { + bookmark_address = (int)std::stoul(address, 0, 16); + + if (strlen(name) == 0) + { + snprintf(name, 32, "Bookmark_%06X", bookmark_address); + } + + Bookmark bookmark; + bookmark.address = bookmark_address; + snprintf(bookmark.name, 32, "%s", name); + m_bookmarks.push_back(bookmark); + ImGui::CloseCurrentPopup(); + + address[0] = 0; + name[0] = 0; + } + catch(const std::invalid_argument&) + { + } + } + + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(90, 0))) { ImGui::CloseCurrentPopup(); } + + ImGui::EndPopup(); + } + + if (m_gui_font != NULL) + ImGui::PopFont(); +} + +void MemEditor::Copy() +{ + int size = (m_selection_end - m_selection_start + 1) * m_mem_word; + uint8_t* data = m_mem_data + (m_selection_start * m_mem_word); + + std::string text; + + for (int i = 0; i < size; i++) { - m_mem_data[i] = data[i - start]; + char byte[4]; + snprintf(byte, 4, "%02X", data[i]); + if (i > 0) + text += " "; + text += byte; } + + SDL_SetClipboardText(text.c_str()); +} + +void MemEditor::Paste() +{ + char* clipboard = SDL_GetClipboardText(); + + if (clipboard != NULL) + { + std::string text(clipboard); + + text.erase(std::remove(text.begin(), text.end(), '\n'), text.end()); + text.erase(std::remove(text.begin(), text.end(), ' '), text.end()); + + int buffer_size = (int)text.size() / 2; + + uint8_t* data = new uint8_t[buffer_size]; + + for (int i = 0; i < buffer_size; i ++) + { + std::string byte = text.substr(i * 2, 2); + + try + { + data[i] = (uint8_t)std::stoul(byte, 0, 16); + } + catch(const std::invalid_argument&) + { + delete[] data; + SDL_free(clipboard); + return; + } + } + + int selection_size = (m_selection_end - m_selection_start + 1) * m_mem_word; + int start = m_selection_start * m_mem_word; + int end = start + std::min(buffer_size, selection_size); + + for (int i = start; i < end; i++) + { + m_mem_data[i] = data[i - start]; + } + + delete[] data; + } + + SDL_free(clipboard); } void MemEditor::JumpToAddress(int address) { - m_jump_to_address = address - m_mem_base_addr; + if (address >= m_mem_base_addr && address < (m_mem_base_addr + m_mem_size)) + m_jump_to_address = address - m_mem_base_addr; +} + +void MemEditor::SelectAll() +{ + m_selection_start = 0; + m_selection_end = m_mem_size - 1; +} + +void MemEditor::ClearSelection() +{ + m_selection_start = m_selection_end = 0; +} + +void MemEditor::SetValueToSelection(int value) +{ + int selection_size = (m_selection_end - m_selection_start + 1) * m_mem_word; + int start = m_selection_start * m_mem_word; + int end = start + selection_size; + int mask = m_mem_word == 1 ? 0xFF : 0xFFFF; + + for (int i = start; i < end; i++) + { + m_mem_data[i] = value & mask; + } +} + +void MemEditor::SaveToFile(const char* file_path) +{ + int size = m_mem_size * m_mem_word; + int row = m_bytes_per_row * m_mem_word; + + FILE* file = fopen(file_path, "w"); + + if (file) + { + for (int i = 0; i < (size - 1); i++) + { + fprintf(file, "%02X ", m_mem_data[i]); + + if ((i % row) == (row - 1)) + fprintf(file, "\n"); + } + + fprintf(file, "%02X", m_mem_data[(size - 1)]); + + fclose(file); + } +} + +void MemEditor::AddBookmark() +{ + m_add_bookmark = true; +} + +void MemEditor::RemoveBookmarks() +{ + m_bookmarks.clear(); +} + +std::vector* MemEditor::GetBookmarks() +{ + return &m_bookmarks; +} + +void MemEditor::SetGuiFont(ImFont* gui_font) +{ + m_gui_font = gui_font; } diff --git a/platforms/desktop-shared/imgui/memory_editor.h b/platforms/desktop-shared/imgui/memory_editor.h index 952ecb8..8d60d3f 100644 --- a/platforms/desktop-shared/imgui/memory_editor.h +++ b/platforms/desktop-shared/imgui/memory_editor.h @@ -22,18 +22,34 @@ #include #include +#include #include "imgui.h" class MemEditor { +public: + struct Bookmark + { + int address; + char name[32]; + }; + public: MemEditor(); ~MemEditor(); - void Draw(uint8_t* mem_data, int mem_size, int base_display_addr = 0x0000); - void Copy(uint8_t** data, int* size); - void Paste(uint8_t* data, int size); + void Draw(uint8_t* mem_data, int mem_size, int base_display_addr = 0x0000, int word = 1, bool ascii = true, bool preview = true, bool options = true, bool cursors = true); + void Copy(); + void Paste(); void JumpToAddress(int address); + void SelectAll(); + void ClearSelection(); + void SetValueToSelection(int value); + void SaveToFile(const char* file_path); + void AddBookmark(); + void RemoveBookmarks(); + std::vector* GetBookmarks(); + void SetGuiFont(ImFont* gui_font); private: bool IsColumnSeparator(int current_column, int column_count); @@ -44,10 +60,12 @@ class MemEditor void DrawCursors(); void DrawOptions(); void DrawDataPreview(int address); - void DrawDataPreviewAsHex(int address); - void DrawDataPreviewAsDec(int address); - void DrawDataPreviewAsBin(int address); + void DrawDataPreviewAsHex(int data); + void DrawDataPreviewAsDec(int data); + void DrawDataPreviewAsBin(int data); int DataPreviewSize(); + void DrawContexMenu(int address, bool cell_hovered); + void BookMarkPopup(); private: float m_separator_column_width; @@ -66,6 +84,13 @@ class MemEditor uint8_t* m_mem_data; int m_mem_size; int m_mem_base_addr; + char m_hex_mem_format[8]; + int m_mem_word; + char m_goto_address[7]; + bool m_add_bookmark; + std::vector m_bookmarks; + ImFont* m_gui_font; + ImDrawList* m_draw_list; }; #endif /* MEM_EDITOR_H */ \ No newline at end of file