1 // Mini memory editor for Dear ImGui (to embed in your game/tools)
2 // Animated GIF: https://twitter.com/ocornut/status/894242704317530112
3 // Get latest version at http://www.github.com/ocornut/imgui_club
5 // Right-click anywhere to access the Options menu!
6 // You can adjust the keyboard repeat delay/rate in ImGuiIO.
7 // The code assume a mono-space font for simplicity! If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before caling this.
10 // static MemoryEditor mem_edit_1; // store your state somewhere
11 // mem_edit_1.DrawWindow("Memory Editor", mem_block, mem_block_size, 0x0000); // create a window and draw memory editor (if you already have a window, use DrawContents())
14 // static MemoryEditor mem_edit_2;
15 // ImGui::Begin("MyWindow")
16 // mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this);
20 // - v0.10: initial version
21 // - v0.11: always refresh active text input with the latest byte from source memory if it's not being edited.
22 // - v0.12: added OptMidRowsCount to allow extra spacing every XX rows.
23 // - v0.13: added optional ReadFn/WriteFn handlers to access memory via a function. various warning fixes for 64-bits.
24 // - v0.14: added GotoAddr member, added GotoAddrAndHighlight() and highlighting. fixed minor scrollbar glitch when resizing.
25 // - v0.15: added maximum window width. minor optimization.
26 // - v0.16: added OptGreyOutZeroes option. various sizing fixes when resizing using the "Rows" drag.
27 // - v0.17: added HighlightFn handler for optional non-contiguous highlighting.
28 // - v0.18: fixes for displaying 64-bits addresses, fixed mouse click gaps introduced in recent changes, cursor tracking scrolling fixes.
29 // - v0.19: fixed auto-focus of next byte leaving WantCaptureKeyboard=false for one frame. we now capture the keyboard during that transition.
30 // - v0.20: added options menu. added OptShowAscii checkbox. added optional HexII display. split Draw() in DrawWindow()/DrawContents(). fixing glyph width. refactoring/cleaning code.
31 // - v0.21: fixes for using DrawContents() in our own window. fixed HexII to actually be useful and not on the wrong side.
32 // - v0.22: clicking Ascii view select the byte in the Hex view. Ascii view highlight selection.
33 // - v0.23: fixed right-arrow triggering a byte write.
34 // - v0.24: changed DragInt("Rows" to use a %d data format (which is desirable since imgui 1.61).
35 // - v0.25: fixed wording: all occurrences of "Rows" renamed to "Columns".
36 // - v0.26: fixed clicking on hex region
37 // - v0.30: added data preview for common data types
38 // - v0.31: added OptUpperCaseHex option to select lower/upper casing display [@samhocevar]
39 // - v0.32: changed signatures to use void* instead of unsigned char*
40 // - v0.33: added OptShowOptions option to hide all the interactive option setting.
41 // - v0.34: binary preview now applies endianess setting [@nicolasnoble]
44 // - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame.
45 // - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
48 #include <stdio.h> // sprintf, scanf
49 #include <stdint.h> // uint8_t, etc.
53 #define ImSnprintf _snprintf
56 #define ImSnprintf snprintf
61 typedef unsigned char u8
;
87 bool Open
; // = true // set to false when DrawWindow() was closed. ignore if not using DrawWindow().
88 bool ReadOnly
; // = false // disable any editing.
89 int Cols
; // = 16 // number of columns to display.
90 bool OptShowOptions
; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them.
91 bool OptShowDataPreview
; // = false // display a footer previewing the decimal/binary/hex/float representation of the currently selected bytes.
92 bool OptShowHexII
; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X".
93 bool OptShowAscii
; // = true // display ASCII representation on the right side.
94 bool OptGreyOutZeroes
; // = true // display null/zero bytes using the TextDisabled color.
95 bool OptUpperCaseHex
; // = true // display hexadecimal values as "FF" instead of "ff".
96 int OptMidColsCount
; // = 8 // set to 0 to disable extra spacing between every mid-cols.
97 int OptAddrDigitsCount
; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr).
98 ImU32 HighlightColor
; // // background color of highlighted bytes.
99 u8 (*ReadFn
)(const u8
* data
, size_t off
); // = NULL // optional handler to read bytes.
100 void (*WriteFn
)(u8
* data
, size_t off
, u8 d
); // = NULL // optional handler to write bytes.
101 bool (*HighlightFn
)(const u8
* data
, size_t off
);//NULL // optional handler to return Highlight property (to support non-contiguous highlighting).
104 bool ContentsWidthChanged
;
105 size_t DataPreviewAddr
;
106 size_t DataEditingAddr
;
107 bool DataEditingTakeFocus
;
108 char DataInputBuf
[32];
109 char AddrInputBuf
[32];
111 size_t HighlightMin
, HighlightMax
;
112 int PreviewEndianess
;
113 DataType PreviewDataType
;
121 OptShowOptions
= true;
122 OptShowDataPreview
= false;
123 OptShowHexII
= false;
125 OptGreyOutZeroes
= true;
126 OptUpperCaseHex
= true;
128 OptAddrDigitsCount
= 0;
129 HighlightColor
= IM_COL32(255, 255, 255, 50);
135 ContentsWidthChanged
= false;
136 DataPreviewAddr
= DataEditingAddr
= (size_t)-1;
137 DataEditingTakeFocus
= false;
138 memset(DataInputBuf
, 0, sizeof(DataInputBuf
));
139 memset(AddrInputBuf
, 0, sizeof(AddrInputBuf
));
140 GotoAddr
= (size_t)-1;
141 HighlightMin
= HighlightMax
= (size_t)-1;
142 PreviewEndianess
= 0;
143 PreviewDataType
= DataType_S32
;
146 void GotoAddrAndHighlight(size_t addr_min
, size_t addr_max
)
149 HighlightMin
= addr_min
;
150 HighlightMax
= addr_max
;
159 float SpacingBetweenMidCols
;
167 void CalcSizes(Sizes
& s
, size_t mem_size
, size_t base_display_addr
)
169 ImGuiStyle
& style
= ImGui::GetStyle();
170 s
.AddrDigitsCount
= OptAddrDigitsCount
;
171 if (s
.AddrDigitsCount
== 0)
172 for (size_t n
= base_display_addr
+ mem_size
- 1; n
> 0; n
>>= 4)
174 s
.LineHeight
= ImGui::GetTextLineHeight();
175 s
.GlyphWidth
= ImGui::CalcTextSize("F").x
+ 1; // We assume the font is mono-space
176 s
.HexCellWidth
= (float)(int)(s
.GlyphWidth
* 2.5f
); // "FF " we include trailing space in the width to easily catch clicks everywhere
177 s
.SpacingBetweenMidCols
= (float)(int)(s
.HexCellWidth
* 0.25f
); // Every OptMidColsCount columns we add a bit of extra spacing
178 s
.PosHexStart
= (s
.AddrDigitsCount
+ 2) * s
.GlyphWidth
;
179 s
.PosHexEnd
= s
.PosHexStart
+ (s
.HexCellWidth
* Cols
);
180 s
.PosAsciiStart
= s
.PosAsciiEnd
= s
.PosHexEnd
;
183 s
.PosAsciiStart
= s
.PosHexEnd
+ s
.GlyphWidth
* 1;
184 if (OptMidColsCount
> 0)
185 s
.PosAsciiStart
+= (float)((Cols
+ OptMidColsCount
- 1) / OptMidColsCount
) * s
.SpacingBetweenMidCols
;
186 s
.PosAsciiEnd
= s
.PosAsciiStart
+ Cols
* s
.GlyphWidth
;
188 s
.WindowWidth
= s
.PosAsciiEnd
+ style
.ScrollbarSize
+ style
.WindowPadding
.x
* 2 + s
.GlyphWidth
;
191 // Standalone Memory Editor window
192 void DrawWindow(const char* title
, void* mem_data
, size_t mem_size
, size_t base_display_addr
= 0x0000)
195 CalcSizes(s
, mem_size
, base_display_addr
);
196 ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f
, 0.0f
), ImVec2(s
.WindowWidth
, FLT_MAX
));
199 if (ImGui::Begin(title
, &Open
, ImGuiWindowFlags_NoScrollbar
))
201 if (ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows
) && ImGui::IsMouseClicked(1))
202 ImGui::OpenPopup("context");
203 DrawContents(mem_data
, mem_size
, base_display_addr
);
204 if (ContentsWidthChanged
)
206 CalcSizes(s
, mem_size
, base_display_addr
);
207 ImGui::SetWindowSize(ImVec2(s
.WindowWidth
, ImGui::GetWindowSize().y
));
213 // Memory Editor contents only
214 void DrawContents(void* mem_data_void_ptr
, size_t mem_size
, size_t base_display_addr
= 0x0000)
216 u8
* mem_data
= (u8
*)mem_data_void_ptr
;
218 CalcSizes(s
, mem_size
, base_display_addr
);
219 ImGuiStyle
& style
= ImGui::GetStyle();
221 // We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
222 // This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.
223 const float height_separator
= style
.ItemSpacing
.y
;
224 float footer_height
= 0;
226 footer_height
+= height_separator
+ ImGui::GetFrameHeightWithSpacing() * 1;
227 if (OptShowDataPreview
)
228 footer_height
+= height_separator
+ ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
229 ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height
), false, ImGuiWindowFlags_NoMove
);
230 ImDrawList
* draw_list
= ImGui::GetWindowDrawList();
232 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding
, ImVec2(0, 0));
233 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing
, ImVec2(0, 0));
235 const int line_total_count
= (int)((mem_size
+ Cols
- 1) / Cols
);
236 ImGuiListClipper
clipper(line_total_count
, s
.LineHeight
);
237 const size_t visible_start_addr
= clipper
.DisplayStart
* Cols
;
238 const size_t visible_end_addr
= clipper
.DisplayEnd
* Cols
;
240 bool data_next
= false;
242 if (ReadOnly
|| DataEditingAddr
>= mem_size
)
243 DataEditingAddr
= (size_t)-1;
244 if (DataPreviewAddr
>= mem_size
)
245 DataPreviewAddr
= (size_t)-1;
247 size_t preview_data_type_size
= OptShowDataPreview
? DataTypeGetSize(PreviewDataType
) : 0;
249 size_t data_editing_addr_backup
= DataEditingAddr
;
250 size_t data_editing_addr_next
= (size_t)-1;
251 if (DataEditingAddr
!= (size_t)-1)
253 // Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)
254 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow
)) && DataEditingAddr
>= (size_t)Cols
) { data_editing_addr_next
= DataEditingAddr
- Cols
; DataEditingTakeFocus
= true; }
255 else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow
)) && DataEditingAddr
< mem_size
- Cols
) { data_editing_addr_next
= DataEditingAddr
+ Cols
; DataEditingTakeFocus
= true; }
256 else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow
)) && DataEditingAddr
> 0) { data_editing_addr_next
= DataEditingAddr
- 1; DataEditingTakeFocus
= true; }
257 else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow
)) && DataEditingAddr
< mem_size
- 1) { data_editing_addr_next
= DataEditingAddr
+ 1; DataEditingTakeFocus
= true; }
259 if (data_editing_addr_next
!= (size_t)-1 && (data_editing_addr_next
/ Cols
) != (data_editing_addr_backup
/ Cols
))
261 // Track cursor movements
262 const int scroll_offset
= ((int)(data_editing_addr_next
/ Cols
) - (int)(data_editing_addr_backup
/ Cols
));
263 const bool scroll_desired
= (scroll_offset
< 0 && data_editing_addr_next
< visible_start_addr
+ Cols
* 2) || (scroll_offset
> 0 && data_editing_addr_next
> visible_end_addr
- Cols
* 2);
265 ImGui::SetScrollY(ImGui::GetScrollY() + scroll_offset
* s
.LineHeight
);
268 // Draw vertical separator
269 ImVec2 window_pos
= ImGui::GetWindowPos();
271 draw_list
->AddLine(ImVec2(window_pos
.x
+ s
.PosAsciiStart
- s
.GlyphWidth
, window_pos
.y
), ImVec2(window_pos
.x
+ s
.PosAsciiStart
- s
.GlyphWidth
, window_pos
.y
+ 9999), ImGui::GetColorU32(ImGuiCol_Border
));
273 const ImU32 color_text
= ImGui::GetColorU32(ImGuiCol_Text
);
274 const ImU32 color_disabled
= OptGreyOutZeroes
? ImGui::GetColorU32(ImGuiCol_TextDisabled
) : color_text
;
276 const char* format_address
= OptUpperCaseHex
? "%0*" _PRISizeT
"X: " : "%0*" _PRISizeT
"x: ";
277 const char* format_data
= OptUpperCaseHex
? "%0*" _PRISizeT
"X" : "%0*" _PRISizeT
"x";
278 const char* format_range
= OptUpperCaseHex
? "Range %0*" _PRISizeT
"X..%0*" _PRISizeT
"X" : "Range %0*" _PRISizeT
"x..%0*" _PRISizeT
"x";
279 const char* format_byte
= OptUpperCaseHex
? "%02X" : "%02x";
280 const char* format_byte_space
= OptUpperCaseHex
? "%02X " : "%02x ";
282 for (int line_i
= clipper
.DisplayStart
; line_i
< clipper
.DisplayEnd
; line_i
++) // display only visible lines
284 size_t addr
= (size_t)(line_i
* Cols
);
285 ImGui::Text(format_address
, s
.AddrDigitsCount
, base_display_addr
+ addr
);
288 for (int n
= 0; n
< Cols
&& addr
< mem_size
; n
++, addr
++)
290 float byte_pos_x
= s
.PosHexStart
+ s
.HexCellWidth
* n
;
291 if (OptMidColsCount
> 0)
292 byte_pos_x
+= (float)(n
/ OptMidColsCount
) * s
.SpacingBetweenMidCols
;
293 ImGui::SameLine(byte_pos_x
);
296 bool is_highlight_from_user_range
= (addr
>= HighlightMin
&& addr
< HighlightMax
);
297 bool is_highlight_from_user_func
= (HighlightFn
&& HighlightFn(mem_data
, addr
));
298 bool is_highlight_from_preview
= (addr
>= DataPreviewAddr
&& addr
< DataPreviewAddr
+ preview_data_type_size
);
299 if (is_highlight_from_user_range
|| is_highlight_from_user_func
|| is_highlight_from_preview
)
301 ImVec2 pos
= ImGui::GetCursorScreenPos();
302 float highlight_width
= s
.GlyphWidth
* 2;
303 bool is_next_byte_highlighted
= (addr
+ 1 < mem_size
) && ((HighlightMax
!= (size_t)-1 && addr
+ 1 < HighlightMax
) || (HighlightFn
&& HighlightFn(mem_data
, addr
+ 1)));
304 if (is_next_byte_highlighted
|| (n
+ 1 == Cols
))
306 highlight_width
= s
.HexCellWidth
;
307 if (OptMidColsCount
> 0 && n
> 0 && (n
+ 1) < Cols
&& ((n
+ 1) % OptMidColsCount
) == 0)
308 highlight_width
+= s
.SpacingBetweenMidCols
;
310 draw_list
->AddRectFilled(pos
, ImVec2(pos
.x
+ highlight_width
, pos
.y
+ s
.LineHeight
), HighlightColor
);
313 if (DataEditingAddr
== addr
)
315 // Display text input on current byte
316 bool data_write
= false;
317 ImGui::PushID((void*)addr
);
318 if (DataEditingTakeFocus
)
320 ImGui::SetKeyboardFocusHere();
321 ImGui::CaptureKeyboardFromApp(true);
322 sprintf(AddrInputBuf
, format_data
, s
.AddrDigitsCount
, base_display_addr
+ addr
);
323 sprintf(DataInputBuf
, format_byte
, ReadFn
? ReadFn(mem_data
, addr
) : mem_data
[addr
]);
325 ImGui::PushItemWidth(s
.GlyphWidth
* 2);
328 // FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.
329 static int Callback(ImGuiInputTextCallbackData
* data
)
331 UserData
* user_data
= (UserData
*)data
->UserData
;
332 if (!data
->HasSelection())
333 user_data
->CursorPos
= data
->CursorPos
;
334 if (data
->SelectionStart
== 0 && data
->SelectionEnd
== data
->BufTextLen
)
336 // When not editing a byte, always rewrite its content (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there)
337 data
->DeleteChars(0, data
->BufTextLen
);
338 data
->InsertChars(0, user_data
->CurrentBufOverwrite
);
339 data
->SelectionStart
= 0;
340 data
->SelectionEnd
= data
->CursorPos
= 2;
344 char CurrentBufOverwrite
[3]; // Input
345 int CursorPos
; // Output
348 user_data
.CursorPos
= -1;
349 sprintf(user_data
.CurrentBufOverwrite
, format_byte
, ReadFn
? ReadFn(mem_data
, addr
) : mem_data
[addr
]);
350 ImGuiInputTextFlags flags
= ImGuiInputTextFlags_CharsHexadecimal
| ImGuiInputTextFlags_EnterReturnsTrue
| ImGuiInputTextFlags_AutoSelectAll
| ImGuiInputTextFlags_NoHorizontalScroll
| ImGuiInputTextFlags_AlwaysInsertMode
| ImGuiInputTextFlags_CallbackAlways
;
351 if (ImGui::InputText("##data", DataInputBuf
, 32, flags
, UserData::Callback
, &user_data
))
352 data_write
= data_next
= true;
353 else if (!DataEditingTakeFocus
&& !ImGui::IsItemActive())
354 DataEditingAddr
= data_editing_addr_next
= (size_t)-1;
355 DataEditingTakeFocus
= false;
356 ImGui::PopItemWidth();
357 if (user_data
.CursorPos
>= 2)
358 data_write
= data_next
= true;
359 if (data_editing_addr_next
!= (size_t)-1)
360 data_write
= data_next
= false;
361 unsigned int data_input_value
= 0;
362 if (data_write
&& sscanf(DataInputBuf
, "%X", &data_input_value
) == 1)
365 WriteFn(mem_data
, addr
, (u8
)data_input_value
);
367 mem_data
[addr
] = (u8
)data_input_value
;
373 // NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
374 u8 b
= ReadFn
? ReadFn(mem_data
, addr
) : mem_data
[addr
];
378 if ((b
>= 32 && b
< 128))
379 ImGui::Text(".%c ", b
);
380 else if (b
== 0xFF && OptGreyOutZeroes
)
381 ImGui::TextDisabled("## ");
385 ImGui::Text(format_byte_space
, b
);
389 if (b
== 0 && OptGreyOutZeroes
)
390 ImGui::TextDisabled("00 ");
392 ImGui::Text(format_byte_space
, b
);
394 if (!ReadOnly
&& ImGui::IsItemHovered() && ImGui::IsMouseClicked(0))
396 DataEditingTakeFocus
= true;
397 data_editing_addr_next
= addr
;
405 ImGui::SameLine(s
.PosAsciiStart
);
406 ImVec2 pos
= ImGui::GetCursorScreenPos();
407 addr
= line_i
* Cols
;
408 ImGui::PushID(line_i
);
409 if (ImGui::InvisibleButton("ascii", ImVec2(s
.PosAsciiEnd
- s
.PosAsciiStart
, s
.LineHeight
)))
411 DataEditingAddr
= DataPreviewAddr
= addr
+ (size_t)((ImGui::GetIO().MousePos
.x
- pos
.x
) / s
.GlyphWidth
);
412 DataEditingTakeFocus
= true;
415 for (int n
= 0; n
< Cols
&& addr
< mem_size
; n
++, addr
++)
417 if (addr
== DataEditingAddr
)
419 draw_list
->AddRectFilled(pos
, ImVec2(pos
.x
+ s
.GlyphWidth
, pos
.y
+ s
.LineHeight
), ImGui::GetColorU32(ImGuiCol_FrameBg
));
420 draw_list
->AddRectFilled(pos
, ImVec2(pos
.x
+ s
.GlyphWidth
, pos
.y
+ s
.LineHeight
), ImGui::GetColorU32(ImGuiCol_TextSelectedBg
));
422 unsigned char c
= ReadFn
? ReadFn(mem_data
, addr
) : mem_data
[addr
];
423 char display_c
= (c
< 32 || c
>= 128) ? '.' : c
;
424 draw_list
->AddText(pos
, (display_c
== '.') ? color_disabled
: color_text
, &display_c
, &display_c
+ 1);
425 pos
.x
+= s
.GlyphWidth
;
430 ImGui::PopStyleVar(2);
433 if (data_next
&& DataEditingAddr
< mem_size
)
435 DataEditingAddr
= DataPreviewAddr
= DataEditingAddr
+ 1;
436 DataEditingTakeFocus
= true;
438 else if (data_editing_addr_next
!= (size_t)-1)
440 DataEditingAddr
= DataPreviewAddr
= data_editing_addr_next
;
443 bool next_show_data_preview
= OptShowDataPreview
;
450 if (ImGui::Button("Options"))
451 ImGui::OpenPopup("context");
452 if (ImGui::BeginPopup("context"))
454 ImGui::PushItemWidth(56);
455 if (ImGui::DragInt("##cols", &Cols
, 0.2f
, 4, 32, "%d cols")) { ContentsWidthChanged
= true; }
456 ImGui::PopItemWidth();
457 ImGui::Checkbox("Show Data Preview", &next_show_data_preview
);
458 ImGui::Checkbox("Show HexII", &OptShowHexII
);
459 if (ImGui::Checkbox("Show Ascii", &OptShowAscii
)) { ContentsWidthChanged
= true; }
460 ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes
);
461 ImGui::Checkbox("Uppercase Hex", &OptUpperCaseHex
);
467 ImGui::Text(format_range
, s
.AddrDigitsCount
, base_display_addr
, s
.AddrDigitsCount
, base_display_addr
+ mem_size
- 1);
469 ImGui::PushItemWidth((s
.AddrDigitsCount
+ 1) * s
.GlyphWidth
+ style
.FramePadding
.x
* 2.0f
);
470 if (ImGui::InputText("##addr", AddrInputBuf
, 32, ImGuiInputTextFlags_CharsHexadecimal
| ImGuiInputTextFlags_EnterReturnsTrue
))
473 if (sscanf(AddrInputBuf
, "%" _PRISizeT
"X", &goto_addr
) == 1)
475 GotoAddr
= goto_addr
- base_display_addr
;
476 HighlightMin
= HighlightMax
= (size_t)-1;
479 ImGui::PopItemWidth();
481 if (GotoAddr
!= (size_t)-1)
483 if (GotoAddr
< mem_size
)
485 ImGui::BeginChild("##scrolling");
486 ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y
+ (GotoAddr
/ Cols
) * ImGui::GetTextLineHeight());
488 DataEditingAddr
= DataPreviewAddr
= GotoAddr
;
489 DataEditingTakeFocus
= true;
491 GotoAddr
= (size_t)-1;
495 if (OptShowDataPreview
)
498 ImGui::AlignTextToFramePadding();
499 ImGui::Text("Preview as:");
501 ImGui::PushItemWidth((s
.GlyphWidth
* 10.0f
) + style
.FramePadding
.x
* 2.0f
+ style
.ItemInnerSpacing
.x
);
502 if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType
), ImGuiComboFlags_HeightLargest
))
504 for (int n
= 0; n
< DataType_COUNT
; n
++)
505 if (ImGui::Selectable(DataTypeGetDesc((DataType
)n
), PreviewDataType
== n
))
506 PreviewDataType
= (DataType
)n
;
509 ImGui::PopItemWidth();
511 ImGui::PushItemWidth((s
.GlyphWidth
* 6.0f
) + style
.FramePadding
.x
* 2.0f
+ style
.ItemInnerSpacing
.x
);
512 ImGui::Combo("##combo_endianess", &PreviewEndianess
, "LE\0BE\0\0");
513 ImGui::PopItemWidth();
516 float x
= s
.GlyphWidth
* 6.0f
;
517 bool has_value
= DataPreviewAddr
!= (size_t)-1;
519 DisplayPreviewData(DataPreviewAddr
, mem_data
, mem_size
, PreviewDataType
, DataFormat_Dec
, buf
, (size_t)IM_ARRAYSIZE(buf
));
520 ImGui::Text("Dec"); ImGui::SameLine(x
); ImGui::TextUnformatted(has_value
? buf
: "N/A");
522 DisplayPreviewData(DataPreviewAddr
, mem_data
, mem_size
, PreviewDataType
, DataFormat_Hex
, buf
, (size_t)IM_ARRAYSIZE(buf
));
523 ImGui::Text("Hex"); ImGui::SameLine(x
); ImGui::TextUnformatted(has_value
? buf
: "N/A");
525 DisplayPreviewData(DataPreviewAddr
, mem_data
, mem_size
, PreviewDataType
, DataFormat_Bin
, buf
, (size_t)IM_ARRAYSIZE(buf
));
526 ImGui::Text("Bin"); ImGui::SameLine(x
); ImGui::TextUnformatted(has_value
? buf
: "N/A");
529 OptShowDataPreview
= next_show_data_preview
;
531 // Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
532 ImGui::SetCursorPosX(s
.WindowWidth
);
535 // Utilities for Data Preview
536 const char* DataTypeGetDesc(DataType data_type
) const
538 const char* descs
[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
539 IM_ASSERT(data_type
>= 0 && data_type
< DataType_COUNT
);
540 return descs
[data_type
];
543 size_t DataTypeGetSize(DataType data_type
) const
545 const size_t sizes
[] = { 1, 1, 2, 2, 4, 4, 8, 8, 4, 8 };
546 IM_ASSERT(data_type
>= 0 && data_type
< DataType_COUNT
);
547 return sizes
[data_type
];
550 const char* DataFormatGetDesc(DataFormat data_format
) const
552 const char* descs
[] = { "Bin", "Dec", "Hex" };
553 IM_ASSERT(data_format
>= 0 && data_format
< DataFormat_COUNT
);
554 return descs
[data_format
];
557 bool IsBigEndian() const
565 static void* EndianessCopyBigEndian(void* _dst
, void* _src
, size_t s
, int is_little_endian
)
567 if (is_little_endian
)
569 uint8_t* dst
= (uint8_t*)_dst
;
570 uint8_t* src
= (uint8_t*)_src
+ s
- 1;
571 for (int i
= 0, n
= (int)s
; i
< n
; ++i
)
572 memcpy(dst
++, src
--, 1);
577 return memcpy(_dst
, _src
, s
);
581 static void* EndianessCopyLittleEndian(void* _dst
, void* _src
, size_t s
, int is_little_endian
)
583 if (is_little_endian
)
585 return memcpy(_dst
, _src
, s
);
589 uint8_t* dst
= (uint8_t*)_dst
;
590 uint8_t* src
= (uint8_t*)_src
+ s
- 1;
591 for (int i
= 0, n
= (int)s
; i
< n
; ++i
)
592 memcpy(dst
++, src
--, 1);
597 void* EndianessCopy(void *dst
, void *src
, size_t size
) const
599 static void *(*fp
)(void *, void *, size_t, int) = NULL
;
601 fp
= IsBigEndian() ? EndianessCopyBigEndian
: EndianessCopyLittleEndian
;
602 return fp(dst
, src
, size
, PreviewEndianess
);
605 const char* FormatBinary(const uint8_t* buf
, int width
) const
607 IM_ASSERT(width
<= 64);
609 static char out_buf
[64 + 8 + 1];
611 for (int j
= n
- 1; j
>= 0; --j
)
613 for (int i
= 0; i
< 8; ++i
)
614 out_buf
[out_n
++] = (buf
[j
] & (1 << (7 - i
))) ? '1' : '0';
615 out_buf
[out_n
++] = ' ';
617 IM_ASSERT(out_n
< IM_ARRAYSIZE(out_buf
));
622 void DisplayPreviewData(size_t addr
, const u8
* mem_data
, size_t mem_size
, DataType data_type
, DataFormat data_format
, char* out_buf
, size_t out_buf_size
) const
625 size_t elem_size
= DataTypeGetSize(data_type
);
626 size_t size
= addr
+ elem_size
> mem_size
? mem_size
- addr
: elem_size
;
628 for (int i
= 0, n
= (int)size
; i
< n
; ++i
)
629 buf
[i
] = ReadFn(mem_data
, addr
+ i
);
631 memcpy(buf
, mem_data
+ addr
, size
);
633 if (data_format
== DataFormat_Bin
)
636 EndianessCopy(binbuf
, buf
, size
);
637 ImSnprintf(out_buf
, out_buf_size
, "%s", FormatBinary(binbuf
, (int)size
* 8));
647 EndianessCopy(&int8
, buf
, size
);
648 if (data_format
== DataFormat_Dec
) { ImSnprintf(out_buf
, out_buf_size
, "%hhd", int8
); return; }
649 if (data_format
== DataFormat_Hex
) { ImSnprintf(out_buf
, out_buf_size
, "0x%02x", int8
& 0xFF); return; }
655 EndianessCopy(&uint8
, buf
, size
);
656 if (data_format
== DataFormat_Dec
) { ImSnprintf(out_buf
, out_buf_size
, "%hhu", uint8
); return; }
657 if (data_format
== DataFormat_Hex
) { ImSnprintf(out_buf
, out_buf_size
, "0x%02x", uint8
& 0XFF); return; }
663 EndianessCopy(&int16
, buf
, size
);
664 if (data_format
== DataFormat_Dec
) { ImSnprintf(out_buf
, out_buf_size
, "%hd", int16
); return; }
665 if (data_format
== DataFormat_Hex
) { ImSnprintf(out_buf
, out_buf_size
, "0x%04x", int16
& 0xFFFF); return; }
671 EndianessCopy(&uint16
, buf
, size
);
672 if (data_format
== DataFormat_Dec
) { ImSnprintf(out_buf
, out_buf_size
, "%hu", uint16
); return; }
673 if (data_format
== DataFormat_Hex
) { ImSnprintf(out_buf
, out_buf_size
, "0x%04x", uint16
& 0xFFFF); return; }
679 EndianessCopy(&int32
, buf
, size
);
680 if (data_format
== DataFormat_Dec
) { ImSnprintf(out_buf
, out_buf_size
, "%d", int32
); return; }
681 if (data_format
== DataFormat_Hex
) { ImSnprintf(out_buf
, out_buf_size
, "0x%08x", int32
); return; }
687 EndianessCopy(&uint32
, buf
, size
);
688 if (data_format
== DataFormat_Dec
) { ImSnprintf(out_buf
, out_buf_size
, "%u", uint32
); return; }
689 if (data_format
== DataFormat_Hex
) { ImSnprintf(out_buf
, out_buf_size
, "0x%08x", uint32
); return; }
695 EndianessCopy(&int64
, buf
, size
);
696 if (data_format
== DataFormat_Dec
) { ImSnprintf(out_buf
, out_buf_size
, "%lld", (long long)int64
); return; }
697 if (data_format
== DataFormat_Hex
) { ImSnprintf(out_buf
, out_buf_size
, "0x%016llx", (long long)int64
); return; }
703 EndianessCopy(&uint64
, buf
, size
);
704 if (data_format
== DataFormat_Dec
) { ImSnprintf(out_buf
, out_buf_size
, "%llu", (long long)uint64
); return; }
705 if (data_format
== DataFormat_Hex
) { ImSnprintf(out_buf
, out_buf_size
, "0x%016llx", (long long)uint64
); return; }
710 float float32
= 0.0f
;
711 EndianessCopy(&float32
, buf
, size
);
712 if (data_format
== DataFormat_Dec
) { ImSnprintf(out_buf
, out_buf_size
, "%f", float32
); return; }
713 if (data_format
== DataFormat_Hex
) { ImSnprintf(out_buf
, out_buf_size
, "%a", float32
); return; }
716 case DataType_Double
:
718 double float64
= 0.0;
719 EndianessCopy(&float64
, buf
, size
);
720 if (data_format
== DataFormat_Dec
) { ImSnprintf(out_buf
, out_buf_size
, "%f", float64
); return; }
721 if (data_format
== DataFormat_Hex
) { ImSnprintf(out_buf
, out_buf_size
, "%a", float64
); return; }
727 IM_ASSERT(0); // Shouldn't reach