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