commonlibsse_ng\re\s/
Setting.rs

1//! # Setting
2//!
3//! This module defines the `Setting` struct, representing various game settings
4//! with multiple data types such as boolean, float, integer, color, string, and unsigned integer.
5//! It safely encapsulates a union for value storage and provides a typed API to interact with it.
6
7use crate::re::Color::Color;
8use crate::re::MemoryManager::free;
9use crate::re::offsets_rtti::RTTI_Setting;
10use crate::re::offsets_vtable::VTABLE_Setting;
11use crate::rel::id::VariantID;
12use core::ffi::{CStr, c_char};
13use core::{fmt, ptr};
14
15/// Represents a game setting with multiple data types.
16///
17/// # Memory Layout:
18/// - `data`: Union containing the setting value (0x08)
19/// - `name`: Pointer to the setting's name (0x10)
20#[repr(C)]
21#[derive(Clone)]
22pub struct Setting {
23    pub vtable: *const SettingVtbl,
24    data: Data,
25    name: *mut c_char,
26}
27const _: () = {
28    assert!(core::mem::offset_of!(Setting, data) == 0x08);
29    assert!(core::mem::offset_of!(Setting, name) == 0x10);
30    assert!(core::mem::size_of::<Setting>() == 0x18);
31};
32
33impl Default for Setting {
34    fn default() -> Self {
35        unsafe { core::mem::zeroed() }
36    }
37}
38
39impl fmt::Debug for Setting {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        f.debug_struct("Setting")
42            .field("vtable", &self.vtable)
43            .field("name", &self.get_name().map(|name| name.to_str().unwrap_or("Invalid Name")))
44            .field("data", &self.get_value())
45            .finish()
46    }
47}
48
49impl PartialEq for Setting {
50    fn eq(&self, other: &Self) -> bool {
51        self.get_type() == other.get_type() && self.get_value() == other.get_value()
52    }
53}
54
55/// Represents the value type of a `Setting`.
56#[repr(u32)]
57#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
58pub enum Type {
59    #[default]
60    Unknown = 0,
61    Bool,
62    Float,
63    SignedInteger,
64    Color,
65    String,
66    UnsignedInteger,
67}
68
69/// A safe wrapper for accessing the value in a `Setting`.
70#[derive(Default, Clone, PartialEq)]
71pub enum SettingValue<'a> {
72    Bool(bool),
73    Float(f32),
74    SignedInteger(i32),
75    Color(Color),
76    /// Maybe utf-8
77    String(&'a CStr),
78    UnsignedInteger(u32),
79    #[default]
80    Unknown,
81}
82
83impl fmt::Debug for SettingValue<'_> {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match self {
86            Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
87            Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
88            Self::SignedInteger(arg0) => f.debug_tuple("SignedInteger").field(arg0).finish(),
89            Self::Color(arg0) => f.debug_tuple("Color").field(arg0).finish(),
90            Self::String(arg0) => f.debug_tuple("String").field(&arg0.to_str()).finish(),
91            Self::UnsignedInteger(arg0) => f.debug_tuple("UnsignedInteger").field(arg0).finish(),
92            Self::Unknown => write!(f, "Unknown"),
93        }
94    }
95}
96
97/// Union representing various setting values.
98///
99/// field name => value type
100#[repr(C)]
101#[derive(Clone, Copy)]
102union Data {
103    pub b: bool,
104    pub f: f32,
105    pub i: i32,
106    pub r: Color,
107    pub s: *const c_char,
108    pub u: u32,
109}
110const _: () = assert!(core::mem::size_of::<Data>() == 0x8);
111
112impl Setting {
113    /// Runtime type info offset for this type.
114    pub const RTTI: VariantID = RTTI_Setting;
115
116    /// Virtual table offset.
117    pub const VTABLE: [VariantID; 1] = VTABLE_Setting;
118
119    /// Checks whether the setting is managed (i.e., dynamically allocated).
120    #[inline]
121    pub const fn is_managed(&self) -> bool {
122        !self.name.is_null() && unsafe { self.name.read() } == b'S' as i8
123    }
124
125    /// Returns the type of the setting based on the name prefix.
126    #[inline]
127    pub const fn get_type(&self) -> Type {
128        match unsafe { self.name.read() } as u8 {
129            b'b' => Type::Bool,
130            b'f' => Type::Float,
131            b'i' => Type::SignedInteger,
132            b'r' => Type::Color,
133            b's' | b'S' => Type::String,
134            b'u' => Type::UnsignedInteger,
135            _ => Type::Unknown,
136        }
137    }
138
139    /// Returns the name as `&CStr` if available.
140    ///
141    /// e.g. key: `"sOk"` -> value: `"OK"` then, return `"sOk`
142    ///
143    /// # Errors
144    /// Returns `None` if the pointer is null.
145    #[inline]
146    pub const fn get_name(&self) -> Option<&CStr> {
147        if self.name.is_null() {
148            return None;
149        }
150        unsafe { Some(CStr::from_ptr(self.name)) }
151    }
152
153    /// Returns the value as a typed enum.
154    #[inline]
155    pub const fn get_value(&self) -> SettingValue<'_> {
156        if self.name.is_null() {
157            return SettingValue::Unknown;
158        }
159
160        unsafe {
161            match self.get_type() {
162                Type::Bool => SettingValue::Bool(self.data.b),
163                Type::Float => SettingValue::Float(self.data.f),
164                Type::SignedInteger => SettingValue::SignedInteger(self.data.i),
165                Type::Color => SettingValue::Color(self.data.r),
166                Type::String => {
167                    let s = self.data.s;
168                    if s.is_null() {
169                        SettingValue::Unknown
170                    } else {
171                        SettingValue::String(CStr::from_ptr(s))
172                    }
173                }
174                Type::UnsignedInteger => SettingValue::UnsignedInteger(self.data.u),
175                Type::Unknown => SettingValue::Unknown,
176            }
177        }
178    }
179}
180
181impl Drop for Setting {
182    fn drop(&mut self) {
183        ((unsafe { &*self.vtable }).CxxDrop)(self);
184    }
185}
186
187/// Virtual function table for `Setting`.
188#[repr(C)]
189pub struct SettingVtbl {
190    pub CxxDrop: fn(this: &mut Setting),
191    pub Unk_01: fn(this: &mut Setting) -> bool,
192}
193
194impl Default for SettingVtbl {
195    #[inline]
196    fn default() -> Self {
197        Self::new()
198    }
199}
200
201impl SettingVtbl {
202    #[inline]
203    pub const fn new() -> Self {
204        fn CxxDrop(this: &mut Setting) {
205            if this.is_managed() {
206                unsafe { free(this.name.cast()) };
207                this.name = ptr::null_mut();
208            }
209        }
210        const fn Unk_01(_this: &mut Setting) -> bool {
211            false
212        }
213        Self { CxxDrop, Unk_01 }
214    }
215}