Skip to main content

forge/mt/
object.rs

1use core::{ffi::c_void, mem::transmute};
2
3use macros::Object;
4use sys::cpp::HasVtable;
5
6use crate::mt::{dti::MtDti, property_list::MtPropertyList};
7
8/// The root class of MT Framework's object system (`MtObject`).
9///
10/// Nearly every game class derives from `MtObject`; it provides the reflection
11/// machinery shared by all of them - a link to the class's [`MtDti`] and the
12/// ability to enumerate its reflected properties as an [`MtPropertyList`]. The
13/// shared virtual-function behaviour (destructors, construction, property
14/// creation, DTI access) lives in the [`Object`] trait, which this type
15/// implements via `#[derive(Object)]`.
16///
17/// Instances are owned by the game; you work with them through references,
18/// usually after obtaining one from the game or constructing a concrete
19/// subclass through its [`MtDti`].
20#[derive(Object)]
21pub struct MtObject;
22
23impl MtObject {
24    /// Returns the [`MtDti`] describing this object's concrete class.
25    pub fn dti(&self) -> &'static MtDti {
26        self.get_dti()
27    }
28
29    /// Collects this object's reflected properties into an [`MtPropertyList`].
30    ///
31    /// Invokes the object's property-creation virtual function to populate a
32    /// fresh list, which can then be iterated to inspect each property.
33    pub fn get_properties(&self) -> MtPropertyList {
34        let mut props = MtPropertyList::new();
35        self.create_property(&mut props);
36        props
37    }
38
39    /// Reinterprets this object as a shared reference to the subclass `T`.
40    ///
41    /// This is an unchecked downcast; the caller must ensure the object is
42    /// actually a `T` (e.g. via [`MtDti::is_a`](crate::mt::dti::MtDti::is_a)).
43    pub fn to<T: Object>(&self) -> &T {
44        unsafe { &*(self as *const Self as *const T) }
45    }
46
47    /// Reinterprets this object as a mutable reference to the subclass `T`.
48    ///
49    /// This is an unchecked downcast; the caller must ensure the object is
50    /// actually a `T` (e.g. via [`MtDti::is_a`](crate::mt::dti::MtDti::is_a)).
51    pub fn to_mut<T: Object>(&mut self) -> &mut T {
52        unsafe { &mut *(self as *mut Self as *mut T) }
53    }
54}
55
56/// Behaviour shared by every MT Framework object, backed by the class's vtable.
57///
58/// This trait exposes the common virtual functions found at the start of every
59/// `MtObject` vtable. It is implemented for concrete object types via
60/// `#[derive(Object)]`; each method dispatches through the corresponding vtable
61/// slot, so calling one runs the game's own implementation for that object.
62pub trait Object: HasVtable {
63    /// Runs the destructor on `self`
64    ///
65    /// # Safety
66    /// `self` is in an undefined state after this function is called.
67    /// It is not safe to continue using the object.
68    fn dtor(&mut self) {
69        let func: unsafe extern "C" fn(&mut Self) = unsafe { transmute(self.get_virtual_function(0)) };
70        unsafe { func(self) }
71    }
72
73    /// Runs the destructor on `self` and deallocates the object
74    ///
75    /// # Safety
76    /// `self` is in an undefined state after this function is called.
77    /// It is not safe to continue using the object.
78    fn destroy(&mut self) {
79        let func: unsafe extern "C" fn(&mut Self) = unsafe { transmute(self.get_virtual_function(1)) };
80        unsafe { func(self) }
81    }
82
83    /// Invokes the object's UI-creation virtual function.
84    ///
85    /// Note: Always unimplemented.
86    fn create_ui(&self) {
87        let func: unsafe extern "C" fn(&Self) = unsafe { transmute(self.get_virtual_function(2)) };
88        unsafe { func(self) }
89    }
90
91    /// Returns whether the object is enabled
92    fn is_enable_instance(&self) -> bool {
93        let func: unsafe extern "C" fn(&Self) -> bool = unsafe { transmute(self.get_virtual_function(3)) };
94        unsafe { func(self) }
95    }
96
97    /// Populates `props` with the object's reflected properties.
98    ///
99    /// Prefer the safe [`MtObject::get_properties`] wrapper, which builds and
100    /// returns the list for you.
101    fn create_property(&self, props: &mut MtPropertyList) {
102        let func: unsafe extern "C" fn(&Self, *mut MtPropertyList) = unsafe { transmute(self.get_virtual_function(4)) };
103        unsafe { func(self, props as *mut MtPropertyList) }
104    }
105
106    /// Returns a reference to the object's [`MtDti`].
107    ///
108    /// Equivalent to the [`MtObject::dti`] wrapper.
109    fn get_dti(&self) -> &'static MtDti {
110        let func: unsafe extern "C" fn(&Self) -> *const c_void = unsafe { transmute(self.get_virtual_function(5)) };
111        let dti = unsafe { func(self) };
112        unsafe { &*(dti as *const MtDti) }
113    }
114
115    /// Converts this object to a reference to an [`MtObject`].
116    fn to_mt_object(&self) -> &MtObject {
117        let ptr = self as *const Self as *const MtObject;
118        unsafe { &*ptr }
119    }
120
121    /// Converts this object to a mutable reference to an [`MtObject`].
122    fn to_mut_mt_object(&mut self) -> &mut MtObject {
123        let ptr = self as *mut Self as *mut MtObject;
124        unsafe { &mut *ptr }
125    }
126
127    /// Reads a value of type `T` from `self` at the given byte offset.
128    ///
129    /// # Safety
130    /// The caller must ensure that `self` is valid for reads of type `T` at the given offset.
131    /// `offset` must be a valid byte offset within `self`.
132    unsafe fn read<T>(&self, offset: usize) -> T {
133        let ptr = self as *const Self as *const u8;
134        unsafe { (ptr.add(offset) as *const T).read_unaligned() }
135    }
136
137    /// Writes a value of type `T` to `self` at the given byte offset.
138    ///
139    /// # Safety
140    /// The caller must ensure that `self` is valid for writes of type `T` at the given offset.
141    /// `offset` must be a valid byte offset within `self`.
142    unsafe fn write<T: Copy>(&mut self, offset: usize, value: &T) {
143        let ptr = self as *mut Self as *mut u8;
144        unsafe { *(ptr.add(offset) as *mut T) = *value };
145    }
146
147    /// Returns a reference to a value of type `T` within `self` at the given byte offset.
148    ///
149    /// # Safety
150    /// The caller must ensure that `self` is valid for reads of type `T` at the given offset.
151    /// `offset` must be a valid byte offset within `self`.
152    unsafe fn get_ref<T>(&self, offset: usize) -> &T {
153        let ptr = self as *const Self as *const u8;
154        unsafe { &*(ptr.add(offset) as *const T) }
155    }
156
157    /// Returns a mutable reference to a value of type `T` within `self` at the given byte offset.
158    ///
159    /// # Safety
160    /// The caller must ensure that `self` is valid for writes of type `T` at the given offset.
161    /// `offset` must be a valid byte offset within `self`.
162    unsafe fn get_mut_ref<T>(&mut self, offset: usize) -> &mut T {
163        let ptr = self as *mut Self as *mut u8;
164        unsafe { &mut *(ptr.add(offset) as *mut T) }
165    }
166}