use std::path::Path;
use jni::JNIEnv;
use jni::objects::{JClass, JString};
use jni::sys::jbyteArray;
use libloading::{Library, Symbol};
use yog_abi::YogReply;
use yog_event::nanite::Nanites;
use crate::mod_loading::{AbiVersionFn, extract_yog};
type NanitesFn = unsafe extern "C" fn(reply: *const YogReply);
fn of_mod(yog: &Path) -> Nanites {
let Some(lib_path) = extract_yog(yog) else {
return Nanites::default();
};
let declared = ask(&lib_path).unwrap_or_default();
if let Err(e) = std::fs::remove_file(&lib_path) {
yog_logging::warn!("nanites: {}: {e}", lib_path.display());
}
declared
}
fn ask(lib_path: &Path) -> Option<Nanites> {
let lib = unsafe { Library::new(lib_path) }.ok()?;
unsafe {
let abi: Symbol<AbiVersionFn> = lib.get(b"yog_abi_version").ok()?;
if !yog_abi::compatible(abi()) {
return None;
}
let nanites: Symbol<NanitesFn> = lib.get(b"yog_mod_nanites").ok()?;
let mut bytes = Vec::new();
nanites(&YogReply::to_vec(&mut bytes));
yog_data::read_bytes(&bytes).ok()
}
}
pub(crate) fn declared(dir: &Path) -> Nanites {
let mut all = Nanites::default();
let Ok(entries) = std::fs::read_dir(dir) else {
return all;
};
for path in entries.flatten().map(|e| e.path()) {
if yog_ids::Format::Mod.is(&path) {
all.injects.extend(of_mod(&path).injects);
}
}
all
}
static ASKED: yog_sync::Mutex<Option<(String, Vec<u8>)>> = yog_sync::Mutex::new(None);
#[unsafe(no_mangle)]
pub extern "system" fn Java_dev_yog_nanite_YogNanites_nativeDeclarations<'l>(
mut env: JNIEnv<'l>,
_class: JClass<'l>,
mods_dir: JString<'l>,
) -> jbyteArray {
crate::jni_util::guarded("nativeDeclarations", std::ptr::null_mut(), || {
let dir: String = env
.get_string(&mods_dir)
.map(Into::into)
.unwrap_or_default();
let mut asked = ASKED.lock();
let bytes = match asked.as_ref() {
Some((d, bytes)) if *d == dir => bytes.clone(),
_ => {
let bytes = yog_data::to_bytes(&declared(Path::new(&dir)));
*asked = Some((dir, bytes.clone()));
bytes
}
};
env.byte_array_from_slice(&bytes)
.map_or(std::ptr::null_mut(), |a| a.into_raw())
})
}