use std::ffi::{CStr, c_char, c_void};
use std::sync::OnceLock;
pub(crate) type MsgFn = unsafe extern "C" fn(format: *const c_char, ...);
#[cfg(test)]
thread_local! {
pub(crate) static TEST_MSG: std::cell::Cell<Option<MsgFn>> = const { std::cell::Cell::new(None) };
}
pub(crate) fn print(message: &CStr) -> bool {
static MSG: OnceLock<Option<MsgFn>> = OnceLock::new();
#[cfg(test)]
let test_msg = TEST_MSG.get();
#[cfg(not(test))]
let test_msg = None;
let Some(msg) = test_msg.or_else(|| *MSG.get_or_init(find_msg)) else {
return false;
};
unsafe { msg(c"%s".as_ptr(), message.as_ptr()) };
true
}
fn find_msg() -> Option<MsgFn> {
if cfg!(miri) {
return None;
}
let address = platform::find_symbol(c"Msg")?;
Some(unsafe { std::mem::transmute::<*mut c_void, MsgFn>(address) })
}
#[cfg(windows)]
mod platform {
use std::ffi::{CStr, c_char, c_void};
use std::ptr::NonNull;
#[link(name = "kernel32")]
unsafe extern "system" {
fn GetModuleHandleA(name: *const c_char) -> *mut c_void;
fn GetProcAddress(module: *mut c_void, name: *const c_char) -> *mut c_void;
}
pub(super) fn find_symbol(name: &CStr) -> Option<*mut c_void> {
let module = NonNull::new(unsafe { GetModuleHandleA(c"tier0.dll".as_ptr()) })?;
NonNull::new(unsafe { GetProcAddress(module.as_ptr(), name.as_ptr()) }).map(NonNull::as_ptr)
}
}
#[cfg(target_os = "linux")]
mod platform {
use std::ffi::{CStr, c_char, c_int, c_void};
use std::ptr::NonNull;
const RTLD_NOW: c_int = 2;
const RTLD_NOLOAD: c_int = 4;
const NAMES: [&CStr; 2] = [c"libtier0.so", c"libtier0_srv.so"];
#[link(name = "dl")]
unsafe extern "C" {
fn dlopen(file: *const c_char, mode: c_int) -> *mut c_void;
fn dlsym(handle: *mut c_void, name: *const c_char) -> *mut c_void;
fn dlclose(handle: *mut c_void) -> c_int;
}
pub(super) fn find_symbol(name: &CStr) -> Option<*mut c_void> {
NAMES.into_iter().find_map(|library| {
let handle = NonNull::new(unsafe { dlopen(library.as_ptr(), RTLD_NOW | RTLD_NOLOAD) })?;
let symbol = unsafe { dlsym(handle.as_ptr(), name.as_ptr()) };
unsafe { dlclose(handle.as_ptr()) };
NonNull::new(symbol).map(NonNull::as_ptr)
})
}
}