1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use faithe::RuntimeOffset;
pub enum FunctionAddress {
    Offset(RuntimeOffset),
    Pointer(*const ()),
}

pub trait FunctionRef {
    type Target;

    fn set_target(&self, new_target: *const ());
    fn get_target(&self) -> Self::Target;
    fn get_ptr(&self) -> *const ();
}

#[macro_export]
macro_rules! function {
    (
        $(
            $vs:vis $name:ident: $(extern $($cc:literal)?)? fn($($arg_id:ident: $arg_ty:ty),*) $(-> $ret_ty:ty)? = $lib_name:tt$sep:tt$var:tt$([$add:tt])?;
        )*
    ) => {
        $(
            #[allow(non_upper_case_globals)]
            $vs static mut $name: $name = $name {
                offset: std::cell::RefCell::new($crate::framework::hooks::FunctionAddress::Offset($crate::faithe::__define_offset!($sep $var)))
            };
            #[allow(non_camel_case_types)]
            $vs struct $name {
                offset: std::cell::RefCell<$crate::framework::hooks::FunctionAddress>,
            }
            unsafe impl ::core::marker::Sync for $name { }

            impl $crate::framework::hooks::FunctionRef for $name {
                type Target = $(extern $($cc)?)? fn($($arg_ty),*) $(-> $ret_ty)?;

                fn get_ptr(&self) -> *const () {
                    match &*self.offset.borrow() {
                        $crate::framework::hooks::FunctionAddress::Offset(offset) => {
                            if !offset.is_resolved() {
                                $crate::faithe::__expect!(offset.try_resolve($lib_name, $crate::faithe::__define_offset2!($($add)?)), "Failed to resolve function's address");
                            }

                            offset.address() as *const ()
                        },
                        $crate::framework::hooks::FunctionAddress::Pointer(ptr) => *ptr
                    }
                }

                fn get_target(&self) -> Self::Target {
                    let address = self.get_ptr();

                    unsafe { ::core::mem::transmute::<_, $(extern $($cc)?)? fn($($arg_ty),*) $(-> $ret_ty)?>(address) }
                }

                fn set_target(&self, new_fn: *const ()) {
                    let mut current = self.offset.borrow_mut();
                    *current = $crate::framework::hooks::FunctionAddress::Pointer(new_fn);
                }
            }

            impl $name {
                #[inline]
                $vs fn call(&self, $($arg_id:$arg_ty),*) $(-> $ret_ty)? {
                    use $crate::framework::hooks::FunctionRef;
                    (self.get_target())($($arg_id),*)
                }
            }
        )*
    };
}