use std::ptr::NonNull;
use pelite::pe64::{Pe, Rva, Va, msvc::RTTICompleteObjectLocator};
use thiserror::Error;
use crate::{Program, is_base_class};
#[derive(Error, Debug)]
#[error("superclass is not an instance of {0}")]
pub struct TryFromSuperclassError(String);
impl TryFromSuperclassError {
pub fn new(subclass: String) -> Self {
TryFromSuperclassError(subclass)
}
}
unsafe fn complete_object_locator(vmt: Va) -> &'static RTTICompleteObjectLocator {
let va = vmt - size_of::<Va>() as Va;
unsafe { &**(va as *const *const _) }
}
pub unsafe trait Superclass: Sized {
fn vmt_rva() -> Rva;
fn vmt_va() -> Va {
Program::current()
.rva_to_va(Self::vmt_rva())
.expect("VMT address not in executable!")
}
fn vmt(&self) -> Va {
*unsafe { NonNull::from_ref(self).cast::<Va>().as_ref() }
}
fn is_subclass<T: Subclass<Self>>(&self) -> bool {
let instance_vmt = self.vmt();
let subclass_vmt = T::vmt_va();
if subclass_vmt == instance_vmt {
return true;
}
let instance_col = unsafe { complete_object_locator(instance_vmt) };
let subclass_col = unsafe { complete_object_locator(subclass_vmt) };
is_base_class(&Program::current(), subclass_col, instance_col).unwrap_or(false)
}
fn as_subclass<T: Subclass<Self>>(&self) -> Option<&T> {
if self.is_subclass::<T>() {
Some(unsafe { NonNull::from_ref(self).cast::<T>().as_ref() })
} else {
None
}
}
fn as_subclass_mut<T: Subclass<Self>>(&mut self) -> Option<&mut T> {
if self.is_subclass::<T>() {
Some(unsafe { NonNull::from_ref(self).cast::<T>().as_mut() })
} else {
None
}
}
}
pub unsafe trait Subclass<T: Superclass> {
fn vmt_rva() -> Rva;
fn vmt_va() -> Va {
Program::current()
.rva_to_va(Self::vmt_rva())
.expect("VMT address not in executable!")
}
fn superclass(&self) -> &T {
unsafe { NonNull::from_ref(self).cast::<T>().as_ref() }
}
fn superclass_mut(&mut self) -> &mut T {
unsafe { NonNull::from_ref(self).cast::<T>().as_mut() }
}
}
unsafe impl<T> Subclass<T> for T
where
T: Superclass,
{
fn vmt_rva() -> Rva {
<T as Superclass>::vmt_rva()
}
}