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