1#![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 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 !is_agent_server {
80 if let Some(lib) = try_load("MaaToolkit") {
81 libs.push(lib);
82 }
83 }
84
85 let main_lib = load_lib(path)?;
86 libs.insert(0, main_lib);
87
88 if !is_agent_server {
89 if let Some(lib) = try_load("MaaAgentClient") {
90 libs.push(lib);
91 }
92 }
93
94 Ok(Self { libs })
95 }
96
97 pub unsafe fn get<T>(
98 &self,
99 symbol: &[u8],
100 ) -> Result<libloading::Symbol<'_, T>, libloading::Error> {
101 for lib in &self.libs {
102 if let Ok(s) = unsafe { lib.get(symbol) } {
103 return Ok(s);
104 }
105 }
106 unsafe { self.libs[0].get(symbol) }
107 }
108}
109
110#[cfg(feature = "dynamic")]
111pub unsafe fn load_library(path: &std::path::Path) -> Result<(), String> {
112 if INSTANCE.get().is_some() {
113 return Err("Library already loaded".to_string());
114 }
115 let lib = unsafe { MaaFramework::new(path).map_err(|e| e.to_string())? };
116 INSTANCE
117 .set(lib)
118 .map_err(|_| "Library already loaded".to_string())
119}
120
121#[cfg(feature = "dynamic")]
122macro_rules! shim {
123 ($name:ident ( $($arg:ident : $type:ty),* $(,)? ) -> $ret:ty) => {
124 pub unsafe fn $name($($arg : $type),*) -> $ret {
125 let lib = INSTANCE.get().expect("MaaFramework library not loaded!");
126 let func = match &lib.$name {
127 Ok(f) => f,
128 Err(e) => panic!("Function {} not loaded: {}", stringify!($name), e),
129 };
130
131 unsafe { func($($arg),*) }
132 }
133 }
134}
135
136#[cfg(feature = "dynamic")]
137include!(concat!(env!("OUT_DIR"), "/shims.rs"));