commonlibsse_ng\re/
rtti.rs

1//! # RTTI Structure Overview
2//!
3//! ASCII art representing the RTTI (Run-Time Type Information) structure in C++.
4//!
5//! - C++ src: https://godbolt.org/z/xn5f96E9s
6//! -  AA ref: https://www.openrce.org/articles/full_view/23
7//!
8//! ```text
9//!                               +------------------+
10//!              +-------------+  | A::RTTI Complete |
11//! +---------+  | A::vftable  |  | Object Locator   |
12//! | class A |  |-------------|  |------------------|
13//! |---------|  | &A_meta     |->| signature (0)    |         +-------------------------+   +-------------------------------+
14//! | _vfptr  |->| &A::A_virt1 |  | offset (0)       |         | A::RTTI Type Descriptor |   | A::RTTI Base Class Descriptor |
15//! | a1      |  | &A::A_virt2 |  | cdOffset (0)     |         |-------------------------|   |-------------------------------|
16//! +---------+  +-------------+  | pTypeDescriptor  |-------->| pVFTable                |-->| pTypeDescriptor               |<-------+
17//!                               | pClassDescriptor |---+     | spare                   |   | numContainedBases (0)         |        |
18//!                               +------------------+   |     | name (?AVA@@)           |   | PMD where (0, -1, 0)          |        |
19//!                                                      |     +-------------------------+   | attributes (0)                |        |
20//!                                                      |                                   +-------------------------------+        |
21//!                                                      |     +------------------------------------+                                 |
22//!                                                      |     | A::RTTI Class Hierarchy Descriptor |                                 |
23//!                                                      |     |------------------------------------|                                 |
24//!                                                      +---->| signature (0)                      |   +--------------------------+  |
25//!                                                            | attributes (0)                     |   | A::RTTI Base Class Array |  |
26//!                                                            | numBaseClasses (1)                 |   |--------------------------|  |
27//!                                                            | pBaseClassArray                    |-->| &A_BCD                   |--+
28//!                                                            +------------------------------------+   +--------------------------+
29//!
30//!                               +------------------+
31//!              +-------------+  | B::RTTI Complete |
32//! +---------+  | B::vftable  |  | Object Locator   |
33//! | class B |  |-------------|  |------------------|
34//! |---------|  | &B_meta     |->| signature (0)    |         +-------------------------+   +-------------------------------+
35//! | _vfptr  |->| &B::B_virt1 |  | offset (0)       |         | B::RTTI Type Descriptor |   | B::RTTI Base Class Descriptor |
36//! | b1      |  | &B::B_virt2 |  | cdOffset (0)     |         |-------------------------|   |-------------------------------|
37//! | b2      |  +-------------+  | pTypeDescriptor  |-------->| pVFTable                |-->| pTypeDescriptor               |<-------+
38//! +---------+                   | pClassDescriptor |---+     | spare                   |   | numContainedBases (0)         |        |
39//!                               +------------------+   |     | name (?AVA@@)           |   | PMD where (0, -1, 0)          |        |
40//!                                                      |     +-------------------------+   | attributes (0)                |        |
41//!                                                      |                                   +-------------------------------+        |
42//!                                                      |     +------------------------------------+                                 |
43//!                                                      |     | B::RTTI Class Hierarchy Descriptor |                                 |
44//!                                                      |     |------------------------------------|                                 |
45//!                                                      +---->| signature (0)                      |   +--------------------------+  |
46//!                                                            | attributes (0)                     |   | B::RTTI Base Class Array |  |
47//!                                                            | numBaseClasses (1)                 |   |--------------------------|  |
48//!                                                            | pBaseClassArray                    |-->| &B_BCD                   |--+
49//!                                                            +------------------------------------+   +--------------------------+
50//!
51//!
52//!                                         +----------------------+
53//!                 +------------------+    | C::RTTI Complete     |
54//! +----------+    | C::vftable for A |    | Object Locator for A |
55//! | class C  |    |------------------|    |----------------------|
56//! |----------|    | &C_meta_A        |--->| signature (0)        |      +-------------------------+   +-------------------------------+
57//! | _vfptr_A |--> | &C::A_virt1      |    | offset (0)           |      | C::RTTI Type Descriptor |   | C::RTTI Base Class Descriptor |
58//! | a1       |    | &C::A_virt2      |    | cdOffset (0)         |      |-------------------------|   |-------------------------------|
59//! | _vfptr_B |-+  +------------------+    | pTypeDescriptor      |----->| pVFTable                |-->| pTypeDescriptor               |
60//! | b1       | |                          | pClassDescriptor     |--+   | spare                   |   | numContainedBases (2)         |
61//! | b2       | |                          +----------------------+  |   | name (?AVC@@)           |   | PMD where (8, -1, 0)          |
62//! | c1       | |                          +----------------------+  |   +-------------------------+   | attributes (0)                |
63//! +----------+ |   +------------------+   | C::RTTI Complete     |  |                                 +-------------------------------+
64//!              |   | C::vftable for B |   | Object Locator for B |  |
65//!              |   |------------------|   |----------------------|  |
66//!              +-> | &C_meta_A        |-->| signature (0)        |  |
67//!                  | &C::A_virt1      |   | offset (8)           |  |  +----------------------+
68//!                  | &C::A_virt2      |   | cdOffset (0)         |  |  | C::RTTI Class        |
69//!                  +------------------+   | pTypeDescriptor      |  |  | Hierarchy Descriptor |
70//!                                         | pClassDescriptor     |  |  |----------------------|
71//!                                         +----------------------+  +->| signature (0)        |  +------------------------------------+
72//!                                                                      | attributes (1)       |  | C::RTTI Base Class Array           |
73//!                                                                      | numBaseClasses (3)   |  |------------------------------------|
74//!                                                                      | pBaseClassArray      |->| &C_BCD -> C::RTTI Base Class Desc. |
75//!                                                                      +----------------------+  | &A_BCD -> A::RTTI Base Class Desc. |
76//!                                                                                                | &B_BCD -> B::RTTI Base Class Desc. |
77//!                                                                                                +------------------------------------+
78//! ```
79
80use crate::re::CxxVirtClass;
81use crate::rel::ResolvableAddress as _;
82use crate::rel::id::DataBaseError;
83use core::ffi::c_void;
84use core::marker::PhantomData;
85use core::ptr::NonNull;
86
87pub mod msvc {
88    use core::ffi::c_char;
89
90    #[repr(C)]
91    #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
92    pub struct TypeInfo {
93        // This field is hidden ptr to virtual function table.
94        __vtbl: &'static TypeInfoVTable, // 0
95
96        _data: *mut std::ffi::c_void, // 08
97        _name: [c_char; 1],           // 10
98    }
99
100    #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
101    struct TypeInfoVTable {
102        /// C++ Destructor
103        delete: fn(), // 00
104    }
105
106    const _: () = {
107        assert!(0x08 == core::mem::offset_of!(TypeInfo, _data));
108        assert!(0x10 == core::mem::offset_of!(TypeInfo, _name));
109        assert!(core::mem::size_of::<TypeInfo>() == 0x18);
110    };
111
112    impl TypeInfo {
113        /// Get mangled name of this class.
114        pub const fn mangled_name(&self) -> *const c_char {
115            self._name.as_ptr()
116        }
117    }
118}
119
120#[repr(C)]
121#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
122pub struct RVA<T> {
123    _rva: u32, // 00
124    _maker: PhantomData<T>,
125}
126
127const _: () = {
128    assert!(core::mem::size_of::<RVA<*const c_void>>() == 0x4);
129};
130
131impl<T> RVA<T> {
132    pub const fn new(rva: u32) -> Self {
133        Self { _rva: rva, _maker: PhantomData::<T> }
134    }
135
136    pub fn get(&self) -> Option<NonNull<T>> {
137        use crate::rel::offset::Offset;
138
139        if self.is_good() {
140            Some(Offset::new(self._rva as usize).address().ok()?.cast())
141        } else {
142            None
143        }
144    }
145
146    pub const fn offset(&self) -> u32 {
147        self._rva
148    }
149
150    const fn is_good(&self) -> bool {
151        self._rva != 0
152    }
153}
154
155#[repr(C)]
156#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
157pub struct PMD {
158    m_disp: i32, // 0
159    p_disp: i32, // 4
160    v_disp: i32, // 8
161}
162
163const _: () = {
164    assert!(0x00 == core::mem::offset_of!(PMD, m_disp));
165    assert!(0x04 == core::mem::offset_of!(PMD, p_disp));
166    assert!(0x08 == core::mem::offset_of!(PMD, v_disp));
167
168    assert!(core::mem::size_of::<PMD>() == 0xc);
169};
170
171bitflags::bitflags! {
172    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
173    struct BaseClassDescriptorAttribute: u32 {
174        const NONE = 0;
175        const NOT_VISIBLE = 1 << 0;
176        const Ambiguous = 1 << 1;
177        const PRIVATE = 1 << 2;
178        const PRIVATE_OR_PROTECTED_BASE = 1 << 3;
179        const VIRTUAL = 1 << 4;
180        const NON_POLYMORPHIC = 1 << 5;
181        const HAS_HIERARCHY_DESCRIPTOR = 1 << 6;
182    }
183}
184
185#[repr(C)]
186#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
187pub struct BaseClassDescriptor {
188    type_descriptor: RVA<msvc::TypeInfo>,     // 00
189    num_contained_bases: u32,                 // 04
190    pmd: PMD,                                 // 08
191    attributes: BaseClassDescriptorAttribute, // 14
192}
193
194const _: () = {
195    assert!(0x00 == core::mem::offset_of!(BaseClassDescriptor, type_descriptor));
196    assert!(0x04 == core::mem::offset_of!(BaseClassDescriptor, num_contained_bases));
197    assert!(0x08 == core::mem::offset_of!(BaseClassDescriptor, pmd));
198    assert!(0x14 == core::mem::offset_of!(BaseClassDescriptor, attributes));
199
200    assert!(core::mem::size_of::<BaseClassDescriptor>() == 0x18);
201};
202
203bitflags::bitflags! {
204    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
205    struct ClassHierarchyDescriptorAttribute: u32 {
206        const NO_INHERITANCE        = 0;
207        const MULTIPLE_INHERITANCE  = 1 << 0;
208        const VIRTUAL_INHERITANCE   = 1 << 1;
209        const AMBIGUOUS_INHERITANCE = 1 << 2;
210    }
211}
212
213#[repr(C)]
214#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
215pub struct ClassHierarchyDescriptor {
216    signature: u32,                                // 00
217    attributes: ClassHierarchyDescriptorAttribute, // 04
218    num_base_classes: u32,                         // 08
219    base_class_array: RVA<BaseClassDescriptor>,    // 0C
220}
221
222const _: () = {
223    assert!(0x00 == core::mem::offset_of!(ClassHierarchyDescriptor, signature));
224    assert!(0x04 == core::mem::offset_of!(ClassHierarchyDescriptor, attributes));
225    assert!(0x08 == core::mem::offset_of!(ClassHierarchyDescriptor, num_base_classes));
226    assert!(0x0C == core::mem::offset_of!(ClassHierarchyDescriptor, base_class_array));
227
228    assert!(core::mem::size_of::<ClassHierarchyDescriptor>() == 0x10);
229};
230
231#[repr(C)]
232#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
233pub struct CompleteObjectLocator {
234    signature: Signature,                            // 00
235    offset: u32,                                     // 04
236    ctor_disp_offset: u32,                           // 08
237    type_descriptor: RVA<msvc::TypeInfo>,            // 0C
238    class_descriptor: RVA<ClassHierarchyDescriptor>, // 10
239}
240
241#[repr(u32)]
242#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
243pub enum Signature {
244    X86 = 0,
245    X64 = 1,
246}
247
248const _: () = {
249    assert!(0x00 == core::mem::offset_of!(CompleteObjectLocator, signature));
250    assert!(0x04 == core::mem::offset_of!(CompleteObjectLocator, offset));
251    assert!(0x08 == core::mem::offset_of!(CompleteObjectLocator, ctor_disp_offset));
252    assert!(0x0C == core::mem::offset_of!(CompleteObjectLocator, type_descriptor));
253    assert!(0x10 == core::mem::offset_of!(CompleteObjectLocator, class_descriptor));
254
255    assert!(core::mem::size_of::<CompleteObjectLocator>() == 0x14);
256};
257
258/// RTTI Dynamic Cast function
259///
260/// # Safety
261/// # Errors
262#[commonlibsse_ng_derive_internal::relocate_fn(se_id = 102238, ae_id = 109689, vr_id = 102238)]
263pub unsafe fn rt_dynamic_cast(
264    in_ptr: *mut c_void,
265    vf_delta: i32,
266    src_type: *mut c_void,
267    target_type: *mut c_void,
268    is_reference: i32,
269) -> *mut c_void {
270}
271
272// TODO: Write tests
273// FIXME: remove unwrap
274/// # Safety
275/// # Errors
276/// # Panics
277pub unsafe fn skyrim_cast<To, From>(from: *mut From) -> Result<*mut To, DataBaseError>
278where
279    From: 'static + CxxVirtClass,
280    To: 'static + CxxVirtClass,
281{
282    use crate::rel::relocation::Relocation;
283    let from_rtti = Relocation::new(From::rtti().address()?).cast::<c_void>();
284    let to_rtti = Relocation::new(To::rtti().address()?).cast::<c_void>();
285
286    Ok(unsafe {
287        rt_dynamic_cast(from.cast::<c_void>(), 0, from_rtti.as_ptr(), to_rtti.as_ptr(), 0)
288    }
289    .cast())
290}