diff --git a/ctru-rs/src/error.rs b/ctru-rs/src/error.rs index b01d898..0dd7cc5 100644 --- a/ctru-rs/src/error.rs +++ b/ctru-rs/src/error.rs @@ -28,7 +28,7 @@ pub type Result = ::std::result::Result; /// # let _runner = test_runner::GdbRunner::default(); /// // We run an unsafe function which returns a `ctru_sys::Result`. /// let result: ctru_sys::Result = unsafe { ctru_sys::hidInit() }; -/// +/// /// // The result code is parsed and any possible error gets returned by the function. /// ResultCode(result)?; /// Ok(()) @@ -153,6 +153,8 @@ impl fmt::Debug for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { + // TODO: should we consider using ctru_sys::osStrError here as well? + // It might do some of the work for us or provide additional details &Self::Os(err) => write!( f, "libctru result code 0x{err:08X}: [{} {}] {}: {}", diff --git a/ctru-rs/src/lib.rs b/ctru-rs/src/lib.rs index 72bdebd..dce7011 100644 --- a/ctru-rs/src/lib.rs +++ b/ctru-rs/src/lib.rs @@ -110,6 +110,7 @@ pub mod console; pub mod error; pub mod linear; pub mod mii; +pub mod os; pub mod prelude; pub mod services; diff --git a/ctru-rs/src/os.rs b/ctru-rs/src/os.rs new file mode 100644 index 0000000..8eea697 --- /dev/null +++ b/ctru-rs/src/os.rs @@ -0,0 +1,154 @@ +//! Utilities to get information about the operating system and hardware state. + +/// System version information. This struct is used for both kernel and firmware versions. +/// +/// # Example +/// ``` +/// # // let _runner = test_runner::GdbRunner::default(); +/// let firm_version = ctru::os::firm_version(); +/// assert_ne!(firm_version.major(), 0); +/// +/// let kernel_version = ctru::os::kernel_version(); +/// assert_ne!(kernel_version.major(), 0); +/// ``` +#[derive(Clone, Copy)] +pub struct Version(u32); + +impl Version { + /// Pack a system version from its components + pub fn new(major: u8, minor: u8, revision: u8) -> Self { + let major = u32::from(major); + let minor = u32::from(minor); + let revision = u32::from(revision); + + Self(major << 24 | minor << 16 | revision << 8) + } + + /// Get the major version from a packed system version. + pub fn major(&self) -> u8 { + (self.0 >> 24).try_into().unwrap() + } + + /// Get the minor version from a packed system version. + pub fn minor(&self) -> u8 { + (self.0 >> 16 & 0xFF).try_into().unwrap() + } + + /// Get the revision from a packed system version. + pub fn revision(&self) -> u8 { + (self.0 >> 8 & 0xFF).try_into().unwrap() + } +} + +/// Get the system's FIRM version. +pub fn firm_version() -> Version { + Version(unsafe { ctru_sys::osGetFirmVersion() }) +} + +/// Get the system's kernel version. +pub fn kernel_version() -> Version { + Version(unsafe { ctru_sys::osGetKernelVersion() }) +} + +// TODO: I can't seem to find good documentation on it, but we could probably +// define enums for firmware type (NATIVE_FIRM, SAFE_FIRM etc.) as well as +// application memory layout. Leaving those as future enhancements for now + +/// A region of memory. Most applications will only use [`Application`](MemRegion::Application) +/// memory, but the other types can be used to query memory usage information. +/// See +/// for more details on the different types of memory. +/// +/// # Example +/// ``` +/// # // let _runner = test_runner::GdbRunner::default(); +/// let all_memory = ctru::os::MemRegion::All; +/// +/// assert!(all_memory.size() > 0); +/// assert!(all_memory.used() > 0); +/// assert!(all_memory.free() > 0); +/// ``` +#[derive(Clone, Copy, Debug)] +#[non_exhaustive] +#[repr(u32)] +pub enum MemRegion { + /// All memory regions. + All = ctru_sys::MEMREGION_ALL, + /// APPLICATION memory. + Application = ctru_sys::MEMREGION_APPLICATION, + /// SYSTEM memory. + System = ctru_sys::MEMREGION_SYSTEM, + /// BASE memory. + Base = ctru_sys::MEMREGION_BASE, +} + +impl MemRegion { + /// Get the total size of this memory region, in bytes. + pub fn size(&self) -> usize { + unsafe { ctru_sys::osGetMemRegionSize(*self as u32) } + .try_into() + .unwrap() + } + + /// Get the number of bytes used within this memory region. + pub fn used(&self) -> usize { + unsafe { ctru_sys::osGetMemRegionUsed(*self as u32) } + .try_into() + .unwrap() + } + + /// Get the number of bytes free within this memory region. + pub fn free(&self) -> usize { + unsafe { ctru_sys::osGetMemRegionFree(*self as u32) } + .try_into() + .unwrap() + } +} + +/// WiFi signal strength. This enum's `u8` representation corresponds with +/// the number of bars displayed in the Home menu. +/// +/// # Example +/// +/// ``` +/// # // let _runner = test_runner::GdbRunner::default(); +/// let strength = ctru::os::WifiStrength::current(); +/// assert!((strength as u8) < 4); +/// ``` +#[derive(Clone, Copy, Debug)] +#[non_exhaustive] +#[repr(u8)] +pub enum WifiStrength { + /// This may indicate a very poor signal quality even worse than `Bad`, + /// or that no network is connected at all. + Disconnected = 0, + /// Poor signal strength. + Bad = 1, + /// Medium signal strength. + Decent = 2, + /// Good signal strength. + Good = 3, +} + +impl WifiStrength { + /// Get the current WiFi signal strength. + pub fn current() -> Self { + match unsafe { ctru_sys::osGetWifiStrength() } { + 0 => Self::Disconnected, + 1 => Self::Bad, + 2 => Self::Decent, + 3 => Self::Good, + other => panic!("Got unexpected WiFi strength value {other}"), + } + } +} + +/// Get the current value of the stereoscopic 3D slider on a scale from 0.0­–­1.0. +pub fn current_3d_slider_state() -> f32 { + unsafe { ctru_sys::osGet3DSliderState() } +} + +/// Whether or not a headset is currently plugged into the device. +pub fn is_headset_connected() -> bool { + unsafe { ctru_sys::osIsHeadsetConnected() } +}