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}