1#[allow(non_camel_case_types, non_snake_case, unused, clippy::all)]
7mod detours {
8 #[cfg(target_pointer_width = "32")]
10 include!("./pregenerated-x86.rs");
11 #[cfg(target_pointer_width = "64")]
12 include!("./pregenerated-x64.rs");
13}
14
15use tracing::debug;
16
17use core::{
18 error::Error,
19 ffi::c_long,
20 fmt::{self, Debug, Display, Formatter},
21};
22
23#[derive(Debug)]
25pub struct DetourHook<F> {
26 func: F,
27}
28
29impl<F: Copy> DetourHook<F> {
30 #[tracing::instrument]
35 pub unsafe fn attach(mut func: F, mut detour: F) -> DetourResult<Self>
36 where
37 F: Debug,
38 {
39 unsafe {
40 wrap_detour_call(|| detours::DetourTransactionBegin())?;
41 wrap_detour_call(|| {
42 use core::ffi::c_void;
43
44 detours::DetourAttach(
45 (&raw mut func).cast(),
46 *(&raw mut detour).cast::<*mut c_void>(),
47 )
48 })?;
49 wrap_detour_call(|| detours::DetourTransactionCommit())?;
50 }
51 debug!("hook attached");
52
53 Ok(DetourHook { func })
54 }
55
56 #[inline(always)]
58 pub fn original_fn(&self) -> F {
59 self.func
60 }
61}
62
63type DetourResult<T> = Result<T, DetourError>;
64
65#[derive(Debug, Clone, Copy)]
67pub struct DetourError(c_long);
68
69impl Display for DetourError {
70 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
71 write!(f, "Detour call error: {:?}", self.0)
72 }
73}
74
75impl Error for DetourError {}
76
77#[inline]
79fn wrap_detour_call(f: impl FnOnce() -> c_long) -> Result<(), DetourError> {
80 let code = f();
81 if code == 0 {
82 Ok(())
83 } else {
84 Err(DetourError(code))
85 }
86}