use core::{marker::PhantomData, ops::ControlFlow};
use bitflag_attr::bitflag;
#[doc(hidden)]
#[cfg(target_os = "psp")]
pub mod macro_helpers;
mod error;
pub use error::{ErrorFacility, SceError};
pub mod audio;
pub mod ctrl;
pub mod display;
pub mod dma;
pub mod ge;
pub mod hprm;
pub mod io;
pub mod libc;
pub mod library;
pub mod loadexec;
pub mod mem;
pub mod module;
pub mod openpsid;
pub mod power;
pub mod suspend;
pub mod thread;
pub mod time;
pub mod usersystemlib;
#[cfg(feature = "non-stub-code")]
pub mod sync;
pub type SceSize = cfg_select! {
target_os = "psp" => usize,
target_pointer_width = "32" => usize,
_ => u32,
};
pub type SceIsize = cfg_select! {
target_os = "psp" => isize,
target_pointer_width = "32" => isize,
_ => i32,
};
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct SceUid(pattern_type!(SceRawUid is 0..=0x7FFFFFFF));
pub type SceRawUid = u32;
impl SceUid {
pub const fn from_raw(raw: u32) -> Option<Self> {
if let 0..=0x7FFFFFFF = raw {
Some(unsafe { Self::from_raw_unchecked(raw) })
} else {
None
}
}
#[inline]
pub const unsafe fn from_raw_unchecked(raw: u32) -> Self {
unsafe { core::mem::transmute(raw) }
}
#[inline]
pub const fn to_inner(self) -> u32 {
unsafe { core::mem::transmute(self) }
}
}
crate::impl_ranged_ty!(SceUid);
impl crate::private::Sealed for SceUid {}
impl Default for SceUid {
fn default() -> Self {
unsafe { Self::from_raw_unchecked(0) }
}
}
impl core::fmt::Debug for SceUid {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_tuple("SceUid").field(&self.to_inner()).finish()
}
}
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[must_use = "this `SceResult` may be an error value, which should be handled"]
pub struct SceResult<T>(u32, PhantomData<T>);
impl<T> SceResult<T> {
pub const fn new(raw: u32) -> Self {
SceResult(raw, PhantomData)
}
#[inline]
pub const fn is_ok(&self) -> bool {
matches!(self.as_inner(), 0..=0x7FFFFFFF)
}
#[inline]
pub const fn is_err(&self) -> bool {
matches!(self.as_inner(), 0x80000001..=0xFFFFFFFF)
}
#[inline]
pub(crate) const fn as_inner(&self) -> u32 {
self.0
}
}
impl<T: SceResultOk> SceResult<T> {
pub fn into_result(self) -> Result<T, SceError> {
match self.as_inner() {
0..=0x7FFFFFFF => unsafe { T::handle_ok_value(self.as_inner()) },
0x80000001..=0xFFFFFFFF => {
Err(unsafe { SceError::from_raw_unchecked(self.as_inner()) })
},
0x80000000 => Err(SceError::INVALID_VALUE),
}
}
pub fn ok(self) -> Option<T> {
self.into_result().ok()
}
pub fn err(self) -> Option<SceError> {
self.into_result().err()
}
pub fn map<U, F>(self, op: F) -> Result<U, SceError>
where
F: FnOnce(T) -> U,
{
self.into_result().map(op)
}
pub fn map_or<U, F>(self, default: U, f: F) -> U
where
F: FnOnce(T) -> U,
{
self.into_result().map_or(default, f)
}
pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
where
D: FnOnce(SceError) -> U,
F: FnOnce(T) -> U,
{
self.into_result().map_or_else(default, f)
}
pub fn map_or_default<U, F>(self, f: F) -> U
where
F: FnOnce(T) -> U,
U: Default,
{
match self.into_result() {
Ok(t) => f(t),
Err(_) => U::default(),
}
}
pub fn map_err<F, O>(self, op: O) -> Result<T, F>
where
O: FnOnce(SceError) -> F,
{
self.into_result().map_err(op)
}
pub fn inspect<F>(self, f: F) -> Self
where
F: FnOnce(&T),
{
if self.is_ok() {
let res = unsafe { T::handle_ok_value(self.as_inner()) };
if let Ok(inner) = res {
f(&inner)
}
}
self
}
pub fn inspect_err<F>(self, f: F) -> Self
where
F: FnOnce(SceError),
{
if self.is_err() {
f(unsafe { SceError::from_raw_unchecked(self.as_inner()) })
}
self
}
}
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[must_use = "this `SceResult` may be an error value, which should be handled"]
pub struct SceResult64<T>(u64, PhantomData<T>);
impl<T> SceResult64<T> {
pub const fn new(raw: u64) -> Self {
SceResult64(raw, PhantomData)
}
#[inline]
pub const fn is_ok(&self) -> bool {
matches!(self.as_inner(), 0..=0xFFFFFFFF_7FFFFFFF)
}
#[inline]
pub const fn is_err(&self) -> bool {
matches!(self.as_inner(), 0xFFFFFFFF_80000001..=0xFFFFFFFF_FFFFFFFF)
}
#[inline]
pub(crate) const fn as_inner(&self) -> u64 {
self.0
}
}
impl<T: SceResultOk> SceResult64<T> {
pub fn into_result(self) -> Result<T, SceError> {
match self.as_inner() {
0..=0xFFFFFFFF_7FFFFFFF => unsafe { T::handle_ok_value64(self.as_inner()) },
0xFFFFFFFF_80000001..=0xFFFFFFFF_FFFFFFFF => {
let err = (self.as_inner() & 0xFFFFFFFF_00000000) as u32;
Err(unsafe { SceError::from_raw_unchecked(err) })
},
0xFFFFFFFF_80000000 => Err(SceError::INVALID_VALUE),
}
}
pub fn ok(self) -> Option<T> {
self.into_result().ok()
}
pub fn err(self) -> Option<SceError> {
self.into_result().err()
}
pub fn map<U, F>(self, op: F) -> Result<U, SceError>
where
F: FnOnce(T) -> U,
{
self.into_result().map(op)
}
pub fn map_or<U, F>(self, default: U, f: F) -> U
where
F: FnOnce(T) -> U,
{
self.into_result().map_or(default, f)
}
pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
where
D: FnOnce(SceError) -> U,
F: FnOnce(T) -> U,
{
self.into_result().map_or_else(default, f)
}
pub fn map_or_default<U, F>(self, f: F) -> U
where
F: FnOnce(T) -> U,
U: Default,
{
match self.into_result() {
Ok(t) => f(t),
Err(_) => U::default(),
}
}
pub fn map_err<F, O>(self, op: O) -> Result<T, F>
where
O: FnOnce(SceError) -> F,
{
self.into_result().map_err(op)
}
pub fn inspect<F>(self, f: F) -> Self
where
F: FnOnce(&T),
{
if self.is_ok() {
let res = unsafe { T::handle_ok_value64(self.as_inner()) };
if let Ok(inner) = res {
f(&inner)
}
}
self
}
pub fn inspect_err<F>(self, f: F) -> Self
where
F: FnOnce(SceError),
{
if self.is_err() {
let err = (self.as_inner() & 0xFFFFFFFF_00000000) as u32;
let err = unsafe { SceError::from_raw_unchecked(err) };
f(err)
}
self
}
}
impl SceResult<()> {
pub const OK: Self = SceResult::new(0);
}
pub unsafe trait SceResultOk: Sized + crate::private::Sealed {
unsafe fn handle_ok_value(ok_value: u32) -> Result<Self, SceError>;
unsafe fn handle_ok_value64(ok_value: u64) -> Result<Self, SceError> {
match ok_value {
0..=0x7FFFFFFF => unsafe { Self::handle_ok_value(ok_value as u32) },
_ => Err(SceError::INVALID_VALUE),
}
}
}
pub unsafe trait SceIntoOkValue: Sized + crate::private::Sealed {
fn into_ok_value(self) -> u32;
}
pub unsafe trait SceInto64OkValue: Sized + crate::private::Sealed {
fn into_ok_value64(self) -> u64;
}
unsafe impl<T: SceIntoOkValue> SceInto64OkValue for T {
fn into_ok_value64(self) -> u64 {
self.into_ok_value() as u64
}
}
macro_rules! __result_ok_int {
($($ty:ty),+) => {
$(
unsafe impl SceResultOk for $ty {
unsafe fn handle_ok_value(ok_value: u32) -> Result<Self, SceError> {
<$ty>::try_from(ok_value).map_err(|_| SceError::INVALID_VALUE)
}
}
unsafe impl SceIntoOkValue for $ty {
fn into_ok_value(self) -> u32 {
self as u32
}
}
)+
};
}
__result_ok_int!(i8, u8, i16, u16, bool);
unsafe impl SceResultOk for i32 {
unsafe fn handle_ok_value(ok_value: u32) -> Result<Self, SceError> {
debug_assert!(ok_value <= 0x7FFFFFFF);
Ok(u32::cast_signed(ok_value))
}
}
unsafe impl SceIntoOkValue for i32 {
fn into_ok_value(self) -> u32 {
self as u32
}
}
unsafe impl SceResultOk for u32 {
unsafe fn handle_ok_value(ok_value: u32) -> Result<Self, SceError> {
debug_assert!(ok_value <= 0x7FFFFFFF);
Ok(ok_value)
}
}
unsafe impl SceIntoOkValue for u32 {
fn into_ok_value(self) -> u32 {
self
}
}
unsafe impl SceResultOk for i64 {
unsafe fn handle_ok_value(ok_value: u32) -> Result<Self, SceError> {
Ok(ok_value as i64)
}
unsafe fn handle_ok_value64(ok_value: u64) -> Result<Self, SceError> {
match ok_value {
0..=0xFFFFFFFF_7FFFFFFF => Ok(u64::cast_signed(ok_value)),
_ => Err(SceError::INVALID_VALUE),
}
}
}
unsafe impl SceResultOk for u64 {
unsafe fn handle_ok_value(ok_value: u32) -> Result<Self, SceError> {
Ok(ok_value as u64)
}
unsafe fn handle_ok_value64(ok_value: u64) -> Result<Self, SceError> {
match ok_value {
0..=0xFFFFFFFF_7FFFFFFF => Ok(ok_value),
_ => Err(SceError::INVALID_VALUE),
}
}
}
unsafe impl SceInto64OkValue for i64 {
fn into_ok_value64(self) -> u64 {
self as u64
}
}
unsafe impl SceInto64OkValue for u64 {
fn into_ok_value64(self) -> u64 {
self
}
}
#[cfg(target_pointer_width = "32")]
unsafe impl SceResultOk for isize {
unsafe fn handle_ok_value(ok_value: u32) -> Result<Self, SceError> {
debug_assert!(ok_value <= 0x7FFFFFFF);
Ok(ok_value as isize)
}
}
#[cfg(target_pointer_width = "32")]
unsafe impl SceIntoOkValue for isize {
fn into_ok_value(self) -> u32 {
self as u32
}
}
#[cfg(target_pointer_width = "32")]
unsafe impl SceResultOk for usize {
unsafe fn handle_ok_value(ok_value: u32) -> Result<Self, SceError> {
debug_assert!(ok_value <= 0x7FFFFFFF);
Ok(ok_value as usize)
}
}
#[cfg(target_pointer_width = "32")]
unsafe impl SceIntoOkValue for usize {
fn into_ok_value(self) -> u32 {
self as u32
}
}
unsafe impl SceResultOk for () {
unsafe fn handle_ok_value(ok_value: u32) -> Result<Self, SceError> {
match ok_value {
0x00 => Ok(()),
_ => Err(SceError::INVALID_VALUE),
}
}
}
unsafe impl SceIntoOkValue for () {
fn into_ok_value(self) -> u32 {
0
}
}
unsafe impl SceResultOk for ! {
unsafe fn handle_ok_value(_ok_value: u32) -> Result<Self, SceError> {
Err(SceError::INVALID_VALUE)
}
}
unsafe impl SceIntoOkValue for ! {
fn into_ok_value(self) -> u32 {
0
}
}
unsafe impl SceResultOk for core::convert::Infallible {
unsafe fn handle_ok_value(_: u32) -> Result<Self, SceError> {
Err(SceError::INVALID_VALUE)
}
}
unsafe impl SceResultOk for SceUid {
unsafe fn handle_ok_value(ok_value: u32) -> Result<Self, SceError> {
debug_assert!(ok_value <= 0x7FFFFFFF);
Ok(unsafe { Self::from_raw_unchecked(ok_value) })
}
}
unsafe impl SceIntoOkValue for SceUid {
fn into_ok_value(self) -> u32 {
self.to_inner()
}
}
impl<T: SceResultOk + SceIntoOkValue> core::ops::Residual<T>
for SceResult<core::convert::Infallible>
{
type TryType = SceResult<T>;
}
impl<T: SceResultOk + SceIntoOkValue> core::ops::FromResidual for SceResult<T> {
fn from_residual(residual: <Self as core::ops::Try>::Residual) -> Self {
Self::new(residual.as_inner())
}
}
impl<T: SceResultOk + SceIntoOkValue> core::ops::Try for SceResult<T> {
type Output = T;
type Residual = SceResult<core::convert::Infallible>;
fn from_output(output: Self::Output) -> Self {
SceResult::new(output.into_ok_value())
}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self.into_result() {
Ok(v) => ControlFlow::Continue(v),
Err(err) => ControlFlow::Break(SceResult::new(err.to_inner())),
}
}
}
impl<T: SceResultOk + SceInto64OkValue> core::ops::Residual<T>
for SceResult64<core::convert::Infallible>
{
type TryType = SceResult64<T>;
}
impl<T: SceResultOk + SceInto64OkValue> core::ops::FromResidual for SceResult64<T> {
fn from_residual(residual: <Self as core::ops::Try>::Residual) -> Self {
Self::new(residual.as_inner())
}
}
impl<T: SceResultOk + SceInto64OkValue> core::ops::Try for SceResult64<T> {
type Output = T;
type Residual = SceResult64<core::convert::Infallible>;
fn from_output(output: Self::Output) -> Self {
SceResult64::new(output.into_ok_value64())
}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self.into_result() {
Ok(v) => ControlFlow::Continue(v),
Err(err) => ControlFlow::Break(SceResult64::new(err.to_inner() as u64)),
}
}
}
#[bitflag(u16)]
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum LibFlags {
NoSpecialFlags = 0x0,
AutoExport = 0x1,
WeakExport = 0x2,
NoLinkExport = 0x4,
WeakImport = 0x8,
SyscallExport = 0x4000,
IsSystemLib = 0x8000,
}
#[macro_export]
#[doc(hidden)]
macro_rules! impl_ranged_ty {
($t:ty) => {
impl ::core::marker::StructuralPartialEq for $t {}
impl Eq for $t {}
impl PartialEq for $t {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.to_inner() == other.to_inner()
}
}
impl Ord for $t {
#[inline]
fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
Ord::cmp(&self.to_inner(), &other.to_inner())
}
}
impl PartialOrd for $t {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
Some(Ord::cmp(self, other))
}
}
impl ::core::hash::Hash for $t {
fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
::core::hash::Hash::hash(&self.to_inner(), state);
}
}
};
}
#[cfg(all(target_os = "psp", feature = "non-stub-code"))]
#[unsafe(naked)]
pub unsafe extern "C" fn set_k1(k1: u32) -> u32 {
core::arch::naked_asm!(
".set noreorder",
".set noat",
"move $v0, $k1",
"jr $ra",
"move $k1, $a0"
)
}
#[cfg(all(target_os = "psp", feature = "non-stub-code"))]
#[unsafe(naked)]
pub extern "C" fn get_k1() -> u32 {
core::arch::naked_asm!(".set noreorder", ".set noat", "jr $ra", "move $v0, $k1")
}
#[cfg(all(target_os = "psp", feature = "non-stub-code"))]
#[unsafe(naked)]
pub extern "C" fn disable_fpu_exceptions() {
core::arch::naked_asm!(
".set noreorder",
".set noat",
"cfc1 $2, $31",
"lui $8, 0x80",
"and $8, $2, $8",
"ctc1 $8, $31",
"jr $31",
"nop",
);
}
#[cfg(all(target_os = "psp", feature = "non-stub-code"))]
#[unsafe(naked)]
pub unsafe extern "C" fn suspend_interrupts() -> u32 {
core::arch::naked_asm!(
".set noreorder",
".set noat",
".word 0x70020024", ".word 0x70000026", "nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"jr $31",
"nop"
)
}
#[cfg(all(target_os = "psp", feature = "non-stub-code"))]
#[unsafe(naked)]
pub unsafe extern "C" fn resume_interrupts(state: u32) {
core::arch::naked_asm!(
".set noreorder",
".set noat",
".word 0x70040026", "nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"jr $31",
"nop",
)
}
#[cfg(all(target_os = "psp", feature = "non-stub-code"))]
#[unsafe(naked)]
pub extern "C" fn get_current_interrupt_status() -> u32 {
core::arch::naked_asm!(
".set noreorder",
".set noat",
".word 0x70020024", "nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"nop",
"jr $31",
"nop"
)
}
#[cfg(all(target_os = "psp", feature = "non-stub-code"))]
pub fn is_interrupt_enabled() -> bool {
get_current_interrupt_status() != 0
}
#[cfg(all(target_os = "psp", feature = "non-stub-code"))]
pub fn spin_loop() {
if is_interrupt_enabled() {
let _ = thread::sceKernelDelayThread(1000);
} else {
core::hint::spin_loop();
}
}
#[cfg(all(target_os = "psp", feature = "non-stub-code"))]
pub(crate) unsafe fn cleanup() {}
#[track_caller]
#[inline(always)]
#[cfg(feature = "non-stub-code")]
pub unsafe fn volatile_write<T>(addr: usize, value: T)
where
T: crate::private::VolatileOpAllowed,
{
unsafe { core::ptr::with_exposed_provenance_mut::<T>(addr).write_volatile(value) };
}
#[track_caller]
#[inline(always)]
#[cfg(feature = "non-stub-code")]
pub unsafe fn volatile_read<T>(addr: usize) -> T
where
T: crate::private::VolatileOpAllowed,
{
unsafe { core::ptr::with_exposed_provenance_mut::<T>(addr).read_volatile() }
}