imgui_memory_editor/
memory_editor.rs

1use std::ffi::c_void;
2
3use imgui::{ImColor32, ImStr, Ui};
4
5
6// TODO: Alias ReadHandlerTrait and writeHandlerTrait to FnMuts once trait_alias is stabilized
7type ReadHandler<'a, T> = Option<Box<dyn FnMut(&T, usize) -> u8 + 'a>>;
8type WriteHandler<'a, T> = Option<Box<dyn FnMut(&mut T, usize, u8) + 'a>>;
9type HighlightHandler<'a, T> = Option<Box<dyn FnMut(&T, usize) -> bool + 'a>>;
10type MemData<'a, 'b, T> = (
11    &'b mut ReadHandler<'a, T>,
12    &'b mut WriteHandler<'a, T>,
13    &'b mut HighlightHandler<'a, T>,
14    &'b mut T
15);
16
17pub struct MemoryEditor<'a, T> {
18    window_name: Option<&'a ImStr>,
19    read_fn: ReadHandler<'a, T>,
20    write_fn: WriteHandler<'a, T>,
21    highlight_fn: HighlightHandler<'a, T>,
22    mem_size: usize,
23    base_addr: usize,
24    raw: sys::MemoryEditor,
25}
26
27impl<'a, T> MemoryEditor<'a, T> {
28    pub fn new() -> MemoryEditor<'a, T> {
29        let mut raw = Default::default();
30        unsafe { sys::Editor_Create(&mut raw) }
31        MemoryEditor {
32            window_name: None,
33            read_fn: None,
34            write_fn: None,
35            highlight_fn: None,
36            mem_size: 0,
37            base_addr: 0,
38            raw,
39        }
40    }
41
42    // Size of memory in bytes (Automatically set if using bytes)
43    #[inline]
44    pub fn mem_size(mut self, mem_size: usize) -> Self {
45        self.mem_size = mem_size;
46        self
47    }
48
49    // The base addr displayed
50    #[inline]
51    pub fn base_addr(mut self, base_addr: usize) -> Self {
52        self.base_addr = base_addr;
53        self
54    }
55
56    // Set to false when DrawWindow() was closed. Ignore if not using DrawWindow().
57    #[inline]
58    pub fn open(&self) -> bool {
59        self.raw.Open
60    }
61    // disable any editing.
62    #[inline]
63    pub fn read_only(mut self, read_only: bool) -> Self {
64        self.raw.ReadOnly = read_only;
65        self
66    }
67    // number of columns to display.
68    #[inline]
69    pub fn cols(mut self, cols: i32) -> Self {
70        self.raw.Cols = cols;
71        self
72    }
73    // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them.
74    #[inline]
75    pub fn show_options(mut self, show_options: bool) -> Self {
76        self.raw.OptShowOptions = show_options;
77        self
78    }
79    // display a footer previewing the decimal/binary/hex/float representation of the currently selected bytes.
80    #[inline]
81    pub fn show_data_preview(mut self, show_data_preview: bool) -> Self {
82        self.raw.OptShowDataPreview = show_data_preview;
83        self
84    }
85    // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X".
86    #[inline]
87    pub fn show_hexii(mut self, show_hexii: bool) -> Self {
88        self.raw.OptShowHexII = show_hexii;
89        self
90    }
91    // display ASCII representation on the right side.
92    #[inline]
93    pub fn show_ascii(mut self, show_ascii: bool) -> Self {
94        self.raw.OptShowAscii = show_ascii;
95        self
96    }
97    // display null/zero bytes using the TextDisabled color.
98    #[inline]
99    pub fn grey_out_zeroes(mut self, grey_out_zeroes: bool) -> Self {
100        self.raw.OptGreyOutZeroes = grey_out_zeroes;
101        self
102    }
103    // display hexadecimal values as "FF" instead of "ff".
104    #[inline]
105    pub fn upper_case_hex(mut self, upper_case_hex: bool) -> Self {
106        self.raw.OptUpperCaseHex = upper_case_hex;
107        self
108    }
109    // set to 0 to disable extra spacing between every mid-cols.
110    #[inline]
111    pub fn mid_cols_count(mut self, mid_cols_count: i32) -> Self {
112        self.raw.OptMidColsCount = mid_cols_count;
113        self
114    }
115    // number of addr digits to display (default calculated based on maximum displayed addr).
116    #[inline]
117    pub fn addr_digits_count(mut self, addr_digits_count: i32) -> Self {
118        self.raw.OptAddrDigitsCount = addr_digits_count;
119        self
120    }
121    // background color of highlighted bytes.
122    #[inline]
123    pub fn highlight_color(mut self, color: ImColor32) -> Self {
124        self.raw.HighlightColor = color.into();
125        self
126    }
127    // optional handler to read bytes.
128    #[inline]
129    pub fn read_fn<F>(mut self, read_fn: F) -> Self where F: FnMut(&T, usize) -> u8 + 'a {
130        self.read_fn = Some(Box::new(read_fn));
131        self
132    }
133    // optional handler to write bytes.
134    #[inline]
135    pub fn write_fn<F>(mut self, write_fn: F) -> Self where F: FnMut(&mut T, usize, u8) + 'a {
136        self.write_fn = Some(Box::new(write_fn));
137        self
138    }
139    // optional handler to return Highlight property (to support non-contiguous highlighting).
140    #[inline]
141    pub fn highlight_fn<F>(mut self, highlight_fn: F) -> Self where F: FnMut(&T, usize) -> bool + 'a {
142        self.highlight_fn = Some(Box::new(highlight_fn));
143        self
144    }
145
146    // When drawing, create a window with this name
147    #[inline]
148    pub fn draw_window(mut self, window_name: &'a ImStr) -> Self {
149        self.window_name = Some(window_name);
150        self
151    }
152    // No longer create a window when drawing
153    #[inline]
154    pub fn no_window(mut self) -> Self {
155        self.window_name = None;
156        self
157    }
158
159    // Draw the memory editor with read and write functions set
160    pub fn draw(&mut self, _: &Ui, user_data: &mut T) {
161        assert!(
162            self.read_fn.is_some() || self.mem_size == 0,
163            "Read Fn must be set if mem size > 0"
164        );
165        assert!(
166            self.write_fn.is_some() || self.raw.ReadOnly || self.mem_size == 0,
167            "Write Fn must be set if not read only and mem size > 0"
168        );
169        self.raw.ReadFn = Some(read_wrapper::<T>);
170        self.raw.WriteFn = Some(write_wrapper::<T>);
171        self.raw.HighlightFn = if self.highlight_fn.is_some() { Some(highlight_wrapper::<T>) } else { None };
172
173        let mut data = (
174            &mut self.read_fn,
175            &mut self.write_fn,
176            &mut self.highlight_fn,
177            user_data
178        );
179        let mem_data = &mut data as *mut MemData<T> as *mut c_void;
180        unsafe { self.draw_raw(mem_data) }
181    }
182
183    pub unsafe fn draw_raw(&mut self, mem_data: *mut c_void) {
184        if let Some(title) = self.window_name {
185            sys::Editor_DrawWindow(
186                &mut self.raw,
187                title.as_ptr(),
188                mem_data,
189                self.mem_size,
190                self.base_addr,
191            );
192        } else {
193            sys::Editor_DrawContents(
194                &mut self.raw,
195                mem_data,
196                self.mem_size,
197                self.base_addr,
198            );
199        }
200    }
201}
202
203impl<'a> MemoryEditor<'a, &[u8]> {
204    pub fn draw_vec(&mut self, _: &Ui, data: &[u8]) {
205        assert!(!self.raw.ReadOnly, "Data muse be a mutable slice if editor is not read only");
206        // TODO: Support highlight fn
207        assert!(
208            self.read_fn.is_none() && self.write_fn.is_none() && self.highlight_fn.is_none(),
209            "Handler functions not supported when using draw_vec. Use draw instead"
210        );
211        self.mem_size = data.len();
212        unsafe { self.draw_raw(data.as_ptr() as *mut c_void) }
213    }
214}
215
216
217// Convenience implementations
218impl<'a> MemoryEditor<'a, &mut [u8]> {
219    pub fn draw_vec(&mut self, _: &Ui, data: &mut [u8]) {
220        // TODO: Support highlight fn
221        assert!(
222            self.read_fn.is_none() && self.write_fn.is_none() && self.highlight_fn.is_none(),
223            "Handler functions not supported when using draw_vec. Use draw instead"
224        );
225        self.mem_size = data.len();
226        unsafe { self.draw_raw(data.as_mut_ptr() as *mut c_void) }
227    }
228}
229
230// These shouldn't get called if no fn is set
231unsafe extern "C" fn read_wrapper<'a, T>(data: *const u8, off: usize) -> u8 {
232    let (read_fn, _, _, user_data) = &mut *(data as *mut MemData<T>);
233    read_fn.as_mut().unwrap()(user_data, off)
234}
235
236unsafe extern "C" fn write_wrapper<'a, T>(data: *mut u8, off: usize, d: u8) {
237    let (_, write_fn, _, user_data) = &mut *(data as *mut MemData<T>);
238    write_fn.as_mut().unwrap()(user_data, off, d);
239}
240
241unsafe extern "C" fn highlight_wrapper<'a, T>(data: *const u8, off: usize) -> bool {
242    let (_, _, highlight_fn, user_data) = &mut *(data as *mut MemData<T>);
243    highlight_fn.as_mut().unwrap()(user_data, off)
244}