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 = libloading::os::windows::Library::load_with_flags(&p, 0x00000008)?;
46 Ok(lib.into())
47 }
48 #[cfg(not(target_os = "windows"))]
49 {
50 let p = p.canonicalize().unwrap_or(p.to_path_buf());
51 libloading::Library::new(&p)
52 }
53 };
54
55 libs.push(load_lib(path)?);
56
57 if let Some(parent) = path.parent() {
58 let file_name = path.file_name().and_then(|s| s.to_str()).unwrap_or("");
59
60 let (prefix, suffix) = if file_name.ends_with(".dll") {
61 ("", ".dll")
62 } else if file_name.starts_with("lib") && file_name.ends_with(".dylib") {
63 ("lib", ".dylib")
64 } else if file_name.starts_with("lib") && file_name.ends_with(".so") {
65 ("lib", ".so")
66 } else {
67 ("", "")
68 };
69
70 let mut try_load = |name: &str| {
71 let p = parent.join(format!("{}{}{}", prefix, name, suffix));
72 if p.exists() {
73 if let Ok(lib) = load_lib(&p) {
74 libs.push(lib);
75 }
76 }
77 };
78
79 try_load("MaaToolkit");
80 try_load("MaaAgentServer");
81 try_load("MaaAgentClient");
82 }
83
84 Ok(Self { libs })
85 }
86
87 pub unsafe fn get<T>(
88 &self,
89 symbol: &[u8],
90 ) -> Result<libloading::Symbol<'_, T>, libloading::Error> {
91 for lib in &self.libs {
92 if let Ok(s) = lib.get(symbol) {
93 return Ok(s);
94 }
95 }
96 self.libs[0].get(symbol)
97 }
98}
99
100#[cfg(feature = "dynamic")]
101pub unsafe fn load_library(path: &std::path::Path) -> Result<(), String> {
102 if INSTANCE.get().is_some() {
103 return Err("Library already loaded".to_string());
104 }
105 let lib = MaaFramework::new(path).map_err(|e| e.to_string())?;
106 INSTANCE
107 .set(lib)
108 .map_err(|_| "Library already loaded".to_string())
109}
110
111#[cfg(feature = "dynamic")]
112macro_rules! shim {
113 ($name:ident ( $($arg:ident : $type:ty),* $(,)? ) -> $ret:ty) => {
114 pub unsafe fn $name($($arg : $type),*) -> $ret {
115 let lib = INSTANCE.get().expect("MaaFramework library not loaded!");
116 (lib.$name)($($arg),*)
117 }
118 }
119}
120
121#[cfg(feature = "dynamic")]
122include!(concat!(env!("OUT_DIR"), "/shims.rs"));