From c2827aa2691ce4c9dfe5ae7cb98d38ba97bb934b Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Sat, 30 Sep 2023 23:54:08 -0400 Subject: [PATCH] Add `os` module for querying OS and hardware state Most of these are dead simple and don't require any service initialization etc., so all we need is a simple wrapper. I didn't implement everything in <3ds/os.h> yet, but got most of the basic ones which seemed like likely use cases to me. --- ctru-rs/src/error.rs | 4 +- ctru-rs/src/lib.rs | 1 + ctru-rs/src/os.rs | 135 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 ctru-rs/src/os.rs diff --git a/ctru-rs/src/error.rs b/ctru-rs/src/error.rs index 41b09f8..62a4646 100644 --- a/ctru-rs/src/error.rs +++ b/ctru-rs/src/error.rs @@ -27,7 +27,7 @@ pub type Result = ::std::result::Result; /// pub fn hid_init() -> Result<()> { /// // 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(()) @@ -152,6 +152,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 ecc7dd9..baa9936 100644 --- a/ctru-rs/src/lib.rs +++ b/ctru-rs/src/lib.rs @@ -107,6 +107,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..58a4e87 --- /dev/null +++ b/ctru-rs/src/os.rs @@ -0,0 +1,135 @@ +//! Utilities to get information about the operating system and hardware state. + +/// System version information. +#[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. +#[non_exhaustive] +#[repr(u8)] +pub enum WifiStrength { + /// This may indicate a very poor signal quality even worse than `Bad`, + /// or it may indicate 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() } +}