binaryninja/
interaction.rs1use std::ffi::{c_char, c_void};
18use std::path::PathBuf;
19
20use binaryninjacore_sys::*;
21
22use crate::string::{BnString, IntoCStr};
23
24pub mod form;
25pub mod handler;
26pub mod report;
27
28pub use form::*;
30pub use handler::*;
31pub use report::*;
32
33pub type MessageBoxButtonSet = BNMessageBoxButtonSet;
34pub type MessageBoxIcon = BNMessageBoxIcon;
35pub type MessageBoxButtonResult = BNMessageBoxButtonResult;
36
37pub fn get_text_line_input(prompt: &str, title: &str) -> Option<String> {
38 let mut value: *mut c_char = std::ptr::null_mut();
39
40 let prompt = prompt.to_cstr();
41 let title = title.to_cstr();
42 let result = unsafe { BNGetTextLineInput(&mut value, prompt.as_ptr(), title.as_ptr()) };
43 if !result {
44 return None;
45 }
46
47 Some(unsafe { BnString::into_string(value) })
48}
49
50pub fn get_integer_input(prompt: &str, title: &str) -> Option<i64> {
51 let mut value: i64 = 0;
52
53 let prompt = prompt.to_cstr();
54 let title = title.to_cstr();
55 let result = unsafe { BNGetIntegerInput(&mut value, prompt.as_ptr(), title.as_ptr()) };
56
57 if !result {
58 return None;
59 }
60
61 Some(value)
62}
63
64pub fn get_address_input(prompt: &str, title: &str) -> Option<u64> {
65 let mut value: u64 = 0;
66
67 let prompt = prompt.to_cstr();
68 let title = title.to_cstr();
69 let result = unsafe {
70 BNGetAddressInput(
71 &mut value,
72 prompt.as_ptr(),
73 title.as_ptr(),
74 std::ptr::null_mut(),
75 0,
76 )
77 };
78
79 if !result {
80 return None;
81 }
82
83 Some(value)
84}
85
86pub fn get_choice_input(prompt: &str, title: &str, choices: &[&str]) -> Option<usize> {
87 let prompt = prompt.to_cstr();
88 let title = title.to_cstr();
89 let mut choices_inner: Vec<BnString> = choices.iter().copied().map(BnString::new).collect();
90 let choices: &mut [*const c_char] = unsafe {
92 core::mem::transmute::<&mut [BnString], &mut [*const c_char]>(&mut choices_inner[..])
93 };
94 let mut result = 0;
95 let succ = unsafe {
96 BNGetChoiceInput(
97 &mut result,
98 prompt.as_ptr(),
99 title.as_ptr(),
100 choices.as_mut_ptr(),
101 choices.len(),
102 )
103 };
104 succ.then_some(result)
105}
106
107pub fn get_large_choice_input(prompt: &str, title: &str, choices: &[&str]) -> Option<usize> {
108 let prompt = prompt.to_cstr();
109 let title = title.to_cstr();
110 let mut choices_inner: Vec<BnString> = choices.iter().copied().map(BnString::new).collect();
111 let choices: &mut [*const c_char] = unsafe {
113 core::mem::transmute::<&mut [BnString], &mut [*const c_char]>(&mut choices_inner[..])
114 };
115 let mut result = 0;
116 let succ = unsafe {
117 BNGetLargeChoiceInput(
118 &mut result,
119 prompt.as_ptr(),
120 title.as_ptr(),
121 choices.as_mut_ptr(),
122 choices.len(),
123 )
124 };
125 succ.then_some(result)
126}
127
128pub fn get_open_filename_input(prompt: &str, extension: &str) -> Option<PathBuf> {
129 let mut value: *mut c_char = std::ptr::null_mut();
130
131 let prompt = prompt.to_cstr();
132 let extension = extension.to_cstr();
133 let result = unsafe { BNGetOpenFileNameInput(&mut value, prompt.as_ptr(), extension.as_ptr()) };
134 if !result {
135 return None;
136 }
137
138 let path = unsafe { BnString::into_string(value) };
139 Some(PathBuf::from(path))
140}
141
142pub fn get_save_filename_input(
143 prompt: &str,
144 extension: &str,
145 default_name: &str,
146) -> Option<PathBuf> {
147 let mut value: *mut c_char = std::ptr::null_mut();
148
149 let prompt = prompt.to_cstr();
150 let extension = extension.to_cstr();
151 let default_name = default_name.to_cstr();
152 let result = unsafe {
153 BNGetSaveFileNameInput(
154 &mut value,
155 prompt.as_ptr(),
156 extension.as_ptr(),
157 default_name.as_ptr(),
158 )
159 };
160 if !result {
161 return None;
162 }
163
164 let path = unsafe { BnString::into_string(value) };
165 Some(PathBuf::from(path))
166}
167
168pub fn get_directory_name_input(prompt: &str, default_name: &str) -> Option<PathBuf> {
169 let mut value: *mut c_char = std::ptr::null_mut();
170
171 let prompt = prompt.to_cstr();
172 let default_name = default_name.to_cstr();
173 let result =
174 unsafe { BNGetDirectoryNameInput(&mut value, prompt.as_ptr(), default_name.as_ptr()) };
175 if !result {
176 return None;
177 }
178
179 let path = unsafe { BnString::into_string(value) };
180 Some(PathBuf::from(path))
181}
182
183pub fn show_message_box(
184 title: &str,
185 text: &str,
186 buttons: MessageBoxButtonSet,
187 icon: MessageBoxIcon,
188) -> MessageBoxButtonResult {
189 let title = title.to_cstr();
190 let text = text.to_cstr();
191 unsafe { BNShowMessageBox(title.as_ptr(), text.as_ptr(), buttons, icon) }
192}
193
194struct TaskContext<F: Fn(Box<dyn Fn(usize, usize) -> Result<(), ()>>)>(F);
195
196pub fn run_progress_dialog<F: Fn(Box<dyn Fn(usize, usize) -> Result<(), ()>>)>(
197 title: &str,
198 can_cancel: bool,
199 task: F,
200) -> Result<(), ()> {
201 let mut ctxt = TaskContext::<F>(task);
202
203 unsafe extern "C" fn cb_task<F: Fn(Box<dyn Fn(usize, usize) -> Result<(), ()>>)>(
204 ctxt: *mut c_void,
205 progress: Option<unsafe extern "C" fn(*mut c_void, usize, usize) -> bool>,
206 progress_ctxt: *mut c_void,
207 ) {
208 ffi_wrap!("run_progress_dialog", {
209 let context = ctxt as *mut TaskContext<F>;
210 let progress_fn = Box::new(move |cur: usize, max: usize| -> Result<(), ()> {
211 match progress {
212 Some(func) => {
213 if (func)(progress_ctxt, cur, max) {
214 Ok(())
215 } else {
216 Err(())
217 }
218 }
219 None => Ok(()),
220 }
221 });
222 ((*context).0)(progress_fn);
223 })
224 }
225
226 let title = title.to_cstr();
227 if unsafe {
228 BNRunProgressDialog(
229 title.as_ptr(),
230 can_cancel,
231 Some(cb_task::<F>),
232 &mut ctxt as *mut _ as *mut c_void,
233 )
234 } {
235 Ok(())
236 } else {
237 Err(())
238 }
239}