Skip to main content

binaryninja/
settings.rs

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