Skip to main content

il2cpp_bridge_rs/structs/core/members/
field.rs

1//! IL2CPP Field definition and functionality
2use crate::api;
3use crate::structs::core::{Class, Type, ValueType};
4use std::ffi::c_void;
5
6/// Hydrated IL2CPP field metadata plus optional bound instance state.
7#[derive(Debug, Clone)]
8pub struct Field {
9    /// Pointer to the internal IL2CPP field structure
10    pub address: *mut c_void,
11    /// Name of the field
12    pub name: String,
13    /// Type information of the field
14    pub type_info: Type,
15    /// Class defining this field
16    pub class: Option<*const Class>,
17    /// Offset of the field within the instance
18    pub offset: i32,
19    /// Flags associated with the field
20    pub flags: i32,
21    /// Whether the field is static
22    pub is_static: bool,
23    /// Whether the field is constant (literal)
24    pub is_literal: bool,
25    /// Whether the field is readonly (init only)
26    pub is_readonly: bool,
27    /// Whether the field is not serialized
28    pub is_not_serialized: bool,
29    /// Whether the field has a special name
30    pub is_special_name: bool,
31    /// Whether the field is pinvoke impl
32    pub is_pinvoke_impl: bool,
33    /// Instance pointer for instance fields
34    pub instance: Option<*mut c_void>,
35}
36
37unsafe impl Send for Field {}
38unsafe impl Sync for Field {}
39
40impl std::fmt::Display for Field {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        write!(f, "{}", self.fmt_field())
43    }
44}
45
46impl Field {
47    /// Generates a string representation of the field, including its value if static
48    ///
49    /// # Returns
50    /// * `String` - The formatted string representation
51    fn fmt_field(&self) -> String {
52        let access = self.get_attribute();
53        let qualifier = if self.is_literal {
54            "const ".to_string()
55        } else {
56            let mut s = String::new();
57            if self.is_static {
58                s.push_str("static ");
59            }
60            if self.is_readonly {
61                s.push_str("readonly ");
62            }
63            s
64        };
65
66        format!(
67            "{} {}{} {}; // 0x{:X}",
68            access,
69            qualifier,
70            self.type_info.cpp_name(),
71            self.name,
72            self.offset
73        )
74    }
75
76    /// Reads the current field value.
77    ///
78    /// For instance fields, the field must have a bound instance pointer. Use
79    /// [`crate::structs::Object::field`] to obtain one.
80    pub unsafe fn get_value<T: Copy + 'static>(&self) -> Result<T, String> {
81        if self.is_static {
82            if let Some(class) = self.class {
83                if (*class).is_generic {
84                    return Err("Cannot read static field of generic class".to_string());
85                }
86            }
87
88            if std::any::TypeId::of::<T>() == std::any::TypeId::of::<ValueType>() {
89                let class = self
90                    .class
91                    .ok_or_else(|| "Field does not have a parent class reference".to_string())?;
92
93                let static_data = api::class_get_static_field_data((*class).address);
94                if static_data.is_null() {
95                    return Err("Static field data is null".to_string());
96                }
97
98                let address = static_data as usize + self.offset as usize;
99
100                let field_type = api::field_get_type(self.address);
101                let type_class = api::class_from_type(field_type);
102
103                if type_class.is_null() {
104                    return Err("Failed to resolve field type class".to_string());
105                }
106
107                let vt = ValueType::from_ptr_with_class(address as *mut c_void, type_class);
108                return Ok(std::ptr::read(&vt as *const _ as *const T));
109            }
110
111            let mut value: T = std::mem::zeroed();
112            api::field_static_get_value(self.address, &mut value as *mut T as *mut c_void);
113            Ok(value)
114        } else {
115            let instance = self.instance.ok_or_else(|| {
116                format!(
117                    "Field '{}' is an instance field but no instance was provided. Use Object::field or set the instance manually.",
118                    self.name
119                )
120            })?;
121
122            if std::any::TypeId::of::<T>() == std::any::TypeId::of::<ValueType>() {
123                let field_type = api::field_get_type(self.address);
124                if field_type.is_null() {
125                    return Err("Failed to get field type".to_string());
126                }
127                let type_class = api::class_from_type(field_type);
128                if type_class.is_null() {
129                    return Err("Failed to resolve class from field type".to_string());
130                }
131
132                let address = instance as usize + self.offset as usize;
133                let vt = ValueType::from_ptr_with_class(address as *mut c_void, type_class);
134                return Ok(std::ptr::read(&vt as *const _ as *const T));
135            }
136
137            let address = instance as usize + self.offset as usize;
138            crate::memory::rw::read(address).map_err(|e| e.to_string())
139        }
140    }
141
142    /// Writes a value to the field.
143    ///
144    /// For instance fields, the field must have a bound instance pointer. Use
145    /// [`crate::structs::Object::field`] to obtain one.
146    pub unsafe fn set_value<T: Copy>(&self, value: T) -> Result<(), String> {
147        if self.is_static {
148            api::field_static_set_value(self.address, &value as *const T as *mut c_void);
149            Ok(())
150        } else {
151            let instance = self.instance.ok_or_else(|| {
152                format!(
153                    "Field '{}' is an instance field but no instance was provided. Use Object::field or set the instance manually.",
154                    self.name
155                )
156            })?;
157
158            let address = instance as usize + self.offset as usize;
159            crate::memory::rw::write(address, value).map_err(|e| e.to_string())
160        }
161    }
162
163    /// Gets the access modifier attribute string
164    fn get_attribute(&self) -> &'static str {
165        match self.flags & api::FIELD_ATTRIBUTE_FIELD_ACCESS_MASK {
166            api::FIELD_ATTRIBUTE_PRIVATE => "private",
167            api::FIELD_ATTRIBUTE_PUBLIC => "public",
168            api::FIELD_ATTRIBUTE_FAMILY => "protected",
169            api::FIELD_ATTRIBUTE_ASSEMBLY => "internal",
170            api::FIELD_ATTRIBUTE_FAM_AND_ASSEM => "private protected",
171            api::FIELD_ATTRIBUTE_FAM_OR_ASSEM => "protected internal",
172            _ => "private",
173        }
174    }
175
176    /// Gets the parent class of the field
177    ///
178    /// # Returns
179    /// * `*mut c_void` - Pointer to the parent class structure
180    pub fn get_parent(&self) -> *mut c_void {
181        unsafe { api::field_get_parent(self.address) }
182    }
183}