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