use core::cell::SyncUnsafeCell;
use crate::{
panicking,
sys::{self, thread::ThreadId},
};
const MAX_ARGC: usize = 19;
pub unsafe fn process_argc_argv(
argc_bytes: usize, argp: *mut u8,
) -> (usize, [*mut u8; MAX_ARGC + 1]) {
let mut argv: [*mut u8; 20] = [core::ptr::null_mut(); 20];
let mut argc = 0;
let mut loc = 0;
let ptr: *mut u8 = argp.cast();
while loc < argc_bytes {
unsafe {
argv[argc] = ptr.add(loc);
let arg_len = crate::private::strlen(argv[argc].cast()) + 1;
loc += arg_len;
argc += 1;
if argc == 19 {
break;
}
}
}
(argc, argv)
}
pub unsafe fn module_start_init(
argc_bytes: usize, argp: *mut core::ffi::c_void,
) -> (usize, [*mut u8; MAX_ARGC + 1]) {
crate::set_psp_os_functions();
unsafe { process_argc_argv(argc_bytes, argp.cast()) }
}
pub unsafe fn init_cwd(arg0: *mut u8) {
if arg0.is_null() {
return;
}
unsafe {
let mut len = 0;
while *arg0.add(len) != 0 {
len += 1;
}
while len > 0 && *arg0.add(len - 1) != b'/' {
len -= 1;
}
if len > 0 {
let tmp = *arg0.add(len);
*arg0.add(len) = 0;
let _res = crate::sys::io::sceIoChdir(arg0 as *const u8);
*arg0.add(len) = tmp;
}
}
}
pub(crate) static MAIN_THREAD_ID: SyncUnsafeCell<Option<ThreadId>> = SyncUnsafeCell::new(None);
pub(crate) static CUSTOM_CLEANUP: SyncUnsafeCell<Option<fn()>> = SyncUnsafeCell::new(None);
pub(crate) fn main_thread_id() -> Option<ThreadId> {
let id = unsafe { &*MAIN_THREAD_ID.get() };
*id
}
pub(crate) fn custom_cleanup() -> Option<fn()> {
let res = unsafe { &*CUSTOM_CLEANUP.get() };
*res
}
pub fn set_custom_cleanup(f: fn()) -> Option<fn()> {
let cleanup = CUSTOM_CLEANUP.get();
let previous = unsafe { cleanup.read() };
unsafe {
cleanup.write(Some(f));
}
previous
}
pub(crate) unsafe fn init(_argc: usize, argv: *const *mut u8) {
crate::set_psp_os_functions();
let main_thread_id = sys::thread::sceKernelGetThreadId().into_result();
if let Ok(id) = main_thread_id {
unsafe { MAIN_THREAD_ID.get().write_volatile(Some(id)) };
}
unsafe {
init_cwd(*argv);
}
if cfg!(pbp) {
crate::enable_home_button();
}
}
pub(crate) fn cleanup() {
use crate::sync::nonpoison::Once;
static CLEANUP: Once = Once::new();
CLEANUP.call_once(|| unsafe {
crate::io::cleanup();
sys::cleanup();
if let Some(f) = custom_cleanup() {
f();
}
});
}
fn handle_rt_panic<T>(e: alloc::boxed::Box<dyn core::any::Any + Send>) -> T {
core::mem::forget(e);
cfg_select! {
panic = "immediate-abort" => {}
_ => {
if let Some(mut out) = crate::os::stdio::panic_output() {
let _ = crate::io::Write::write_fmt(
&mut out,
format_args!(
"fatal runtime error: {}, aborting\n",
format_args!("drop of the panic payload panicked")
),
);
}
}
}
crate::process::abort()
}
#[cfg(not(test))]
fn psp_start_internal(
main: &(dyn Fn() -> i32 + Sync + core::panic::RefUnwindSafe), argc: usize, argv: *const *mut u8,
) -> isize {
panicking::catch_unwind(move || {
unsafe { init(argc, argv) };
let ret_code = panicking::catch_unwind(main).unwrap_or_else(move |payload| {
let payload = panicking::AssertUnwindSafe(payload);
panicking::catch_unwind(move || drop({ payload }.0)).unwrap_or_else(move |e| {
core::mem::forget(e); cfg_select! {
panic = "immediate-abort" => {}
_ => {
if let Some(mut out) = crate::os::stdio::panic_output() {
let _ = crate::io::Write::write_fmt(
&mut out,
format_args!(
"fatal runtime error: {}, aborting\n",
format_args!("drop of the panic payload panicked")
),
);
}
}
}
crate::process::abort()
});
101
});
let ret_code = ret_code as isize;
cleanup();
ret_code
})
.unwrap_or_else(handle_rt_panic)
}
#[cfg(not(any(test, doctest)))]
#[doc(hidden)]
pub fn psp_start<T: crate::process::Termination + 'static>(
main: fn() -> T, argc: usize, argv: *const *mut u8,
) -> isize {
psp_start_internal(&move || main().report().to_i32(), argc, argv)
}