use std::collections::HashMap;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::{ffi, mem, ptr};
use binaryninjacore_sys::*;
use crate::binaryview::BinaryView;
use crate::databuffer::DataBuffer;
use crate::disassembly::InstructionTextToken;
use crate::filemetadata::FileMetadata;
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
use crate::string::{BnStrCompatible, BnString};
#[repr(transparent)]
pub struct Database {
handle: ptr::NonNull<BNDatabase>,
}
impl Database {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNDatabase>) -> Self {
Self { handle }
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNDatabase {
&mut *self.handle.as_ptr()
}
pub fn snapshot(&self, id: i64) -> Option<Snapshot> {
let result = unsafe { BNGetDatabaseSnapshot(self.as_raw(), id) };
ptr::NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
}
pub fn snapshots(&self) -> Array<Snapshot> {
let mut count = 0;
let result = unsafe { BNGetDatabaseSnapshots(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
pub fn current_snapshot(&self) -> Option<Snapshot> {
let result = unsafe { BNGetDatabaseCurrentSnapshot(self.as_raw()) };
ptr::NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
}
pub fn set_current_snapshot(&self, value: &Snapshot) {
unsafe { BNSetDatabaseCurrentSnapshot(self.as_raw(), value.id()) }
}
pub fn write_snapshot_data<N: BnStrCompatible>(
&self,
parents: &[i64],
file: &BinaryView,
name: N,
data: &KeyValueStore,
auto_save: bool,
) -> i64 {
let name_raw = name.into_bytes_with_nul();
let name_ptr = name_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe {
BNWriteDatabaseSnapshotData(
self.as_raw(),
parents.as_ptr() as *mut _,
parents.len(),
file.handle,
name_ptr,
data.as_raw(),
auto_save,
ptr::null_mut(),
Some(cb_progress_nop),
)
}
}
pub fn write_snapshot_data_with_progress<N, F>(
&self,
parents: &[i64],
file: &BinaryView,
name: N,
data: &KeyValueStore,
auto_save: bool,
mut progress: F,
) -> i64
where
N: BnStrCompatible,
F: FnMut(usize, usize) -> bool,
{
let name_raw = name.into_bytes_with_nul();
let name_ptr = name_raw.as_ref().as_ptr() as *const ffi::c_char;
let ctxt = &mut progress as *mut _ as *mut ffi::c_void;
unsafe {
BNWriteDatabaseSnapshotData(
self.as_raw(),
parents.as_ptr() as *mut _,
parents.len(),
file.handle,
name_ptr,
data.as_raw(),
auto_save,
ctxt,
Some(cb_progress::<F>),
)
}
}
pub fn trim_snapshot(&self, id: i64) -> Result<(), ()> {
if unsafe { BNTrimDatabaseSnapshot(self.as_raw(), id) } {
Ok(())
} else {
Err(())
}
}
pub fn remove_snapshot(&self, id: i64) -> Result<(), ()> {
if unsafe { BNRemoveDatabaseSnapshot(self.as_raw(), id) } {
Ok(())
} else {
Err(())
}
}
pub fn has_global<S: BnStrCompatible>(&self, key: S) -> bool {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe { BNDatabaseHasGlobal(self.as_raw(), key_ptr) != 0 }
}
pub fn global_keys(&self) -> Array<BnString> {
let mut count = 0;
let result = unsafe { BNGetDatabaseGlobalKeys(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
pub fn globals(&self) -> HashMap<String, String> {
self.global_keys()
.iter()
.filter_map(|key| Some((key.to_string(), self.read_global(key)?.to_string())))
.collect()
}
pub fn read_global<S: BnStrCompatible>(&self, key: S) -> Option<BnString> {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
let result = unsafe { BNReadDatabaseGlobal(self.as_raw(), key_ptr) };
unsafe { ptr::NonNull::new(result).map(|_| BnString::from_raw(result)) }
}
pub fn write_global<K: BnStrCompatible, V: BnStrCompatible>(&self, key: K, value: V) -> bool {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
let value_raw = value.into_bytes_with_nul();
let value_ptr = value_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe { BNWriteDatabaseGlobal(self.as_raw(), key_ptr, value_ptr) }
}
pub fn read_global_data<S: BnStrCompatible>(&self, key: S) -> Option<DataBuffer> {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
let result = unsafe { BNReadDatabaseGlobalData(self.as_raw(), key_ptr) };
ptr::NonNull::new(result).map(|_| DataBuffer::from_raw(result))
}
pub fn write_global_data<K: BnStrCompatible>(&self, key: K, value: &DataBuffer) -> bool {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe { BNWriteDatabaseGlobalData(self.as_raw(), key_ptr, value.as_raw()) }
}
pub fn file(&self) -> Ref<FileMetadata> {
let result = unsafe { BNGetDatabaseFile(self.as_raw()) };
assert!(!result.is_null());
unsafe { Ref::new(FileMetadata::from_raw(result)) }
}
pub fn analysis_cache(&self) -> KeyValueStore {
let result = unsafe { BNReadDatabaseAnalysisCache(self.as_raw()) };
unsafe { KeyValueStore::from_raw(ptr::NonNull::new(result).unwrap()) }
}
pub fn reload_connection(&self) {
unsafe { BNDatabaseReloadConnection(self.as_raw()) }
}
pub fn write_analysis_cache(&self, val: &KeyValueStore) -> Result<(), ()> {
if unsafe { BNWriteDatabaseAnalysisCache(self.as_raw(), val.as_raw()) } {
Ok(())
} else {
Err(())
}
}
pub fn snapshot_has_data(&self, id: i64) -> bool {
unsafe { BNSnapshotHasData(self.as_raw(), id) }
}
}
impl Clone for Database {
fn clone(&self) -> Self {
unsafe { Self::from_raw(ptr::NonNull::new(BNNewDatabaseReference(self.as_raw())).unwrap()) }
}
}
impl Drop for Database {
fn drop(&mut self) {
unsafe { BNFreeDatabase(self.as_raw()) }
}
}
#[repr(transparent)]
pub struct Snapshot {
handle: ptr::NonNull<BNSnapshot>,
}
impl Snapshot {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNSnapshot>) -> Self {
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNSnapshot) -> &Self {
mem::transmute(handle)
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNSnapshot {
&mut *self.handle.as_ptr()
}
pub fn database(&self) -> Database {
unsafe {
Database::from_raw(ptr::NonNull::new(BNGetSnapshotDatabase(self.as_raw())).unwrap())
}
}
pub fn id(&self) -> i64 {
unsafe { BNGetSnapshotId(self.as_raw()) }
}
pub fn name(&self) -> BnString {
unsafe { BnString::from_raw(BNGetSnapshotName(self.as_raw())) }
}
pub fn set_name<S: BnStrCompatible>(&self, value: S) {
let value_raw = value.into_bytes_with_nul();
let value_ptr = value_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe { BNSetSnapshotName(self.as_raw(), value_ptr) }
}
pub fn is_auto_save(&self) -> bool {
unsafe { BNIsSnapshotAutoSave(self.as_raw()) }
}
pub fn has_contents(&self) -> bool {
unsafe { BNSnapshotHasContents(self.as_raw()) }
}
pub fn has_undo(&self) -> bool {
unsafe { BNSnapshotHasUndo(self.as_raw()) }
}
pub fn first_parent(&self) -> Option<Snapshot> {
let result = unsafe { BNGetSnapshotFirstParent(self.as_raw()) };
ptr::NonNull::new(result).map(|s| unsafe { Snapshot::from_raw(s) })
}
pub fn parents(&self) -> Array<Snapshot> {
let mut count = 0;
let result = unsafe { BNGetSnapshotParents(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
pub fn children(&self) -> Array<Snapshot> {
let mut count = 0;
let result = unsafe { BNGetSnapshotChildren(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
pub fn file_contents(&self) -> Option<DataBuffer> {
self.has_contents().then(|| unsafe {
let result = BNGetSnapshotFileContents(self.as_raw());
assert!(!result.is_null());
DataBuffer::from_raw(result)
})
}
pub fn file_contents_hash(&self) -> Option<DataBuffer> {
self.has_contents().then(|| unsafe {
let result = BNGetSnapshotFileContentsHash(self.as_raw());
assert!(!result.is_null());
DataBuffer::from_raw(result)
})
}
pub fn undo_entries(&self) -> Array<UndoEntry> {
assert!(self.has_undo());
let mut count = 0;
let result = unsafe { BNGetSnapshotUndoEntries(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
pub fn undo_entries_with_progress<F: FnMut(usize, usize) -> bool>(
&self,
mut progress: F,
) -> Array<UndoEntry> {
assert!(self.has_undo());
let ctxt = &mut progress as *mut _ as *mut ffi::c_void;
let mut count = 0;
let result = unsafe {
BNGetSnapshotUndoEntriesWithProgress(
self.as_raw(),
ctxt,
Some(cb_progress::<F>),
&mut count,
)
};
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
pub fn read_data(&self) -> KeyValueStore {
let result = unsafe { BNReadSnapshotData(self.as_raw()) };
unsafe { KeyValueStore::from_raw(ptr::NonNull::new(result).unwrap()) }
}
pub fn read_data_with_progress<F: FnMut(usize, usize) -> bool>(
&self,
mut progress: F,
) -> KeyValueStore {
let ctxt = &mut progress as *mut _ as *mut ffi::c_void;
let result =
unsafe { BNReadSnapshotDataWithProgress(self.as_raw(), ctxt, Some(cb_progress::<F>)) };
unsafe { KeyValueStore::from_raw(ptr::NonNull::new(result).unwrap()) }
}
pub fn undo_data(&self) -> DataBuffer {
let result = unsafe { BNGetSnapshotUndoData(self.as_raw()) };
assert!(!result.is_null());
DataBuffer::from_raw(result)
}
pub fn store_data<F: FnMut(usize, usize) -> bool>(
&self,
data: KeyValueStore,
mut progress: F,
) -> bool {
let ctxt = &mut progress as *mut _ as *mut ffi::c_void;
unsafe { BNSnapshotStoreData(self.as_raw(), data.as_raw(), ctxt, Some(cb_progress::<F>)) }
}
pub fn has_ancestor(self, other: &Snapshot) -> bool {
unsafe { BNSnapshotHasAncestor(self.as_raw(), other.as_raw()) }
}
}
impl Clone for Snapshot {
fn clone(&self) -> Self {
unsafe { Self::from_raw(ptr::NonNull::new(BNNewSnapshotReference(self.as_raw())).unwrap()) }
}
}
impl Drop for Snapshot {
fn drop(&mut self) {
unsafe { BNFreeSnapshot(self.as_raw()) }
}
}
impl CoreArrayProvider for Snapshot {
type Raw = *mut BNSnapshot;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for Snapshot {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeSnapshotList(raw, count);
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::ref_from_raw(raw)
}
}
#[repr(transparent)]
pub struct KeyValueStore {
handle: ptr::NonNull<BNKeyValueStore>,
}
impl KeyValueStore {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNKeyValueStore>) -> Self {
Self { handle }
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNKeyValueStore {
&mut *self.handle.as_ptr()
}
pub fn keys(&self) -> Array<BnString> {
let mut count = 0;
let result = unsafe { BNGetKeyValueStoreKeys(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
pub fn value<S: BnStrCompatible>(&self, key: S) -> Option<DataBuffer> {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
let result = unsafe { BNGetKeyValueStoreBuffer(self.as_raw(), key_ptr) };
ptr::NonNull::new(result).map(|_| DataBuffer::from_raw(result))
}
pub fn set_value<S: BnStrCompatible>(&self, key: S, value: &DataBuffer) -> bool {
let key_raw = key.into_bytes_with_nul();
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe { BNSetKeyValueStoreBuffer(self.as_raw(), key_ptr, value.as_raw()) }
}
pub fn serialized_data(&self) -> DataBuffer {
let result = unsafe { BNGetKeyValueStoreSerializedData(self.as_raw()) };
assert!(!result.is_null());
DataBuffer::from_raw(result)
}
pub fn begin_namespace<S: BnStrCompatible>(&self, name: S) {
let name_raw = name.into_bytes_with_nul();
let name_ptr = name_raw.as_ref().as_ptr() as *const ffi::c_char;
unsafe { BNBeginKeyValueStoreNamespace(self.as_raw(), name_ptr) }
}
pub fn end_namespace(&self) {
unsafe { BNEndKeyValueStoreNamespace(self.as_raw()) }
}
pub fn empty(&self) -> bool {
unsafe { BNIsKeyValueStoreEmpty(self.as_raw()) }
}
pub fn value_size(&self) -> usize {
unsafe { BNGetKeyValueStoreValueSize(self.as_raw()) }
}
pub fn data_size(&self) -> usize {
unsafe { BNGetKeyValueStoreDataSize(self.as_raw()) }
}
pub fn value_storage_size(self) -> usize {
unsafe { BNGetKeyValueStoreValueStorageSize(self.as_raw()) }
}
pub fn namespace_size(self) -> usize {
unsafe { BNGetKeyValueStoreNamespaceSize(self.as_raw()) }
}
}
impl Clone for KeyValueStore {
fn clone(&self) -> Self {
unsafe {
Self::from_raw(ptr::NonNull::new(BNNewKeyValueStoreReference(self.as_raw())).unwrap())
}
}
}
impl Drop for KeyValueStore {
fn drop(&mut self) {
unsafe { BNFreeKeyValueStore(self.as_raw()) }
}
}
#[repr(transparent)]
pub struct UndoEntry {
handle: ptr::NonNull<BNUndoEntry>,
}
impl UndoEntry {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNUndoEntry>) -> Self {
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNUndoEntry) -> &Self {
mem::transmute(handle)
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNUndoEntry {
&mut *self.handle.as_ptr()
}
pub fn id(&self) -> BnString {
let result = unsafe { BNUndoEntryGetId(self.as_raw()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
pub fn actions(&self) -> Array<UndoAction> {
let mut count = 0;
let result = unsafe { BNUndoEntryGetActions(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
pub fn time(&self) -> SystemTime {
let m = Duration::from_secs(unsafe { BNUndoEntryGetTimestamp(self.as_raw()) });
UNIX_EPOCH + m
}
}
impl Clone for UndoEntry {
fn clone(&self) -> Self {
unsafe {
Self::from_raw(ptr::NonNull::new(BNNewUndoEntryReference(self.as_raw())).unwrap())
}
}
}
impl Drop for UndoEntry {
fn drop(&mut self) {
unsafe { BNFreeUndoEntry(self.as_raw()) }
}
}
impl CoreArrayProvider for UndoEntry {
type Raw = *mut BNUndoEntry;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for UndoEntry {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeUndoEntryList(raw, count);
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::ref_from_raw(raw)
}
}
#[repr(transparent)]
pub struct UndoAction {
handle: ptr::NonNull<BNUndoAction>,
}
impl UndoAction {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNUndoAction>) -> Self {
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNUndoAction) -> &Self {
mem::transmute(handle)
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNUndoAction {
&mut *self.handle.as_ptr()
}
pub fn summary_text(&self) -> BnString {
let result = unsafe { BNUndoActionGetSummaryText(self.as_raw()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
pub fn summary(&self) -> Array<InstructionTextToken> {
let mut count = 0;
let result = unsafe { BNUndoActionGetSummary(self.as_raw(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
}
impl Clone for UndoAction {
fn clone(&self) -> Self {
unsafe {
Self::from_raw(ptr::NonNull::new(BNNewUndoActionReference(self.as_raw())).unwrap())
}
}
}
impl Drop for UndoAction {
fn drop(&mut self) {
unsafe { BNFreeUndoAction(self.as_raw()) }
}
}
impl CoreArrayProvider for UndoAction {
type Raw = *mut BNUndoAction;
type Context = ();
type Wrapped<'a> = &'a Self;
}
unsafe impl CoreArrayProviderInner for UndoAction {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeUndoActionList(raw, count);
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Self::ref_from_raw(raw)
}
}
unsafe extern "C" fn cb_progress<F: FnMut(usize, usize) -> bool>(
ctxt: *mut ffi::c_void,
arg1: usize,
arg2: usize,
) -> bool {
let ctxt: &mut F = &mut *(ctxt as *mut F);
ctxt(arg1, arg2)
}
unsafe extern "C" fn cb_progress_nop(_ctxt: *mut ffi::c_void, _arg1: usize, _arg2: usize) -> bool {
true
}