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}