diff --git a/asr-derive/src/lib.rs b/asr-derive/src/lib.rs index cc667d6..9f4b339 100644 --- a/asr-derive/src/lib.rs +++ b/asr-derive/src/lib.rs @@ -3,14 +3,15 @@ use proc_macro::TokenStream; use quote::{quote, quote_spanned}; use syn::{spanned::Spanned, Data, DeriveInput, Expr, ExprLit, Lit, Meta}; -/// Generates a `register` method for a struct that automatically registers its -/// fields as settings and returns the struct with the user's settings applied. +/// Implements the `Gui` trait for a struct that allows you to register its +/// fields as settings widgets and returns the struct with the user's settings +/// applied. /// /// # Example /// /// ```no_run -/// #[derive(Settings)] -/// struct MySettings { +/// #[derive(Gui)] +/// struct Settings { /// /// General Settings /// _general_settings: Title, /// /// Use Game Time @@ -20,19 +21,17 @@ use syn::{spanned::Spanned, Data, DeriveInput, Expr, ExprLit, Lit, Meta}; /// } /// ``` /// -/// This will generate the following code: +/// The type can then be used like so: /// /// ```no_run -/// impl MySettings { -/// pub fn register() -> Self { -/// asr::user_settings::add_title("_general_settings", "General Settings", 0); -/// let use_game_time = asr::user_settings::add_bool("use_game_time", "Use Game Time", false); -/// asr::user_settings::set_tooltip("use_game_time", "This is the tooltip."); -/// Self { use_game_time } -/// } +/// let mut settings = Settings::register(); +/// +/// loop { +/// settings.update(); +/// // Do something with the settings. /// } /// ``` -#[proc_macro_derive(Settings, attributes(default, heading_level))] +#[proc_macro_derive(Gui, attributes(default, heading_level))] pub fn settings_macro(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); @@ -58,14 +57,21 @@ pub fn settings_macro(input: TokenStream) -> TokenStream { let mut tooltip_string = String::new(); let mut is_in_tooltip = false; for attr in &field.attrs { - let Meta::NameValue(nv) = &attr.meta else { continue }; - let Some(ident) = nv.path.get_ident() else { continue }; + let Meta::NameValue(nv) = &attr.meta else { + continue; + }; + let Some(ident) = nv.path.get_ident() else { + continue; + }; if ident != "doc" { continue; } let Expr::Lit(ExprLit { lit: Lit::Str(s), .. - }) = &nv.value else { continue }; + }) = &nv.value + else { + continue; + }; let value = s.value(); let value = value.trim(); let target_string = if is_in_tooltip { @@ -102,7 +108,9 @@ pub fn settings_macro(input: TokenStream) -> TokenStream { .attrs .iter() .filter_map(|x| { - let Meta::NameValue(nv) = &x.meta else { return None }; + let Meta::NameValue(nv) = &x.meta else { + return None; + }; let span = nv.span(); if nv.path.is_ident("default") { let value = &nv.value; @@ -119,18 +127,30 @@ pub fn settings_macro(input: TokenStream) -> TokenStream { } quote! { - impl #struct_name { - pub fn register() -> Self { + impl asr::settings::Gui for #struct_name { + fn register() -> Self { Self { #(#field_names: { - let mut args = <#field_tys as asr::user_settings::Setting>::Args::default(); + let mut args = <#field_tys as asr::settings::gui::Widget>::Args::default(); #args_init - let mut value = asr::user_settings::Setting::register(#field_name_strings, #field_descs, args); + let mut value = asr::settings::gui::Widget::register(#field_name_strings, #field_descs, args); #field_tooltips value },)* } } + + fn update_from(&mut self, settings_map: &asr::settings::Map) { + #({ + let mut args = <#field_tys as asr::settings::gui::Widget>::Args::default(); + #args_init + asr::settings::gui::Widget::update_from(&mut self.#field_names, settings_map, #field_name_strings, args); + })* + } + + fn update(&mut self) { + self.update_from(&asr::settings::Map::load()); + } } } .into() diff --git a/asr-derive/src/unity.rs b/asr-derive/src/unity.rs index a6a4b86..2c33649 100644 --- a/asr-derive/src/unity.rs +++ b/asr-derive/src/unity.rs @@ -26,7 +26,9 @@ pub fn process(input: TokenStream, mono_module: impl ToTokens) -> TokenStream { let field_name = field.ident.clone().unwrap(); let span = field_name.span(); let is_static = field.attrs.iter().any(|x| { - let Meta::Path(path) = &x.meta else { return false }; + let Meta::Path(path) = &x.meta else { + return false; + }; path.is_ident("static_field") }); field_reads.push(if is_static { @@ -42,13 +44,19 @@ pub fn process(input: TokenStream, mono_module: impl ToTokens) -> TokenStream { .attrs .iter() .find_map(|x| { - let Meta::NameValue(name_value) = &x.meta else { return None }; + let Meta::NameValue(name_value) = &x.meta else { + return None; + }; if !name_value.path.is_ident("rename") { return None; } let Expr::Lit(ExprLit { - lit: Lit::Str(name), .. - }) = &name_value.value else { return None }; + lit: Lit::Str(name), + .. + }) = &name_value.value + else { + return None; + }; Some(name.value()) }) .unwrap_or_else(|| field.ident.clone().unwrap().to_string()); @@ -97,7 +105,7 @@ pub fn process(input: TokenStream, mono_module: impl ToTokens) -> TokenStream { let class = image.wait_get_class(process, module, #stuct_name_string).await; #( - let #field_names = class.wait_get_field(process, module, #lookup_names).await; + let #field_names = class.wait_get_field_offset(process, module, #lookup_names).await; )* #binding_name { diff --git a/src/emulator/gba/retroarch.rs b/src/emulator/gba/retroarch.rs index a4a9156..c708ce7 100644 --- a/src/emulator/gba/retroarch.rs +++ b/src/emulator/gba/retroarch.rs @@ -1,6 +1,4 @@ -use crate::{ - file_format::pe, signature::Signature, Address, Address32, Address64, Process, -}; +use crate::{file_format::pe, signature::Signature, Address, Address32, Address64, Process}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct State { @@ -32,8 +30,10 @@ impl State { self.core_base = core_address; match core_name { - "vbam_libretro.dll" | "vba_next_libretro.dll" | "mednafen_gba_libretro.dll" => self.vba(game, is_64_bit, core_name), - "mgba_libretro.dll" => super::mgba::State::find_ram(&mut super::mgba::State, game), + "vbam_libretro.dll" | "vba_next_libretro.dll" | "mednafen_gba_libretro.dll" => { + self.vba(game, is_64_bit, core_name) + } + "mgba_libretro.dll" => super::mgba::State::find_ram(&super::mgba::State, game), "gpsp_libretro.dll" => self.gpsp(game, is_64_bit, core_name), _ => None, } @@ -70,8 +70,8 @@ impl State { return None; } } - - addr + + addr }; let ewram = game.read::(ewram_pointer).ok()?; @@ -93,7 +93,7 @@ impl State { let ptr = SIG2.scan_process_range(game, module_range)?; game.read::(ptr + 1).ok()?.into() }; - + let ewram = game.read::(ewram_pointer).ok()?; let iwram = game.read::(iwram_pointer).ok()?; diff --git a/src/future/mod.rs b/src/future/mod.rs index 2106841..1fa1c0e 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -413,8 +413,7 @@ macro_rules! async_main { let size = mem::size_of::(); const PAGE_SIZE: usize = 64 << 10; assert!(mem::align_of::() <= PAGE_SIZE); - // TODO: div_ceil - let pages = (size + (PAGE_SIZE - 1)) / PAGE_SIZE; + let pages = size.div_ceil(PAGE_SIZE); #[cfg(target_arch = "wasm32")] let old_page_count = core::arch::wasm32::memory_grow(0, pages); diff --git a/src/game_engine/unity/il2cpp.rs b/src/game_engine/unity/il2cpp.rs index 47daa26..4361ad0 100644 --- a/src/game_engine/unity/il2cpp.rs +++ b/src/game_engine/unity/il2cpp.rs @@ -320,7 +320,7 @@ impl Image { } } -/// A .NET class that is part of an [`Image`](Image). +/// A .NET class that is part of an [`Image`]. pub struct Class { class: Address, } @@ -420,7 +420,7 @@ impl Class { /// the offset of the field from the start of an instance of the class. If /// it's a static field, the offset will be from the start of the static /// table. This is the `await`able version of the - /// [`get_field`](Self::get_field) function. + /// [`get_field_offset`](Self::get_field_offset) function. pub async fn wait_get_field_offset( &self, process: &Process, diff --git a/src/game_engine/unity/mono.rs b/src/game_engine/unity/mono.rs index 59488dc..6e79dad 100644 --- a/src/game_engine/unity/mono.rs +++ b/src/game_engine/unity/mono.rs @@ -316,7 +316,7 @@ impl Image { } } -/// A .NET class that is part of an [`Image`](Image). +/// A .NET class that is part of an [`Image`]. pub struct Class { class: Address, } @@ -464,7 +464,7 @@ impl Class { /// the offset of the field from the start of an instance of the class. If /// it's a static field, the offset will be from the start of the static /// table. This is the `await`able version of the - /// [`get_field`](Self::get_field) function. + /// [`get_field_offset`](Self::get_field_offset) function. pub async fn wait_get_field_offset( &self, process: &Process, @@ -838,7 +838,7 @@ fn detect_version(process: &Process) -> Option { const SIG_202X: Signature<6> = Signature::new("00 32 30 32 ?? 2E"); let Some(addr) = SIG_202X.scan_process_range(process, unity_module) else { - return Some(Version::V2) + return Some(Version::V2); }; const ZERO: u8 = b'0'; diff --git a/src/game_engine/unity/scene.rs b/src/game_engine/unity/scene.rs index c16eef7..ec99d8d 100644 --- a/src/game_engine/unity/scene.rs +++ b/src/game_engine/unity/scene.rs @@ -124,9 +124,8 @@ impl SceneManager { self.get_current_scene(process)?.index(process, self) } - /// Returns the full path to the current scene. Use - /// [`get_scene_name`](Self::get_scene_name) afterwards to - /// get the scene name. + /// Returns the full path to the current scene. Use [`get_scene_name`] + /// afterwards to get the scene name. pub fn get_current_scene_path( &self, process: &Process, @@ -336,9 +335,10 @@ impl Transform { let name_ptr = { match scene_manager.is_il2cpp { true => { - let Ok(name_ptr) = scene_manager - .read_pointer(process, vtable + 2_u64.wrapping_mul(scene_manager.pointer_size() as _)) - else { + let Ok(name_ptr) = scene_manager.read_pointer( + process, + vtable + 2_u64.wrapping_mul(scene_manager.pointer_size() as _), + ) else { return false; }; diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index ce51ce4..21164a1 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -5,8 +5,8 @@ mod memory_range; mod process; mod sys; +pub mod settings; pub mod timer; -pub mod user_settings; /// An error returned by a runtime function. #[derive(Debug)] @@ -56,7 +56,7 @@ pub fn get_os() -> Result, Error> { let mut buf = arrayvec::ArrayString::<16>::new(); // SAFETY: We provide a valid pointer and length to the buffer. We check // whether the buffer was successfully filled and set the length of the - // buffer accordingly. + // buffer accordingly. The buffer is guaranteed to be valid UTF-8. unsafe { let mut len = buf.capacity(); let success = sys::runtime_get_os(buf.as_mut_ptr(), &mut len); @@ -80,7 +80,7 @@ pub fn get_arch() -> Result, Error> { let mut buf = arrayvec::ArrayString::<16>::new(); // SAFETY: We provide a valid pointer and length to the buffer. We check // whether the buffer was successfully filled and set the length of the - // buffer accordingly. + // buffer accordingly. The buffer is guaranteed to be valid UTF-8. unsafe { let mut len = buf.capacity(); let success = sys::runtime_get_arch(buf.as_mut_ptr(), &mut len); diff --git a/src/runtime/user_settings.rs b/src/runtime/settings/gui.rs similarity index 52% rename from src/runtime/user_settings.rs rename to src/runtime/settings/gui.rs index b090cc0..03134b5 100644 --- a/src/runtime/user_settings.rs +++ b/src/runtime/settings/gui.rs @@ -1,13 +1,17 @@ -//! This module allows you to add settings that the user can modify. - -use super::sys; +//! This module allows you to add settings widgets to the settings GUI that the +//! user can modify. #[cfg(feature = "derive")] -pub use asr_derive::Settings; +pub use asr_derive::Gui; + +use crate::runtime::sys; -/// Adds a new boolean setting that the user can modify. This will return either -/// the specified default value or the value that the user has set. The key is -/// used to store the setting and needs to be unique across all types of +use super::map::Map; + +/// Adds a new boolean setting widget to the settings GUI that the user can +/// modify. This will return either the specified default value or the value +/// that the user has set. The key is used to store the setting in the global +/// settings [`Map`](super::Map) and needs to be unique across all types of /// settings. #[inline] pub fn add_bool(key: &str, description: &str, default_value: bool) -> bool { @@ -24,7 +28,7 @@ pub fn add_bool(key: &str, description: &str, default_value: bool) -> bool { } } -/// Adds a new title to the user settings. This is used to group settings +/// Adds a new title widget to the settings GUI. This is used to group settings /// together. The heading level determines the size of the title. The top level /// titles use a heading level of 0. The key needs to be unique across all types /// of settings. @@ -43,30 +47,55 @@ pub fn add_title(key: &str, description: &str, heading_level: u32) { } } -/// Adds a tooltip to a setting based on its key. A tooltip is useful for +/// Adds a tooltip to a setting widget based on its key. A tooltip is useful for /// explaining the purpose of a setting to the user. #[inline] pub fn set_tooltip(key: &str, tooltip: &str) { - // SAFETY: We provide valid pointers and lengths to key and description. - // They are also guaranteed to be valid UTF-8 strings. + // SAFETY: We provide valid pointers and lengths to key and tooltip. They + // are also guaranteed to be valid UTF-8 strings. unsafe { sys::user_settings_set_tooltip(key.as_ptr(), key.len(), tooltip.as_ptr(), tooltip.len()) } } -/// A type that can be registered as a user setting. This is an internal trait -/// that you don't need to worry about. -pub trait Setting { - /// The arguments that are needed to register the setting. +/// A trait that can be derived to describe an entire settings GUI through a +/// struct declaration. Check the derive macro [`Gui`](macro@Gui) for more +/// information. +pub trait Gui { + /// Registers the settings by adding all the widgets to the settings GUI and + /// initializing the settings with the values that the user has set or their + /// default values if they haven't been modified yet. + fn register() -> Self; + + /// Updates the settings with the values that the user has set from the + /// currently set global settings map. + fn update(&mut self) { + self.update_from(&Map::load()); + } + + /// Updates the settings with the values that the user has set from the + /// settings map provided. + fn update_from(&mut self, settings_map: &Map); +} + +/// A settings widget that can be added to the settings GUI. This is an internal +/// trait that you don't need to worry about. +#[doc(hidden)] +pub trait Widget { + /// The arguments that are needed to register the widget. type Args: Default; - /// Registers the setting with the given key, description and default value. + /// Registers the widget with the given key, description and default value. /// Returns the value that the user has set or the default value if the user /// did not set a value. fn register(key: &str, description: &str, args: Self::Args) -> Self; + /// Updates the value of the setting based on the value that the user has + /// set in the provided settings map. + fn update_from(&mut self, settings_map: &Map, key: &str, args: Self::Args); } /// The arguments that are needed to register a boolean setting. This is an /// internal type that you don't need to worry about. +#[doc(hidden)] #[derive(Default)] #[non_exhaustive] pub struct BoolArgs { @@ -74,13 +103,21 @@ pub struct BoolArgs { pub default: bool, } -impl Setting for bool { +impl Widget for bool { type Args = BoolArgs; #[inline] fn register(key: &str, description: &str, args: Self::Args) -> Self { add_bool(key, description, args.default) } + + #[inline] + fn update_from(&mut self, settings_map: &Map, key: &str, args: Self::Args) { + *self = settings_map + .get(key) + .and_then(|value| value.get_bool()) + .unwrap_or(args.default); + } } /// A title that can be used to group settings together. @@ -88,6 +125,7 @@ pub struct Title; /// The arguments that are needed to register a title. This is an internal type /// that you don't need to worry about. +#[doc(hidden)] #[derive(Default)] #[non_exhaustive] pub struct TitleArgs { @@ -96,7 +134,7 @@ pub struct TitleArgs { pub heading_level: u32, } -impl Setting for Title { +impl Widget for Title { type Args = TitleArgs; #[inline] @@ -104,4 +142,7 @@ impl Setting for Title { add_title(key, description, args.heading_level); Self } + + #[inline] + fn update_from(&mut self, _settings_map: &Map, _key: &str, _args: Self::Args) {} } diff --git a/src/runtime/settings/map.rs b/src/runtime/settings/map.rs new file mode 100644 index 0000000..104191c --- /dev/null +++ b/src/runtime/settings/map.rs @@ -0,0 +1,98 @@ +use crate::runtime::sys; + +use super::Value; + +/// A map consisting of settings that are configured. Every setting has a string +/// based key and a [`Value`]. There is a global settings map that represents +/// all the settings that the user has configured at the given time. Settings +/// that are unmodified are usually not stored in the map. The global map is +/// what gets stored to disk and loaded from disk. Any changes made in the +/// settings GUI will be reflected in the global map and vice versa. The key of +/// the settings widget is used as the key for the settings map. Additional +/// settings that are not part of the GUI can be stored in the map as well, such +/// as a version of the settings for handling old versions of an auto splitter. +#[derive(Debug)] +#[repr(transparent)] +pub struct Map(pub(super) sys::SettingsMap); + +impl Drop for Map { + #[inline] + fn drop(&mut self) { + // SAFETY: The handle is valid and we own it, so it's our responsibility + // to free it. + unsafe { sys::settings_map_free(self.0) } + } +} + +impl Clone for Map { + #[inline] + fn clone(&self) -> Self { + // SAFETY: The handle is valid, so we can safely copy it. + Self(unsafe { sys::settings_map_copy(self.0) }) + } +} + +impl Default for Map { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Map { + /// Creates a new empty settings map. + #[inline] + pub fn new() -> Self { + // SAFETY: This is always safe to call. + Self(unsafe { sys::settings_map_new() }) + } + + /// Loads a copy of the currently set global settings map. Any changes to it + /// are only perceived if it's stored back. + #[inline] + pub fn load() -> Self { + // SAFETY: This is always safe to call. + Self(unsafe { sys::settings_map_load() }) + } + + /// Stores a copy of the settings map as the new global settings map. This + /// will overwrite the previous global settings map. There's a chance that + /// the settings map was changed in the meantime, so those changes could get + /// lost. Prefer using [`store_if_unchanged`](Self::store_if_unchanged) if + /// you want to avoid that. + #[inline] + pub fn store(&self) { + // SAFETY: The handle is valid, so it can be stored. + unsafe { sys::settings_map_store(self.0) } + } + + /// Stores a copy of the new settings map as the new global settings map if + /// the map has not changed in the meantime. This is done by comparing the + /// old map. Returns [`true`] if the map was stored successfully. + /// Returns [`false`] if the map was changed in the meantime. + #[inline] + pub fn store_if_unchanged(&self, old: &Self) -> bool { + // SAFETY: Both handles are valid. + unsafe { sys::settings_map_store_if_unchanged(old.0, self.0) } + } + + /// Inserts a copy of the setting value into the settings map based on the + /// key. If the key already exists, it will be overwritten. + #[inline] + pub fn insert(&self, key: &str, value: &Value) { + // SAFETY: The settings map handle is valid. We provide a valid pointer + // and length to the key which is guaranteed to be valid UTF-8. The + // setting value handle is also valid. + unsafe { sys::settings_map_insert(self.0, key.as_ptr(), key.len(), value.0) } + } + + /// Gets a copy of the setting value from the settings map based on the key. + /// Returns [`None`] if the key does not exist. Any changes to it are only + /// perceived if it's stored back. + #[inline] + pub fn get(&self, key: &str) -> Option { + // SAFETY: The settings map handle is valid. We provide a valid pointer + // and length to the key which is guaranteed to be valid UTF-8. + unsafe { sys::settings_map_get(self.0, key.as_ptr(), key.len()).map(Value) } + } +} diff --git a/src/runtime/settings/mod.rs b/src/runtime/settings/mod.rs new file mode 100644 index 0000000..318b496 --- /dev/null +++ b/src/runtime/settings/mod.rs @@ -0,0 +1,9 @@ +//! Support for interacting with the settings of the auto splitter. + +pub mod gui; +mod map; +mod value; + +pub use gui::Gui; +pub use map::*; +pub use value::*; diff --git a/src/runtime/settings/value.rs b/src/runtime/settings/value.rs new file mode 100644 index 0000000..d80e66a --- /dev/null +++ b/src/runtime/settings/value.rs @@ -0,0 +1,50 @@ +use core::mem::MaybeUninit; + +use crate::runtime::sys; + +/// A value of a setting. This can be a value of any type that a setting can +/// hold. Currently only boolean settings are supported. +#[derive(Debug)] +#[repr(transparent)] +pub struct Value(pub(super) sys::SettingValue); + +impl Drop for Value { + #[inline] + fn drop(&mut self) { + // SAFETY: The handle is valid and we own it, so it's our responsibility + // to free it. + unsafe { sys::setting_value_free(self.0) } + } +} + +impl Value { + /// Creates a new setting value from a value of a supported type. + #[inline] + pub fn new(value: impl Into) -> Self { + value.into() + } + + /// Returns the value as a boolean if it is a boolean. + #[inline] + pub fn get_bool(&self) -> Option { + // SAFETY: The handle is valid. We provide a valid pointer to a boolean. + // After the function call we check the return value and if it's true, + // the boolean is initialized and we can return it. + unsafe { + let mut out = MaybeUninit::uninit(); + if sys::setting_value_get_bool(self.0, out.as_mut_ptr()) { + Some(out.assume_init()) + } else { + None + } + } + } +} + +impl From for Value { + #[inline] + fn from(value: bool) -> Self { + // SAFETY: This is always safe to call. + Self(unsafe { sys::setting_value_new_bool(value) }) + } +} diff --git a/src/runtime/sys.rs b/src/runtime/sys.rs index 873f181..42be7e4 100644 --- a/src/runtime/sys.rs +++ b/src/runtime/sys.rs @@ -34,6 +34,14 @@ impl TimerState { pub const ENDED: Self = Self(3); } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(transparent)] +pub struct SettingsMap(NonZeroU64); + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(transparent)] +pub struct SettingValue(NonZeroU64); + extern "C" { /// Gets the state that the timer currently is in. pub fn timer_get_state() -> TimerState; @@ -177,4 +185,61 @@ extern "C" { tooltip_ptr: *const u8, tooltip_len: usize, ); + + /// Creates a new settings map. You own the settings map and are responsible + /// for freeing it. + pub fn settings_map_new() -> SettingsMap; + /// Frees a settings map. + pub fn settings_map_free(map: SettingsMap); + /// Loads a copy of the currently set global settings map. Any changes to it + /// are only perceived if it's stored back. You own the settings map and are + /// responsible for freeing it. + pub fn settings_map_load() -> SettingsMap; + /// Stores a copy of the settings map as the new global settings map. This + /// will overwrite the previous global settings map. You still retain + /// ownership of the map, which means you still need to free it. There's a + /// chance that the settings map was changed in the meantime, so those + /// changes could get lost. Prefer using `settings_map_store_if_unchanged` + /// if you want to avoid that. + pub fn settings_map_store(map: SettingsMap); + /// Stores a copy of the new settings map as the new global settings map if + /// the map has not changed in the meantime. This is done by comparing the + /// old map. You still retain ownership of both maps, which means you still + /// need to free them. Returns `true` if the map was stored successfully. + /// Returns `false` if the map was changed in the meantime. + pub fn settings_map_store_if_unchanged(old_map: SettingsMap, new_map: SettingsMap) -> bool; + /// Copies a settings map. No changes inside the copy affect the original + /// settings map. You own the new settings map and are responsible for + /// freeing it. + pub fn settings_map_copy(map: SettingsMap) -> SettingsMap; + /// Inserts a copy of the setting value into the settings map based on the + /// key. If the key already exists, it will be overwritten. You still retain + /// ownership of the setting value, which means you still need to free it. + pub fn settings_map_insert( + map: SettingsMap, + key_ptr: *const u8, + key_len: usize, + value: SettingValue, + ); + /// Gets a copy of the setting value from the settings map based on the key. + /// Returns `None` if the key does not exist. Any changes to it are only + /// perceived if it's stored back. You own the setting value and are + /// responsible for freeing it. + pub fn settings_map_get( + map: SettingsMap, + key_ptr: *const u8, + key_len: usize, + ) -> Option; + + /// Creates a new boolean setting value. You own the setting value and are + /// responsible for freeing it. + pub fn setting_value_new_bool(value: bool) -> SettingValue; + /// Frees a setting value. + pub fn setting_value_free(value: SettingValue); + /// Gets the value of a boolean setting value by storing it into the pointer + /// provided. Returns `false` if the setting value is not a boolean. No + /// value is stored into the pointer in that case. No matter what happens, + /// you still retain ownership of the setting value, which means you still + /// need to free it. + pub fn setting_value_get_bool(value: SettingValue, value_ptr: *mut bool) -> bool; }