use super::{khook, sourcehook};
use std::ffi::c_void;
use std::marker::PhantomData;
use std::mem::{self, size_of};
use std::ptr::NonNull;
pub(super) const HOOK_MANAGERS: usize = 64;
pub unsafe trait HookArg: Copy + 'static {}
pub unsafe trait HookReturn: Copy + 'static {}
macro_rules! scalars {
($($ty:ty),* $(,)?) => {$(
unsafe impl HookArg for $ty {}
unsafe impl HookReturn for $ty {}
)*};
}
scalars!(
bool, i8, u8, i16, u16, i32, u32, i64, u64, isize, usize, f32, f64
);
pub trait Signature: Copy + 'static + sealed::Sealed {
type Args: Copy + 'static;
type Output: HookReturn;
type This: 'static;
#[doc(hidden)]
const PARAMETER_SIZES: &'static [usize];
#[doc(hidden)]
const THUNKS: Thunks<Self>;
#[doc(hidden)]
fn from_address(address: NonNull<c_void>) -> Self;
#[doc(hidden)]
unsafe fn invoke(function: Self, this: *mut Self::This, args: Self::Args) -> Self::Output;
#[doc(hidden)]
fn address(self) -> NonNull<c_void>;
}
#[doc(hidden)]
#[derive(Clone, Copy)]
pub struct Thunks<S: 'static> {
pub(super) khook_call_original: S,
pub(super) khook_make_return: S,
pub(super) khook_post: S,
pub(super) khook_pre: S,
pub(super) sourcehook_call: S,
pub(super) sourcehook_hook_functions: &'static [S; HOOK_MANAGERS],
}
struct Trampolines<S>(PhantomData<S>);
unsafe impl HookReturn for () {}
unsafe impl<T: 'static> HookArg for *const T {}
unsafe impl<T: 'static> HookReturn for *const T {}
unsafe impl<T: 'static> HookArg for *mut T {}
unsafe impl<T: 'static> HookReturn for *mut T {}
unsafe impl<T: 'static> HookArg for Option<NonNull<T>> {}
unsafe impl<T: 'static> HookReturn for Option<NonNull<T>> {}
macro_rules! signatures {
($(($($arg:ident: $Arg:ident),*))*) => {$(
impl<T: 'static, R: HookReturn, $($Arg: HookArg),*>
Trampolines<unsafe extern "C" fn(*mut T $(, $Arg)*) -> R>
{
unsafe extern "C" fn khook_call_original(this: *mut T $(, $arg: $Arg)*) -> R {
unsafe {
khook::call_original::<unsafe extern "C" fn(*mut T $(, $Arg)*) -> R>(
this,
($($arg,)*),
)
}
}
unsafe extern "C" fn khook_make_return(this: *mut T $(, $arg: $Arg)*) -> R {
let _ = (this, $($arg),*);
unsafe { khook::make_return::<R>() }
}
unsafe extern "C" fn khook_post(this: *mut T $(, $arg: $Arg)*) -> R {
unsafe {
khook::post::<unsafe extern "C" fn(*mut T $(, $Arg)*) -> R>(this, ($($arg,)*))
}
}
unsafe extern "C" fn khook_pre(this: *mut T $(, $arg: $Arg)*) -> R {
unsafe {
khook::pre::<unsafe extern "C" fn(*mut T $(, $Arg)*) -> R>(this, ($($arg,)*))
}
}
unsafe extern "C" fn sourcehook_call(delegate: *mut T $(, $arg: $Arg)*) -> R {
unsafe {
sourcehook::call::<unsafe extern "C" fn(*mut T $(, $Arg)*) -> R>(
delegate.cast(),
($($arg,)*),
)
}
}
unsafe extern "C" fn sourcehook_hook_function<const MANAGER: usize>(
this: *mut T
$(, $arg: $Arg)*
) -> R {
unsafe {
sourcehook::hook_function::<unsafe extern "C" fn(*mut T $(, $Arg)*) -> R>(
MANAGER,
this,
($($arg,)*),
)
}
}
}
impl<T: 'static, R: HookReturn, $($Arg: HookArg),*> sealed::Sealed
for unsafe extern "C" fn(*mut T $(, $Arg)*) -> R
{
}
impl<T: 'static, R: HookReturn, $($Arg: HookArg),*> Signature
for unsafe extern "C" fn(*mut T $(, $Arg)*) -> R
{
type Args = ($($Arg,)*);
type Output = R;
type This = T;
const PARAMETER_SIZES: &'static [usize] = &[$(size_of::<$Arg>()),*];
const THUNKS: Thunks<Self> = Thunks {
khook_call_original: Trampolines::<Self>::khook_call_original,
khook_make_return: Trampolines::<Self>::khook_make_return,
khook_post: Trampolines::<Self>::khook_post,
khook_pre: Trampolines::<Self>::khook_pre,
sourcehook_call: Trampolines::<Self>::sourcehook_call,
sourcehook_hook_functions: &hook_managers!(
Trampolines::<Self>::sourcehook_hook_function
),
};
fn address(self) -> NonNull<c_void> {
unsafe { NonNull::new_unchecked(self as *mut c_void) }
}
fn from_address(address: NonNull<c_void>) -> Self {
unsafe { mem::transmute::<*mut c_void, Self>(address.as_ptr()) }
}
unsafe fn invoke(function: Self, this: *mut T, args: Self::Args) -> R {
let ($($arg,)*) = args;
unsafe { function(this $(, $arg)*) }
}
}
)*};
}
signatures! {
()
(a0: A0)
(a0: A0, a1: A1)
(a0: A0, a1: A1, a2: A2)
(a0: A0, a1: A1, a2: A2, a3: A3)
(a0: A0, a1: A1, a2: A2, a3: A3, a4: A4)
(a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5)
(a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6)
(a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7)
(a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8)
(a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9)
(a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9, a10: A10)
(
a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9, a10: A10,
a11: A11
)
(
a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9, a10: A10,
a11: A11, a12: A12
)
(
a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9, a10: A10,
a11: A11, a12: A12, a13: A13
)
(
a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9, a10: A10,
a11: A11, a12: A12, a13: A13, a14: A14
)
(
a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9, a10: A10,
a11: A11, a12: A12, a13: A13, a14: A14, a15: A15
)
}
pub(super) fn nothing<R: HookReturn>() -> R {
unsafe { mem::zeroed() }
}
mod sealed {
pub trait Sealed {}
}