Skip to main content

maa_framework_sys/
lib.rs

1//! Raw FFI bindings to the C API (auto-generated).
2
3#![allow(non_upper_case_globals)]
4#![allow(non_camel_case_types)]
5#![allow(non_snake_case)]
6#![allow(dead_code)]
7#![allow(rustdoc::broken_intra_doc_links)]
8
9#[cfg(not(feature = "dynamic"))]
10include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
11
12#[cfg(feature = "dynamic")]
13mod dynamic {
14    #![allow(non_upper_case_globals)]
15    #![allow(non_camel_case_types)]
16    #![allow(non_snake_case)]
17    #![allow(dead_code)]
18    #![allow(rustdoc::broken_intra_doc_links)]
19    use super::CompositeLibrary;
20    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
21}
22
23#[cfg(feature = "dynamic")]
24pub use dynamic::*;
25
26#[cfg(feature = "dynamic")]
27static INSTANCE: once_cell::sync::OnceCell<MaaFramework> = once_cell::sync::OnceCell::new();
28
29#[cfg(feature = "dynamic")]
30pub struct CompositeLibrary {
31    libs: Vec<libloading::Library>,
32}
33
34#[cfg(feature = "dynamic")]
35impl CompositeLibrary {
36    pub unsafe fn new<P: AsRef<std::ffi::OsStr>>(path: P) -> Result<Self, libloading::Error> {
37        let path = std::path::Path::new(path.as_ref());
38        let mut libs = Vec::new();
39
40        let load_lib = |p: &std::path::Path| -> Result<libloading::Library, libloading::Error> {
41            #[cfg(target_os = "windows")]
42            {
43                //LOAD_WITH_ALTERED_SEARCH_PATH (0x00000008)
44                let p = p.canonicalize().unwrap_or(p.to_path_buf());
45                let lib =
46                    unsafe { libloading::os::windows::Library::load_with_flags(&p, 0x00000008)? };
47                Ok(lib.into())
48            }
49            #[cfg(not(target_os = "windows"))]
50            {
51                let p = p.canonicalize().unwrap_or(p.to_path_buf());
52                unsafe { libloading::Library::new(&p) }
53            }
54        };
55
56        let file_name = path.file_name().unwrap_or_default().to_string_lossy();
57        let dir = path.parent().unwrap_or(std::path::Path::new(""));
58
59        let prefix = if cfg!(target_os = "windows") {
60            ""
61        } else {
62            "lib"
63        };
64        let ext = if cfg!(target_os = "windows") {
65            ".dll"
66        } else if cfg!(target_os = "macos") {
67            ".dylib"
68        } else {
69            ".so"
70        };
71
72        let try_load = |name: &str| {
73            let p = dir.join(format!("{}{}{}", prefix, name, ext));
74            load_lib(&p).ok()
75        };
76
77        let is_agent_server = file_name.contains("MaaAgentServer");
78
79        if let Some(lib) = try_load("MaaToolkit") {
80            libs.push(lib);
81        }
82
83        let main_lib = load_lib(path)?;
84        libs.insert(0, main_lib);
85
86        if !is_agent_server {
87            if let Some(lib) = try_load("MaaAgentClient") {
88                libs.push(lib);
89            }
90        }
91
92        Ok(Self { libs })
93    }
94
95    pub unsafe fn get<T>(
96        &self,
97        symbol: &[u8],
98    ) -> Result<libloading::Symbol<'_, T>, libloading::Error> {
99        for lib in &self.libs {
100            if let Ok(s) = unsafe { lib.get(symbol) } {
101                return Ok(s);
102            }
103        }
104        unsafe { self.libs[0].get(symbol) }
105    }
106}
107
108#[cfg(feature = "dynamic")]
109pub unsafe fn load_library(path: &std::path::Path) -> Result<(), String> {
110    if INSTANCE.get().is_some() {
111        return Err("Library already loaded".to_string());
112    }
113    let lib = unsafe { MaaFramework::new(path).map_err(|e| e.to_string())? };
114    INSTANCE
115        .set(lib)
116        .map_err(|_| "Library already loaded".to_string())
117}
118
119#[cfg(feature = "dynamic")]
120macro_rules! shim {
121    ($name:ident ( $($arg:ident : $type:ty),* $(,)? ) -> $ret:ty) => {
122        pub unsafe fn $name($($arg : $type),*) -> $ret {
123            let lib = INSTANCE.get().expect("MaaFramework library not loaded!");
124            let func = match &lib.$name {
125                Ok(f) => f,
126                Err(e) => panic!("Function {} not loaded: {}", stringify!($name), e),
127            };
128
129            unsafe { func($($arg),*) }
130        }
131    }
132}
133
134#[cfg(feature = "dynamic")]
135include!(concat!(env!("OUT_DIR"), "/shims.rs"));