binaryninja/
settings.rs

1// Copyright 2021-2025 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! An interface for reading, writing, and creating new settings
16
17use binaryninjacore_sys::*;
18use std::ffi::c_char;
19use std::fmt::Debug;
20
21use crate::binary_view::BinaryView;
22use crate::rc::*;
23use crate::string::{BnString, IntoCStr};
24
25use crate::function::Function;
26
27pub type SettingsScope = BNSettingsScope;
28
29pub const DEFAULT_INSTANCE_ID: &str = "default";
30pub const GLOBAL_INSTANCE_ID: &str = "";
31
32#[derive(PartialEq, Eq, Hash)]
33pub struct Settings {
34    pub(crate) handle: *mut BNSettings,
35}
36
37impl Settings {
38    pub(crate) unsafe fn ref_from_raw(handle: *mut BNSettings) -> Ref<Self> {
39        debug_assert!(!handle.is_null());
40        Ref::new(Self { handle })
41    }
42
43    pub fn new() -> Ref<Self> {
44        Self::new_with_id(GLOBAL_INSTANCE_ID)
45    }
46
47    pub fn new_with_id(instance_id: &str) -> Ref<Self> {
48        let instance_id = instance_id.to_cstr();
49        unsafe { Self::ref_from_raw(BNCreateSettings(instance_id.as_ptr())) }
50    }
51
52    pub fn set_resource_id(&self, resource_id: &str) {
53        let resource_id = resource_id.to_cstr();
54        unsafe { BNSettingsSetResourceId(self.handle, resource_id.as_ptr()) };
55    }
56
57    pub fn serialize_schema(&self) -> String {
58        unsafe { BnString::into_string(BNSettingsSerializeSchema(self.handle)) }
59    }
60
61    pub fn deserialize_schema(&self, schema: &str) -> bool {
62        self.deserialize_schema_with_scope(schema, SettingsScope::SettingsAutoScope)
63    }
64
65    pub fn deserialize_schema_with_scope(&self, schema: &str, scope: SettingsScope) -> bool {
66        let schema = schema.to_cstr();
67        unsafe { BNSettingsDeserializeSchema(self.handle, schema.as_ptr(), scope, true) }
68    }
69
70    pub fn contains(&self, key: &str) -> bool {
71        let key = key.to_cstr();
72
73        unsafe { BNSettingsContains(self.handle, key.as_ptr()) }
74    }
75
76    pub fn keys(&self) -> Array<BnString> {
77        let mut count = 0;
78        let result = unsafe { BNSettingsKeysList(self.handle, &mut count) };
79        assert!(!result.is_null());
80        unsafe { Array::new(result as *mut *mut c_char, count, ()) }
81    }
82
83    pub fn get_bool(&self, key: &str) -> bool {
84        self.get_bool_with_opts(key, &mut QueryOptions::default())
85    }
86
87    pub fn get_bool_with_opts(&self, key: &str, options: &mut QueryOptions) -> bool {
88        let key = key.to_cstr();
89        let view_ptr = match options.view.as_ref() {
90            Some(view) => view.handle,
91            _ => std::ptr::null_mut(),
92        };
93        let func_ptr = match options.function.as_ref() {
94            Some(func) => func.handle,
95            _ => std::ptr::null_mut(),
96        };
97        unsafe {
98            BNSettingsGetBool(
99                self.handle,
100                key.as_ptr(),
101                view_ptr,
102                func_ptr,
103                &mut options.scope,
104            )
105        }
106    }
107
108    pub fn get_double(&self, key: &str) -> f64 {
109        self.get_double_with_opts(key, &mut QueryOptions::default())
110    }
111
112    pub fn get_double_with_opts(&self, key: &str, options: &mut QueryOptions) -> f64 {
113        let key = key.to_cstr();
114        let view_ptr = match options.view.as_ref() {
115            Some(view) => view.handle,
116            _ => std::ptr::null_mut(),
117        };
118        let func_ptr = match options.function.as_ref() {
119            Some(func) => func.handle,
120            _ => std::ptr::null_mut(),
121        };
122        unsafe {
123            BNSettingsGetDouble(
124                self.handle,
125                key.as_ptr(),
126                view_ptr,
127                func_ptr,
128                &mut options.scope,
129            )
130        }
131    }
132
133    pub fn get_integer(&self, key: &str) -> u64 {
134        self.get_integer_with_opts(key, &mut QueryOptions::default())
135    }
136
137    pub fn get_integer_with_opts(&self, key: &str, options: &mut QueryOptions) -> u64 {
138        let key = key.to_cstr();
139        let view_ptr = match options.view.as_ref() {
140            Some(view) => view.handle,
141            _ => std::ptr::null_mut(),
142        };
143        let func_ptr = match options.function.as_ref() {
144            Some(func) => func.handle,
145            _ => std::ptr::null_mut(),
146        };
147        unsafe {
148            BNSettingsGetUInt64(
149                self.handle,
150                key.as_ptr(),
151                view_ptr,
152                func_ptr,
153                &mut options.scope,
154            )
155        }
156    }
157
158    pub fn get_string(&self, key: &str) -> String {
159        self.get_string_with_opts(key, &mut QueryOptions::default())
160    }
161
162    pub fn get_string_with_opts(&self, key: &str, options: &mut QueryOptions) -> String {
163        let key = key.to_cstr();
164        let view_ptr = match options.view.as_ref() {
165            Some(view) => view.handle,
166            _ => std::ptr::null_mut(),
167        };
168        let func_ptr = match options.function.as_ref() {
169            Some(func) => func.handle,
170            _ => std::ptr::null_mut(),
171        };
172        unsafe {
173            BnString::into_string(BNSettingsGetString(
174                self.handle,
175                key.as_ptr(),
176                view_ptr,
177                func_ptr,
178                &mut options.scope,
179            ))
180        }
181    }
182
183    pub fn get_string_list(&self, key: &str) -> Array<BnString> {
184        self.get_string_list_with_opts(key, &mut QueryOptions::default())
185    }
186
187    pub fn get_string_list_with_opts(
188        &self,
189        key: &str,
190        options: &mut QueryOptions,
191    ) -> Array<BnString> {
192        let key = key.to_cstr();
193        let view_ptr = match options.view.as_ref() {
194            Some(view) => view.handle,
195            _ => std::ptr::null_mut(),
196        };
197        let func_ptr = match options.function.as_ref() {
198            Some(func) => func.handle,
199            _ => std::ptr::null_mut(),
200        };
201        let mut size: usize = 0;
202        unsafe {
203            Array::new(
204                BNSettingsGetStringList(
205                    self.handle,
206                    key.as_ptr(),
207                    view_ptr,
208                    func_ptr,
209                    &mut options.scope,
210                    &mut size,
211                ) as *mut *mut c_char,
212                size,
213                (),
214            )
215        }
216    }
217
218    pub fn get_json(&self, key: &str) -> String {
219        self.get_json_with_opts(key, &mut QueryOptions::default())
220    }
221
222    pub fn get_json_with_opts(&self, key: &str, options: &mut QueryOptions) -> String {
223        let key = key.to_cstr();
224        let view_ptr = match options.view.as_ref() {
225            Some(view) => view.handle,
226            _ => std::ptr::null_mut(),
227        };
228        let func_ptr = match options.function.as_ref() {
229            Some(func) => func.handle,
230            _ => std::ptr::null_mut(),
231        };
232        unsafe {
233            BnString::into_string(BNSettingsGetJson(
234                self.handle,
235                key.as_ptr(),
236                view_ptr,
237                func_ptr,
238                &mut options.scope,
239            ))
240        }
241    }
242
243    pub fn set_bool(&self, key: &str, value: bool) {
244        self.set_bool_with_opts(key, value, &QueryOptions::default())
245    }
246
247    pub fn set_bool_with_opts(&self, key: &str, value: bool, options: &QueryOptions) {
248        let key = key.to_cstr();
249        let view_ptr = match options.view.as_ref() {
250            Some(view) => view.handle,
251            _ => std::ptr::null_mut(),
252        };
253        let func_ptr = match options.function.as_ref() {
254            Some(func) => func.handle,
255            _ => std::ptr::null_mut(),
256        };
257        unsafe {
258            BNSettingsSetBool(
259                self.handle,
260                view_ptr,
261                func_ptr,
262                options.scope,
263                key.as_ptr(),
264                value,
265            );
266        }
267    }
268
269    pub fn set_double(&self, key: &str, value: f64) {
270        self.set_double_with_opts(key, value, &QueryOptions::default())
271    }
272    pub fn set_double_with_opts(&self, key: &str, value: f64, options: &QueryOptions) {
273        let key = key.to_cstr();
274        let view_ptr = match options.view.as_ref() {
275            Some(view) => view.handle,
276            _ => std::ptr::null_mut(),
277        };
278        let func_ptr = match options.function.as_ref() {
279            Some(func) => func.handle,
280            _ => std::ptr::null_mut(),
281        };
282        unsafe {
283            BNSettingsSetDouble(
284                self.handle,
285                view_ptr,
286                func_ptr,
287                options.scope,
288                key.as_ptr(),
289                value,
290            );
291        }
292    }
293
294    pub fn set_integer(&self, key: &str, value: u64) {
295        self.set_integer_with_opts(key, value, &QueryOptions::default())
296    }
297
298    pub fn set_integer_with_opts(&self, key: &str, value: u64, options: &QueryOptions) {
299        let key = key.to_cstr();
300        let view_ptr = match options.view.as_ref() {
301            Some(view) => view.handle,
302            _ => std::ptr::null_mut(),
303        };
304        let func_ptr = match options.function.as_ref() {
305            Some(func) => func.handle,
306            _ => std::ptr::null_mut(),
307        };
308        unsafe {
309            BNSettingsSetUInt64(
310                self.handle,
311                view_ptr,
312                func_ptr,
313                options.scope,
314                key.as_ptr(),
315                value,
316            );
317        }
318    }
319
320    pub fn set_string(&self, key: &str, value: &str) {
321        self.set_string_with_opts(key, value, &QueryOptions::default())
322    }
323
324    pub fn set_string_with_opts(&self, key: &str, value: &str, options: &QueryOptions) {
325        let key = key.to_cstr();
326        let value = value.to_cstr();
327        let view_ptr = match options.view.as_ref() {
328            Some(view) => view.handle,
329            _ => std::ptr::null_mut(),
330        };
331        let func_ptr = match options.function.as_ref() {
332            Some(func) => func.handle,
333            _ => std::ptr::null_mut(),
334        };
335        unsafe {
336            BNSettingsSetString(
337                self.handle,
338                view_ptr,
339                func_ptr,
340                options.scope,
341                key.as_ptr(),
342                value.as_ptr(),
343            );
344        }
345    }
346
347    pub fn set_string_list<I: IntoIterator<Item = String>>(&self, key: &str, value: I) -> bool {
348        self.set_string_list_with_opts(key, value, &QueryOptions::default())
349    }
350
351    pub fn set_string_list_with_opts<I: IntoIterator<Item = String>>(
352        &self,
353        key: &str,
354        value: I,
355        options: &QueryOptions,
356    ) -> bool {
357        let key = key.to_cstr();
358        let raw_list: Vec<_> = value.into_iter().map(|s| s.to_cstr()).collect();
359        let mut raw_list_ptr: Vec<_> = raw_list.iter().map(|s| s.as_ptr()).collect();
360
361        let view_ptr = match options.view.as_ref() {
362            Some(view) => view.handle,
363            _ => std::ptr::null_mut(),
364        };
365        let func_ptr = match options.function.as_ref() {
366            Some(func) => func.handle,
367            _ => std::ptr::null_mut(),
368        };
369        unsafe {
370            BNSettingsSetStringList(
371                self.handle,
372                view_ptr,
373                func_ptr,
374                options.scope,
375                key.as_ptr(),
376                raw_list_ptr.as_mut_ptr(),
377                raw_list_ptr.len(),
378            )
379        }
380    }
381
382    pub fn set_json(&self, key: &str, value: &str) -> bool {
383        self.set_json_with_opts(key, value, &QueryOptions::default())
384    }
385
386    pub fn set_json_with_opts(&self, key: &str, value: &str, options: &QueryOptions) -> bool {
387        let key = key.to_cstr();
388        let value = value.to_cstr();
389        let view_ptr = match options.view.as_ref() {
390            Some(view) => view.handle,
391            _ => std::ptr::null_mut(),
392        };
393        let func_ptr = match options.function.as_ref() {
394            Some(func) => func.handle,
395            _ => std::ptr::null_mut(),
396        };
397        unsafe {
398            BNSettingsSetJson(
399                self.handle,
400                view_ptr,
401                func_ptr,
402                options.scope,
403                key.as_ptr(),
404                value.as_ptr(),
405            )
406        }
407    }
408
409    pub fn get_property_string(&self, key: &str, property: &str) -> String {
410        let key = key.to_cstr();
411        let property = property.to_cstr();
412        unsafe {
413            BnString::into_string(BNSettingsQueryPropertyString(
414                self.handle,
415                key.as_ptr(),
416                property.as_ptr(),
417            ))
418        }
419    }
420
421    pub fn get_property_string_list(&self, key: &str, property: &str) -> Array<BnString> {
422        let key = key.to_cstr();
423        let property = property.to_cstr();
424        let mut size: usize = 0;
425        unsafe {
426            Array::new(
427                BNSettingsQueryPropertyStringList(
428                    self.handle,
429                    key.as_ptr(),
430                    property.as_ptr(),
431                    &mut size,
432                ) as *mut *mut c_char,
433                size,
434                (),
435            )
436        }
437    }
438
439    pub fn update_bool_property(&self, key: &str, property: &str, value: bool) {
440        let key = key.to_cstr();
441        let property = property.to_cstr();
442        unsafe {
443            BNSettingsUpdateBoolProperty(self.handle, key.as_ptr(), property.as_ptr(), value);
444        }
445    }
446
447    pub fn update_integer_property(&self, key: &str, property: &str, value: u64) {
448        let key = key.to_cstr();
449        let property = property.to_cstr();
450        unsafe {
451            BNSettingsUpdateUInt64Property(self.handle, key.as_ptr(), property.as_ptr(), value);
452        }
453    }
454
455    pub fn update_double_property(&self, key: &str, property: &str, value: f64) {
456        let key = key.to_cstr();
457        let property = property.to_cstr();
458        unsafe {
459            BNSettingsUpdateDoubleProperty(self.handle, key.as_ptr(), property.as_ptr(), value);
460        }
461    }
462
463    pub fn update_string_property(&self, key: &str, property: &str, value: &str) {
464        let key = key.to_cstr();
465        let property = property.to_cstr();
466        let value = value.to_cstr();
467        unsafe {
468            BNSettingsUpdateStringProperty(
469                self.handle,
470                key.as_ptr(),
471                property.as_ptr(),
472                value.as_ptr(),
473            );
474        }
475    }
476
477    pub fn update_string_list_property<I: IntoIterator<Item = String>>(
478        &self,
479        key: &str,
480        property: &str,
481        value: I,
482    ) {
483        let key = key.to_cstr();
484        let property = property.to_cstr();
485        let raw_list: Vec<_> = value.into_iter().map(|s| s.to_cstr()).collect();
486        let mut raw_list_ptr: Vec<_> = raw_list.iter().map(|s| s.as_ptr()).collect();
487
488        unsafe {
489            BNSettingsUpdateStringListProperty(
490                self.handle,
491                key.as_ptr(),
492                property.as_ptr(),
493                raw_list_ptr.as_mut_ptr(),
494                raw_list_ptr.len(),
495            );
496        }
497    }
498
499    pub fn register_group(&self, group: &str, title: &str) -> bool {
500        let group = group.to_cstr();
501        let title = title.to_cstr();
502
503        unsafe { BNSettingsRegisterGroup(self.handle, group.as_ptr(), title.as_ptr()) }
504    }
505
506    pub fn register_setting_json(&self, group: &str, properties: &str) -> bool {
507        let group = group.to_cstr();
508        let properties = properties.to_cstr();
509
510        unsafe { BNSettingsRegisterSetting(self.handle, group.as_ptr(), properties.as_ptr()) }
511    }
512
513    // TODO: register_setting but type-safely turn it into json
514}
515
516impl Default for Ref<Settings> {
517    fn default() -> Self {
518        Settings::new_with_id(DEFAULT_INSTANCE_ID)
519    }
520}
521
522unsafe impl Send for Settings {}
523unsafe impl Sync for Settings {}
524
525impl ToOwned for Settings {
526    type Owned = Ref<Self>;
527
528    fn to_owned(&self) -> Self::Owned {
529        unsafe { RefCountable::inc_ref(self) }
530    }
531}
532
533unsafe impl RefCountable for Settings {
534    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
535        Ref::new(Self {
536            handle: BNNewSettingsReference(handle.handle),
537        })
538    }
539
540    unsafe fn dec_ref(handle: &Self) {
541        BNFreeSettings(handle.handle);
542    }
543}
544
545#[derive(Debug, Clone)]
546pub struct QueryOptions<'a> {
547    pub scope: SettingsScope,
548    pub view: Option<&'a BinaryView>,
549    pub function: Option<Ref<Function>>,
550}
551
552impl<'a> QueryOptions<'a> {
553    pub fn new() -> Self {
554        Self::default()
555    }
556
557    pub fn new_with_view(view: &'a BinaryView) -> Self {
558        Self {
559            view: Some(view),
560            ..Default::default()
561        }
562    }
563
564    pub fn new_with_func(func: Ref<Function>) -> Self {
565        Self {
566            function: Some(func),
567            ..Default::default()
568        }
569    }
570
571    /// Set the query to target a specific view, this will be overridden if a function is targeted.
572    pub fn with_view(mut self, view: &'a BinaryView) -> Self {
573        self.view = Some(view);
574        self
575    }
576
577    pub fn with_scope(mut self, scope: SettingsScope) -> Self {
578        self.scope = scope;
579        self
580    }
581
582    /// Set the query to target a specific function, this will override the target view.
583    pub fn with_function(mut self, function: Ref<Function>) -> Self {
584        self.function = Some(function);
585        self
586    }
587}
588
589impl Default for QueryOptions<'_> {
590    fn default() -> Self {
591        Self {
592            view: None,
593            scope: SettingsScope::SettingsAutoScope,
594            function: None,
595        }
596    }
597}