use super::khook::KhHook;
use crate::hook::HookReturn;
use crate::sys::khook::{Action, IKHook};
use crate::sys::sourcehook::{IShDelegate, ISourceHook, MetaRes};
use std::cell::Cell;
use std::ffi::c_void;
use std::mem::{self, size_of};
use std::ptr;
thread_local! {
pub(crate) static FOREIGN_KHOOK: Cell<*mut IKHook> = const { Cell::new(ptr::null_mut()) };
}
#[repr(C)]
pub(crate) struct ForeignDelegate<R> {
vtable: *const *mut c_void,
sourcehook: *mut ISourceHook,
pub(crate) result: Cell<MetaRes>,
value: R,
functions: [*mut c_void; 3],
}
impl<R: HookReturn> ForeignDelegate<R> {
pub(crate) fn new<A: Copy + 'static>(
sourcehook: *mut ISourceHook,
result: MetaRes,
value: R,
) -> Box<Self> {
let mut delegate = Box::new(Self {
vtable: ptr::null(),
sourcehook,
result: Cell::new(result),
value,
functions: [
is_equal as unsafe extern "C" fn(*mut IShDelegate, *mut IShDelegate) -> bool
as *mut c_void,
delete_this as unsafe extern "C" fn(*mut IShDelegate) as *mut c_void,
call::<A, R> as unsafe extern "C" fn(*mut Self, A) -> R as *mut c_void,
],
});
delegate.vtable = delegate.functions.as_ptr();
delegate
}
pub(crate) fn ptr(&self) -> *mut IShDelegate {
ptr::from_ref(self).cast::<IShDelegate>().cast_mut()
}
}
unsafe extern "C" fn call<A, R: HookReturn>(delegate: *mut ForeignDelegate<R>, _arg: A) -> R {
let delegate = unsafe { &*delegate };
unsafe {
((*(*delegate.sourcehook).vtable).set_res)(delegate.sourcehook, delegate.result.get())
};
delegate.value
}
unsafe extern "C" fn copy<R: HookReturn>(destination: *mut R, value: *const R) {
unsafe { destination.write(value.read()) };
}
unsafe extern "C" fn delete_this(_this: *mut IShDelegate) {}
unsafe extern "C" fn destroy<R>(_value: *mut R) {}
unsafe extern "C" fn is_equal(this: *mut IShDelegate, other: *mut IShDelegate) -> bool {
this == other
}
unsafe extern "C" fn khook_call_original<T, A, R: HookReturn>(this: *mut T, arg: A) -> R {
let khook = FOREIGN_KHOOK.get();
unsafe {
let functions = &*(*khook).vtable;
let original =
mem::transmute::<*mut c_void, unsafe extern "C" fn(*mut T, A) -> R>((functions
.get_original_function)(
khook
));
let mut value = original(this, arg);
(functions.save_return_value)(
khook,
Action::IGNORE,
ptr::from_mut(&mut value).cast(),
size_of::<R>(),
copy::<R> as unsafe extern "C" fn(*mut R, *const R) as *mut c_void,
destroy::<R> as unsafe extern "C" fn(*mut R) as *mut c_void,
true,
);
value
}
}
unsafe extern "C" fn khook_make_return<T, A, R: HookReturn>(_this: *mut T, _arg: A) -> R {
let khook = FOREIGN_KHOOK.get();
unsafe {
let functions = &*(*khook).vtable;
let value = (functions.get_current_value_ptr)(khook, true).cast::<R>();
let value = match value.is_null() {
true => mem::zeroed(),
false => value.read(),
};
(functions.destroy_return_value)(khook);
value
}
}
unsafe extern "C" fn khook_supersede<T, A, R: HookReturn>(_this: *mut T, _arg: A) -> R {
let khook = FOREIGN_KHOOK.get();
unsafe {
let functions = &*(*khook).vtable;
let mut value = (functions.get_context_ptr)(khook).cast::<R>().read();
(functions.save_return_value)(
khook,
Action::SUPERSEDE,
ptr::from_mut(&mut value).cast(),
size_of::<R>(),
copy::<R> as unsafe extern "C" fn(*mut R, *const R) as *mut c_void,
destroy::<R> as unsafe extern "C" fn(*mut R) as *mut c_void,
false,
);
mem::zeroed()
}
}
pub(crate) fn khook_superseding<T, A, R>(value: *const R) -> KhHook
where
T: 'static,
A: Copy + 'static,
R: HookReturn,
{
KhHook {
context: value.cast_mut().cast(),
pre: khook_supersede::<T, A, R> as unsafe extern "C" fn(*mut T, A) -> R as *mut c_void,
post: ptr::null_mut(),
make_return: khook_make_return::<T, A, R> as unsafe extern "C" fn(*mut T, A) -> R
as *mut c_void,
call_original: khook_call_original::<T, A, R> as unsafe extern "C" fn(*mut T, A) -> R
as *mut c_void,
stack_size: 0,
}
}