use std::{mem, ptr};
use crate::raw::root::RED4ext as red;
use crate::types::{
Bitfield, CName, Class, ClassFlags, ClassHandle, Enum, Function, GameEngine, GlobalFunction,
PoolRef, RedArray, RedHashMap, Ref, RwSpinLockReadGuard, RwSpinLockWriteGuard,
ScriptableSystem, Type,
};
#[repr(transparent)]
pub struct RttiSystem(red::CRTTISystem);
impl RttiSystem {
#[inline]
pub fn get<'a>() -> RwSpinLockReadGuard<'a, Self> {
unsafe {
let rtti = red::CRTTISystem_Get();
let lock = &(*rtti).typesLock;
RwSpinLockReadGuard::new(lock, ptr::NonNull::new_unchecked(rtti as _))
}
}
#[inline]
pub fn get_class(&self, name: CName) -> Option<&Class> {
let ty = unsafe { (self.vft().get_class)(self, name) };
unsafe { ty.cast::<Class>().as_ref() }
}
#[inline]
pub fn get_type(&self, name: CName) -> Option<&Type> {
let ty = unsafe { (self.vft().get_type)(self, name) };
unsafe { ty.cast::<Type>().as_ref() }
}
#[inline]
pub fn get_enum(&self, name: CName) -> Option<&Enum> {
let ty = unsafe { (self.vft().get_enum)(self, name) };
unsafe { ty.cast::<Enum>().as_ref() }
}
#[inline]
pub fn get_bitfield(&self, name: CName) -> Option<&Bitfield> {
let ty = unsafe { (self.vft().get_bitfield)(self, name) };
unsafe { ty.cast::<Bitfield>().as_ref() }
}
#[inline]
pub fn get_function(&self, name: CName) -> Option<&Function> {
let ty = unsafe { (self.vft().get_function)(self, name) };
unsafe { ty.cast::<Function>().as_ref() }
}
#[inline]
pub fn get_native_types(&self) -> RedArray<&Type> {
let mut out = RedArray::default();
unsafe {
(self.vft().get_native_types)(self, &mut out as *mut _ as *mut RedArray<*mut Type>)
};
out
}
#[inline]
pub fn get_enums(&self) -> RedArray<&Enum> {
let mut out = RedArray::default();
unsafe { (self.vft().get_enums)(self, &mut out as *mut _ as *mut RedArray<*mut Enum>) };
out
}
#[inline]
pub fn get_bitfields(&self, scripted_only: bool) -> RedArray<&Bitfield> {
let mut out = RedArray::default();
unsafe {
(self.vft().get_bitfields)(
self,
&mut out as *mut _ as *mut RedArray<*mut Bitfield>,
scripted_only,
)
};
out
}
#[inline]
pub fn get_global_functions(&self) -> RedArray<&Function> {
let mut out = RedArray::default();
unsafe {
(self.vft().get_global_functions)(
self,
&mut out as *mut _ as *mut RedArray<*mut Function>,
)
};
out
}
#[inline]
pub fn get_class_functions(&self) -> RedArray<&Function> {
let mut out = RedArray::default();
unsafe {
(self.vft().get_class_functions)(
self,
&mut out as *mut _ as *mut RedArray<*mut Function>,
)
};
out
}
#[inline]
pub fn get_classes(&self, base: &Class, include_abstract: bool) -> RedArray<&Class> {
let mut out = RedArray::default();
unsafe {
(self.vft().get_classes)(
self,
base,
&mut out as *mut _ as *mut RedArray<*mut Class>,
None,
include_abstract,
)
};
out
}
#[inline]
pub fn get_derived_classes(&self, base: &Class) -> RedArray<&Class> {
let mut out = RedArray::default();
unsafe {
(self.vft().get_derived_classes)(
self,
base,
&mut out as *mut _ as *mut RedArray<*mut Class>,
)
};
out
}
#[inline]
pub fn get_class_by_script_name(&self, name: CName) -> Option<&Class> {
let ty = unsafe { (self.vft().get_class_by_script_name)(self, name) };
unsafe { ty.cast::<Class>().as_ref() }
}
#[inline]
pub fn get_enum_by_script_name(&self, name: CName) -> Option<&Enum> {
let ty = unsafe { (self.vft().get_enum_by_script_name)(self, name) };
unsafe { ty.cast::<Enum>().as_ref() }
}
#[inline]
pub fn type_map(&self) -> &RedHashMap<CName, &Type> {
unsafe { &*(&self.0.types as *const _ as *const RedHashMap<CName, &Type>) }
}
#[inline]
pub fn script_to_native_map(&self) -> &RedHashMap<CName, CName> {
unsafe { &*(&self.0.scriptToNative as *const _ as *const RedHashMap<CName, CName>) }
}
#[inline]
pub fn native_to_script_map(&self) -> &RedHashMap<CName, CName> {
unsafe { &*(&self.0.nativeToScript as *const _ as *const RedHashMap<CName, CName>) }
}
#[inline]
pub fn resolve_static_method_by_full_name(&self, full_name: &str) -> Option<&Function> {
fn resolve_native(rtti: &RttiSystem, class: CName, method: CName) -> Option<&Function> {
rtti.get_class(class)?
.static_methods()
.iter()
.find(|m| m.as_function().name() == method)
.map(|m| m.as_function())
}
let mut parts = full_name.as_bytes().split(|&c| c == b':');
let class = CName::from_bytes(parts.next()?);
parts.next()?; let method = CName::from_bytes(parts.next()?);
self.get_function(CName::new(full_name))
.or_else(|| resolve_native(self, class, method))
}
pub fn resolve_static_context(&self, class: CName) -> Option<Ref<ScriptableSystem>> {
let game = GameEngine::get().game_instance();
let get_context = |class| Some(game.get_system(self.get_class(class)?.as_type()));
let ctx = get_context(class)?;
if ctx.is_null() {
get_context(CName::new("cpPlayerSystem"))
} else {
Some(ctx)
}
}
#[inline]
fn vft(&self) -> &RttiSystemVft {
unsafe { &*(self.0._base.vtable_ as *const RttiSystemVft) }
}
}
#[repr(transparent)]
pub struct RttiSystemMut(red::CRTTISystem);
impl RttiSystemMut {
#[inline]
pub fn get() -> RwSpinLockWriteGuard<'static, Self> {
unsafe {
let rtti = red::CRTTISystem_Get();
let lock = &(*rtti).typesLock;
RwSpinLockWriteGuard::new(lock, ptr::NonNull::new_unchecked(rtti as _))
}
}
pub fn get_class(&mut self, name: CName) -> Option<&mut Class> {
let (types, types_by_id, type_ids) = self.split_types();
if let Some(ty) = types.get_mut(&name) {
return ty.as_class_mut();
}
let &id = type_ids.get(&name)?;
types_by_id.get_mut(&id)?.as_class_mut()
}
pub fn register_class(&mut self, mut class: ClassHandle) {
let id = unsafe { red::RTTIRegistrator::GetNextId() };
self.type_map()
.insert(class.as_ref().name(), class.as_mut().as_type_mut());
self.type_by_id_map()
.insert(id, class.as_mut().as_type_mut());
self.type_id_map().insert(class.as_ref().name(), id);
}
#[inline]
pub fn register_function(&mut self, function: PoolRef<GlobalFunction>) {
unsafe { (self.vft().register_function)(self, &*function) }
mem::forget(function);
}
#[inline]
fn type_map(&mut self) -> &mut RedHashMap<CName, &mut Type> {
unsafe { &mut *(&mut self.0.types as *mut _ as *mut RedHashMap<CName, &mut Type>) }
}
#[inline]
fn type_by_id_map(&mut self) -> &mut RedHashMap<u32, &mut Type> {
unsafe { &mut *(&mut self.0.typesByAsyncId as *mut _ as *mut RedHashMap<u32, &mut Type>) }
}
#[inline]
fn type_id_map(&mut self) -> &mut RedHashMap<CName, u32> {
unsafe { &mut *(&mut self.0.typeAsyncIds as *mut _ as *mut RedHashMap<CName, u32>) }
}
#[inline]
#[allow(clippy::type_complexity)]
fn split_types(
&mut self,
) -> (
&mut RedHashMap<CName, &mut Type>,
&mut RedHashMap<u32, &mut Type>,
&mut RedHashMap<CName, u32>,
) {
unsafe {
(
&mut *(&mut self.0.types as *mut _ as *mut RedHashMap<CName, &mut Type>),
&mut *(&mut self.0.typesByAsyncId as *mut _ as *mut RedHashMap<u32, &mut Type>),
&mut *(&mut self.0.typeAsyncIds as *mut _ as *mut RedHashMap<CName, u32>),
)
}
}
#[inline]
fn vft(&self) -> &RttiSystemVft {
unsafe { &*(self.0._base.vtable_ as *const RttiSystemVft) }
}
}
#[repr(C)]
struct RttiSystemVft {
get_type: unsafe extern "fastcall" fn(this: *const RttiSystem, name: CName) -> *mut Type,
get_type_by_async_id:
unsafe extern "fastcall" fn(this: *const RttiSystem, async_id: u32) -> *mut Type,
get_class: unsafe extern "fastcall" fn(this: *const RttiSystem, name: CName) -> *mut Class,
get_enum: unsafe extern "fastcall" fn(this: *const RttiSystem, name: CName) -> *mut Enum,
get_bitfield:
unsafe extern "fastcall" fn(this: *const RttiSystem, name: CName) -> *mut Bitfield,
_sub_28: unsafe extern "fastcall" fn(this: *const RttiSystem),
get_function:
unsafe extern "fastcall" fn(this: *const RttiSystem, name: CName) -> *mut Function,
_sub_38: unsafe extern "fastcall" fn(this: *const RttiSystem),
get_native_types:
unsafe extern "fastcall" fn(this: *const RttiSystem, out: *mut RedArray<*mut Type>),
get_global_functions:
unsafe extern "fastcall" fn(this: *const RttiSystem, out: *mut RedArray<*mut Function>),
_sub_50: unsafe extern "fastcall" fn(this: *const RttiSystem),
get_class_functions:
unsafe extern "fastcall" fn(this: *const RttiSystem, out: *mut RedArray<*mut Function>),
get_enums: unsafe extern "fastcall" fn(this: *const RttiSystem, out: *mut RedArray<*mut Enum>),
get_bitfields: unsafe extern "fastcall" fn(
this: *const RttiSystem,
out: *mut RedArray<*mut Bitfield>,
scripted_only: bool,
),
get_classes: unsafe extern "fastcall" fn(
this: *const RttiSystem,
base_class: *const Class,
out: *mut RedArray<*mut Class>,
filter: Option<unsafe extern "C" fn(*const Class) -> bool>,
include_abstract: bool,
),
get_derived_classes: unsafe extern "fastcall" fn(
this: *const RttiSystem,
base_class: *const Class,
out: *mut RedArray<*mut Class>,
),
register_type: unsafe extern "fastcall" fn(this: *mut RttiSystem, ty: *mut Type, async_id: u32),
_sub_88: unsafe extern "fastcall" fn(this: *const RttiSystem),
_sub_90: unsafe extern "fastcall" fn(this: *const RttiSystem),
unregister_type: unsafe extern "fastcall" fn(this: *mut RttiSystem, ty: *mut Type),
register_function:
unsafe extern "fastcall" fn(this: *const RttiSystemMut, function: *const GlobalFunction),
unregister_function:
unsafe extern "fastcall" fn(this: *const RttiSystem, function: *const GlobalFunction),
_sub_b0: unsafe extern "fastcall" fn(this: *const RttiSystem),
_sub_b8: unsafe extern "fastcall" fn(this: *const RttiSystem),
_add_register_callback: unsafe extern "fastcall" fn(
this: *const RttiSystem,
function: unsafe extern "C" fn() -> (),
),
_add_post_register_callback: unsafe extern "fastcall" fn(
this: *const RttiSystem,
function: unsafe extern "C" fn() -> (),
),
_sub_d0: unsafe extern "fastcall" fn(this: *const RttiSystem),
_sub_d8: unsafe extern "fastcall" fn(this: *const RttiSystem),
_create_scripted_class: unsafe extern "fastcall" fn(
this: *mut RttiSystem,
name: CName,
flags: ClassFlags,
parent: *const Class,
),
_create_scripted_enum: unsafe extern "fastcall" fn(
this: *const RttiSystem,
name: CName,
size: i8,
variants: *mut RedArray<u64>,
),
_create_scripted_bitfield:
unsafe extern "fastcall" fn(this: *const RttiSystem, name: CName, bits: *mut RedArray<u64>),
_initialize_script_runtime: unsafe extern "fastcall" fn(this: *const RttiSystem),
register_script_name: unsafe extern "fastcall" fn(
this: *const RttiSystem,
native_name: CName,
script_name: CName,
),
get_class_by_script_name:
unsafe extern "fastcall" fn(this: *const RttiSystem, name: CName) -> *const Class,
get_enum_by_script_name:
unsafe extern "fastcall" fn(this: *const RttiSystem, name: CName) -> *const Enum,
_convert_native_to_script_name:
unsafe extern "fastcall" fn(this: *const RttiSystem, name: red::CName) -> red::CName,
_convert_script_to_native_name:
unsafe extern "fastcall" fn(this: *const RttiSystem, name: red::CName) -> red::CName,
}
#[derive(Debug)]
pub struct RttiRegistrator;
impl RttiRegistrator {
pub fn add(
register: Option<unsafe extern "C" fn()>,
post_register: Option<unsafe extern "C" fn()>,
) {
unsafe { red::RTTIRegistrator::Add(register, post_register, false) };
}
}