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) -> &MtDti {
26        unsafe { &*(self.get_dti() as *const MtDti) }
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 as *mut MtPropertyList as *mut c_void);
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    fn dtor(&mut self) {
65        let func: unsafe extern "C" fn(&mut Self) = unsafe { transmute(self.get_virtual_function(0)) };
66        unsafe { func(self) }
67    }
68
69    /// Runs the destructor on `self` and deallocates the object
70    fn destroy(&mut self) {
71        let func: unsafe extern "C" fn(&mut Self) = unsafe { transmute(self.get_virtual_function(1)) };
72        unsafe { func(self) }
73    }
74
75    /// Invokes the object's UI-creation virtual function (vtable slot 2).
76    fn create_ui(&self) {
77        let func: unsafe extern "C" fn(&Self) = unsafe { transmute(self.get_virtual_function(2)) };
78        unsafe { func(self) }
79    }
80
81    /// Returns whether the object is currently an enabled instance (vtable slot
82    /// 3).
83    fn is_enable_instance(&self) -> bool {
84        let func: unsafe extern "C" fn(&Self) -> bool = unsafe { transmute(self.get_virtual_function(3)) };
85        unsafe { func(self) }
86    }
87
88    /// Populates `props` (an [`MtPropertyList`]) with the object's reflected
89    /// properties (vtable slot 4).
90    ///
91    /// Prefer the safe [`MtObject::get_properties`] wrapper, which builds and
92    /// returns the list for you.
93    fn create_property(&self, props: *mut c_void) {
94        let func: unsafe extern "C" fn(&Self, *mut c_void) = unsafe { transmute(self.get_virtual_function(4)) };
95        unsafe { func(self, props) }
96    }
97
98    /// Returns a raw pointer to the object's [`MtDti`] (vtable slot 5).
99    ///
100    /// Prefer the safe [`MtObject::dti`] wrapper, which returns a typed
101    /// reference.
102    fn get_dti(&self) -> *const c_void {
103        let func: unsafe extern "C" fn(&Self) -> *const c_void = unsafe { transmute(self.get_virtual_function(5)) };
104        unsafe { func(self) }
105    }
106}