use crate::{Error, Result};
#[cfg(target_os = "android")]
use std::ffi::CString;
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
#[cfg(target_os = "android")]
use std::thread;
#[cfg(target_os = "android")]
use std::time::Duration;
static APP_DATA: OnceLock<PathBuf> = OnceLock::new();
#[cfg(target_os = "android")]
const MAX_RETRIES: u32 = 30;
#[cfg(target_os = "android")]
const RETRY_DELAY: Duration = Duration::from_secs(2);
#[cfg(target_os = "android")]
pub fn init(target_image: &str, on_ready: impl FnOnce() + Send + 'static) {
android_logger::init_once(
android_logger::Config::default().with_max_level(log::LevelFilter::Debug),
);
log::info!("cleat init: waiting for {target_image}...");
let c_name = CString::new(target_image).unwrap();
let image = target_image.to_owned();
thread::spawn(move || {
for i in 0..=MAX_RETRIES {
let handle = unsafe { libc::dlopen(c_name.as_ptr(), libc::RTLD_NOLOAD) };
if !handle.is_null() {
unsafe { libc::dlclose(handle) };
log::info!("cleat init: {image} found (attempt {i})");
il2cpp_bridge_rs::init(&image, on_ready);
return;
}
if i < MAX_RETRIES {
thread::sleep(RETRY_DELAY);
}
}
log::error!("cleat init: {image} not loaded after {MAX_RETRIES} retries");
});
}
#[cfg(not(target_os = "android"))]
pub fn init(_target_image: &str, _on_ready: impl FnOnce() + Send + 'static) {
log::info!("cleat init: skipped (not on Android)");
}
pub fn set_app_data(path: impl AsRef<Path>) {
let _ = APP_DATA.set(path.as_ref().to_path_buf());
}
pub fn app_data() -> Result<&'static Path> {
APP_DATA
.get()
.map(|p| p.as_path())
.ok_or(Error::NotInitialized)
}