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.
43 // - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame.
44 // - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
47 #include <stdio.h> // sprintf, scanf
48 #include <stdint.h> // uint8_t, etc.
52 #define snprintf _snprintf
59 typedef unsigned char u8
;
85 bool Open
; // = true // set to false when DrawWindow() was closed. ignore if not using DrawWindow().
86 bool ReadOnly
; // = false // disable any editing.
87 int Cols
; // = 16 // number of columns to display.
88 bool OptShowOptions
; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them.
89 bool OptShowDataPreview
; // = false // display a footer previewing the decimal/binary/hex/float representation of the currently selected bytes.
90 bool OptShowHexII
; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X".
91 bool OptShowAscii
; // = true // display ASCII representation on the right side.
92 bool OptGreyOutZeroes
; // = true // display null/zero bytes using the TextDisabled color.
93 bool OptUpperCaseHex
; // = true // display hexadecimal values as "FF" instead of "ff".
94 int OptMidColsCount
; // = 8 // set to 0 to disable extra spacing between every mid-cols.
95 int OptAddrDigitsCount
; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr).
96 ImU32 HighlightColor
; // // background color of highlighted bytes.
97 u8 (*ReadFn
)(const u8
* data
, size_t off
); // = NULL // optional handler to read bytes.
98 void (*WriteFn
)(u8
* data
, size_t off
, u8 d
); // = NULL // optional handler to write bytes.
99 bool (*HighlightFn
)(const u8
* data
, size_t off
);//NULL // optional handler to return Highlight property (to support non-contiguous highlighting).
102 bool ContentsWidthChanged
;
103 size_t DataPreviewAddr
;
104 size_t DataEditingAddr
;
105 bool DataEditingTakeFocus
;
106 char DataInputBuf
[32];
107 char AddrInputBuf
[32];
109 size_t HighlightMin
, HighlightMax
;
110 int PreviewEndianess
;
111 DataType PreviewDataType
;
119 OptShowOptions
= true;
120 OptShowDataPreview
= false;
121 OptShowHexII
= false;
123 OptGreyOutZeroes
= true;
124 OptUpperCaseHex
= true;
126 OptAddrDigitsCount
= 0;
127 HighlightColor
= IM_COL32(255, 255, 255, 50);
133 ContentsWidthChanged
= false;
134 DataPreviewAddr
= DataEditingAddr
= (size_t)-1;
135 DataEditingTakeFocus
= false;
136 memset(DataInputBuf
, 0, sizeof(DataInputBuf
));
137 memset(AddrInputBuf
, 0, sizeof(AddrInputBuf
));
138 GotoAddr
= (size_t)-1;
139 HighlightMin
= HighlightMax
= (size_t)-1;
140 PreviewEndianess
= 0;
141 PreviewDataType
= DataType_S32
;
144 void GotoAddrAndHighlight(size_t addr_min
, size_t addr_max
)
147 HighlightMin
= addr_min
;
148 HighlightMax
= addr_max
;
157 float SpacingBetweenMidCols
;
165 void CalcSizes(Sizes
& s
, size_t mem_size
, size_t base_display_addr
)
167 ImGuiStyle
& style
= ImGui::GetStyle();
168 s
.AddrDigitsCount
= OptAddrDigitsCount
;
169 if (s
.AddrDigitsCount
== 0)
170 for (size_t n
= base_display_addr
+ mem_size
- 1; n
> 0; n
>>= 4)
172 s
.LineHeight
= ImGui::GetTextLineHeight();
173 s
.GlyphWidth
= ImGui::CalcTextSize("F").x
+ 1; // We assume the font is mono-space
174 s
.HexCellWidth
= (float)(int)(s
.GlyphWidth
* 2.5f
); // "FF " we include trailing space in the width to easily catch clicks everywhere
175 s
.SpacingBetweenMidCols
= (float)(int)(s
.HexCellWidth
* 0.25f
); // Every OptMidColsCount columns we add a bit of extra spacing
176 s
.PosHexStart
= (s
.AddrDigitsCount
+ 2) * s
.GlyphWidth
;
177 s
.PosHexEnd
= s
.PosHexStart
+ (s
.HexCellWidth
* Cols
);
178 s
.PosAsciiStart
= s
.PosAsciiEnd
= s
.PosHexEnd
;
181 s
.PosAsciiStart
= s
.PosHexEnd
+ s
.GlyphWidth
* 1;
182 if (OptMidColsCount
> 0)
183 s
.PosAsciiStart
+= ((Cols
+ OptMidColsCount
- 1) / OptMidColsCount
) * s
.SpacingBetweenMidCols
;
184 s
.PosAsciiEnd
= s
.PosAsciiStart
+ Cols
* s
.GlyphWidth
;
186 s
.WindowWidth
= s
.PosAsciiEnd
+ style
.ScrollbarSize
+ style
.WindowPadding
.x
* 2 + s
.GlyphWidth
;
189 // Standalone Memory Editor window
190 void DrawWindow(const char* title
, void* mem_data
, size_t mem_size
, size_t base_display_addr
= 0x0000)
193 CalcSizes(s
, mem_size
, base_display_addr
);
194 ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f
, 0.0f
), ImVec2(s
.WindowWidth
, FLT_MAX
));
197 if (ImGui::Begin(title
, &Open
, ImGuiWindowFlags_NoScrollbar
))
199 if (ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows
) && ImGui::IsMouseClicked(1))
200 ImGui::OpenPopup("context");
201 DrawContents(mem_data
, mem_size
, base_display_addr
);
202 if (ContentsWidthChanged
)
204 CalcSizes(s
, mem_size
, base_display_addr
);
205 ImGui::SetWindowSize(ImVec2(s
.WindowWidth
, ImGui::GetWindowSize().y
));
211 // Memory Editor contents only
212 void DrawContents(void* mem_data_void_ptr
, size_t mem_size
, size_t base_display_addr
= 0x0000)
214 u8
* mem_data
= (u8
*)mem_data_void_ptr
;
216 CalcSizes(s
, mem_size
, base_display_addr
);
217 ImGuiStyle
& style
= ImGui::GetStyle();
219 // We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
220 // 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.
221 const float height_separator
= style
.ItemSpacing
.y
;
222 float footer_height
= 0;
224 footer_height
+= height_separator
+ ImGui::GetFrameHeightWithSpacing() * 1;
225 if (OptShowDataPreview
)
226 footer_height
+= height_separator
+ ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
227 ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height
), false, ImGuiWindowFlags_NoMove
);
228 ImDrawList
* draw_list
= ImGui::GetWindowDrawList();
230 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding
, ImVec2(0, 0));
231 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing
, ImVec2(0, 0));
233 const int line_total_count
= (int)((mem_size
+ Cols
- 1) / Cols
);
234 ImGuiListClipper
clipper(line_total_count
, s
.LineHeight
);
235 const size_t visible_start_addr
= clipper
.DisplayStart
* Cols
;
236 const size_t visible_end_addr
= clipper
.DisplayEnd
* Cols
;
238 bool data_next
= false;
240 if (ReadOnly
|| DataEditingAddr
>= mem_size
)
241 DataEditingAddr
= (size_t)-1;
242 if (DataPreviewAddr
>= mem_size
)
243 DataPreviewAddr
= (size_t)-1;
245 size_t preview_data_type_size
= OptShowDataPreview
? DataTypeGetSize(PreviewDataType
) : 0;
247 size_t data_editing_addr_backup
= DataEditingAddr
;
248 size_t data_editing_addr_next
= (size_t)-1;
249 if (DataEditingAddr
!= (size_t)-1)
251 // 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)
252 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow
)) && DataEditingAddr
>= (size_t)Cols
) { data_editing_addr_next
= DataEditingAddr
- Cols
; DataEditingTakeFocus
= true; }
253 else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow
)) && DataEditingAddr
< mem_size
- Cols
) { data_editing_addr_next
= DataEditingAddr
+ Cols
; DataEditingTakeFocus
= true; }
254 else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow
)) && DataEditingAddr
> 0) { data_editing_addr_next
= DataEditingAddr
- 1; DataEditingTakeFocus
= true; }
255 else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow
)) && DataEditingAddr
< mem_size
- 1) { data_editing_addr_next
= DataEditingAddr
+ 1; DataEditingTakeFocus
= true; }
257 if (data_editing_addr_next
!= (size_t)-1 && (data_editing_addr_next
/ Cols
) != (data_editing_addr_backup
/ Cols
))
259 // Track cursor movements
260 const int scroll_offset
= ((int)(data_editing_addr_next
/ Cols
) - (int)(data_editing_addr_backup
/ Cols
));
261 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);
263 ImGui::SetScrollY(ImGui::GetScrollY() + scroll_offset
* s
.LineHeight
);
266 // Draw vertical separator
267 ImVec2 window_pos
= ImGui::GetWindowPos();
269 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
));
271 const ImU32 color_text
= ImGui::GetColorU32(ImGuiCol_Text
);
272 const ImU32 color_disabled
= OptGreyOutZeroes
? ImGui::GetColorU32(ImGuiCol_TextDisabled
) : color_text
;
274 const char* format_address
= OptUpperCaseHex
? "%0*" _PRISizeT
"X: " : "%0*" _PRISizeT
"x: ";
275 const char* format_data
= OptUpperCaseHex
? "%0*" _PRISizeT
"X" : "%0*" _PRISizeT
"x";
276 const char* format_range
= OptUpperCaseHex
? "Range %0*" _PRISizeT
"X..%0*" _PRISizeT
"X" : "Range %0*" _PRISizeT
"x..%0*" _PRISizeT
"x";
277 const char* format_byte
= OptUpperCaseHex
? "%02X" : "%02x";
278 const char* format_byte_space
= OptUpperCaseHex
? "%02X " : "%02x ";
280 for (int line_i
= clipper
.DisplayStart
; line_i
< clipper
.DisplayEnd
; line_i
++) // display only visible lines
282 size_t addr
= (size_t)(line_i
* Cols
);
283 ImGui::Text(format_address
, s
.AddrDigitsCount
, base_display_addr
+ addr
);
286 for (int n
= 0; n
< Cols
&& addr
< mem_size
; n
++, addr
++)
288 float byte_pos_x
= s
.PosHexStart
+ s
.HexCellWidth
* n
;
289 if (OptMidColsCount
> 0)
290 byte_pos_x
+= (n
/ OptMidColsCount
) * s
.SpacingBetweenMidCols
;
291 ImGui::SameLine(byte_pos_x
);
294 bool is_highlight_from_user_range
= (addr
>= HighlightMin
&& addr
< HighlightMax
);
295 bool is_highlight_from_user_func
= (HighlightFn
&& HighlightFn(mem_data
, addr
));
296 bool is_highlight_from_preview
= (addr
>= DataPreviewAddr
&& addr
< DataPreviewAddr
+ preview_data_type_size
);
297 if (is_highlight_from_user_range
|| is_highlight_from_user_func
|| is_highlight_from_preview
)
299 ImVec2 pos
= ImGui::GetCursorScreenPos();
300 float highlight_width
= s
.GlyphWidth
* 2;
301 bool is_next_byte_highlighted
= (addr
+ 1 < mem_size
) && ((HighlightMax
!= (size_t)-1 && addr
+ 1 < HighlightMax
) || (HighlightFn
&& HighlightFn(mem_data
, addr
+ 1)));
302 if (is_next_byte_highlighted
|| (n
+ 1 == Cols
))
304 highlight_width
= s
.HexCellWidth
;
305 if (OptMidColsCount
> 0 && n
> 0 && (n
+ 1) < Cols
&& ((n
+ 1) % OptMidColsCount
) == 0)
306 highlight_width
+= s
.SpacingBetweenMidCols
;
308 draw_list
->AddRectFilled(pos
, ImVec2(pos
.x
+ highlight_width
, pos
.y
+ s
.LineHeight
), HighlightColor
);
311 if (DataEditingAddr
== addr
)
313 // Display text input on current byte
314 bool data_write
= false;
315 ImGui::PushID((void*)addr
);
316 if (DataEditingTakeFocus
)
318 ImGui::SetKeyboardFocusHere();
319 ImGui::CaptureKeyboardFromApp(true);
320 sprintf(AddrInputBuf
, format_data
, s
.AddrDigitsCount
, base_display_addr
+ addr
);
321 sprintf(DataInputBuf
, format_byte
, ReadFn
? ReadFn(mem_data
, addr
) : mem_data
[addr
]);
323 ImGui::PushItemWidth(s
.GlyphWidth
* 2);
326 // 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.
327 static int Callback(ImGuiInputTextCallbackData
* data
)
329 UserData
* user_data
= (UserData
*)data
->UserData
;
330 if (!data
->HasSelection())
331 user_data
->CursorPos
= data
->CursorPos
;
332 if (data
->SelectionStart
== 0 && data
->SelectionEnd
== data
->BufTextLen
)
334 // 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)
335 data
->DeleteChars(0, data
->BufTextLen
);
336 data
->InsertChars(0, user_data
->CurrentBufOverwrite
);
337 data
->SelectionStart
= 0;
338 data
->SelectionEnd
= data
->CursorPos
= 2;
342 char CurrentBufOverwrite
[3]; // Input
343 int CursorPos
; // Output
346 user_data
.CursorPos
= -1;
347 sprintf(user_data
.CurrentBufOverwrite
, format_byte
, ReadFn
? ReadFn(mem_data
, addr
) : mem_data
[addr
]);
348 ImGuiInputTextFlags flags
= ImGuiInputTextFlags_CharsHexadecimal
| ImGuiInputTextFlags_EnterReturnsTrue
| ImGuiInputTextFlags_AutoSelectAll
| ImGuiInputTextFlags_NoHorizontalScroll
| ImGuiInputTextFlags_AlwaysInsertMode
| ImGuiInputTextFlags_CallbackAlways
;
349 if (ImGui::InputText("##data", DataInputBuf
, 32, flags
, UserData::Callback
, &user_data
))
350 data_write
= data_next
= true;
351 else if (!DataEditingTakeFocus
&& !ImGui::IsItemActive())
352 DataEditingAddr
= data_editing_addr_next
= (size_t)-1;
353 DataEditingTakeFocus
= false;
354 ImGui::PopItemWidth();
355 if (user_data
.CursorPos
>= 2)
356 data_write
= data_next
= true;
357 if (data_editing_addr_next
!= (size_t)-1)
358 data_write
= data_next
= false;
359 int data_input_value
;
360 if (data_write
&& sscanf(DataInputBuf
, "%X", &data_input_value
) == 1)
363 WriteFn(mem_data
, addr
, (u8
)data_input_value
);
365 mem_data
[addr
] = (u8
)data_input_value
;
371 // NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
372 u8 b
= ReadFn
? ReadFn(mem_data
, addr
) : mem_data
[addr
];
376 if ((b
>= 32 && b
< 128))
377 ImGui::Text(".%c ", b
);
378 else if (b
== 0xFF && OptGreyOutZeroes
)
379 ImGui::TextDisabled("## ");
383 ImGui::Text(format_byte_space
, b
);
387 if (b
== 0 && OptGreyOutZeroes
)
388 ImGui::TextDisabled("00 ");
390 ImGui::Text(format_byte_space
, b
);
392 if (!ReadOnly
&& ImGui::IsItemHovered() && ImGui::IsMouseClicked(0))
394 DataEditingTakeFocus
= true;
395 data_editing_addr_next
= addr
;
403 ImGui::SameLine(s
.PosAsciiStart
);
404 ImVec2 pos
= ImGui::GetCursorScreenPos();
405 addr
= line_i
* Cols
;
406 ImGui::PushID(line_i
);
407 if (ImGui::InvisibleButton("ascii", ImVec2(s
.PosAsciiEnd
- s
.PosAsciiStart
, s
.LineHeight
)))
409 DataEditingAddr
= DataPreviewAddr
= addr
+ (size_t)((ImGui::GetIO().MousePos
.x
- pos
.x
) / s
.GlyphWidth
);
410 DataEditingTakeFocus
= true;
413 for (int n
= 0; n
< Cols
&& addr
< mem_size
; n
++, addr
++)
415 if (addr
== DataEditingAddr
)
417 draw_list
->AddRectFilled(pos
, ImVec2(pos
.x
+ s
.GlyphWidth
, pos
.y
+ s
.LineHeight
), ImGui::GetColorU32(ImGuiCol_FrameBg
));
418 draw_list
->AddRectFilled(pos
, ImVec2(pos
.x
+ s
.GlyphWidth
, pos
.y
+ s
.LineHeight
), ImGui::GetColorU32(ImGuiCol_TextSelectedBg
));
420 unsigned char c
= ReadFn
? ReadFn(mem_data
, addr
) : mem_data
[addr
];
421 char display_c
= (c
< 32 || c
>= 128) ? '.' : c
;
422 draw_list
->AddText(pos
, (display_c
== '.') ? color_disabled
: color_text
, &display_c
, &display_c
+ 1);
423 pos
.x
+= s
.GlyphWidth
;
428 ImGui::PopStyleVar(2);
431 if (data_next
&& DataEditingAddr
< mem_size
)
433 DataEditingAddr
= DataPreviewAddr
= DataEditingAddr
+ 1;
434 DataEditingTakeFocus
= true;
436 else if (data_editing_addr_next
!= (size_t)-1)
438 DataEditingAddr
= DataPreviewAddr
= data_editing_addr_next
;
441 bool next_show_data_preview
= OptShowDataPreview
;
448 if (ImGui::Button("Options"))
449 ImGui::OpenPopup("context");
450 if (ImGui::BeginPopup("context"))
452 ImGui::PushItemWidth(56);
453 if (ImGui::DragInt("##cols", &Cols
, 0.2f
, 4, 32, "%d cols")) { ContentsWidthChanged
= true; }
454 ImGui::PopItemWidth();
455 ImGui::Checkbox("Show Data Preview", &next_show_data_preview
);
456 ImGui::Checkbox("Show HexII", &OptShowHexII
);
457 if (ImGui::Checkbox("Show Ascii", &OptShowAscii
)) { ContentsWidthChanged
= true; }
458 ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes
);
459 ImGui::Checkbox("Uppercase Hex", &OptUpperCaseHex
);
465 ImGui::Text(format_range
, s
.AddrDigitsCount
, base_display_addr
, s
.AddrDigitsCount
, base_display_addr
+ mem_size
- 1);
467 ImGui::PushItemWidth((s
.AddrDigitsCount
+ 1) * s
.GlyphWidth
+ style
.FramePadding
.x
* 2.0f
);
468 if (ImGui::InputText("##addr", AddrInputBuf
, 32, ImGuiInputTextFlags_CharsHexadecimal
| ImGuiInputTextFlags_EnterReturnsTrue
))
471 if (sscanf(AddrInputBuf
, "%" _PRISizeT
"X", &goto_addr
) == 1)
473 GotoAddr
= goto_addr
- base_display_addr
;
474 HighlightMin
= HighlightMax
= (size_t)-1;
477 ImGui::PopItemWidth();
479 if (GotoAddr
!= (size_t)-1)
481 if (GotoAddr
< mem_size
)
483 ImGui::BeginChild("##scrolling");
484 ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y
+ (GotoAddr
/ Cols
) * ImGui::GetTextLineHeight());
486 DataEditingAddr
= DataPreviewAddr
= GotoAddr
;
487 DataEditingTakeFocus
= true;
489 GotoAddr
= (size_t)-1;
493 if (OptShowDataPreview
)
496 ImGui::AlignTextToFramePadding();
497 ImGui::Text("Preview as:");
499 ImGui::PushItemWidth((s
.GlyphWidth
* 10.0f
) + style
.FramePadding
.x
* 2.0f
+ style
.ItemInnerSpacing
.x
);
500 if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType
), ImGuiComboFlags_HeightLargest
))
502 for (int n
= 0; n
< DataType_COUNT
; n
++)
503 if (ImGui::Selectable(DataTypeGetDesc((DataType
)n
), PreviewDataType
== n
))
504 PreviewDataType
= (DataType
)n
;
507 ImGui::PopItemWidth();
509 ImGui::PushItemWidth((s
.GlyphWidth
* 6.0f
) + style
.FramePadding
.x
* 2.0f
+ style
.ItemInnerSpacing
.x
);
510 ImGui::Combo("##combo_endianess", &PreviewEndianess
, "LE\0BE\0\0");
511 ImGui::PopItemWidth();
514 float x
= s
.GlyphWidth
* 6.0f
;
515 bool has_value
= DataPreviewAddr
!= (size_t)-1;
517 DisplayPreviewData(DataPreviewAddr
, mem_data
, mem_size
, PreviewDataType
, DataFormat_Dec
, buf
, (size_t)IM_ARRAYSIZE(buf
));
518 ImGui::Text("Dec"); ImGui::SameLine(x
); ImGui::TextUnformatted(has_value
? buf
: "N/A");
520 DisplayPreviewData(DataPreviewAddr
, mem_data
, mem_size
, PreviewDataType
, DataFormat_Hex
, buf
, (size_t)IM_ARRAYSIZE(buf
));
521 ImGui::Text("Hex"); ImGui::SameLine(x
); ImGui::TextUnformatted(has_value
? buf
: "N/A");
523 DisplayPreviewData(DataPreviewAddr
, mem_data
, mem_size
, PreviewDataType
, DataFormat_Bin
, buf
, (size_t)IM_ARRAYSIZE(buf
));
524 ImGui::Text("Bin"); ImGui::SameLine(x
); ImGui::TextUnformatted(has_value
? buf
: "N/A");
527 OptShowDataPreview
= next_show_data_preview
;
529 // Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
530 ImGui::SetCursorPosX(s
.WindowWidth
);
533 // Utilities for Data Preview
534 const char* DataTypeGetDesc(DataType data_type
) const
536 const char* descs
[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
537 IM_ASSERT(data_type
>= 0 && data_type
< DataType_COUNT
);
538 return descs
[data_type
];
541 size_t DataTypeGetSize(DataType data_type
) const
543 const size_t sizes
[] = { 1, 1, 2, 2, 4, 4, 8, 8, 4, 8 };
544 IM_ASSERT(data_type
>= 0 && data_type
< DataType_COUNT
);
545 return sizes
[data_type
];
548 const char* DataFormatGetDesc(DataFormat data_format
) const
550 const char* descs
[] = { "Bin", "Dec", "Hex" };
551 IM_ASSERT(data_format
>= 0 && data_format
< DataFormat_COUNT
);
552 return descs
[data_format
];
555 bool IsBigEndian() const
563 static void* EndianessCopyBigEndian(void* _dst
, void* _src
, size_t s
, int is_little_endian
)
565 if (is_little_endian
)
567 uint8_t* dst
= (uint8_t*)_dst
;
568 uint8_t* src
= (uint8_t*)_src
+ s
- 1;
569 for (int i
= 0, n
= (int)s
; i
< n
; ++i
)
570 memcpy(dst
++, src
--, 1);
575 return memcpy(_dst
, _src
, s
);
579 static void* EndianessCopyLittleEndian(void* _dst
, void* _src
, size_t s
, int is_little_endian
)
581 if (is_little_endian
)
583 return memcpy(_dst
, _src
, s
);
587 uint8_t* dst
= (uint8_t*)_dst
;
588 uint8_t* src
= (uint8_t*)_src
+ s
- 1;
589 for (int i
= 0, n
= (int)s
; i
< n
; ++i
)
590 memcpy(dst
++, src
--, 1);
595 void* EndianessCopy(void *dst
, void *src
, size_t size
) const
597 static void *(*fp
)(void *, void *, size_t, int) = NULL
;
599 fp
= IsBigEndian() ? EndianessCopyBigEndian
: EndianessCopyLittleEndian
;
600 return fp(dst
, src
, size
, PreviewEndianess
);
603 const char* FormatBinary(const uint8_t* buf
, int width
) const
605 IM_ASSERT(width
<= 64);
607 static char out_buf
[64 + 8 + 1];
608 for (int j
= 0, n
= width
/ 8; j
< n
; ++j
)
610 for (int i
= 0; i
< 8; ++i
)
611 out_buf
[out_n
++] = (buf
[j
] & (1 << (7 - i
))) ? '1' : '0';
612 out_buf
[out_n
++] = ' ';
615 IM_ASSERT(out_n
< IM_ARRAYSIZE(out_buf
));
619 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
622 size_t elem_size
= DataTypeGetSize(data_type
);
623 size_t size
= addr
+ elem_size
> mem_size
? mem_size
- addr
: elem_size
;
625 for (int i
= 0, n
= (int)size
; i
< n
; ++i
)
626 buf
[i
] = ReadFn(mem_data
, addr
+ i
);
628 memcpy(buf
, mem_data
+ addr
, size
);
630 if (data_format
== DataFormat_Bin
)
632 snprintf(out_buf
, out_buf_size
, "%s", FormatBinary(buf
, (int)size
* 8));
642 EndianessCopy(&int8
, buf
, size
);
643 if (data_format
== DataFormat_Dec
) { snprintf(out_buf
, out_buf_size
, "%hhd", int8
); return; }
644 if (data_format
== DataFormat_Hex
) { snprintf(out_buf
, out_buf_size
, "0x%02x", int8
& 0xFF); return; }
650 EndianessCopy(&uint8
, buf
, size
);
651 if (data_format
== DataFormat_Dec
) { snprintf(out_buf
, out_buf_size
, "%hhu", uint8
); return; }
652 if (data_format
== DataFormat_Hex
) { snprintf(out_buf
, out_buf_size
, "0x%02x", uint8
& 0XFF); return; }
658 EndianessCopy(&int16
, buf
, size
);
659 if (data_format
== DataFormat_Dec
) { snprintf(out_buf
, out_buf_size
, "%hd", int16
); return; }
660 if (data_format
== DataFormat_Hex
) { snprintf(out_buf
, out_buf_size
, "0x%04x", int16
& 0xFFFF); return; }
666 EndianessCopy(&uint16
, buf
, size
);
667 if (data_format
== DataFormat_Dec
) { snprintf(out_buf
, out_buf_size
, "%hu", uint16
); return; }
668 if (data_format
== DataFormat_Hex
) { snprintf(out_buf
, out_buf_size
, "0x%04x", uint16
& 0xFFFF); return; }
674 EndianessCopy(&int32
, buf
, size
);
675 if (data_format
== DataFormat_Dec
) { snprintf(out_buf
, out_buf_size
, "%d", int32
); return; }
676 if (data_format
== DataFormat_Hex
) { snprintf(out_buf
, out_buf_size
, "0x%08x", int32
); return; }
682 EndianessCopy(&uint32
, buf
, size
);
683 if (data_format
== DataFormat_Dec
) { snprintf(out_buf
, out_buf_size
, "%u", uint32
); return; }
684 if (data_format
== DataFormat_Hex
) { snprintf(out_buf
, out_buf_size
, "0x%08x", uint32
); return; }
690 EndianessCopy(&int64
, buf
, size
);
691 if (data_format
== DataFormat_Dec
) { snprintf(out_buf
, out_buf_size
, "%lld", (long long)int64
); return; }
692 if (data_format
== DataFormat_Hex
) { snprintf(out_buf
, out_buf_size
, "0x%016llx", (long long)int64
); return; }
698 EndianessCopy(&uint64
, buf
, size
);
699 if (data_format
== DataFormat_Dec
) { snprintf(out_buf
, out_buf_size
, "%llu", (long long)uint64
); return; }
700 if (data_format
== DataFormat_Hex
) { snprintf(out_buf
, out_buf_size
, "0x%016llx", (long long)uint64
); return; }
705 float float32
= 0.0f
;
706 EndianessCopy(&float32
, buf
, size
);
707 if (data_format
== DataFormat_Dec
) { snprintf(out_buf
, out_buf_size
, "%f", float32
); return; }
708 if (data_format
== DataFormat_Hex
) { snprintf(out_buf
, out_buf_size
, "%a", float32
); return; }
711 case DataType_Double
:
713 double float64
= 0.0;
714 EndianessCopy(&float64
, buf
, size
);
715 if (data_format
== DataFormat_Dec
) { snprintf(out_buf
, out_buf_size
, "%f", float64
); return; }
716 if (data_format
== DataFormat_Hex
) { snprintf(out_buf
, out_buf_size
, "%a", float64
); return; }
720 IM_ASSERT(0); // Shouldn't reach