use binaryninjacore_sys::*;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
use std::path::PathBuf;
use crate::binaryview::BinaryView;
use crate::rc::Ref;
use crate::string::{BnStrCompatible, BnString};
pub fn get_text_line_input(prompt: &str, title: &str) -> Option<String> {
let mut value: *mut libc::c_char = std::ptr::null_mut();
let result = unsafe {
BNGetTextLineInput(
&mut value,
prompt.into_bytes_with_nul().as_ptr() as *mut _,
title.into_bytes_with_nul().as_ptr() as *mut _,
)
};
if !result {
return None;
}
Some(unsafe { BnString::from_raw(value).to_string() })
}
pub fn get_integer_input(prompt: &str, title: &str) -> Option<i64> {
let mut value: i64 = 0;
let result = unsafe {
BNGetIntegerInput(
&mut value,
prompt.into_bytes_with_nul().as_ptr() as *mut _,
title.into_bytes_with_nul().as_ptr() as *mut _,
)
};
if !result {
return None;
}
Some(value)
}
pub fn get_address_input(prompt: &str, title: &str) -> Option<u64> {
let mut value: u64 = 0;
let result = unsafe {
BNGetAddressInput(
&mut value,
prompt.into_bytes_with_nul().as_ptr() as *mut _,
title.into_bytes_with_nul().as_ptr() as *mut _,
std::ptr::null_mut(),
0,
)
};
if !result {
return None;
}
Some(value)
}
pub fn get_open_filename_input(prompt: &str, extension: &str) -> Option<PathBuf> {
let mut value: *mut libc::c_char = std::ptr::null_mut();
let result = unsafe {
BNGetOpenFileNameInput(
&mut value,
prompt.into_bytes_with_nul().as_ptr() as *mut _,
extension.into_bytes_with_nul().as_ptr() as *mut _,
)
};
if !result {
return None;
}
let string = unsafe { BnString::from_raw(value) };
Some(PathBuf::from(string.as_str()))
}
pub fn get_save_filename_input(prompt: &str, title: &str, default_name: &str) -> Option<PathBuf> {
let mut value: *mut libc::c_char = std::ptr::null_mut();
let result = unsafe {
BNGetSaveFileNameInput(
&mut value,
prompt.into_bytes_with_nul().as_ptr() as *mut _,
title.into_bytes_with_nul().as_ptr() as *mut _,
default_name.into_bytes_with_nul().as_ptr() as *mut _,
)
};
if !result {
return None;
}
let string = unsafe { BnString::from_raw(value) };
Some(PathBuf::from(string.as_str()))
}
pub fn get_directory_name_input(prompt: &str, default_name: &str) -> Option<PathBuf> {
let mut value: *mut libc::c_char = std::ptr::null_mut();
let result = unsafe {
BNGetDirectoryNameInput(
&mut value,
prompt.into_bytes_with_nul().as_ptr() as *mut _,
default_name.into_bytes_with_nul().as_ptr() as *mut _,
)
};
if !result {
return None;
}
let string = unsafe { BnString::from_raw(value) };
Some(PathBuf::from(string.as_str()))
}
pub type MessageBoxButtonSet = BNMessageBoxButtonSet;
pub type MessageBoxIcon = BNMessageBoxIcon;
pub type MessageBoxButtonResult = BNMessageBoxButtonResult;
pub fn show_message_box(
title: &str,
text: &str,
buttons: MessageBoxButtonSet,
icon: MessageBoxIcon,
) -> MessageBoxButtonResult {
unsafe {
BNShowMessageBox(
title.into_bytes_with_nul().as_ptr() as *mut _,
text.into_bytes_with_nul().as_ptr() as *mut _,
buttons,
icon,
)
}
}
pub enum FormResponses {
None,
String(String),
Integer(i64),
Address(u64),
Index(usize),
}
enum FormData {
Label {
_text: BnString,
},
Text {
_prompt: BnString,
_default: Option<BnString>,
},
Choice {
_prompt: BnString,
_choices: Vec<BnString>,
_raw: Vec<*const c_char>,
},
File {
_prompt: BnString,
_ext: BnString,
_default: Option<BnString>,
},
FileSave {
_prompt: BnString,
_ext: BnString,
_default_name: BnString,
_default: Option<BnString>,
},
}
pub struct FormInputBuilder {
fields: Vec<BNFormInputField>,
data: Vec<FormData>,
}
impl FormInputBuilder {
pub fn new() -> Self {
Self {
fields: vec![],
data: vec![],
}
}
pub fn label_field(mut self, text: &str) -> Self {
let text = BnString::new(text);
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
result.type_ = BNFormInputFieldType::LabelFormField;
result.hasDefault = false;
result.prompt = text.as_ref().as_ptr() as *const c_char;
self.fields.push(result);
self.data.push(FormData::Label { _text: text });
self
}
pub fn seperator_field(mut self) -> Self {
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
result.type_ = BNFormInputFieldType::SeparatorFormField;
result.hasDefault = false;
self.fields.push(result);
self
}
pub fn text_field(mut self, prompt: &str, default: Option<&str>) -> Self {
let prompt = BnString::new(prompt);
let default = default.map(BnString::new);
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
result.type_ = BNFormInputFieldType::TextLineFormField;
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
result.hasDefault = default.is_some();
if let Some(ref default) = default {
result.stringDefault = default.as_ref().as_ptr() as *const c_char;
}
self.fields.push(result);
self.data.push(FormData::Text {
_prompt: prompt,
_default: default,
});
self
}
pub fn multiline_field(mut self, prompt: &str, default: Option<&str>) -> Self {
let prompt = BnString::new(prompt);
let default = default.map(BnString::new);
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
result.type_ = BNFormInputFieldType::MultilineTextFormField;
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
result.hasDefault = default.is_some();
if let Some(ref default) = default {
result.stringDefault = default.as_ref().as_ptr() as *const c_char;
}
self.fields.push(result);
self.data.push(FormData::Text {
_prompt: prompt,
_default: default,
});
self
}
pub fn integer_field(mut self, prompt: &str, default: Option<i64>) -> Self {
let prompt = BnString::new(prompt);
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
result.type_ = BNFormInputFieldType::IntegerFormField;
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
result.hasDefault = default.is_some();
if let Some(default) = default {
result.intDefault = default;
}
self.fields.push(result);
self.data.push(FormData::Label { _text: prompt });
self
}
pub fn address_field(
mut self,
prompt: &str,
view: Option<Ref<BinaryView>>,
current_address: Option<u64>,
default: Option<u64>,
) -> Self {
let prompt = BnString::new(prompt);
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
result.type_ = BNFormInputFieldType::AddressFormField;
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
if let Some(view) = view {
result.view = unsafe { Ref::into_raw(view) }.handle;
}
result.currentAddress = current_address.unwrap_or(0);
result.hasDefault = default.is_some();
if let Some(default) = default {
result.addressDefault = default;
}
self.fields.push(result);
self.data.push(FormData::Label { _text: prompt });
self
}
pub fn choice_field(mut self, prompt: &str, choices: &[&str], default: Option<usize>) -> Self {
let prompt = BnString::new(prompt);
let choices: Vec<BnString> = choices.iter().map(|&s| BnString::new(s)).collect();
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
result.type_ = BNFormInputFieldType::ChoiceFormField;
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
let mut raw_choices: Vec<*const c_char> = choices
.iter()
.map(|c| c.as_ref().as_ptr() as *const c_char)
.collect();
result.choices = raw_choices.as_mut_ptr();
result.count = choices.len();
result.hasDefault = default.is_some();
if let Some(default) = default {
result.indexDefault = default;
}
self.fields.push(result);
self.data.push(FormData::Choice {
_prompt: prompt,
_choices: choices,
_raw: raw_choices,
});
self
}
pub fn open_file_field(
mut self,
prompt: &str,
ext: Option<&str>,
default: Option<&str>,
) -> Self {
let prompt = BnString::new(prompt);
let ext = if let Some(ext) = ext {
BnString::new(ext)
} else {
BnString::new("")
};
let default = default.map(BnString::new);
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
result.type_ = BNFormInputFieldType::OpenFileNameFormField;
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
result.ext = ext.as_ref().as_ptr() as *const c_char;
result.hasDefault = default.is_some();
if let Some(ref default) = default {
result.stringDefault = default.as_ref().as_ptr() as *const c_char;
}
self.fields.push(result);
self.data.push(FormData::File {
_prompt: prompt,
_ext: ext,
_default: default,
});
self
}
pub fn save_file_field(
mut self,
prompt: &str,
ext: Option<&str>,
default_name: Option<&str>,
default: Option<&str>,
) -> Self {
let prompt = BnString::new(prompt);
let ext = if let Some(ext) = ext {
BnString::new(ext)
} else {
BnString::new("")
};
let default_name = if let Some(default_name) = default_name {
BnString::new(default_name)
} else {
BnString::new("")
};
let default = default.map(BnString::new);
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
result.type_ = BNFormInputFieldType::SaveFileNameFormField;
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
result.ext = ext.as_ref().as_ptr() as *const c_char;
result.defaultName = default_name.as_ref().as_ptr() as *const c_char;
result.hasDefault = default.is_some();
if let Some(ref default) = default {
result.stringDefault = default.as_ref().as_ptr() as *const c_char;
}
self.fields.push(result);
self.data.push(FormData::FileSave {
_prompt: prompt,
_ext: ext,
_default_name: default_name,
_default: default,
});
self
}
pub fn directory_name_field(
mut self,
prompt: &str,
default_name: Option<&str>,
default: Option<&str>,
) -> Self {
let prompt = BnString::new(prompt);
let default_name = if let Some(default_name) = default_name {
BnString::new(default_name)
} else {
BnString::new("")
};
let default = default.map(BnString::new);
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
result.type_ = BNFormInputFieldType::DirectoryNameFormField;
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
result.defaultName = default_name.as_ref().as_ptr() as *const c_char;
result.hasDefault = default.is_some();
if let Some(ref default) = default {
result.stringDefault = default.as_ref().as_ptr() as *const c_char;
}
self.fields.push(result);
self.data.push(FormData::File {
_prompt: prompt,
_ext: default_name,
_default: default,
});
self
}
pub fn get_form_input(&mut self, title: &str) -> Vec<FormResponses> {
if unsafe {
BNGetFormInput(
self.fields.as_mut_ptr(),
self.fields.len(),
title.into_bytes_with_nul().as_ptr() as *const _,
)
} {
let result = self
.fields
.iter()
.map(|form_field| match form_field.type_ {
BNFormInputFieldType::LabelFormField
| BNFormInputFieldType::SeparatorFormField => FormResponses::None,
BNFormInputFieldType::TextLineFormField
| BNFormInputFieldType::MultilineTextFormField
| BNFormInputFieldType::OpenFileNameFormField
| BNFormInputFieldType::SaveFileNameFormField
| BNFormInputFieldType::DirectoryNameFormField => {
FormResponses::String(unsafe {
CStr::from_ptr(form_field.stringResult)
.to_str()
.unwrap()
.to_owned()
})
}
BNFormInputFieldType::IntegerFormField => {
FormResponses::Integer(form_field.intResult)
}
BNFormInputFieldType::AddressFormField => {
FormResponses::Address(form_field.addressResult)
}
BNFormInputFieldType::ChoiceFormField => {
FormResponses::Index(form_field.indexResult)
}
})
.collect();
unsafe { BNFreeFormInputResults(self.fields.as_mut_ptr(), self.fields.len()) };
result
} else {
vec![]
}
}
}
impl Default for FormInputBuilder {
fn default() -> Self {
Self::new()
}
}
struct TaskContext<F: Fn(Box<dyn Fn(usize, usize) -> Result<(), ()>>)>(F);
pub fn run_progress_dialog<F: Fn(Box<dyn Fn(usize, usize) -> Result<(), ()>>)>(
title: &str,
can_cancel: bool,
task: F,
) -> Result<(), ()> {
let mut ctxt = TaskContext::<F>(task);
unsafe extern "C" fn cb_task<F: Fn(Box<dyn Fn(usize, usize) -> Result<(), ()>>)>(
ctxt: *mut c_void,
progress: Option<unsafe extern "C" fn(*mut c_void, usize, usize) -> bool>,
progress_ctxt: *mut c_void,
) {
ffi_wrap!("run_progress_dialog", {
let context = ctxt as *mut TaskContext<F>;
let progress_fn = Box::new(move |cur: usize, max: usize| -> Result<(), ()> {
match progress {
Some(func) => {
if (func)(progress_ctxt, cur, max) {
Ok(())
} else {
Err(())
}
}
None => Ok(()),
}
});
((*context).0)(progress_fn);
})
}
if unsafe {
BNRunProgressDialog(
title.into_bytes_with_nul().as_ptr() as *mut _,
can_cancel,
Some(cb_task::<F>),
&mut ctxt as *mut _ as *mut c_void,
)
} {
Ok(())
} else {
Err(())
}
}