use std::{sync::{Arc, Mutex, TryLockError, atomic::AtomicBool}, time::Duration, os::raw::c_char, thread::JoinHandle};
lazy_static::lazy_static! {
static ref STDOUT_OVERRIDE_THREAD: Mutex<Option<JoinHandle<()>>> = Mutex::new(None);
}
static SHUTDOWN_FLAG: AtomicBool = AtomicBool::new(false);
pub fn override_stdout() {
let mut join_handle = STDOUT_OVERRIDE_THREAD.lock().unwrap();
if join_handle.is_some() {
return;
}
unsafe {
let (_lib, _path) = crate::open_library!("tier0").expect("Failed to open tier0.dll");
#[allow(non_snake_case)]
let ConMsg: extern "C" fn(*const c_char, ...) = *{
#[cfg(target_os = "windows")] {
_lib.get({
#[cfg(all(target_os = "windows", target_pointer_width = "64"))] {
b"?ConMsg@@YAXPEBDZZ\0"
}
#[cfg(all(target_os = "windows", target_pointer_width = "32"))] {
b"?ConMsg@@YAXPBDZZ\0"
}
})
}
#[cfg(any(target_os = "linux", target_os = "macos"))] {
_lib.get(b"ConMsg\0").or_else(|_| _lib.get(b"_Z6ConMsgPKcz\0"))
}
}.expect("Failed to find ConMsg");
let output_buf = Arc::new(Mutex::new(Vec::new()));
let output_buf_ref = output_buf.clone();
join_handle.replace(std::thread::spawn(move || loop {
match output_buf.try_lock() {
Ok(mut data) => if !data.is_empty() {
data.push(0); ConMsg(data.as_ptr() as *const i8);
data.truncate(0);
},
Err(TryLockError::Poisoned(err)) => panic!("{}", err),
Err(TryLockError::WouldBlock) => {
std::hint::spin_loop();
std::thread::yield_now();
continue
}
}
if SHUTDOWN_FLAG.load(std::sync::atomic::Ordering::Relaxed) {
break;
}
std::thread::sleep(Duration::from_millis(250));
}));
std::io::set_output_capture(Some(output_buf_ref));
};
}
pub fn restore_stdout() {
SHUTDOWN_FLAG.store(true, std::sync::atomic::Ordering::Release);
if let Some(join_handle) = STDOUT_OVERRIDE_THREAD.lock().unwrap().take() {
let _ = join_handle.join();
}
std::io::set_output_capture(None); }