diff --git a/ctru-rs/examples/system-configuration.rs b/ctru-rs/examples/system-configuration.rs new file mode 100644 index 0000000..47e70c6 --- /dev/null +++ b/ctru-rs/examples/system-configuration.rs @@ -0,0 +1,34 @@ +use ctru::console::Console; +use ctru::gfx::Gfx; +use ctru::services::apt::Apt; +use ctru::services::hid::{Hid, KeyPad}; +use ctru::services::cfgu::Cfgu; + +fn main() { + ctru::init(); + let gfx = Gfx::init().expect("Couldn't obtain GFX controller"); + let hid = Hid::init().expect("Couldn't obtain HID controller"); + let apt = Apt::init().expect("Couldn't obtain APT controller"); + let cfgu = Cfgu::init().expect("Couldn't obtain CFGU controller"); + let _console = Console::init(gfx.top_screen.borrow_mut()); + + println!("\x1b[0;0H{}", format!("Region: {:?}", cfgu.get_region().unwrap())); + println!("\x1b[10;0H{}", format!("Language: {:?}", cfgu.get_language().unwrap())); + println!("\x1b[20;0H{}", format!("Model: {:?}", cfgu.get_model().unwrap())); + + // Main loop + while apt.main_loop() { + //Scan all the inputs. This should be done once for each frame + hid.scan_input(); + + if hid.keys_down().contains(KeyPad::KEY_START) { + break; + } + // Flush and swap framebuffers + gfx.flush_buffers(); + gfx.swap_buffers(); + + //Wait for VBlank + gfx.wait_for_vblank(); + } +} diff --git a/ctru-rs/src/services/cfgu.rs b/ctru-rs/src/services/cfgu.rs new file mode 100644 index 0000000..bc8d986 --- /dev/null +++ b/ctru-rs/src/services/cfgu.rs @@ -0,0 +1,212 @@ +//! Configuration service +//! +//! This module contains basic methods to retrieve and change configuration from the console. + +#[derive(Copy, Clone, Debug)] +#[repr(u32)] +pub enum Region { + Japan = ctru_sys::CFG_REGION_JPN, + USA = ctru_sys::CFG_REGION_USA, + Europe = ctru_sys::CFG_REGION_EUR, + Australia = ctru_sys::CFG_REGION_AUS, + China = ctru_sys::CFG_REGION_CHN, + Korea = ctru_sys::CFG_REGION_KOR, + Taiwan = ctru_sys::CFG_REGION_TWN, +} + +#[derive(Copy, Clone, Debug)] +#[repr(u32)] +pub enum Language { + Japanese = ctru_sys::CFG_LANGUAGE_JP, + English = ctru_sys::CFG_LANGUAGE_EN, + French = ctru_sys::CFG_LANGUAGE_FR, + German = ctru_sys::CFG_LANGUAGE_DE, + Italian = ctru_sys::CFG_LANGUAGE_IT, + Spanish = ctru_sys::CFG_LANGUAGE_ES, + SimplifiedChinese = ctru_sys::CFG_LANGUAGE_ZH, + Korean = ctru_sys::CFG_LANGUAGE_KO, + Dutch = ctru_sys::CFG_LANGUAGE_NL, + Portuguese = ctru_sys::CFG_LANGUAGE_PT, + Russian = ctru_sys::CFG_LANGUAGE_RU, + TraditionalChinese = ctru_sys::CFG_LANGUAGE_TW, +} + +#[derive(Copy, Clone, Debug)] +#[repr(u32)] +pub enum SystemModel { + Model3DS = ctru_sys::CFG_MODEL_3DS, + Model3DSXL = ctru_sys::CFG_MODEL_3DSXL, + ModelNew3DS = ctru_sys::CFG_MODEL_N3DS, + Model2DS = ctru_sys::CFG_MODEL_2DS, + ModelNew3DSXL = ctru_sys::CFG_MODEL_N3DSXL, + ModelNew2DSXL = ctru_sys::CFG_MODEL_N2DSXL, +} + +/// Represents the configuration service. No actions can be performed +/// until an instance of this struct is created. +/// +/// The service exits when all instances of this struct go out of scope. +pub struct Cfgu(()); + +impl Cfgu { + /// Initializes the CFGU service. + /// + /// # Errors + /// + /// This function will return Err if there was an error initializing the + /// CFGU service. + /// + /// ctrulib services are reference counted, so this function may be called + /// as many times as desired and the service will not exit until all + /// instances of Cfgu drop out of scope. + pub fn init() -> crate::Result { + unsafe { + let r = ctru_sys::cfguInit(); + if r < 0 { + Err(r.into()) + } else { + Ok(Cfgu(())) + } + } + } + + /// Gets system region from secure info + pub fn get_region(&self) -> crate::Result { + let mut region: u8 = 0; + + let r = unsafe { ctru_sys::CFGU_SecureInfoGetRegion(&mut region) }; + if r < 0 { + Err(r.into()) + } else { + // The system shouldn't give an invalid value + Ok(Region::try_from(region).unwrap()) + } + } + + /// Gets system's model + pub fn get_model(&self) -> crate::Result { + let mut model: u8 = 0; + + let r = unsafe { ctru_sys::CFGU_GetSystemModel(&mut model) }; + if r < 0 { + Err(r.into()) + } else { + // The system shouldn't give an invalid value + Ok(SystemModel::try_from(model).unwrap()) + } + } + + /// Gets system's language + pub fn get_language(&self) -> crate::Result { + let mut language: u8 = 0; + + let r = unsafe { ctru_sys::CFGU_GetSystemLanguage(&mut language) }; + if r < 0 { + Err(r.into()) + } else { + // The system shouldn't give an invalid value + Ok(Language::try_from(language).unwrap()) + } + } + + /// Checks if NFC is supported by the console + pub fn is_nfc_supported(&self) -> crate::Result { + let mut supported: bool = false; + + let r = unsafe { ctru_sys::CFGU_IsNFCSupported(&mut supported) }; + if r < 0 { + Err(r.into()) + } else { + Ok(supported) + } + } + + /// Check if the console is from the 2DS family (2DS, New2DS, New2DSXL) + pub fn is_2ds_family(&self) -> crate::Result { + let mut is_2ds_family: u8 = 0; + + let r = unsafe { ctru_sys::CFGU_GetModelNintendo2DS(&mut is_2ds_family) }; + if r < 0 { + Err(r.into()) + } else { + Ok(is_2ds_family == 0) + } + } +} + +impl Drop for Cfgu { + fn drop(&mut self) { + unsafe { + ctru_sys::cfguExit(); + } + } +} + +macro_rules! from_type_to_u8 { + ($from_type:ty) => { + impl From<$from_type> for u8 { + fn from(v: $from_type) -> Self { + v as u8 + } + } + }; +} + +from_type_to_u8!(Region); +from_type_to_u8!(Language); +from_type_to_u8!(SystemModel); + +impl TryFrom for Region { + type Error = (); + + fn try_from(value: u8) -> Result { + match value as u32 { + ctru_sys::CFG_REGION_JPN => Ok(Region::Japan), + ctru_sys::CFG_REGION_USA => Ok(Region::USA), + ctru_sys::CFG_REGION_EUR => Ok(Region::Europe), + ctru_sys::CFG_REGION_AUS => Ok(Region::Australia), + ctru_sys::CFG_REGION_CHN => Ok(Region::China), + ctru_sys::CFG_REGION_KOR => Ok(Region::Korea), + ctru_sys::CFG_REGION_TWN => Ok(Region::Taiwan), + _ => Err(()), + } + } +} + +impl TryFrom for Language { + type Error = (); + + fn try_from(value: u8) -> Result { + match value as u32 { + ctru_sys::CFG_LANGUAGE_JP => Ok(Language::Japanese), + ctru_sys::CFG_LANGUAGE_EN => Ok(Language::English), + ctru_sys::CFG_LANGUAGE_FR => Ok(Language::French), + ctru_sys::CFG_LANGUAGE_DE => Ok(Language::German), + ctru_sys::CFG_LANGUAGE_IT => Ok(Language::Italian), + ctru_sys::CFG_LANGUAGE_ES => Ok(Language::Spanish), + ctru_sys::CFG_LANGUAGE_ZH => Ok(Language::SimplifiedChinese), + ctru_sys::CFG_LANGUAGE_KO => Ok(Language::Korean), + ctru_sys::CFG_LANGUAGE_NL => Ok(Language::Dutch), + ctru_sys::CFG_LANGUAGE_PT => Ok(Language::Portuguese), + ctru_sys::CFG_LANGUAGE_RU => Ok(Language::Russian), + ctru_sys::CFG_LANGUAGE_TW => Ok(Language::TraditionalChinese), + _ => Err(()), + } + } +} + +impl TryFrom for SystemModel { + type Error = (); + + fn try_from(value: u8) -> Result { + match value as u32 { + ctru_sys::CFG_MODEL_3DS => Ok(SystemModel::Model3DS), + ctru_sys::CFG_MODEL_3DSXL => Ok(SystemModel::Model3DSXL), + ctru_sys::CFG_MODEL_N3DS => Ok(SystemModel::ModelNew3DS), + ctru_sys::CFG_MODEL_2DS => Ok(SystemModel::Model2DS), + ctru_sys::CFG_MODEL_N3DSXL => Ok(SystemModel::ModelNew3DSXL), + ctru_sys::CFG_MODEL_N2DSXL => Ok(SystemModel::ModelNew2DSXL), + _ => Err(()), + } + } +} diff --git a/ctru-rs/src/services/mod.rs b/ctru-rs/src/services/mod.rs index 0dc750c..8e14bb1 100644 --- a/ctru-rs/src/services/mod.rs +++ b/ctru-rs/src/services/mod.rs @@ -1,4 +1,5 @@ pub mod apt; +pub mod cfgu; pub mod fs; pub mod gspgpu; pub mod hid;