1use crate::binary_view::BinaryView;
16use crate::database::Database;
17use crate::rc::*;
18use crate::string::*;
19use binaryninjacore_sys::{
20 BNBeginUndoActions, BNCloseFile, BNCommitUndoActions, BNCreateDatabase, BNCreateFileMetadata,
21 BNFileMetadata, BNFileMetadataGetSessionId, BNForgetUndoActions, BNFreeFileMetadata,
22 BNGetCurrentOffset, BNGetCurrentView, BNGetExistingViews, BNGetFileMetadataDatabase,
23 BNGetFileViewOfType, BNGetFilename, BNGetProjectFile, BNIsAnalysisChanged,
24 BNIsBackedByDatabase, BNIsFileModified, BNMarkFileModified, BNMarkFileSaved, BNNavigate,
25 BNNewFileReference, BNOpenDatabaseForConfiguration, BNOpenExistingDatabase, BNRedo,
26 BNRevertUndoActions, BNSaveAutoSnapshot, BNSetFilename, BNUndo,
27};
28use binaryninjacore_sys::{BNCreateDatabaseWithProgress, BNOpenExistingDatabaseWithProgress};
29use std::ffi::c_void;
30use std::fmt::Debug;
31use std::path::Path;
32
33use crate::progress::ProgressCallback;
34use crate::project::file::ProjectFile;
35use std::ptr::{self, NonNull};
36
37#[derive(PartialEq, Eq, Hash)]
38pub struct FileMetadata {
39 pub(crate) handle: *mut BNFileMetadata,
40}
41
42impl FileMetadata {
43 pub(crate) fn from_raw(handle: *mut BNFileMetadata) -> Self {
44 Self { handle }
45 }
46
47 pub(crate) fn ref_from_raw(handle: *mut BNFileMetadata) -> Ref<Self> {
48 unsafe { Ref::new(Self { handle }) }
49 }
50
51 pub fn new() -> Ref<Self> {
52 Self::ref_from_raw(unsafe { BNCreateFileMetadata() })
53 }
54
55 pub fn with_filename(name: &str) -> Ref<Self> {
56 let ret = FileMetadata::new();
57 ret.set_filename(name);
58 ret
59 }
60
61 pub fn close(&self) {
62 unsafe {
63 BNCloseFile(self.handle);
64 }
65 }
66
67 pub fn session_id(&self) -> usize {
68 unsafe { BNFileMetadataGetSessionId(self.handle) }
69 }
70
71 pub fn filename(&self) -> String {
72 unsafe {
73 let raw = BNGetFilename(self.handle);
74 BnString::into_string(raw)
75 }
76 }
77
78 pub fn set_filename(&self, name: &str) {
79 let name = name.to_cstr();
80
81 unsafe {
82 BNSetFilename(self.handle, name.as_ptr());
83 }
84 }
85
86 pub fn modified(&self) -> bool {
87 unsafe { BNIsFileModified(self.handle) }
88 }
89
90 pub fn mark_modified(&self) {
91 unsafe {
92 BNMarkFileModified(self.handle);
93 }
94 }
95
96 pub fn mark_saved(&self) {
97 unsafe {
98 BNMarkFileSaved(self.handle);
99 }
100 }
101
102 pub fn is_analysis_changed(&self) -> bool {
103 unsafe { BNIsAnalysisChanged(self.handle) }
104 }
105
106 pub fn is_database_backed(&self) -> bool {
107 self.is_database_backed_for_view_type("")
108 }
109
110 pub fn is_database_backed_for_view_type(&self, view_type: &str) -> bool {
111 let view_type = view_type.to_cstr();
112
113 unsafe { BNIsBackedByDatabase(self.handle, view_type.as_ref().as_ptr() as *const _) }
114 }
115
116 pub fn run_undoable_transaction<F: FnOnce() -> Result<T, E>, T, E>(
127 &self,
128 func: F,
129 ) -> Result<T, E> {
130 let undo = self.begin_undo_actions(false);
131 let result = func();
132 match result {
133 Ok(t) => {
134 self.commit_undo_actions(&undo);
135 Ok(t)
136 }
137 Err(e) => {
138 self.revert_undo_actions(&undo);
139 Err(e)
140 }
141 }
142 }
143
144 pub fn begin_undo_actions(&self, anonymous_allowed: bool) -> String {
152 unsafe { BnString::into_string(BNBeginUndoActions(self.handle, anonymous_allowed)) }
153 }
154
155 pub fn commit_undo_actions(&self, id: &str) {
163 let id = id.to_cstr();
164 unsafe {
165 BNCommitUndoActions(self.handle, id.as_ref().as_ptr() as *const _);
166 }
167 }
168
169 pub fn revert_undo_actions(&self, id: &str) {
177 let id = id.to_cstr();
178 unsafe {
179 BNRevertUndoActions(self.handle, id.as_ref().as_ptr() as *const _);
180 }
181 }
182
183 pub fn forget_undo_actions(&self, id: &str) {
191 let id = id.to_cstr();
192 unsafe {
193 BNForgetUndoActions(self.handle, id.as_ref().as_ptr() as *const _);
194 }
195 }
196
197 pub fn undo(&self) {
198 unsafe {
199 BNUndo(self.handle);
200 }
201 }
202
203 pub fn redo(&self) {
204 unsafe {
205 BNRedo(self.handle);
206 }
207 }
208
209 pub fn current_view(&self) -> String {
210 unsafe { BnString::into_string(BNGetCurrentView(self.handle)) }
211 }
212
213 pub fn current_offset(&self) -> u64 {
214 unsafe { BNGetCurrentOffset(self.handle) }
215 }
216
217 pub fn navigate_to(&self, view: &str, offset: u64) -> Result<(), ()> {
227 let view = view.to_cstr();
228
229 unsafe {
230 if BNNavigate(self.handle, view.as_ref().as_ptr() as *const _, offset) {
231 Ok(())
232 } else {
233 Err(())
234 }
235 }
236 }
237
238 pub fn view_of_type(&self, view: &str) -> Option<Ref<BinaryView>> {
248 let view = view.to_cstr();
249
250 unsafe {
251 let raw_view_ptr = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _);
252 match raw_view_ptr.is_null() {
253 false => Some(BinaryView::ref_from_raw(raw_view_ptr)),
254 true => None,
255 }
256 }
257 }
258
259 pub fn view_types(&self) -> Array<BnString> {
260 let mut count = 0;
261 unsafe {
262 let types = BNGetExistingViews(self.handle, &mut count);
263 Array::new(types, count, ())
264 }
265 }
266
267 pub fn project_file(&self) -> Option<Ref<ProjectFile>> {
269 unsafe {
270 let res = NonNull::new(BNGetProjectFile(self.handle))?;
271 Some(ProjectFile::ref_from_raw(res))
272 }
273 }
274
275 pub fn create_database(&self, file_path: impl AsRef<Path>) -> bool {
276 let Some(raw_view) = self.view_of_type("Raw") else {
278 return false;
279 };
280
281 let file_path = file_path.as_ref().to_cstr();
282 unsafe {
283 BNCreateDatabase(
284 raw_view.handle,
285 file_path.as_ptr() as *mut _,
286 ptr::null_mut(),
287 )
288 }
289 }
290
291 pub fn create_database_with_progress<P: ProgressCallback>(
293 &self,
294 file_path: impl AsRef<Path>,
295 mut progress: P,
296 ) -> bool {
297 let Some(raw_view) = self.view_of_type("Raw") else {
299 return false;
300 };
301 let file_path = file_path.as_ref().to_cstr();
302 unsafe {
303 BNCreateDatabaseWithProgress(
304 raw_view.handle,
305 file_path.as_ptr() as *mut _,
306 &mut progress as *mut P as *mut c_void,
307 Some(P::cb_progress_callback),
308 ptr::null_mut(),
309 )
310 }
311 }
312
313 pub fn save_auto_snapshot(&self) -> bool {
314 let Some(raw_view) = self.view_of_type("Raw") else {
316 return false;
317 };
318
319 unsafe { BNSaveAutoSnapshot(raw_view.handle, ptr::null_mut() as *mut _) }
320 }
321
322 pub fn open_database_for_configuration(&self, file: &Path) -> Result<Ref<BinaryView>, ()> {
323 let file = file.to_cstr();
324 unsafe {
325 let bv =
326 BNOpenDatabaseForConfiguration(self.handle, file.as_ref().as_ptr() as *const _);
327
328 if bv.is_null() {
329 Err(())
330 } else {
331 Ok(BinaryView::ref_from_raw(bv))
332 }
333 }
334 }
335
336 pub fn open_database(&self, file: &Path) -> Result<Ref<BinaryView>, ()> {
337 let file = file.to_cstr();
338 let view = unsafe { BNOpenExistingDatabase(self.handle, file.as_ptr()) };
339
340 if view.is_null() {
341 Err(())
342 } else {
343 Ok(unsafe { BinaryView::ref_from_raw(view) })
344 }
345 }
346
347 pub fn open_database_with_progress<P: ProgressCallback>(
348 &self,
349 file: &Path,
350 mut progress: P,
351 ) -> Result<Ref<BinaryView>, ()> {
352 let file = file.to_cstr();
353
354 let view = unsafe {
355 BNOpenExistingDatabaseWithProgress(
356 self.handle,
357 file.as_ptr(),
358 &mut progress as *mut P as *mut c_void,
359 Some(P::cb_progress_callback),
360 )
361 };
362
363 if view.is_null() {
364 Err(())
365 } else {
366 Ok(unsafe { BinaryView::ref_from_raw(view) })
367 }
368 }
369
370 pub fn database(&self) -> Option<Ref<Database>> {
372 let result = unsafe { BNGetFileMetadataDatabase(self.handle) };
373 NonNull::new(result).map(|handle| unsafe { Database::ref_from_raw(handle) })
374 }
375}
376
377impl Debug for FileMetadata {
378 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
379 f.debug_struct("FileMetadata")
380 .field("filename", &self.filename())
381 .field("session_id", &self.session_id())
382 .field("modified", &self.modified())
383 .field("is_analysis_changed", &self.is_analysis_changed())
384 .field("current_view_type", &self.current_view())
385 .field("current_offset", &self.current_offset())
386 .field("view_types", &self.view_types().to_vec())
387 .finish()
388 }
389}
390
391unsafe impl Send for FileMetadata {}
392unsafe impl Sync for FileMetadata {}
393
394impl ToOwned for FileMetadata {
395 type Owned = Ref<Self>;
396
397 fn to_owned(&self) -> Self::Owned {
398 unsafe { RefCountable::inc_ref(self) }
399 }
400}
401
402unsafe impl RefCountable for FileMetadata {
403 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
404 Ref::new(Self {
405 handle: BNNewFileReference(handle.handle),
406 })
407 }
408
409 unsafe fn dec_ref(handle: &Self) {
410 BNFreeFileMetadata(handle.handle);
411 }
412}