yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
imgui_memory_editor.h
Go to the documentation of this file.
1#pragma once
2
3#include <stdint.h> // uint8_t, etc.
4#include <stdio.h> // sprintf, scanf
5
7
8#ifdef _MSC_VER
9#define _PRISizeT "I"
10#define ImSnprintf _snprintf
11#else
12#define _PRISizeT "z"
13#define ImSnprintf snprintf
14#endif
15
16#ifdef _MSC_VER
17#pragma warning(push)
18#pragma warning(disable : 4996) // warning C4996: 'sprintf': This function or \
19 // variable may be unsafe.
20#endif
21
22namespace yaze {
23namespace gui {
24
32
33 // Settings
34 bool Open; // = true // set to false when DrawWindow() was closed. ignore
35 // if not using DrawWindow().
36 bool ReadOnly; // = false // disable any editing.
37 int Cols; // = 16 // number of columns to display.
38 bool OptShowOptions; // = true // display options button/context menu. when
39 // disabled, options will be locked unless you provide
40 // your own UI for them.
41 bool OptShowDataPreview; // = false // display a footer previewing the
42 // decimal/binary/hex/float representation of the
43 // currently selected bytes.
44 bool OptShowHexII; // = false // display values in HexII representation
45 // instead of regular hexadecimal: hide null/zero bytes,
46 // ascii values as ".X".
47 bool OptShowAscii; // = true // display ASCII representation on the right
48 // side.
49 bool OptGreyOutZeroes; // = true // display null/zero bytes using the
50 // TextDisabled color.
51 bool OptUpperCaseHex; // = true // display hexadecimal values as "FF"
52 // instead of "ff".
53 int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between
54 // every mid-cols.
55 int OptAddrDigitsCount; // = 0 // number of addr digits to display
56 // (default calculated based on maximum displayed
57 // addr).
58 float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of
59 // the widget to add custom widgets
60 ImU32 HighlightColor; // // background color of highlighted bytes.
61 ImU8 (*ReadFn)(const ImU8* data,
62 size_t off); // = 0 // optional handler to read bytes.
63 void (*WriteFn)(ImU8* data, size_t off,
64 ImU8 d); // = 0 // optional handler to write bytes.
65 bool (*HighlightFn)(
66 const ImU8* data,
67 size_t off); //= 0 // optional handler to return Highlight property
68 //(to support non-contiguous highlighting).
69
70 // [Internal State]
75 char DataInputBuf[32];
76 char AddrInputBuf[32];
77 size_t GotoAddr;
80 ImGuiDataType PreviewDataType;
81
82 // Expanded
83 const ImU8* ComparisonData;
84
85 void SetComparisonData(const void* data) {
86 ComparisonData = static_cast<const ImU8*>(data);
87 }
88
90 // Settings
91 Open = true;
92 ReadOnly = false;
93 Cols = 16;
94 OptShowOptions = true;
95 OptShowDataPreview = false;
96 OptShowHexII = false;
97 OptShowAscii = true;
98 OptGreyOutZeroes = true;
99 OptUpperCaseHex = true;
100 OptMidColsCount = 8;
103 HighlightColor = IM_COL32(255, 255, 255, 50);
104 ReadFn = nullptr;
105 WriteFn = nullptr;
106 HighlightFn = nullptr;
107
108 // State/Internals
109 ContentsWidthChanged = false;
110 DataPreviewAddr = DataEditingAddr = (size_t)-1;
111 DataEditingTakeFocus = false;
112 memset(DataInputBuf, 0, sizeof(DataInputBuf));
113 memset(AddrInputBuf, 0, sizeof(AddrInputBuf));
114 GotoAddr = (size_t)-1;
115 HighlightMin = HighlightMax = (size_t)-1;
117 PreviewDataType = ImGuiDataType_S32;
118 }
119
120 void GotoAddrAndHighlight(size_t addr_min, size_t addr_max) {
121 GotoAddr = addr_min;
122 HighlightMin = addr_min;
123 HighlightMax = addr_max;
124 }
125
126 struct Sizes {
137
138 Sizes() { memset(this, 0, sizeof(*this)); }
139 };
140
141 void CalcSizes(Sizes& s, size_t mem_size, size_t base_display_addr) {
142 ImGuiStyle& style = ImGui::GetStyle();
144 if (s.AddrDigitsCount == 0)
145 for (size_t n = base_display_addr + mem_size - 1; n > 0; n >>= 4)
146 s.AddrDigitsCount++;
147 s.LineHeight = ImGui::GetTextLineHeight();
148 s.GlyphWidth =
149 ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space
150 s.HexCellWidth =
151 (float)(int)(s.GlyphWidth *
152 2.5f); // "FF " we include trailing space in the width to
153 // easily catch clicks everywhere
155 (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns
156 // we add a bit of extra spacing
157 s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth;
160 if (OptShowAscii) {
161 s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
162 if (OptMidColsCount > 0)
163 s.PosAsciiStart +=
164 (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) *
167 }
168 s.WindowWidth = s.PosAsciiEnd + style.ScrollbarSize +
169 style.WindowPadding.x * 2 + s.GlyphWidth;
170 }
171
172 // Standalone Memory Editor window
173 void DrawWindow(const char* title, void* mem_data, size_t mem_size,
174 size_t base_display_addr = 0x0000) {
175 Sizes s;
176 CalcSizes(s, mem_size, base_display_addr);
177 ImGui::SetNextWindowSize(ImVec2(s.WindowWidth, s.WindowWidth * 0.60f),
178 ImGuiCond_FirstUseEver);
179 ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f),
180 ImVec2(s.WindowWidth, FLT_MAX));
181
182 Open = true;
183 if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar)) {
184 if (ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) &&
185 ImGui::IsMouseReleased(ImGuiMouseButton_Right))
186 ImGui::OpenPopup("context");
187 DrawContents(mem_data, mem_size, base_display_addr);
189 CalcSizes(s, mem_size, base_display_addr);
190 ImGui::SetWindowSize(ImVec2(s.WindowWidth, ImGui::GetWindowSize().y));
191 }
192 }
193 ImGui::End();
194 }
195
196 // Memory Editor contents only
197 void DrawContents(void* mem_data_void, size_t mem_size,
198 size_t base_display_addr = 0x0000) {
199 if (Cols < 1)
200 Cols = 1;
201
202 ImU8* mem_data = (ImU8*)mem_data_void;
203 Sizes s;
204 CalcSizes(s, mem_size, base_display_addr);
205 ImGuiStyle& style = ImGui::GetStyle();
206
207 // We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in
208 // order to prevent click from moving the window. This is used as a facility
209 // since our main click detection code doesn't assign an ActiveId so the
210 // click would normally be caught as a window-move.
211 const float height_separator = style.ItemSpacing.y;
212 float footer_height = OptFooterExtraHeight;
213 if (OptShowOptions)
214 footer_height +=
215 height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
217 footer_height += height_separator +
218 ImGui::GetFrameHeightWithSpacing() * 1 +
219 ImGui::GetTextLineHeightWithSpacing() * 3;
220 ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height), false,
221 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
222 ImDrawList* draw_list = ImGui::GetWindowDrawList();
223
224 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
225 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
226
227 // We are not really using the clipper API correctly here, because we rely
228 // on visible_start_addr/visible_end_addr for our scrolling function.
229 const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
230 ImGuiListClipper clipper;
231 clipper.Begin(line_total_count, s.LineHeight);
232
233 bool data_next = false;
234
235 if (ReadOnly || DataEditingAddr >= mem_size)
236 DataEditingAddr = (size_t)-1;
237 if (DataPreviewAddr >= mem_size)
238 DataPreviewAddr = (size_t)-1;
239
240 size_t preview_data_type_size =
242
243 size_t data_editing_addr_next = (size_t)-1;
244 if (DataEditingAddr != (size_t)-1) {
245 // Move cursor but only apply on next frame so scrolling with be
246 // synchronized (because currently we can't change the scrolling while the
247 // window is being rendered)
248 if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) &&
249 (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) {
250 data_editing_addr_next = DataEditingAddr - Cols;
251 } else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) &&
252 (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols) {
253 data_editing_addr_next = DataEditingAddr + Cols;
254 } else if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) &&
255 (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) {
256 data_editing_addr_next = DataEditingAddr - 1;
257 } else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) &&
258 (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) {
259 data_editing_addr_next = DataEditingAddr + 1;
260 }
261 }
262
263 // Draw vertical separator
264 ImVec2 window_pos = ImGui::GetWindowPos();
265 if (OptShowAscii)
266 draw_list->AddLine(
267 ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y),
268 ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth,
269 window_pos.y + 9999),
270 ImGui::GetColorU32(ImGuiCol_Border));
271
272 const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text);
273 const ImU32 color_disabled = OptGreyOutZeroes
274 ? ImGui::GetColorU32(ImGuiCol_TextDisabled)
275 : color_text;
276
277 const char* format_address =
278 OptUpperCaseHex ? "%0*" _PRISizeT "X: " : "%0*" _PRISizeT "x: ";
279 const char* format_data =
280 OptUpperCaseHex ? "%0*" _PRISizeT "X" : "%0*" _PRISizeT "x";
281 const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x";
282 const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
283
284 while (clipper.Step())
285 for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd;
286 line_i++) // display only visible lines
287 {
288 size_t addr = (size_t)(line_i * Cols);
289 ImGui::Text(format_address, s.AddrDigitsCount,
290 base_display_addr + addr);
291
292 // Draw Hexadecimal
293 for (int n = 0; n < Cols && addr < mem_size; n++, addr++) {
294 float byte_pos_x = s.PosHexStart + s.HexCellWidth * n;
295 if (OptMidColsCount > 0)
296 byte_pos_x +=
297 (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
298 ImGui::SameLine(byte_pos_x);
299
300 // Draw highlight
301 bool is_highlight_from_user_range =
302 (addr >= HighlightMin && addr < HighlightMax);
303 bool is_highlight_from_user_func =
304 (HighlightFn && HighlightFn(mem_data, addr));
305 bool is_highlight_from_preview =
306 (addr >= DataPreviewAddr &&
307 addr < DataPreviewAddr + preview_data_type_size);
308 if (is_highlight_from_user_range || is_highlight_from_user_func ||
309 is_highlight_from_preview) {
310 ImVec2 pos = ImGui::GetCursorScreenPos();
311 float highlight_width = s.GlyphWidth * 2;
312 bool is_next_byte_highlighted =
313 (addr + 1 < mem_size) &&
314 ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) ||
315 (HighlightFn && HighlightFn(mem_data, addr + 1)));
316 if (is_next_byte_highlighted || (n + 1 == Cols)) {
317 highlight_width = s.HexCellWidth;
318 if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols &&
319 ((n + 1) % OptMidColsCount) == 0)
320 highlight_width += s.SpacingBetweenMidCols;
321 }
322 draw_list->AddRectFilled(
323 pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight),
325 }
326
327 bool comparison_rom_diff = false;
328 if (ComparisonData != nullptr) {
329 int a = mem_data[addr];
330 int b = ComparisonData[addr];
331 if (a != b) {
332 ImVec2 pos = ImGui::GetCursorScreenPos();
333 float highlight_width = s.GlyphWidth * 2;
334 bool is_next_byte_highlighted =
335 (addr + 1 < mem_size) &&
336 ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) ||
337 (HighlightFn && HighlightFn(mem_data, addr + 1)));
338 if (is_next_byte_highlighted || (n + 1 == Cols)) {
339 highlight_width = s.HexCellWidth;
340 if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols &&
341 ((n + 1) % OptMidColsCount) == 0)
342 highlight_width += s.SpacingBetweenMidCols;
343 }
344 draw_list->AddRectFilled(
345 pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight),
346 IM_COL32(255, 0, 0, 50));
347 }
348 }
349
350 if (DataEditingAddr == addr) {
351 // Display text input on current byte
352 bool data_write = false;
353 ImGui::PushID((void*)addr);
355 ImGui::SetKeyboardFocusHere(0);
356 sprintf(AddrInputBuf, format_data, s.AddrDigitsCount,
357 base_display_addr + addr);
358 sprintf(DataInputBuf, format_byte,
359 ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
360 }
361 struct UserData {
362 // FIXME: We should have a way to retrieve the text edit cursor
363 // position more easily in the API, this is rather tedious. This
364 // is such a ugly mess we may be better off not using InputText()
365 // at all here.
366 static int Callback(ImGuiInputTextCallbackData* data) {
367 UserData* user_data = (UserData*)data->UserData;
368 if (!data->HasSelection())
369 user_data->CursorPos = data->CursorPos;
370 if (data->SelectionStart == 0 &&
371 data->SelectionEnd == data->BufTextLen) {
372 // When not editing a byte, always refresh its InputText
373 // content pulled from underlying memory data (this is a bit
374 // tricky, since InputText technically "owns" the master copy
375 // of the buffer we edit it in there)
376 data->DeleteChars(0, data->BufTextLen);
377 data->InsertChars(0, user_data->CurrentBufOverwrite);
378 data->SelectionStart = 0;
379 data->SelectionEnd = 2;
380 data->CursorPos = 0;
381 }
382 return 0;
383 }
384 char CurrentBufOverwrite[3]; // Input
385 int CursorPos; // Output
386 };
387 UserData user_data;
388 user_data.CursorPos = -1;
389 sprintf(user_data.CurrentBufOverwrite, format_byte,
390 ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
391 ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal |
392 ImGuiInputTextFlags_EnterReturnsTrue |
393 ImGuiInputTextFlags_AutoSelectAll |
394 ImGuiInputTextFlags_NoHorizontalScroll |
395 ImGuiInputTextFlags_CallbackAlways;
396#if IMGUI_VERSION_NUM >= 18104
397 flags |= ImGuiInputTextFlags_AlwaysOverwrite;
398#else
399 flags |= ImGuiInputTextFlags_AlwaysInsertMode;
400#endif
401 ImGui::SetNextItemWidth(s.GlyphWidth * 2);
402 if (ImGui::InputText("##data", DataInputBuf,
403 IM_ARRAYSIZE(DataInputBuf), flags,
404 UserData::Callback, &user_data))
405 data_write = data_next = true;
406 else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
407 DataEditingAddr = data_editing_addr_next = (size_t)-1;
408 DataEditingTakeFocus = false;
409 if (user_data.CursorPos >= 2)
410 data_write = data_next = true;
411 if (data_editing_addr_next != (size_t)-1)
412 data_write = data_next = false;
413 unsigned int data_input_value = 0;
414 if (data_write &&
415 sscanf(DataInputBuf, "%X", &data_input_value) == 1) {
416 if (WriteFn)
417 WriteFn(mem_data, addr, (ImU8)data_input_value);
418 else
419 mem_data[addr] = (ImU8)data_input_value;
420 }
421 ImGui::PopID();
422 } else {
423 // NB: The trailing space is not visible but ensure there's no gap
424 // that the mouse cannot click on.
425 ImU8 b = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
426
427 if (OptShowHexII) {
428 if ((b >= 32 && b < 128))
429 ImGui::Text(".%c ", b);
430 else if (b == 0xFF && OptGreyOutZeroes)
431 ImGui::TextDisabled("## ");
432 else if (b == 0x00)
433 ImGui::Text(" ");
434 else
435 ImGui::Text(format_byte_space, b);
436 } else {
437 if (b == 0 && OptGreyOutZeroes)
438 ImGui::TextDisabled("00 ");
439 else
440 ImGui::Text(format_byte_space, b);
441 }
442 if (!ReadOnly && ImGui::IsItemHovered() &&
443 ImGui::IsMouseClicked(0)) {
445 data_editing_addr_next = addr;
446 }
447 }
448 }
449
450 if (OptShowAscii) {
451 // Draw ASCII values
452 ImGui::SameLine(s.PosAsciiStart);
453 ImVec2 pos = ImGui::GetCursorScreenPos();
454 addr = line_i * Cols;
455 ImGui::PushID(line_i);
456 if (ImGui::InvisibleButton(
457 "ascii",
458 ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight))) {
460 addr +
461 (size_t)((ImGui::GetIO().MousePos.x - pos.x) / s.GlyphWidth);
463 }
464 ImGui::PopID();
465 for (int n = 0; n < Cols && addr < mem_size; n++, addr++) {
466 if (addr == DataEditingAddr) {
467 draw_list->AddRectFilled(
468 pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight),
469 ImGui::GetColorU32(ImGuiCol_FrameBg));
470 draw_list->AddRectFilled(
471 pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight),
472 ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
473 }
474 unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
475 char display_c = (c < 32 || c >= 128) ? '.' : c;
476 draw_list->AddText(pos,
477 (display_c == c) ? color_text : color_disabled,
478 &display_c, &display_c + 1);
479 pos.x += s.GlyphWidth;
480 }
481 }
482 }
483 ImGui::PopStyleVar(2);
484 ImGui::EndChild();
485
486 // Notify the main window of our ideal child content size (FIXME: we are
487 // missing an API to get the contents size from the child)
488 ImGui::SetCursorPosX(s.WindowWidth);
489
490 if (data_next && DataEditingAddr + 1 < mem_size) {
493 } else if (data_editing_addr_next != (size_t)-1) {
494 DataEditingAddr = DataPreviewAddr = data_editing_addr_next;
496 }
497
498 const bool lock_show_data_preview = OptShowDataPreview;
499 if (OptShowOptions) {
500 ImGui::Separator();
501 DrawOptionsLine(s, mem_data, mem_size, base_display_addr);
502 }
503
504 if (lock_show_data_preview) {
505 ImGui::Separator();
506 DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
507 }
508 }
509
510 void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size,
511 size_t base_display_addr) {
512 IM_UNUSED(mem_data);
513 ImGuiStyle& style = ImGui::GetStyle();
514 const char* format_range =
515 OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X"
516 : "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x";
517
518 // Options menu
519 if (ImGui::Button(ICON_MD_SETTINGS " Options"))
520 ImGui::OpenPopup("context");
521 if (ImGui::BeginPopup("context")) {
522 ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f);
523 if (ImGui::DragInt(ICON_MD_VIEW_COLUMN " ##cols", &Cols, 0.2f, 4, 32, "%d cols")) {
525 if (Cols < 1)
526 Cols = 1;
527 }
528 ImGui::Checkbox(ICON_MD_PREVIEW " Show Data Preview", &OptShowDataPreview);
529 ImGui::Checkbox(ICON_MD_HEXAGON " Show HexII", &OptShowHexII);
530 if (ImGui::Checkbox(ICON_MD_ABC " Show Ascii", &OptShowAscii)) {
532 }
533 ImGui::Checkbox(ICON_MD_FORMAT_COLOR_RESET " Grey out zeroes", &OptGreyOutZeroes);
534 ImGui::Checkbox(ICON_MD_KEYBOARD_CAPSLOCK " Uppercase Hex", &OptUpperCaseHex);
535
536 ImGui::EndPopup();
537 }
538
539 ImGui::SameLine();
540 ImGui::Text(ICON_MD_ZOOM_OUT_MAP);
541 ImGui::SameLine();
542 ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr,
543 s.AddrDigitsCount, base_display_addr + mem_size - 1);
544 ImGui::SameLine();
545 ImGui::SetNextItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth +
546 style.FramePadding.x * 2.0f);
547 if (ImGui::InputText("##addr", AddrInputBuf, IM_ARRAYSIZE(AddrInputBuf),
548 ImGuiInputTextFlags_CharsHexadecimal |
549 ImGuiInputTextFlags_EnterReturnsTrue)) {
550 size_t goto_addr;
551 if (sscanf(AddrInputBuf, "%" _PRISizeT "X", &goto_addr) == 1) {
552 GotoAddr = goto_addr - base_display_addr;
553 HighlightMin = HighlightMax = (size_t)-1;
554 }
555 }
556 ImGui::SameLine();
557 ImGui::Text(ICON_MD_LOCATION_ON);
558
559 if (GotoAddr != (size_t)-1) {
560 if (GotoAddr < mem_size) {
561 ImGui::BeginChild("##scrolling");
562 ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y +
563 (GotoAddr / Cols) *
564 ImGui::GetTextLineHeight());
565 ImGui::EndChild();
568 }
569 GotoAddr = (size_t)-1;
570 }
571 }
572
573 void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size,
574 size_t base_display_addr) {
575 IM_UNUSED(base_display_addr);
576 ImU8* mem_data = (ImU8*)mem_data_void;
577 ImGuiStyle& style = ImGui::GetStyle();
578 ImGui::AlignTextToFramePadding();
579 ImGui::Text(ICON_MD_SEARCH " Preview as:");
580 ImGui::SameLine();
581 ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) +
582 style.FramePadding.x * 2.0f +
583 style.ItemInnerSpacing.x);
584 if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType),
585 ImGuiComboFlags_HeightLargest)) {
586 for (int n = 0; n < ImGuiDataType_COUNT; n++)
587 if (ImGui::Selectable(DataTypeGetDesc((ImGuiDataType)n),
588 PreviewDataType == n))
589 PreviewDataType = (ImGuiDataType)n;
590 ImGui::EndCombo();
591 }
592 ImGui::SameLine();
593 ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) +
594 style.FramePadding.x * 2.0f +
595 style.ItemInnerSpacing.x);
596 ImGui::Combo("##combo_endianess", &PreviewEndianess, "LE\0BE\0\0");
597
598 char buf[128] = "";
599 float x = s.GlyphWidth * 6.0f;
600 bool has_value = DataPreviewAddr != (size_t)-1;
601 if (has_value)
603 DataFormat_Dec, buf, (size_t)IM_ARRAYSIZE(buf));
604 ImGui::Text(ICON_MD_NUMBERS " Dec");
605 ImGui::SameLine(x);
606 ImGui::TextUnformatted(has_value ? buf : "N/A");
607 if (has_value)
609 DataFormat_Hex, buf, (size_t)IM_ARRAYSIZE(buf));
610 ImGui::Text(ICON_MD_HEXAGON " Hex");
611 ImGui::SameLine(x);
612 ImGui::TextUnformatted(has_value ? buf : "N/A");
613 if (has_value)
615 DataFormat_Bin, buf, (size_t)IM_ARRAYSIZE(buf));
616 buf[IM_ARRAYSIZE(buf) - 1] = 0;
617 ImGui::Text(ICON_MD_DATA_ARRAY " Bin");
618 ImGui::SameLine(x);
619 ImGui::TextUnformatted(has_value ? buf : "N/A");
620 }
621
622 // Utilities for Data Preview
623 const char* DataTypeGetDesc(ImGuiDataType data_type) const {
624 const char* descs[] = {"Int8", "Uint8", "Int16", "Uint16", "Int32",
625 "Uint32", "Int64", "Uint64", "Float", "Double"};
626 IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
627 return descs[data_type];
628 }
629
630 size_t DataTypeGetSize(ImGuiDataType data_type) const {
631 const size_t sizes[] = {
632 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double)};
633 IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
634 return sizes[data_type];
635 }
636
637 const char* DataFormatGetDesc(DataFormat data_format) const {
638 const char* descs[] = {"Bin", "Dec", "Hex"};
639 IM_ASSERT(data_format >= 0 && data_format < DataFormat_COUNT);
640 return descs[data_format];
641 }
642
643 bool IsBigEndian() const {
644 uint16_t x = 1;
645 char c[2];
646 memcpy(c, &x, 2);
647 return c[0] != 0;
648 }
649
650 static void* EndianessCopyBigEndian(void* _dst, void* _src, size_t s,
651 int is_little_endian) {
652 if (is_little_endian) {
653 uint8_t* dst = (uint8_t*)_dst;
654 uint8_t* src = (uint8_t*)_src + s - 1;
655 for (int i = 0, n = (int)s; i < n; ++i)
656 memcpy(dst++, src--, 1);
657 return _dst;
658 } else {
659 return memcpy(_dst, _src, s);
660 }
661 }
662
663 static void* EndianessCopyLittleEndian(void* _dst, void* _src, size_t s,
664 int is_little_endian) {
665 if (is_little_endian) {
666 return memcpy(_dst, _src, s);
667 } else {
668 uint8_t* dst = (uint8_t*)_dst;
669 uint8_t* src = (uint8_t*)_src + s - 1;
670 for (int i = 0, n = (int)s; i < n; ++i)
671 memcpy(dst++, src--, 1);
672 return _dst;
673 }
674 }
675
676 void* EndianessCopy(void* dst, void* src, size_t size) const {
677 static void* (*fp)(void*, void*, size_t, int) = nullptr;
678 if (fp == nullptr)
680 return fp(dst, src, size, PreviewEndianess);
681 }
682
683 const char* FormatBinary(const uint8_t* buf, int width) const {
684 IM_ASSERT(width <= 64);
685 size_t out_n = 0;
686 static char out_buf[64 + 8 + 1];
687 int n = width / 8;
688 for (int j = n - 1; j >= 0; --j) {
689 for (int i = 0; i < 8; ++i)
690 out_buf[out_n++] = (buf[j] & (1 << (7 - i))) ? '1' : '0';
691 out_buf[out_n++] = ' ';
692 }
693 IM_ASSERT(out_n < IM_ARRAYSIZE(out_buf));
694 out_buf[out_n] = 0;
695 return out_buf;
696 }
697
698 // [Internal]
699 void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size,
700 ImGuiDataType data_type, DataFormat data_format,
701 char* out_buf, size_t out_buf_size) const {
702 uint8_t buf[8];
703 size_t elem_size = DataTypeGetSize(data_type);
704 size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
705 if (ReadFn)
706 for (int i = 0, n = (int)size; i < n; ++i)
707 buf[i] = ReadFn(mem_data, addr + i);
708 else
709 memcpy(buf, mem_data + addr, size);
710
711 if (data_format == DataFormat_Bin) {
712 uint8_t binbuf[8];
713 EndianessCopy(binbuf, buf, size);
714 ImSnprintf(out_buf, out_buf_size, "%s",
715 FormatBinary(binbuf, (int)size * 8));
716 return;
717 }
718
719 out_buf[0] = 0;
720 switch (data_type) {
721 case ImGuiDataType_S8: {
722 int8_t int8 = 0;
723 EndianessCopy(&int8, buf, size);
724 if (data_format == DataFormat_Dec) {
725 ImSnprintf(out_buf, out_buf_size, "%hhd", int8);
726 return;
727 }
728 if (data_format == DataFormat_Hex) {
729 ImSnprintf(out_buf, out_buf_size, "0x%02x", int8 & 0xFF);
730 return;
731 }
732 break;
733 }
734 case ImGuiDataType_U8: {
735 uint8_t uint8 = 0;
736 EndianessCopy(&uint8, buf, size);
737 if (data_format == DataFormat_Dec) {
738 ImSnprintf(out_buf, out_buf_size, "%hhu", uint8);
739 return;
740 }
741 if (data_format == DataFormat_Hex) {
742 ImSnprintf(out_buf, out_buf_size, "0x%02x", uint8 & 0xFF);
743 return;
744 }
745 break;
746 }
747 case ImGuiDataType_S16: {
748 int16_t int16 = 0;
749 EndianessCopy(&int16, buf, size);
750 if (data_format == DataFormat_Dec) {
751 ImSnprintf(out_buf, out_buf_size, "%hd", int16);
752 return;
753 }
754 if (data_format == DataFormat_Hex) {
755 ImSnprintf(out_buf, out_buf_size, "0x%04x", int16 & 0xFFFF);
756 return;
757 }
758 break;
759 }
760 case ImGuiDataType_U16: {
761 uint16_t uint16 = 0;
762 EndianessCopy(&uint16, buf, size);
763 if (data_format == DataFormat_Dec) {
764 ImSnprintf(out_buf, out_buf_size, "%hu", uint16);
765 return;
766 }
767 if (data_format == DataFormat_Hex) {
768 ImSnprintf(out_buf, out_buf_size, "0x%04x", uint16 & 0xFFFF);
769 return;
770 }
771 break;
772 }
773 case ImGuiDataType_S32: {
774 int32_t int32 = 0;
775 EndianessCopy(&int32, buf, size);
776 if (data_format == DataFormat_Dec) {
777 ImSnprintf(out_buf, out_buf_size, "%d", int32);
778 return;
779 }
780 if (data_format == DataFormat_Hex) {
781 ImSnprintf(out_buf, out_buf_size, "0x%08x", int32);
782 return;
783 }
784 break;
785 }
786 case ImGuiDataType_U32: {
787 uint32_t uint32 = 0;
788 EndianessCopy(&uint32, buf, size);
789 if (data_format == DataFormat_Dec) {
790 ImSnprintf(out_buf, out_buf_size, "%u", uint32);
791 return;
792 }
793 if (data_format == DataFormat_Hex) {
794 ImSnprintf(out_buf, out_buf_size, "0x%08x", uint32);
795 return;
796 }
797 break;
798 }
799 case ImGuiDataType_S64: {
800 int64_t int64 = 0;
801 EndianessCopy(&int64, buf, size);
802 if (data_format == DataFormat_Dec) {
803 ImSnprintf(out_buf, out_buf_size, "%lld", (long long)int64);
804 return;
805 }
806 if (data_format == DataFormat_Hex) {
807 ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)int64);
808 return;
809 }
810 break;
811 }
812 case ImGuiDataType_U64: {
813 uint64_t uint64 = 0;
814 EndianessCopy(&uint64, buf, size);
815 if (data_format == DataFormat_Dec) {
816 ImSnprintf(out_buf, out_buf_size, "%llu", (unsigned long long)uint64);
817 return;
818 }
819 if (data_format == DataFormat_Hex) {
820 ImSnprintf(out_buf, out_buf_size, "0x%016llx",
821 (unsigned long long)uint64);
822 return;
823 }
824 break;
825 }
826 case ImGuiDataType_Float: {
827 float float32 = 0.0f;
828 EndianessCopy(&float32, buf, size);
829 if (data_format == DataFormat_Dec) {
830 ImSnprintf(out_buf, out_buf_size, "%f", float32);
831 return;
832 }
833 if (data_format == DataFormat_Hex) {
834 ImSnprintf(out_buf, out_buf_size, "%a", float32);
835 return;
836 }
837 break;
838 }
839 case ImGuiDataType_Double: {
840 double float64 = 0.0;
841 EndianessCopy(&float64, buf, size);
842 if (data_format == DataFormat_Dec) {
843 ImSnprintf(out_buf, out_buf_size, "%f", float64);
844 return;
845 }
846 if (data_format == DataFormat_Hex) {
847 ImSnprintf(out_buf, out_buf_size, "%a", float64);
848 return;
849 }
850 break;
851 }
852 case ImGuiDataType_COUNT:
853 break;
854 } // Switch
855 }
856};
857
858} // namespace gui
859} // namespace yaze
860
861#ifdef _MSC_VER
862#pragma warning(pop)
863#endif
#define ICON_MD_LOCATION_ON
Definition icons.h:1137
#define ICON_MD_SETTINGS
Definition icons.h:1699
#define ICON_MD_FORMAT_COLOR_RESET
Definition icons.h:831
#define ICON_MD_SEARCH
Definition icons.h:1673
#define ICON_MD_KEYBOARD_CAPSLOCK
Definition icons.h:1035
#define ICON_MD_DATA_ARRAY
Definition icons.h:519
#define ICON_MD_ABC
Definition icons.h:69
#define ICON_MD_NUMBERS
Definition icons.h:1343
#define ICON_MD_PREVIEW
Definition icons.h:1512
#define ICON_MD_ZOOM_OUT_MAP
Definition icons.h:2197
#define ICON_MD_HEXAGON
Definition icons.h:937
#define ICON_MD_VIEW_COLUMN
Definition icons.h:2081
#define ImSnprintf
#define _PRISizeT
bool(* HighlightFn)(const ImU8 *data, size_t off)
void(* WriteFn)(ImU8 *data, size_t off, ImU8 d)
void SetComparisonData(const void *data)
void DrawPreviewData(size_t addr, const ImU8 *mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char *out_buf, size_t out_buf_size) const
void DrawPreviewLine(const Sizes &s, void *mem_data_void, size_t mem_size, size_t base_display_addr)
static void * EndianessCopyLittleEndian(void *_dst, void *_src, size_t s, int is_little_endian)
ImU8(* ReadFn)(const ImU8 *data, size_t off)
void GotoAddrAndHighlight(size_t addr_min, size_t addr_max)
const char * DataFormatGetDesc(DataFormat data_format) const
void * EndianessCopy(void *dst, void *src, size_t size) const
void DrawWindow(const char *title, void *mem_data, size_t mem_size, size_t base_display_addr=0x0000)
void DrawOptionsLine(const Sizes &s, void *mem_data, size_t mem_size, size_t base_display_addr)
const char * DataTypeGetDesc(ImGuiDataType data_type) const
void DrawContents(void *mem_data_void, size_t mem_size, size_t base_display_addr=0x0000)
void CalcSizes(Sizes &s, size_t mem_size, size_t base_display_addr)
static void * EndianessCopyBigEndian(void *_dst, void *_src, size_t s, int is_little_endian)
size_t DataTypeGetSize(ImGuiDataType data_type) const
const char * FormatBinary(const uint8_t *buf, int width) const