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