From 32db2c6dbe2ad594fc84c5ba4be666bf08338fd3 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Thu, 20 Oct 2022 21:28:54 +0200 Subject: [PATCH 01/27] Initial mii selector implementation --- ctru-rs/src/applets/mii_selector.rs | 166 ++++++++++++++++++++++++++++ ctru-rs/src/applets/mod.rs | 1 + 2 files changed, 167 insertions(+) create mode 100644 ctru-rs/src/applets/mii_selector.rs diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs new file mode 100644 index 0000000..a54729a --- /dev/null +++ b/ctru-rs/src/applets/mii_selector.rs @@ -0,0 +1,166 @@ +use std::ffi::{CString}; +use bitflags::bitflags; + +#[derive(Debug, Clone)] +pub enum MiiConfigIndex { + Index(u32), + All, +} + +#[derive(Debug, Clone)] +pub enum MiiType { + Guest { + index: u32, + name: String, + }, + User, +} + +bitflags! { + pub struct Options: u32 { + const MII_SELECTOR_CANCEL = ctru_sys::MIISELECTOR_CANCEL; + const MII_SELECTOR_GUESTS = ctru_sys::MIISELECTOR_GUESTS; + const MII_SELECTOR_TOP = ctru_sys::MIISELECTOR_TOP; + const MII_SELECTOR_GUEST_START = ctru_sys::MIISELECTOR_GUESTSTART; + } +} + +pub struct MiiSelector { + config: Box, +} + +pub struct MiiSelectorReturn { + raw_return: Box, + pub is_mii_selected: bool, + pub mii_type: MiiType, + pub checksum: u16, +} + +impl MiiSelector { + pub fn init() -> Self { + let mut config = Box::::default(); + unsafe { ctru_sys::miiSelectorInit(config.as_mut()); } + Self { config } + } + + pub fn set_title(&mut self, text: &str) { + // This can only fail if the text contains NUL bytes in the string... which seems + // unlikely and is documented + let c_text = CString::new(text).expect("Cstring::new failed"); + unsafe { ctru_sys::miiSelectorSetTitle(self.config.as_mut(), c_text.as_ptr()); } + } + + pub fn set_options(&mut self, options: Options) { + unsafe { ctru_sys::miiSelectorSetOptions(self.config.as_mut(), options.bits) } + } + + pub fn whitelist_guest_mii(&mut self, mii_index: MiiConfigIndex) { + match mii_index { + MiiConfigIndex::Index(i) => { + unsafe { ctru_sys::miiSelectorWhitelistGuestMii(self.config.as_mut(), i) } + } + MiiConfigIndex::All => { + unsafe { ctru_sys::miiSelectorWhitelistGuestMii(self.config.as_mut(), ctru_sys::MIISELECTOR_GUESTMII_SLOTS) } + } + } + } + + pub fn blacklist_guest_mii(&mut self, mii_index: MiiConfigIndex) { + match mii_index { + MiiConfigIndex::Index(i) => { + unsafe { ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), i) } + } + MiiConfigIndex::All => { + unsafe { ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), ctru_sys::MIISELECTOR_GUESTMII_SLOTS) } + } + } + } + + pub fn whitelist_user_mii(&mut self, mii_index: MiiConfigIndex) { + match mii_index { + MiiConfigIndex::Index(i) => { + unsafe { ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), i) } + } + MiiConfigIndex::All => { + unsafe { ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), ctru_sys::MIISELECTOR_USERMII_SLOTS) } + } + } + } + + pub fn blacklist_user_mii(&mut self, mii_index: MiiConfigIndex) { + match mii_index { + MiiConfigIndex::Index(i) => { + unsafe { ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), i) } + } + MiiConfigIndex::All => { + unsafe { ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), ctru_sys::MIISELECTOR_USERMII_SLOTS) } + } + } + } + + // This function is static inline in libctru + // https://github.com/devkitPro/libctru/blob/af5321c78ee5c72a55b526fd2ed0d95ca1c05af9/libctru/include/3ds/applets/miiselector.h#L155 + pub fn set_initial_index(&mut self, index: u32) { + self.config.initial_index = index + } + + pub fn launch(&mut self) -> MiiSelectorReturn { + let mut return_val = Box::::default(); + unsafe { + ctru_sys::miiSelectorLaunch(self.config.as_mut(), return_val.as_mut()) + } + + return_val.into() + } +} + +impl MiiSelectorReturn { + pub fn name(&self) -> String { + let mut tmp = [0u8; 36]; + unsafe { ctru_sys::miiSelectorReturnGetName(self.raw_return.as_ref(), tmp.as_mut_ptr(), 36) } + + let len = unsafe { libc::strlen(tmp.as_ptr()) }; + let utf8 = unsafe { std::str::from_utf8_unchecked(&tmp[..len]) }; + + utf8.to_owned() + } + + pub fn author(&self) -> String { + let mut tmp = [0u8; 30]; + unsafe { ctru_sys::miiSelectorReturnGetName(self.raw_return.as_ref(), tmp.as_mut_ptr(), 30) } + let utf8 = unsafe { std::str::from_utf8_unchecked(&tmp) }; + + utf8.to_owned() + } + + pub fn valid_checksum(&self) -> bool { + unsafe { ctru_sys::miiSelectorChecksumIsValid(self.raw_return.as_ref()) } + } +} + +impl From> for MiiSelectorReturn { + fn from(ret: Box) -> Self { + let checksum = ret.checksum; + let no_mii_selected = ret.no_mii_selected; + let guest_mii_index_clone = ret.guest_mii_index; + let mut guest_mii_name = ret.guest_mii_name; + + MiiSelectorReturn { + raw_return: ret, + is_mii_selected: no_mii_selected == 0, + mii_type: if guest_mii_index_clone != 0xFFFFFFFF { + MiiType::Guest { + index: guest_mii_index_clone, + name: { + let utf16_be = &mut guest_mii_name; + utf16_be.reverse(); + String::from_utf16(utf16_be.as_slice()).unwrap() + }, + } + } else { + MiiType::User + }, + checksum, + } + } +} \ No newline at end of file diff --git a/ctru-rs/src/applets/mod.rs b/ctru-rs/src/applets/mod.rs index 73469a3..47eec73 100644 --- a/ctru-rs/src/applets/mod.rs +++ b/ctru-rs/src/applets/mod.rs @@ -1 +1,2 @@ pub mod swkbd; +pub mod mii_selector; \ No newline at end of file From b33411f8ac52ef19770e61fb410ac1f11c330ab1 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Thu, 20 Oct 2022 22:16:58 +0200 Subject: [PATCH 02/27] Fix bug with author and impl ``From`` for ``MiiConfigIndex`` --- ctru-rs/src/applets/mii_selector.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index a54729a..599a480 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -127,8 +127,10 @@ impl MiiSelectorReturn { pub fn author(&self) -> String { let mut tmp = [0u8; 30]; - unsafe { ctru_sys::miiSelectorReturnGetName(self.raw_return.as_ref(), tmp.as_mut_ptr(), 30) } - let utf8 = unsafe { std::str::from_utf8_unchecked(&tmp) }; + unsafe { ctru_sys::miiSelectorReturnGetAuthor(self.raw_return.as_ref(), tmp.as_mut_ptr(), 30) } + + let len = unsafe { libc::strlen(tmp.as_ptr()) }; + let utf8 = unsafe { std::str::from_utf8_unchecked(&tmp[..len]) }; utf8.to_owned() } @@ -163,4 +165,10 @@ impl From> for MiiSelectorReturn { checksum, } } +} + +impl From for MiiConfigIndex { + fn from(v: u32) -> Self { + Self::Index(v) + } } \ No newline at end of file From 4b435614b4b5f340ae5246f9fe33b042cd3aebb3 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Thu, 20 Oct 2022 22:17:52 +0200 Subject: [PATCH 03/27] Add ``mii_selector`` example --- ctru-rs/examples/mii_selector.rs | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 ctru-rs/examples/mii_selector.rs diff --git a/ctru-rs/examples/mii_selector.rs b/ctru-rs/examples/mii_selector.rs new file mode 100644 index 0000000..a9d8779 --- /dev/null +++ b/ctru-rs/examples/mii_selector.rs @@ -0,0 +1,41 @@ +use ctru::applets::mii_selector::MiiSelector; +use ctru::prelude::*; + +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 _console = Console::init(gfx.top_screen.borrow_mut()); + + let mut mii_selector = MiiSelector::init(); + mii_selector.set_initial_index(3); + mii_selector.blacklist_user_mii(0.into()); + mii_selector.set_title("Great Mii Selector!"); + + let result = mii_selector.launch(); + + println!("\x1b[0;0HIs Mii selected?: {:?}", result.is_mii_selected); + println!("\x1b[2;0HValid checksum?: {:?}", result.valid_checksum()); + println!("\x1b[4;0HMii type: {:?}", result.mii_type); + println!("\x1b[6;0HMii checksum: {:?}", result.checksum); + println!("\x1b[8;0HName: {:?}", result.name()); + println!("\x1b[12;0HAuthor: {:?}", result.author()); + + // 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(); + } +} \ No newline at end of file From de51d97d1ca5427dc978b05bd8336ca3ee686984 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Thu, 20 Oct 2022 23:13:27 +0200 Subject: [PATCH 04/27] Cargo fmt --- ctru-rs/examples/mii_selector.rs | 2 +- ctru-rs/src/applets/mii_selector.rs | 89 +++++++++++++++++------------ ctru-rs/src/applets/mod.rs | 2 +- 3 files changed, 54 insertions(+), 39 deletions(-) diff --git a/ctru-rs/examples/mii_selector.rs b/ctru-rs/examples/mii_selector.rs index a9d8779..40c774e 100644 --- a/ctru-rs/examples/mii_selector.rs +++ b/ctru-rs/examples/mii_selector.rs @@ -38,4 +38,4 @@ fn main() { //Wait for VBlank gfx.wait_for_vblank(); } -} \ No newline at end of file +} diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index 599a480..634cd88 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -1,5 +1,5 @@ -use std::ffi::{CString}; use bitflags::bitflags; +use std::ffi::CString; #[derive(Debug, Clone)] pub enum MiiConfigIndex { @@ -9,10 +9,7 @@ pub enum MiiConfigIndex { #[derive(Debug, Clone)] pub enum MiiType { - Guest { - index: u32, - name: String, - }, + Guest { index: u32, name: String }, User, } @@ -39,7 +36,9 @@ pub struct MiiSelectorReturn { impl MiiSelector { pub fn init() -> Self { let mut config = Box::::default(); - unsafe { ctru_sys::miiSelectorInit(config.as_mut()); } + unsafe { + ctru_sys::miiSelectorInit(config.as_mut()); + } Self { config } } @@ -47,7 +46,9 @@ impl MiiSelector { // This can only fail if the text contains NUL bytes in the string... which seems // unlikely and is documented let c_text = CString::new(text).expect("Cstring::new failed"); - unsafe { ctru_sys::miiSelectorSetTitle(self.config.as_mut(), c_text.as_ptr()); } + unsafe { + ctru_sys::miiSelectorSetTitle(self.config.as_mut(), c_text.as_ptr()); + } } pub fn set_options(&mut self, options: Options) { @@ -56,45 +57,57 @@ impl MiiSelector { pub fn whitelist_guest_mii(&mut self, mii_index: MiiConfigIndex) { match mii_index { - MiiConfigIndex::Index(i) => { - unsafe { ctru_sys::miiSelectorWhitelistGuestMii(self.config.as_mut(), i) } - } - MiiConfigIndex::All => { - unsafe { ctru_sys::miiSelectorWhitelistGuestMii(self.config.as_mut(), ctru_sys::MIISELECTOR_GUESTMII_SLOTS) } - } + MiiConfigIndex::Index(i) => unsafe { + ctru_sys::miiSelectorWhitelistGuestMii(self.config.as_mut(), i) + }, + MiiConfigIndex::All => unsafe { + ctru_sys::miiSelectorWhitelistGuestMii( + self.config.as_mut(), + ctru_sys::MIISELECTOR_GUESTMII_SLOTS, + ) + }, } } pub fn blacklist_guest_mii(&mut self, mii_index: MiiConfigIndex) { match mii_index { - MiiConfigIndex::Index(i) => { - unsafe { ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), i) } - } - MiiConfigIndex::All => { - unsafe { ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), ctru_sys::MIISELECTOR_GUESTMII_SLOTS) } - } + MiiConfigIndex::Index(i) => unsafe { + ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), i) + }, + MiiConfigIndex::All => unsafe { + ctru_sys::miiSelectorBlacklistGuestMii( + self.config.as_mut(), + ctru_sys::MIISELECTOR_GUESTMII_SLOTS, + ) + }, } } pub fn whitelist_user_mii(&mut self, mii_index: MiiConfigIndex) { match mii_index { - MiiConfigIndex::Index(i) => { - unsafe { ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), i) } - } - MiiConfigIndex::All => { - unsafe { ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), ctru_sys::MIISELECTOR_USERMII_SLOTS) } - } + MiiConfigIndex::Index(i) => unsafe { + ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), i) + }, + MiiConfigIndex::All => unsafe { + ctru_sys::miiSelectorWhitelistUserMii( + self.config.as_mut(), + ctru_sys::MIISELECTOR_USERMII_SLOTS, + ) + }, } } pub fn blacklist_user_mii(&mut self, mii_index: MiiConfigIndex) { match mii_index { - MiiConfigIndex::Index(i) => { - unsafe { ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), i) } - } - MiiConfigIndex::All => { - unsafe { ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), ctru_sys::MIISELECTOR_USERMII_SLOTS) } - } + MiiConfigIndex::Index(i) => unsafe { + ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), i) + }, + MiiConfigIndex::All => unsafe { + ctru_sys::miiSelectorBlacklistUserMii( + self.config.as_mut(), + ctru_sys::MIISELECTOR_USERMII_SLOTS, + ) + }, } } @@ -106,9 +119,7 @@ impl MiiSelector { pub fn launch(&mut self) -> MiiSelectorReturn { let mut return_val = Box::::default(); - unsafe { - ctru_sys::miiSelectorLaunch(self.config.as_mut(), return_val.as_mut()) - } + unsafe { ctru_sys::miiSelectorLaunch(self.config.as_mut(), return_val.as_mut()) } return_val.into() } @@ -117,7 +128,9 @@ impl MiiSelector { impl MiiSelectorReturn { pub fn name(&self) -> String { let mut tmp = [0u8; 36]; - unsafe { ctru_sys::miiSelectorReturnGetName(self.raw_return.as_ref(), tmp.as_mut_ptr(), 36) } + unsafe { + ctru_sys::miiSelectorReturnGetName(self.raw_return.as_ref(), tmp.as_mut_ptr(), 36) + } let len = unsafe { libc::strlen(tmp.as_ptr()) }; let utf8 = unsafe { std::str::from_utf8_unchecked(&tmp[..len]) }; @@ -127,7 +140,9 @@ impl MiiSelectorReturn { pub fn author(&self) -> String { let mut tmp = [0u8; 30]; - unsafe { ctru_sys::miiSelectorReturnGetAuthor(self.raw_return.as_ref(), tmp.as_mut_ptr(), 30) } + unsafe { + ctru_sys::miiSelectorReturnGetAuthor(self.raw_return.as_ref(), tmp.as_mut_ptr(), 30) + } let len = unsafe { libc::strlen(tmp.as_ptr()) }; let utf8 = unsafe { std::str::from_utf8_unchecked(&tmp[..len]) }; @@ -171,4 +186,4 @@ impl From for MiiConfigIndex { fn from(v: u32) -> Self { Self::Index(v) } -} \ No newline at end of file +} diff --git a/ctru-rs/src/applets/mod.rs b/ctru-rs/src/applets/mod.rs index 47eec73..f5813ad 100644 --- a/ctru-rs/src/applets/mod.rs +++ b/ctru-rs/src/applets/mod.rs @@ -1,2 +1,2 @@ +pub mod mii_selector; pub mod swkbd; -pub mod mii_selector; \ No newline at end of file From 3c9bd8f2febb0d94141a1e9db236a01538a25470 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Fri, 21 Oct 2022 19:59:41 +0200 Subject: [PATCH 05/27] Implement MiiData parsing --- ctru-rs/src/applets/mii_selector.rs | 4 + ctru-rs/src/lib.rs | 1 + ctru-rs/src/mii.rs | 435 ++++++++++++++++++++++++++++ 3 files changed, 440 insertions(+) create mode 100644 ctru-rs/src/mii.rs diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index 634cd88..dc88bde 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -1,5 +1,6 @@ use bitflags::bitflags; use std::ffi::CString; +use crate::mii::MiiData; #[derive(Debug, Clone)] pub enum MiiConfigIndex { @@ -28,6 +29,7 @@ pub struct MiiSelector { pub struct MiiSelectorReturn { raw_return: Box, + pub mii_data: MiiData, pub is_mii_selected: bool, pub mii_type: MiiType, pub checksum: u16, @@ -158,12 +160,14 @@ impl MiiSelectorReturn { impl From> for MiiSelectorReturn { fn from(ret: Box) -> Self { let checksum = ret.checksum; + let raw_mii_data = ret.mii._bindgen_opaque_blob.clone(); let no_mii_selected = ret.no_mii_selected; let guest_mii_index_clone = ret.guest_mii_index; let mut guest_mii_name = ret.guest_mii_name; MiiSelectorReturn { raw_return: ret, + mii_data: raw_mii_data.into(), is_mii_selected: no_mii_selected == 0, mii_type: if guest_mii_index_clone != 0xFFFFFFFF { MiiType::Guest { diff --git a/ctru-rs/src/lib.rs b/ctru-rs/src/lib.rs index 264d0ef..20282c6 100644 --- a/ctru-rs/src/lib.rs +++ b/ctru-rs/src/lib.rs @@ -78,6 +78,7 @@ pub mod error; pub mod gfx; pub mod prelude; pub mod services; +pub mod mii; cfg_if::cfg_if! { if #[cfg(all(feature = "romfs", romfs_exists))] { diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs new file mode 100644 index 0000000..b211d76 --- /dev/null +++ b/ctru-rs/src/mii.rs @@ -0,0 +1,435 @@ +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum RegionLock { + NoLock, + Japan, + USA, + Europe, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Charset { + JapanUSAEurope, + China, + Korea, + Taiwan, +} + +#[derive(Clone, Debug)] +pub struct MiiDataOptions { + pub is_copying_allowed: bool, + pub is_profanity_flag_enabled: bool, + pub region_lock: RegionLock, + pub charset: Charset, +} + +#[derive(Clone, Debug)] +pub struct SelectorPosition { + pub page_index: u8, + pub slot_index: u8, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum OriginConsole { + ConsoleWii, + ConsoleDSi, + Console3DS, + ConsoleWiiUSwitch +} + +#[derive(Clone, Debug)] +pub struct ConsoleIdentity { + pub origin_console: OriginConsole, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum MiiSex { + Male, + Female, +} + +#[derive(Clone, Debug)] +pub struct Details { + pub sex: MiiSex, + pub birthday_month: u8, + pub birthday_day: u8, + pub shirt_color: u8, + pub is_favorite: bool, +} + +#[derive(Clone, Debug)] +pub struct FaceStyle { + pub is_sharing_enabled: bool, + pub shape: u8, + pub skin_color: u8, +} + +#[derive(Clone, Debug)] +pub struct FaceDetails { + pub wrinkles: u8, + pub makeup: u8, +} + +#[derive(Clone, Debug)] +pub struct HairDetails { + pub color: u8, + pub is_flipped: bool, +} + +#[derive(Clone, Debug)] +pub struct EyeDetails { + pub style: u8, + pub color: u8, + pub scale: u8, + pub y_scale: u8, + pub rotation: u8, + pub x_spacing: u8, + pub y_position: u8, +} + +#[derive(Clone, Debug)] +pub struct EyebrowDetails { + pub style: u8, + pub color: u8, + pub scale: u8, + pub y_scale: u8, + pub rotation: u8, + pub x_spacing: u8, + pub y_position: u8, +} + +#[derive(Clone, Debug)] +pub struct NoseDetails { + pub style: u8, + pub scale: u8, + pub y_position: u8, +} + +#[derive(Clone, Debug)] +pub struct MouthDetails { + pub style: u8, + pub color: u8, + pub scale: u8, + pub y_scale: u8, +} + +#[derive(Clone, Debug)] +pub struct MustacheDetails { + pub mouth_y_position: u8, + pub mustache_style: u8, +} + +#[derive(Clone, Debug)] +pub struct BeardDetails { + pub style: u8, + pub color: u8, + pub scale: u8, + pub y_position: u8, +} + +#[derive(Clone, Debug)] +pub struct GlassDetails { + pub style: u8, + pub color: u8, + pub scale: u8, + pub y_position: u8, +} + +#[derive(Clone, Debug)] +pub struct MoleDetails { + pub is_enabled: bool, + pub scale: u8, + pub x_position: u8, + pub y_position: u8, +} + +#[derive(Clone, Debug)] +pub struct MiiData { + pub mii_options: MiiDataOptions, + pub mii_selector_position: SelectorPosition, + pub mii_console_identity: ConsoleIdentity, + + pub system_id: [u8; 8], + pub mac_address: [u8; 6], + + pub mii_details: Details, + pub mii_name: String, + + pub height: u8, + pub width: u8, + + pub face_style: FaceStyle, + pub face_details: FaceDetails, + + pub hair_style: u8, + + pub hair_details: HairDetails, + pub eye_details: EyeDetails, + pub eyebrow_details: EyebrowDetails, + pub nose_details: NoseDetails, + pub mouth_details: MouthDetails, + pub mustache_details: MustacheDetails, + pub beard_details: BeardDetails, + pub glass_details: GlassDetails, + pub mole_details: MoleDetails, + + pub author_name: String, +} + +impl From<[u8; 92]> for MiiData { + fn from(raw_mii_data: [u8; 92]) -> Self { + // Source for the representation and what each thing means: https://www.3dbrew.org/wiki/Mii + let raw_options = vec_bit(raw_mii_data[0x1]); + let raw_position = vec_bit(raw_mii_data[0x2]); + let raw_device = vec_bit(raw_mii_data[0x3]); + let system_id = [raw_mii_data[0x4], raw_mii_data[0x5], raw_mii_data[0x6], raw_mii_data[0x7], raw_mii_data[0x8], raw_mii_data[0x9], raw_mii_data[0xA], raw_mii_data[0xB]]; + let creator_mac = [raw_mii_data[0x10], raw_mii_data[0x11], raw_mii_data[0x12], raw_mii_data[0x13], raw_mii_data[0x14], raw_mii_data[0x15]]; + let raw_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x18, 0x19]).try_into().unwrap(); + let raw_utf16_name = &raw_mii_data.as_slice()[0x1A..0x2D]; + let height = raw_mii_data[0x2E]; + let width = raw_mii_data[0x2F]; + let raw_face_style = vec_bit(raw_mii_data[0x30]); + let raw_face_details = vec_bit(raw_mii_data[0x31]); + let hair_style = raw_mii_data[0x32]; + let raw_hair_details = vec_bit(raw_mii_data[0x33]); + let raw_eye_details: [bool; 32] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x34, 0x35, 0x36, 0x37]).try_into().unwrap(); + let raw_eyebrow_details: [bool; 32] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x38, 0x39, 0x3A, 0x3B]).try_into().unwrap(); + let raw_nose_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x3C, 0x3D]).try_into().unwrap(); + let raw_mouth_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x3E, 0x3F]).try_into().unwrap(); + let raw_mustache_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x40, 0x41]).try_into().unwrap(); + let raw_beard_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x42, 0x42]).try_into().unwrap(); + let raw_glass_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x44, 0x45]).try_into().unwrap(); + let raw_mole_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x46, 0x47]).try_into().unwrap(); + let raw_utf16_author = &raw_mii_data.as_slice()[0x48..0x5C]; + + let mii_name = utf16_byte_pairs_to_string(raw_utf16_name); + let author_name = utf16_byte_pairs_to_string(raw_utf16_author); + + let options = MiiDataOptions { + is_copying_allowed: *raw_options.first().unwrap(), + is_profanity_flag_enabled: *raw_options.get(1).unwrap(), + region_lock: { + let first_bit = raw_options[3]; + let second_bit = raw_options[2]; + if !first_bit && !second_bit { // 0b00 + RegionLock::NoLock + } else if !first_bit && second_bit { // 0b01 + RegionLock::Japan + } else if first_bit && !second_bit { // 0b10 + RegionLock::USA + } else { + RegionLock::Europe + } + }, + charset: { + let first_bit = raw_options[5]; + let second_bit = raw_options[4]; + if !first_bit && !second_bit { // 0b00 + Charset::JapanUSAEurope + } else if !first_bit && second_bit { // 0b01 + Charset::China + } else if first_bit && !second_bit { // 0b10 + Charset::Korea + } else { + Charset::Taiwan + } + }, + }; + + let position = SelectorPosition { + page_index: partial_vec_to_u8_with_reverse(&raw_position[0..3]), + //page_index: vec_bit_to_u8([false, false, false, false, raw_position[3], raw_position[2], raw_position[1], raw_position[0]]), + slot_index: partial_vec_to_u8_with_reverse(&raw_position[4..7]) + }; + + let device = ConsoleIdentity { + origin_console: { + let first_bit = raw_device[6]; + let second_bit = raw_device[5]; + let third_bit = raw_device[4]; + + if !first_bit && !second_bit && third_bit { + OriginConsole::ConsoleWii + } else if !first_bit && second_bit && !third_bit { + OriginConsole::ConsoleDSi + } else if !first_bit && second_bit && third_bit { + OriginConsole::Console3DS + } else { + OriginConsole::ConsoleWiiUSwitch + } + } + }; + + let details = Details { + sex: { + let first_bit = raw_details[0]; + if first_bit { + MiiSex::Female + } else { + MiiSex::Male + } + }, + birthday_month: partial_vec_to_u8_with_reverse(&raw_details[1..4]), + birthday_day: partial_vec_to_u8_with_reverse(&raw_details[5..9]), + shirt_color: partial_vec_to_u8_with_reverse(&raw_details[10..13]), + is_favorite: raw_details[14], + }; + + let face_style = FaceStyle { + is_sharing_enabled: !raw_face_style[1], + shape: partial_vec_to_u8_with_reverse(&raw_face_style[1..4]), + skin_color: partial_vec_to_u8_with_reverse(&raw_face_style[5..7]), + }; + + let face_details = FaceDetails { + wrinkles: partial_vec_to_u8_with_reverse(&raw_face_details[0..3]), + makeup: partial_vec_to_u8_with_reverse(&raw_face_details[4..7]), + }; + + let hair_details = HairDetails { + color: partial_vec_to_u8_with_reverse(&raw_hair_details[0..2]), + is_flipped: raw_hair_details[3], + }; + + let eye_details = EyeDetails { + style: partial_vec_to_u8_with_reverse(&raw_eye_details[0..5]), + color: partial_vec_to_u8_with_reverse(&raw_eye_details[6..8]), + scale: partial_vec_to_u8_with_reverse(&raw_eye_details[9..12]), + y_scale: partial_vec_to_u8_with_reverse(&raw_eye_details[13..15]), + rotation: partial_vec_to_u8_with_reverse(&raw_eye_details[16..20]), + x_spacing: partial_vec_to_u8_with_reverse(&raw_eye_details[21..24]), + y_position: partial_vec_to_u8_with_reverse(&raw_eye_details[25..29]), + }; + + let eyebrow_details = EyebrowDetails { + style: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[0..4]), + color: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[5..7]), + scale: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[8..11]), + y_scale: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[12..14]), + rotation: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[16..19]), + x_spacing: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[21..24]), + y_position: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[25..29]), + }; + + let nose_details = NoseDetails { + style: partial_vec_to_u8_with_reverse(&raw_nose_details[0..4]), + scale: partial_vec_to_u8_with_reverse(&raw_nose_details[5..8]), + y_position: partial_vec_to_u8_with_reverse(&raw_nose_details[9..13]), + }; + + let mouth_details = MouthDetails { + style: partial_vec_to_u8_with_reverse(&raw_mouth_details[0..5]), + color: partial_vec_to_u8_with_reverse(&raw_mouth_details[6..8]), + scale: partial_vec_to_u8_with_reverse(&raw_mouth_details[9..12]), + y_scale: partial_vec_to_u8_with_reverse(&raw_mouth_details[13..15]), + }; + + let mustache_details = MustacheDetails { + mouth_y_position: partial_vec_to_u8_with_reverse(&raw_mustache_details[0..4]), + mustache_style: partial_vec_to_u8_with_reverse(&raw_mustache_details[5..7]), + }; + + let beard_details = BeardDetails { + style: partial_vec_to_u8_with_reverse(&raw_beard_details[0..2]), + color: partial_vec_to_u8_with_reverse(&raw_beard_details[3..5]), + scale: partial_vec_to_u8_with_reverse(&raw_beard_details[6..9]), + y_position: partial_vec_to_u8_with_reverse(&raw_beard_details[10..14]), + }; + + let glass_details = GlassDetails { + style: partial_vec_to_u8_with_reverse(&raw_glass_details[0..3]), + color: partial_vec_to_u8_with_reverse(&raw_glass_details[4..6]), + scale: partial_vec_to_u8_with_reverse(&raw_glass_details[7..10]), + y_position: partial_vec_to_u8_with_reverse(&raw_glass_details[11..15]), + }; + + let mole_details = MoleDetails { + is_enabled: raw_mole_details[0], + scale: partial_vec_to_u8_with_reverse(&raw_mole_details[1..4]), + x_position: partial_vec_to_u8_with_reverse(&raw_mole_details[5..9]), + y_position: partial_vec_to_u8_with_reverse(&raw_mole_details[10..14]), + }; + + MiiData { + mii_options: options, + mii_selector_position: position, + mii_console_identity: device, + system_id, + mac_address: creator_mac, + mii_details: details, + mii_name, + height, + width, + face_style, + face_details, + hair_style, + hair_details, + eye_details, + eyebrow_details, + nose_details, + mouth_details, + mustache_details, + beard_details, + glass_details, + mole_details, + author_name, + } + } +} + +// Methods to handle "_bits_", ``bitvec`` cannot compile to 32-bit targets, so I had to create a few +// helper methods + +/// Transforms a u8 into a [bool; 8] +fn vec_bit(data: u8) -> [bool; 8] { + (0..8) + .map(|i| (data & (1 << i)) != 0) + .collect::>() + .try_into() + .unwrap() +} + +/// Transforms a [bool; 8] into an u8 +fn vec_bit_to_u8(data: [bool; 8]) -> u8 { + let mut result: u8 = 0; + data + .map(|v| if v { 1_u8 } else { 0_u8 }) + .iter() + .for_each(|&bit| { + result <<= 1; + result ^= bit; + }); + result +} + +/// The reverse allows to write things on a more _humane_ way, but return a LE u8 +fn partial_vec_to_u8_with_reverse(data: &[bool]) -> u8 { + let leading_zeroes_to_add = 8 - data.len(); + let leading_zeroes = vec![false; leading_zeroes_to_add]; + let mut val = [data, leading_zeroes.as_slice()].concat(); + val.reverse(); + vec_bit_to_u8(val.try_into().unwrap()) +} + +/// UTF-16 Strings are give in pairs of bytes (u8), this converts them into an _actual_ string +fn utf16_byte_pairs_to_string(data: &[u8]) -> String { + let raw_utf16_composed = data + .chunks(2) + .collect::>() + .iter() + .map(|v| { + u16::from_le_bytes([*v.get(0).unwrap_or(&0), *v.get(1).unwrap_or(&0)]) + }) + .collect::>(); + + String::from_utf16(raw_utf16_composed.as_slice()).unwrap().replace("\0", "") +} + +/// Gets the values from the slice and concatenates them +fn get_and_concat_vec_bit(data: &[u8], get_values: &[usize]) -> Vec { + get_values.iter() + .map(|v| { + vec_bit(data[*v]) + }) + .collect::>() + .concat() +} \ No newline at end of file From e6cc659ba3f7f6285a42c529fe8cffad8ebaab89 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Fri, 21 Oct 2022 20:00:10 +0200 Subject: [PATCH 06/27] Rename Mii Selector example --- ctru-rs/examples/{mii_selector.rs => mii-selector.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ctru-rs/examples/{mii_selector.rs => mii-selector.rs} (100%) diff --git a/ctru-rs/examples/mii_selector.rs b/ctru-rs/examples/mii-selector.rs similarity index 100% rename from ctru-rs/examples/mii_selector.rs rename to ctru-rs/examples/mii-selector.rs From 19ebd494d39506cef81f09f02fb24a56607c54ed Mon Sep 17 00:00:00 2001 From: TechiePi Date: Fri, 21 Oct 2022 20:02:03 +0200 Subject: [PATCH 07/27] Add MiiData example --- ctru-rs/examples/mii-selector.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ctru-rs/examples/mii-selector.rs b/ctru-rs/examples/mii-selector.rs index 40c774e..ade3164 100644 --- a/ctru-rs/examples/mii-selector.rs +++ b/ctru-rs/examples/mii-selector.rs @@ -17,11 +17,12 @@ fn main() { let result = mii_selector.launch(); println!("\x1b[0;0HIs Mii selected?: {:?}", result.is_mii_selected); - println!("\x1b[2;0HValid checksum?: {:?}", result.valid_checksum()); - println!("\x1b[4;0HMii type: {:?}", result.mii_type); - println!("\x1b[6;0HMii checksum: {:?}", result.checksum); - println!("\x1b[8;0HName: {:?}", result.name()); - println!("\x1b[12;0HAuthor: {:?}", result.author()); + println!("\x1b[1;0HValid checksum?: {:?}", result.valid_checksum()); + println!("\x1b[2;0HMii type: {:?}", result.mii_type); + println!("\x1b[3;0HMii checksum: {:?}", result.checksum); + println!("\x1b[4;0HName: {:?}", result.name()); + println!("\x1b[5;0HAuthor: {:?}", result.author()); + println!("\x1b[5;0HDoes it have moles?: {:?}", result.mii_data.mole_details.is_enabled); // Main loop while apt.main_loop() { From 0a6654aa55b81cb04ab07a3cf47925e3e1d2f3fe Mon Sep 17 00:00:00 2001 From: TechiePi Date: Fri, 21 Oct 2022 20:02:52 +0200 Subject: [PATCH 08/27] Clippy --- ctru-rs/src/applets/mii_selector.rs | 2 +- ctru-rs/src/mii.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index dc88bde..9a4a860 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -160,7 +160,7 @@ impl MiiSelectorReturn { impl From> for MiiSelectorReturn { fn from(ret: Box) -> Self { let checksum = ret.checksum; - let raw_mii_data = ret.mii._bindgen_opaque_blob.clone(); + let raw_mii_data = ret.mii._bindgen_opaque_blob; let no_mii_selected = ret.no_mii_selected; let guest_mii_index_clone = ret.guest_mii_index; let mut guest_mii_name = ret.guest_mii_name; diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index b211d76..f3b75ee 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -417,11 +417,11 @@ fn utf16_byte_pairs_to_string(data: &[u8]) -> String { .collect::>() .iter() .map(|v| { - u16::from_le_bytes([*v.get(0).unwrap_or(&0), *v.get(1).unwrap_or(&0)]) + u16::from_le_bytes([*v.first().unwrap_or(&0), *v.get(1).unwrap_or(&0)]) }) .collect::>(); - String::from_utf16(raw_utf16_composed.as_slice()).unwrap().replace("\0", "") + String::from_utf16(raw_utf16_composed.as_slice()).unwrap().replace('\0', "") } /// Gets the values from the slice and concatenates them From 1dbb576651a80e580a21a7d6137de2cb640dbf82 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Fri, 21 Oct 2022 20:04:06 +0200 Subject: [PATCH 09/27] Remove comment --- ctru-rs/src/mii.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index f3b75ee..daaf173 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -237,7 +237,6 @@ impl From<[u8; 92]> for MiiData { let position = SelectorPosition { page_index: partial_vec_to_u8_with_reverse(&raw_position[0..3]), - //page_index: vec_bit_to_u8([false, false, false, false, raw_position[3], raw_position[2], raw_position[1], raw_position[0]]), slot_index: partial_vec_to_u8_with_reverse(&raw_position[4..7]) }; From 4b99e92b19b691929e814e6ebb60c59484a35ea2 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Fri, 21 Oct 2022 21:57:05 +0200 Subject: [PATCH 10/27] Cargo fmt & clippy --- ctru-rs/examples/mii-selector.rs | 5 +- ctru-rs/src/applets/mii_selector.rs | 2 +- ctru-rs/src/lib.rs | 2 +- ctru-rs/src/mii.rs | 117 +++++++++++++++++++--------- 4 files changed, 87 insertions(+), 39 deletions(-) diff --git a/ctru-rs/examples/mii-selector.rs b/ctru-rs/examples/mii-selector.rs index ade3164..92075ed 100644 --- a/ctru-rs/examples/mii-selector.rs +++ b/ctru-rs/examples/mii-selector.rs @@ -22,7 +22,10 @@ fn main() { println!("\x1b[3;0HMii checksum: {:?}", result.checksum); println!("\x1b[4;0HName: {:?}", result.name()); println!("\x1b[5;0HAuthor: {:?}", result.author()); - println!("\x1b[5;0HDoes it have moles?: {:?}", result.mii_data.mole_details.is_enabled); + println!( + "\x1b[5;0HDoes it have moles?: {:?}", + result.mii_data.mole_details.is_enabled + ); // Main loop while apt.main_loop() { diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index 9a4a860..82bebd8 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -1,6 +1,6 @@ +use crate::mii::MiiData; use bitflags::bitflags; use std::ffi::CString; -use crate::mii::MiiData; #[derive(Debug, Clone)] pub enum MiiConfigIndex { diff --git a/ctru-rs/src/lib.rs b/ctru-rs/src/lib.rs index 20282c6..f461822 100644 --- a/ctru-rs/src/lib.rs +++ b/ctru-rs/src/lib.rs @@ -76,9 +76,9 @@ pub mod applets; pub mod console; pub mod error; pub mod gfx; +pub mod mii; pub mod prelude; pub mod services; -pub mod mii; cfg_if::cfg_if! { if #[cfg(all(feature = "romfs", romfs_exists))] { diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index daaf173..b944843 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -33,7 +33,7 @@ pub enum OriginConsole { ConsoleWii, ConsoleDSi, Console3DS, - ConsoleWiiUSwitch + ConsoleWiiUSwitch, } #[derive(Clone, Debug)] @@ -181,9 +181,28 @@ impl From<[u8; 92]> for MiiData { let raw_options = vec_bit(raw_mii_data[0x1]); let raw_position = vec_bit(raw_mii_data[0x2]); let raw_device = vec_bit(raw_mii_data[0x3]); - let system_id = [raw_mii_data[0x4], raw_mii_data[0x5], raw_mii_data[0x6], raw_mii_data[0x7], raw_mii_data[0x8], raw_mii_data[0x9], raw_mii_data[0xA], raw_mii_data[0xB]]; - let creator_mac = [raw_mii_data[0x10], raw_mii_data[0x11], raw_mii_data[0x12], raw_mii_data[0x13], raw_mii_data[0x14], raw_mii_data[0x15]]; - let raw_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x18, 0x19]).try_into().unwrap(); + let system_id = [ + raw_mii_data[0x4], + raw_mii_data[0x5], + raw_mii_data[0x6], + raw_mii_data[0x7], + raw_mii_data[0x8], + raw_mii_data[0x9], + raw_mii_data[0xA], + raw_mii_data[0xB], + ]; + let creator_mac = [ + raw_mii_data[0x10], + raw_mii_data[0x11], + raw_mii_data[0x12], + raw_mii_data[0x13], + raw_mii_data[0x14], + raw_mii_data[0x15], + ]; + let raw_details: [bool; 16] = + get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x18, 0x19]) + .try_into() + .unwrap(); let raw_utf16_name = &raw_mii_data.as_slice()[0x1A..0x2D]; let height = raw_mii_data[0x2E]; let width = raw_mii_data[0x2F]; @@ -191,14 +210,38 @@ impl From<[u8; 92]> for MiiData { let raw_face_details = vec_bit(raw_mii_data[0x31]); let hair_style = raw_mii_data[0x32]; let raw_hair_details = vec_bit(raw_mii_data[0x33]); - let raw_eye_details: [bool; 32] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x34, 0x35, 0x36, 0x37]).try_into().unwrap(); - let raw_eyebrow_details: [bool; 32] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x38, 0x39, 0x3A, 0x3B]).try_into().unwrap(); - let raw_nose_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x3C, 0x3D]).try_into().unwrap(); - let raw_mouth_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x3E, 0x3F]).try_into().unwrap(); - let raw_mustache_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x40, 0x41]).try_into().unwrap(); - let raw_beard_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x42, 0x42]).try_into().unwrap(); - let raw_glass_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x44, 0x45]).try_into().unwrap(); - let raw_mole_details: [bool; 16] = get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x46, 0x47]).try_into().unwrap(); + let raw_eye_details: [bool; 32] = + get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x34, 0x35, 0x36, 0x37]) + .try_into() + .unwrap(); + let raw_eyebrow_details: [bool; 32] = + get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x38, 0x39, 0x3A, 0x3B]) + .try_into() + .unwrap(); + let raw_nose_details: [bool; 16] = + get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x3C, 0x3D]) + .try_into() + .unwrap(); + let raw_mouth_details: [bool; 16] = + get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x3E, 0x3F]) + .try_into() + .unwrap(); + let raw_mustache_details: [bool; 16] = + get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x40, 0x41]) + .try_into() + .unwrap(); + let raw_beard_details: [bool; 16] = + get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x42, 0x42]) + .try_into() + .unwrap(); + let raw_glass_details: [bool; 16] = + get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x44, 0x45]) + .try_into() + .unwrap(); + let raw_mole_details: [bool; 16] = + get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x46, 0x47]) + .try_into() + .unwrap(); let raw_utf16_author = &raw_mii_data.as_slice()[0x48..0x5C]; let mii_name = utf16_byte_pairs_to_string(raw_utf16_name); @@ -210,11 +253,14 @@ impl From<[u8; 92]> for MiiData { region_lock: { let first_bit = raw_options[3]; let second_bit = raw_options[2]; - if !first_bit && !second_bit { // 0b00 + if !first_bit && !second_bit { + // 0b00 RegionLock::NoLock - } else if !first_bit && second_bit { // 0b01 + } else if !first_bit && second_bit { + // 0b01 RegionLock::Japan - } else if first_bit && !second_bit { // 0b10 + } else if first_bit && !second_bit { + // 0b10 RegionLock::USA } else { RegionLock::Europe @@ -223,11 +269,14 @@ impl From<[u8; 92]> for MiiData { charset: { let first_bit = raw_options[5]; let second_bit = raw_options[4]; - if !first_bit && !second_bit { // 0b00 + if !first_bit && !second_bit { + // 0b00 Charset::JapanUSAEurope - } else if !first_bit && second_bit { // 0b01 + } else if !first_bit && second_bit { + // 0b01 Charset::China - } else if first_bit && !second_bit { // 0b10 + } else if first_bit && !second_bit { + // 0b10 Charset::Korea } else { Charset::Taiwan @@ -237,7 +286,7 @@ impl From<[u8; 92]> for MiiData { let position = SelectorPosition { page_index: partial_vec_to_u8_with_reverse(&raw_position[0..3]), - slot_index: partial_vec_to_u8_with_reverse(&raw_position[4..7]) + slot_index: partial_vec_to_u8_with_reverse(&raw_position[4..7]), }; let device = ConsoleIdentity { @@ -255,7 +304,7 @@ impl From<[u8; 92]> for MiiData { } else { OriginConsole::ConsoleWiiUSwitch } - } + }, }; let details = Details { @@ -390,13 +439,10 @@ fn vec_bit(data: u8) -> [bool; 8] { /// Transforms a [bool; 8] into an u8 fn vec_bit_to_u8(data: [bool; 8]) -> u8 { let mut result: u8 = 0; - data - .map(|v| if v { 1_u8 } else { 0_u8 }) - .iter() - .for_each(|&bit| { - result <<= 1; - result ^= bit; - }); + data.map(u8::from).iter().for_each(|&bit| { + result <<= 1; + result ^= bit; + }); result } @@ -415,20 +461,19 @@ fn utf16_byte_pairs_to_string(data: &[u8]) -> String { .chunks(2) .collect::>() .iter() - .map(|v| { - u16::from_le_bytes([*v.first().unwrap_or(&0), *v.get(1).unwrap_or(&0)]) - }) + .map(|v| u16::from_le_bytes([*v.first().unwrap_or(&0), *v.get(1).unwrap_or(&0)])) .collect::>(); - String::from_utf16(raw_utf16_composed.as_slice()).unwrap().replace('\0', "") + String::from_utf16(raw_utf16_composed.as_slice()) + .unwrap() + .replace('\0', "") } /// Gets the values from the slice and concatenates them fn get_and_concat_vec_bit(data: &[u8], get_values: &[usize]) -> Vec { - get_values.iter() - .map(|v| { - vec_bit(data[*v]) - }) + get_values + .iter() + .map(|v| vec_bit(data[*v])) .collect::>() .concat() -} \ No newline at end of file +} From 1d92f0f48cc398f336bc7bb7df75b463c1742e97 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Sun, 23 Oct 2022 15:46:20 +0200 Subject: [PATCH 11/27] Apply suggestions --- ctru-rs/src/mii.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index b944843..d990434 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -1,6 +1,6 @@ #[derive(Clone, Debug, Eq, PartialEq)] pub enum RegionLock { - NoLock, + None, Japan, USA, Europe, From fe0d03cbb7c043d375e5a15a87ac4d417e048b75 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Sun, 23 Oct 2022 16:56:58 +0200 Subject: [PATCH 12/27] Fix Mii Selector example --- ctru-rs/examples/mii-selector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctru-rs/examples/mii-selector.rs b/ctru-rs/examples/mii-selector.rs index 92075ed..2732cdb 100644 --- a/ctru-rs/examples/mii-selector.rs +++ b/ctru-rs/examples/mii-selector.rs @@ -23,7 +23,7 @@ fn main() { println!("\x1b[4;0HName: {:?}", result.name()); println!("\x1b[5;0HAuthor: {:?}", result.author()); println!( - "\x1b[5;0HDoes it have moles?: {:?}", + "\x1b[6;0HDoes the Mii have moles?: {:?}", result.mii_data.mole_details.is_enabled ); From be87134b4077d5b4f2dc557c9ae10dfff0fe99a1 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Sun, 23 Oct 2022 17:04:44 +0200 Subject: [PATCH 13/27] Fix ``RegionLock::None`` refactor --- ctru-rs/src/mii.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index d990434..396cee1 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -255,7 +255,7 @@ impl From<[u8; 92]> for MiiData { let second_bit = raw_options[2]; if !first_bit && !second_bit { // 0b00 - RegionLock::NoLock + RegionLock::None } else if !first_bit && second_bit { // 0b01 RegionLock::Japan From 7ca152a02a797b4cc67abfe7fab39a96767e89de Mon Sep 17 00:00:00 2001 From: TechiePi Date: Mon, 24 Oct 2022 02:40:10 +0200 Subject: [PATCH 14/27] Apply some suggestions --- ctru-rs/src/applets/mii_selector.rs | 114 ++++++------------ ctru-rs/src/mii.rs | 174 ++++++++++++---------------- 2 files changed, 106 insertions(+), 182 deletions(-) diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index 82bebd8..9602a35 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -28,13 +28,16 @@ pub struct MiiSelector { } pub struct MiiSelectorReturn { - raw_return: Box, pub mii_data: MiiData, pub is_mii_selected: bool, pub mii_type: MiiType, pub checksum: u16, } +pub enum MiiLaunchError { + InvalidChecksum, +} + impl MiiSelector { pub fn init() -> Self { let mut config = Box::::default(); @@ -47,7 +50,7 @@ impl MiiSelector { pub fn set_title(&mut self, text: &str) { // This can only fail if the text contains NUL bytes in the string... which seems // unlikely and is documented - let c_text = CString::new(text).expect("Cstring::new failed"); + let c_text = CString::new(text).expect("Failed to convert the title text into a CString"); unsafe { ctru_sys::miiSelectorSetTitle(self.config.as_mut(), c_text.as_ptr()); } @@ -58,59 +61,39 @@ impl MiiSelector { } pub fn whitelist_guest_mii(&mut self, mii_index: MiiConfigIndex) { - match mii_index { - MiiConfigIndex::Index(i) => unsafe { - ctru_sys::miiSelectorWhitelistGuestMii(self.config.as_mut(), i) - }, - MiiConfigIndex::All => unsafe { - ctru_sys::miiSelectorWhitelistGuestMii( - self.config.as_mut(), - ctru_sys::MIISELECTOR_GUESTMII_SLOTS, - ) - }, - } + let index = match mii_index { + MiiConfigIndex::Index(i) => i, + MiiConfigIndex::All => ctru_sys::MIISELECTOR_GUESTMII_SLOTS, + }; + + unsafe { ctru_sys::miiSelectorWhitelistGuestMii(self.config.as_mut(), index) } } pub fn blacklist_guest_mii(&mut self, mii_index: MiiConfigIndex) { - match mii_index { - MiiConfigIndex::Index(i) => unsafe { - ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), i) - }, - MiiConfigIndex::All => unsafe { - ctru_sys::miiSelectorBlacklistGuestMii( - self.config.as_mut(), - ctru_sys::MIISELECTOR_GUESTMII_SLOTS, - ) - }, - } + let index = match mii_index { + MiiConfigIndex::Index(i) => i, + MiiConfigIndex::All => ctru_sys::MIISELECTOR_GUESTMII_SLOTS, + }; + + unsafe { ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), index) } } pub fn whitelist_user_mii(&mut self, mii_index: MiiConfigIndex) { - match mii_index { - MiiConfigIndex::Index(i) => unsafe { - ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), i) - }, - MiiConfigIndex::All => unsafe { - ctru_sys::miiSelectorWhitelistUserMii( - self.config.as_mut(), - ctru_sys::MIISELECTOR_USERMII_SLOTS, - ) - }, - } + let index = match mii_index { + MiiConfigIndex::Index(i) => i, + MiiConfigIndex::All => ctru_sys::MIISELECTOR_USERMII_SLOTS, + }; + + unsafe { ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), index) } } pub fn blacklist_user_mii(&mut self, mii_index: MiiConfigIndex) { - match mii_index { - MiiConfigIndex::Index(i) => unsafe { - ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), i) - }, - MiiConfigIndex::All => unsafe { - ctru_sys::miiSelectorBlacklistUserMii( - self.config.as_mut(), - ctru_sys::MIISELECTOR_USERMII_SLOTS, - ) - }, - } + let index = match mii_index { + MiiConfigIndex::Index(i) => i, + MiiConfigIndex::All => ctru_sys::MIISELECTOR_USERMII_SLOTS, + }; + + unsafe { ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), index) } } // This function is static inline in libctru @@ -119,54 +102,27 @@ impl MiiSelector { self.config.initial_index = index } - pub fn launch(&mut self) -> MiiSelectorReturn { + pub fn launch(&mut self) -> Result { let mut return_val = Box::::default(); unsafe { ctru_sys::miiSelectorLaunch(self.config.as_mut(), return_val.as_mut()) } - return_val.into() - } -} - -impl MiiSelectorReturn { - pub fn name(&self) -> String { - let mut tmp = [0u8; 36]; - unsafe { - ctru_sys::miiSelectorReturnGetName(self.raw_return.as_ref(), tmp.as_mut_ptr(), 36) - } - - let len = unsafe { libc::strlen(tmp.as_ptr()) }; - let utf8 = unsafe { std::str::from_utf8_unchecked(&tmp[..len]) }; - - utf8.to_owned() - } - - pub fn author(&self) -> String { - let mut tmp = [0u8; 30]; - unsafe { - ctru_sys::miiSelectorReturnGetAuthor(self.raw_return.as_ref(), tmp.as_mut_ptr(), 30) + if unsafe { ctru_sys::miiSelectorChecksumIsValid(return_val.as_mut()) } { + Ok(return_val.into()) + } else { + Err(MiiLaunchError::InvalidChecksum) } - - let len = unsafe { libc::strlen(tmp.as_ptr()) }; - let utf8 = unsafe { std::str::from_utf8_unchecked(&tmp[..len]) }; - - utf8.to_owned() - } - - pub fn valid_checksum(&self) -> bool { - unsafe { ctru_sys::miiSelectorChecksumIsValid(self.raw_return.as_ref()) } } } impl From> for MiiSelectorReturn { fn from(ret: Box) -> Self { let checksum = ret.checksum; - let raw_mii_data = ret.mii._bindgen_opaque_blob; + let raw_mii_data = ret.mii; let no_mii_selected = ret.no_mii_selected; let guest_mii_index_clone = ret.guest_mii_index; let mut guest_mii_name = ret.guest_mii_name; MiiSelectorReturn { - raw_return: ret, mii_data: raw_mii_data.into(), is_mii_selected: no_mii_selected == 0, mii_type: if guest_mii_index_clone != 0xFFFFFFFF { diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index 396cee1..c966b92 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum RegionLock { None, Japan, @@ -6,7 +6,7 @@ pub enum RegionLock { Europe, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Charset { JapanUSAEurope, China, @@ -14,7 +14,7 @@ pub enum Charset { Taiwan, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct MiiDataOptions { pub is_copying_allowed: bool, pub is_profanity_flag_enabled: bool, @@ -22,13 +22,13 @@ pub struct MiiDataOptions { pub charset: Charset, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct SelectorPosition { pub page_index: u8, pub slot_index: u8, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum OriginConsole { ConsoleWii, ConsoleDSi, @@ -36,18 +36,18 @@ pub enum OriginConsole { ConsoleWiiUSwitch, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct ConsoleIdentity { pub origin_console: OriginConsole, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum MiiSex { Male, Female, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct Details { pub sex: MiiSex, pub birthday_month: u8, @@ -56,26 +56,26 @@ pub struct Details { pub is_favorite: bool, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct FaceStyle { pub is_sharing_enabled: bool, pub shape: u8, pub skin_color: u8, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct FaceDetails { pub wrinkles: u8, pub makeup: u8, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct HairDetails { pub color: u8, pub is_flipped: bool, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct EyeDetails { pub style: u8, pub color: u8, @@ -86,7 +86,7 @@ pub struct EyeDetails { pub y_position: u8, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct EyebrowDetails { pub style: u8, pub color: u8, @@ -97,14 +97,14 @@ pub struct EyebrowDetails { pub y_position: u8, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct NoseDetails { pub style: u8, pub scale: u8, pub y_position: u8, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct MouthDetails { pub style: u8, pub color: u8, @@ -112,13 +112,13 @@ pub struct MouthDetails { pub y_scale: u8, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct MustacheDetails { pub mouth_y_position: u8, pub mustache_style: u8, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct BeardDetails { pub style: u8, pub color: u8, @@ -126,7 +126,7 @@ pub struct BeardDetails { pub y_position: u8, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct GlassDetails { pub style: u8, pub color: u8, @@ -134,7 +134,7 @@ pub struct GlassDetails { pub y_position: u8, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct MoleDetails { pub is_enabled: bool, pub scale: u8, @@ -144,15 +144,15 @@ pub struct MoleDetails { #[derive(Clone, Debug)] pub struct MiiData { - pub mii_options: MiiDataOptions, - pub mii_selector_position: SelectorPosition, - pub mii_console_identity: ConsoleIdentity, + pub options: MiiDataOptions, + pub selector_position: SelectorPosition, + pub console_identity: ConsoleIdentity, pub system_id: [u8; 8], pub mac_address: [u8; 6], - pub mii_details: Details, - pub mii_name: String, + pub details: Details, + pub name: String, pub height: u8, pub width: u8, @@ -175,8 +175,9 @@ pub struct MiiData { pub author_name: String, } -impl From<[u8; 92]> for MiiData { - fn from(raw_mii_data: [u8; 92]) -> Self { +impl From for MiiData { + fn from(mii_data: ctru_sys::MiiData) -> Self { + let raw_mii_data = mii_data._bindgen_opaque_blob; // Source for the representation and what each thing means: https://www.3dbrew.org/wiki/Mii let raw_options = vec_bit(raw_mii_data[0x1]); let raw_position = vec_bit(raw_mii_data[0x2]); @@ -200,10 +201,10 @@ impl From<[u8; 92]> for MiiData { raw_mii_data[0x15], ]; let raw_details: [bool; 16] = - get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x18, 0x19]) + get_and_concat_vec_bit(&raw_mii_data, &[0x18, 0x19]) .try_into() .unwrap(); - let raw_utf16_name = &raw_mii_data.as_slice()[0x1A..0x2D]; + let raw_utf16_name = &raw_mii_data[0x1A..0x2D]; let height = raw_mii_data[0x2E]; let width = raw_mii_data[0x2F]; let raw_face_style = vec_bit(raw_mii_data[0x30]); @@ -211,75 +212,59 @@ impl From<[u8; 92]> for MiiData { let hair_style = raw_mii_data[0x32]; let raw_hair_details = vec_bit(raw_mii_data[0x33]); let raw_eye_details: [bool; 32] = - get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x34, 0x35, 0x36, 0x37]) + get_and_concat_vec_bit(&raw_mii_data, &[0x34, 0x35, 0x36, 0x37]) .try_into() .unwrap(); let raw_eyebrow_details: [bool; 32] = - get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x38, 0x39, 0x3A, 0x3B]) + get_and_concat_vec_bit(&raw_mii_data, &[0x38, 0x39, 0x3A, 0x3B]) .try_into() .unwrap(); let raw_nose_details: [bool; 16] = - get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x3C, 0x3D]) + get_and_concat_vec_bit(&raw_mii_data, &[0x3C, 0x3D]) .try_into() .unwrap(); let raw_mouth_details: [bool; 16] = - get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x3E, 0x3F]) + get_and_concat_vec_bit(&raw_mii_data, &[0x3E, 0x3F]) .try_into() .unwrap(); let raw_mustache_details: [bool; 16] = - get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x40, 0x41]) + get_and_concat_vec_bit(&raw_mii_data, &[0x40, 0x41]) .try_into() .unwrap(); let raw_beard_details: [bool; 16] = - get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x42, 0x42]) + get_and_concat_vec_bit(&raw_mii_data, &[0x42, 0x42]) .try_into() .unwrap(); let raw_glass_details: [bool; 16] = - get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x44, 0x45]) + get_and_concat_vec_bit(&raw_mii_data, &[0x44, 0x45]) .try_into() .unwrap(); let raw_mole_details: [bool; 16] = - get_and_concat_vec_bit(raw_mii_data.as_slice(), &[0x46, 0x47]) + get_and_concat_vec_bit(&raw_mii_data, &[0x46, 0x47]) .try_into() .unwrap(); - let raw_utf16_author = &raw_mii_data.as_slice()[0x48..0x5C]; + let raw_utf16_author = &raw_mii_data[0x48..0x5C]; let mii_name = utf16_byte_pairs_to_string(raw_utf16_name); let author_name = utf16_byte_pairs_to_string(raw_utf16_author); let options = MiiDataOptions { - is_copying_allowed: *raw_options.first().unwrap(), - is_profanity_flag_enabled: *raw_options.get(1).unwrap(), + is_copying_allowed: raw_options[0], + is_profanity_flag_enabled: raw_options[1], region_lock: { - let first_bit = raw_options[3]; - let second_bit = raw_options[2]; - if !first_bit && !second_bit { - // 0b00 - RegionLock::None - } else if !first_bit && second_bit { - // 0b01 - RegionLock::Japan - } else if first_bit && !second_bit { - // 0b10 - RegionLock::USA - } else { - RegionLock::Europe + match (raw_options[3], raw_options[2]) { + (false, false) => RegionLock::None, + (false, true) => RegionLock::Japan, + (true, false) => RegionLock::USA, + (true, true) => RegionLock::Europe, } }, charset: { - let first_bit = raw_options[5]; - let second_bit = raw_options[4]; - if !first_bit && !second_bit { - // 0b00 - Charset::JapanUSAEurope - } else if !first_bit && second_bit { - // 0b01 - Charset::China - } else if first_bit && !second_bit { - // 0b10 - Charset::Korea - } else { - Charset::Taiwan + match (raw_options[5], raw_options[4]) { + (false, false) => Charset::JapanUSAEurope, + (false, true) => Charset::China, + (true, false) => Charset::Korea, + (true, true) => Charset::Taiwan, } }, }; @@ -291,29 +276,20 @@ impl From<[u8; 92]> for MiiData { let device = ConsoleIdentity { origin_console: { - let first_bit = raw_device[6]; - let second_bit = raw_device[5]; - let third_bit = raw_device[4]; - - if !first_bit && !second_bit && third_bit { - OriginConsole::ConsoleWii - } else if !first_bit && second_bit && !third_bit { - OriginConsole::ConsoleDSi - } else if !first_bit && second_bit && third_bit { - OriginConsole::Console3DS - } else { - OriginConsole::ConsoleWiiUSwitch + match (raw_device[6], raw_device[5], raw_device[4]) { + (false, false, true) => OriginConsole::ConsoleWii, + (false, true, false) => OriginConsole::ConsoleDSi, + (false, true, true) => OriginConsole::Console3DS, + _ => OriginConsole::ConsoleWiiUSwitch, } }, }; let details = Details { sex: { - let first_bit = raw_details[0]; - if first_bit { - MiiSex::Female - } else { - MiiSex::Male + match raw_details[0] { + true => MiiSex::Female, + false => MiiSex::Male, } }, birthday_month: partial_vec_to_u8_with_reverse(&raw_details[1..4]), @@ -398,13 +374,13 @@ impl From<[u8; 92]> for MiiData { }; MiiData { - mii_options: options, - mii_selector_position: position, - mii_console_identity: device, + options: options, + selector_position: position, + console_identity: device, system_id, mac_address: creator_mac, - mii_details: details, - mii_name, + details: details, + name: mii_name, height, width, face_style, @@ -438,12 +414,9 @@ fn vec_bit(data: u8) -> [bool; 8] { /// Transforms a [bool; 8] into an u8 fn vec_bit_to_u8(data: [bool; 8]) -> u8 { - let mut result: u8 = 0; - data.map(u8::from).iter().for_each(|&bit| { - result <<= 1; - result ^= bit; - }); - result + data.into_iter().fold(0, |result, bit| { + (result << 1) ^ u8::from(bit) + }) } /// The reverse allows to write things on a more _humane_ way, but return a LE u8 @@ -458,22 +431,17 @@ fn partial_vec_to_u8_with_reverse(data: &[bool]) -> u8 { /// UTF-16 Strings are give in pairs of bytes (u8), this converts them into an _actual_ string fn utf16_byte_pairs_to_string(data: &[u8]) -> String { let raw_utf16_composed = data - .chunks(2) - .collect::>() - .iter() - .map(|v| u16::from_le_bytes([*v.first().unwrap_or(&0), *v.get(1).unwrap_or(&0)])) + .chunks_exact(2) + .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]])) .collect::>(); - String::from_utf16(raw_utf16_composed.as_slice()) - .unwrap() - .replace('\0', "") + String::from_utf16_lossy(raw_utf16_composed.as_slice()) } /// Gets the values from the slice and concatenates them fn get_and_concat_vec_bit(data: &[u8], get_values: &[usize]) -> Vec { get_values .iter() - .map(|v| vec_bit(data[*v])) - .collect::>() - .concat() + .flat_map(|v| vec_bit(data[*v])) + .collect() } From a9569b5e5ac4241407efa9d53be677aa380ab3a6 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Mon, 24 Oct 2022 03:01:05 +0200 Subject: [PATCH 15/27] Improve ``mii-selector`` example and a few fixes --- ctru-rs/examples/mii-selector.rs | 17 ++++++++--------- ctru-rs/src/applets/mii_selector.rs | 3 +++ ctru-rs/src/mii.rs | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ctru-rs/examples/mii-selector.rs b/ctru-rs/examples/mii-selector.rs index 2732cdb..1b89532 100644 --- a/ctru-rs/examples/mii-selector.rs +++ b/ctru-rs/examples/mii-selector.rs @@ -14,16 +14,15 @@ fn main() { mii_selector.blacklist_user_mii(0.into()); mii_selector.set_title("Great Mii Selector!"); - let result = mii_selector.launch(); - - println!("\x1b[0;0HIs Mii selected?: {:?}", result.is_mii_selected); - println!("\x1b[1;0HValid checksum?: {:?}", result.valid_checksum()); - println!("\x1b[2;0HMii type: {:?}", result.mii_type); - println!("\x1b[3;0HMii checksum: {:?}", result.checksum); - println!("\x1b[4;0HName: {:?}", result.name()); - println!("\x1b[5;0HAuthor: {:?}", result.author()); + let result = mii_selector.launch().unwrap(); + + println!("Is Mii selected?: {:?}", result.is_mii_selected); + println!("Mii type: {:?}", result.mii_type); + println!("Mii checksum: {:?}", result.checksum); + println!("Name: {:?}", result.mii_data.name); + println!("Author: {:?}", result.mii_data.author_name); println!( - "\x1b[6;0HDoes the Mii have moles?: {:?}", + "Does the Mii have moles?: {:?}", result.mii_data.mole_details.is_enabled ); diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index 9602a35..d4fe6d1 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -23,10 +23,12 @@ bitflags! { } } +#[derive(Clone, Debug)] pub struct MiiSelector { config: Box, } +#[derive(Clone, Debug)] pub struct MiiSelectorReturn { pub mii_data: MiiData, pub is_mii_selected: bool, @@ -34,6 +36,7 @@ pub struct MiiSelectorReturn { pub checksum: u16, } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum MiiLaunchError { InvalidChecksum, } diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index c966b92..b9d7285 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -435,7 +435,7 @@ fn utf16_byte_pairs_to_string(data: &[u8]) -> String { .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]])) .collect::>(); - String::from_utf16_lossy(raw_utf16_composed.as_slice()) + String::from_utf16_lossy(raw_utf16_composed.as_slice()).replace("\0", "") } /// Gets the values from the slice and concatenates them From d7a684363375eaf71fbc09b117b770c6a0f3ae9b Mon Sep 17 00:00:00 2001 From: TechiePi Date: Mon, 24 Oct 2022 03:01:39 +0200 Subject: [PATCH 16/27] Clippy & cargo fmt --- ctru-rs/src/mii.rs | 65 +++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index b9d7285..565d181 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -200,10 +200,9 @@ impl From for MiiData { raw_mii_data[0x14], raw_mii_data[0x15], ]; - let raw_details: [bool; 16] = - get_and_concat_vec_bit(&raw_mii_data, &[0x18, 0x19]) - .try_into() - .unwrap(); + let raw_details: [bool; 16] = get_and_concat_vec_bit(&raw_mii_data, &[0x18, 0x19]) + .try_into() + .unwrap(); let raw_utf16_name = &raw_mii_data[0x1A..0x2D]; let height = raw_mii_data[0x2E]; let width = raw_mii_data[0x2F]; @@ -219,30 +218,24 @@ impl From for MiiData { get_and_concat_vec_bit(&raw_mii_data, &[0x38, 0x39, 0x3A, 0x3B]) .try_into() .unwrap(); - let raw_nose_details: [bool; 16] = - get_and_concat_vec_bit(&raw_mii_data, &[0x3C, 0x3D]) - .try_into() - .unwrap(); - let raw_mouth_details: [bool; 16] = - get_and_concat_vec_bit(&raw_mii_data, &[0x3E, 0x3F]) - .try_into() - .unwrap(); - let raw_mustache_details: [bool; 16] = - get_and_concat_vec_bit(&raw_mii_data, &[0x40, 0x41]) - .try_into() - .unwrap(); - let raw_beard_details: [bool; 16] = - get_and_concat_vec_bit(&raw_mii_data, &[0x42, 0x42]) - .try_into() - .unwrap(); - let raw_glass_details: [bool; 16] = - get_and_concat_vec_bit(&raw_mii_data, &[0x44, 0x45]) - .try_into() - .unwrap(); - let raw_mole_details: [bool; 16] = - get_and_concat_vec_bit(&raw_mii_data, &[0x46, 0x47]) - .try_into() - .unwrap(); + let raw_nose_details: [bool; 16] = get_and_concat_vec_bit(&raw_mii_data, &[0x3C, 0x3D]) + .try_into() + .unwrap(); + let raw_mouth_details: [bool; 16] = get_and_concat_vec_bit(&raw_mii_data, &[0x3E, 0x3F]) + .try_into() + .unwrap(); + let raw_mustache_details: [bool; 16] = get_and_concat_vec_bit(&raw_mii_data, &[0x40, 0x41]) + .try_into() + .unwrap(); + let raw_beard_details: [bool; 16] = get_and_concat_vec_bit(&raw_mii_data, &[0x42, 0x42]) + .try_into() + .unwrap(); + let raw_glass_details: [bool; 16] = get_and_concat_vec_bit(&raw_mii_data, &[0x44, 0x45]) + .try_into() + .unwrap(); + let raw_mole_details: [bool; 16] = get_and_concat_vec_bit(&raw_mii_data, &[0x46, 0x47]) + .try_into() + .unwrap(); let raw_utf16_author = &raw_mii_data[0x48..0x5C]; let mii_name = utf16_byte_pairs_to_string(raw_utf16_name); @@ -374,12 +367,12 @@ impl From for MiiData { }; MiiData { - options: options, + options, selector_position: position, console_identity: device, system_id, mac_address: creator_mac, - details: details, + details, name: mii_name, height, width, @@ -414,9 +407,8 @@ fn vec_bit(data: u8) -> [bool; 8] { /// Transforms a [bool; 8] into an u8 fn vec_bit_to_u8(data: [bool; 8]) -> u8 { - data.into_iter().fold(0, |result, bit| { - (result << 1) ^ u8::from(bit) - }) + data.into_iter() + .fold(0, |result, bit| (result << 1) ^ u8::from(bit)) } /// The reverse allows to write things on a more _humane_ way, but return a LE u8 @@ -435,13 +427,10 @@ fn utf16_byte_pairs_to_string(data: &[u8]) -> String { .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]])) .collect::>(); - String::from_utf16_lossy(raw_utf16_composed.as_slice()).replace("\0", "") + String::from_utf16_lossy(raw_utf16_composed.as_slice()).replace('\0', "") } /// Gets the values from the slice and concatenates them fn get_and_concat_vec_bit(data: &[u8], get_values: &[usize]) -> Vec { - get_values - .iter() - .flat_map(|v| vec_bit(data[*v])) - .collect() + get_values.iter().flat_map(|v| vec_bit(data[*v])).collect() } From dd5b9f72a6569173876d422e149811c83e7e1805 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Mon, 24 Oct 2022 14:06:27 +0200 Subject: [PATCH 17/27] Fix issue related to endianness and improve wording --- ctru-rs/src/mii.rs | 92 +++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index 565d181..b969c78 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -263,8 +263,8 @@ impl From for MiiData { }; let position = SelectorPosition { - page_index: partial_vec_to_u8_with_reverse(&raw_position[0..3]), - slot_index: partial_vec_to_u8_with_reverse(&raw_position[4..7]), + page_index: partial_u8_bits_to_u8(&raw_position[0..3]), + slot_index: partial_u8_bits_to_u8(&raw_position[4..7]), }; let device = ConsoleIdentity { @@ -285,85 +285,85 @@ impl From for MiiData { false => MiiSex::Male, } }, - birthday_month: partial_vec_to_u8_with_reverse(&raw_details[1..4]), - birthday_day: partial_vec_to_u8_with_reverse(&raw_details[5..9]), - shirt_color: partial_vec_to_u8_with_reverse(&raw_details[10..13]), + birthday_month: partial_u8_bits_to_u8(&raw_details[1..4]), + birthday_day: partial_u8_bits_to_u8(&raw_details[5..9]), + shirt_color: partial_u8_bits_to_u8(&raw_details[10..13]), is_favorite: raw_details[14], }; let face_style = FaceStyle { is_sharing_enabled: !raw_face_style[1], - shape: partial_vec_to_u8_with_reverse(&raw_face_style[1..4]), - skin_color: partial_vec_to_u8_with_reverse(&raw_face_style[5..7]), + shape: partial_u8_bits_to_u8(&raw_face_style[1..4]), + skin_color: partial_u8_bits_to_u8(&raw_face_style[5..7]), }; let face_details = FaceDetails { - wrinkles: partial_vec_to_u8_with_reverse(&raw_face_details[0..3]), - makeup: partial_vec_to_u8_with_reverse(&raw_face_details[4..7]), + wrinkles: partial_u8_bits_to_u8(&raw_face_details[0..3]), + makeup: partial_u8_bits_to_u8(&raw_face_details[4..7]), }; let hair_details = HairDetails { - color: partial_vec_to_u8_with_reverse(&raw_hair_details[0..2]), + color: partial_u8_bits_to_u8(&raw_hair_details[0..2]), is_flipped: raw_hair_details[3], }; let eye_details = EyeDetails { - style: partial_vec_to_u8_with_reverse(&raw_eye_details[0..5]), - color: partial_vec_to_u8_with_reverse(&raw_eye_details[6..8]), - scale: partial_vec_to_u8_with_reverse(&raw_eye_details[9..12]), - y_scale: partial_vec_to_u8_with_reverse(&raw_eye_details[13..15]), - rotation: partial_vec_to_u8_with_reverse(&raw_eye_details[16..20]), - x_spacing: partial_vec_to_u8_with_reverse(&raw_eye_details[21..24]), - y_position: partial_vec_to_u8_with_reverse(&raw_eye_details[25..29]), + style: partial_u8_bits_to_u8(&raw_eye_details[0..5]), + color: partial_u8_bits_to_u8(&raw_eye_details[6..8]), + scale: partial_u8_bits_to_u8(&raw_eye_details[9..12]), + y_scale: partial_u8_bits_to_u8(&raw_eye_details[13..15]), + rotation: partial_u8_bits_to_u8(&raw_eye_details[16..20]), + x_spacing: partial_u8_bits_to_u8(&raw_eye_details[21..24]), + y_position: partial_u8_bits_to_u8(&raw_eye_details[25..29]), }; let eyebrow_details = EyebrowDetails { - style: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[0..4]), - color: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[5..7]), - scale: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[8..11]), - y_scale: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[12..14]), - rotation: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[16..19]), - x_spacing: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[21..24]), - y_position: partial_vec_to_u8_with_reverse(&raw_eyebrow_details[25..29]), + style: partial_u8_bits_to_u8(&raw_eyebrow_details[0..4]), + color: partial_u8_bits_to_u8(&raw_eyebrow_details[5..7]), + scale: partial_u8_bits_to_u8(&raw_eyebrow_details[8..11]), + y_scale: partial_u8_bits_to_u8(&raw_eyebrow_details[12..14]), + rotation: partial_u8_bits_to_u8(&raw_eyebrow_details[16..19]), + x_spacing: partial_u8_bits_to_u8(&raw_eyebrow_details[21..24]), + y_position: partial_u8_bits_to_u8(&raw_eyebrow_details[25..29]), }; let nose_details = NoseDetails { - style: partial_vec_to_u8_with_reverse(&raw_nose_details[0..4]), - scale: partial_vec_to_u8_with_reverse(&raw_nose_details[5..8]), - y_position: partial_vec_to_u8_with_reverse(&raw_nose_details[9..13]), + style: partial_u8_bits_to_u8(&raw_nose_details[0..4]), + scale: partial_u8_bits_to_u8(&raw_nose_details[5..8]), + y_position: partial_u8_bits_to_u8(&raw_nose_details[9..13]), }; let mouth_details = MouthDetails { - style: partial_vec_to_u8_with_reverse(&raw_mouth_details[0..5]), - color: partial_vec_to_u8_with_reverse(&raw_mouth_details[6..8]), - scale: partial_vec_to_u8_with_reverse(&raw_mouth_details[9..12]), - y_scale: partial_vec_to_u8_with_reverse(&raw_mouth_details[13..15]), + style: partial_u8_bits_to_u8(&raw_mouth_details[0..5]), + color: partial_u8_bits_to_u8(&raw_mouth_details[6..8]), + scale: partial_u8_bits_to_u8(&raw_mouth_details[9..12]), + y_scale: partial_u8_bits_to_u8(&raw_mouth_details[13..15]), }; let mustache_details = MustacheDetails { - mouth_y_position: partial_vec_to_u8_with_reverse(&raw_mustache_details[0..4]), - mustache_style: partial_vec_to_u8_with_reverse(&raw_mustache_details[5..7]), + mouth_y_position: partial_u8_bits_to_u8(&raw_mustache_details[0..4]), + mustache_style: partial_u8_bits_to_u8(&raw_mustache_details[5..7]), }; let beard_details = BeardDetails { - style: partial_vec_to_u8_with_reverse(&raw_beard_details[0..2]), - color: partial_vec_to_u8_with_reverse(&raw_beard_details[3..5]), - scale: partial_vec_to_u8_with_reverse(&raw_beard_details[6..9]), - y_position: partial_vec_to_u8_with_reverse(&raw_beard_details[10..14]), + style: partial_u8_bits_to_u8(&raw_beard_details[0..2]), + color: partial_u8_bits_to_u8(&raw_beard_details[3..5]), + scale: partial_u8_bits_to_u8(&raw_beard_details[6..9]), + y_position: partial_u8_bits_to_u8(&raw_beard_details[10..14]), }; let glass_details = GlassDetails { - style: partial_vec_to_u8_with_reverse(&raw_glass_details[0..3]), - color: partial_vec_to_u8_with_reverse(&raw_glass_details[4..6]), - scale: partial_vec_to_u8_with_reverse(&raw_glass_details[7..10]), - y_position: partial_vec_to_u8_with_reverse(&raw_glass_details[11..15]), + style: partial_u8_bits_to_u8(&raw_glass_details[0..3]), + color: partial_u8_bits_to_u8(&raw_glass_details[4..6]), + scale: partial_u8_bits_to_u8(&raw_glass_details[7..10]), + y_position: partial_u8_bits_to_u8(&raw_glass_details[11..15]), }; let mole_details = MoleDetails { is_enabled: raw_mole_details[0], - scale: partial_vec_to_u8_with_reverse(&raw_mole_details[1..4]), - x_position: partial_vec_to_u8_with_reverse(&raw_mole_details[5..9]), - y_position: partial_vec_to_u8_with_reverse(&raw_mole_details[10..14]), + scale: partial_u8_bits_to_u8(&raw_mole_details[1..4]), + x_position: partial_u8_bits_to_u8(&raw_mole_details[5..9]), + y_position: partial_u8_bits_to_u8(&raw_mole_details[10..14]), }; MiiData { @@ -411,12 +411,10 @@ fn vec_bit_to_u8(data: [bool; 8]) -> u8 { .fold(0, |result, bit| (result << 1) ^ u8::from(bit)) } -/// The reverse allows to write things on a more _humane_ way, but return a LE u8 -fn partial_vec_to_u8_with_reverse(data: &[bool]) -> u8 { +fn partial_u8_bits_to_u8(data: &[bool]) -> u8 { let leading_zeroes_to_add = 8 - data.len(); let leading_zeroes = vec![false; leading_zeroes_to_add]; let mut val = [data, leading_zeroes.as_slice()].concat(); - val.reverse(); vec_bit_to_u8(val.try_into().unwrap()) } From 91f65a0a558900fa45c7861a6f8fdabb0d21c646 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Mon, 24 Oct 2022 14:12:07 +0200 Subject: [PATCH 18/27] Add docs --- ctru-rs/src/applets/mii_selector.rs | 41 +++++++++++++++++++++++++++-- ctru-rs/src/mii.rs | 33 +++++++++++++++++++++-- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index d4fe6d1..342b797 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -1,13 +1,20 @@ +//! Mii Selector applet +//! +//! This module contains the methods to launch the Mii Selector. + use crate::mii::MiiData; use bitflags::bitflags; use std::ffi::CString; +/// Index of a Mii used to configure some parameters of the Mii Selector +/// Can be either a single index, or _all_ Miis #[derive(Debug, Clone)] pub enum MiiConfigIndex { Index(u32), All, } +/// The type of a Mii with their respective data #[derive(Debug, Clone)] pub enum MiiType { Guest { index: u32, name: String }, @@ -15,19 +22,36 @@ pub enum MiiType { } bitflags! { + /// Options for the Mii Selector pub struct Options: u32 { + /// Show the cancel button const MII_SELECTOR_CANCEL = ctru_sys::MIISELECTOR_CANCEL; + /// Make guest Miis selectable const MII_SELECTOR_GUESTS = ctru_sys::MIISELECTOR_GUESTS; + /// Show on the top screen const MII_SELECTOR_TOP = ctru_sys::MIISELECTOR_TOP; + /// Start on the guest's page const MII_SELECTOR_GUEST_START = ctru_sys::MIISELECTOR_GUESTSTART; } } +/// An instance of the Mii Selector +/// +/// # Example +/// ``` +/// use ctru::applets::mii_selector::MiiSelector; +/// +/// let mut mii_selector = MiiSelector::init(); +/// mii_selector.set_title("Example Mii selector"); +/// +/// let result = mii_selector.launch().unwrap(); +/// ``` #[derive(Clone, Debug)] pub struct MiiSelector { config: Box, } +/// Return value from a MiiSelector's launch #[derive(Clone, Debug)] pub struct MiiSelectorReturn { pub mii_data: MiiData, @@ -36,12 +60,14 @@ pub struct MiiSelectorReturn { pub checksum: u16, } +/// Error type for the Mii selector #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum MiiLaunchError { InvalidChecksum, } impl MiiSelector { + /// Initializes a Mii Selector pub fn init() -> Self { let mut config = Box::::default(); unsafe { @@ -50,6 +76,7 @@ impl MiiSelector { Self { config } } + /// Set the title of the Mii Selector pub fn set_title(&mut self, text: &str) { // This can only fail if the text contains NUL bytes in the string... which seems // unlikely and is documented @@ -59,10 +86,12 @@ impl MiiSelector { } } + /// Set the options of the Mii Selector pub fn set_options(&mut self, options: Options) { unsafe { ctru_sys::miiSelectorSetOptions(self.config.as_mut(), options.bits) } } + /// Whitelist a guest Mii pub fn whitelist_guest_mii(&mut self, mii_index: MiiConfigIndex) { let index = match mii_index { MiiConfigIndex::Index(i) => i, @@ -72,6 +101,7 @@ impl MiiSelector { unsafe { ctru_sys::miiSelectorWhitelistGuestMii(self.config.as_mut(), index) } } + /// Blacklist a guest Mii pub fn blacklist_guest_mii(&mut self, mii_index: MiiConfigIndex) { let index = match mii_index { MiiConfigIndex::Index(i) => i, @@ -81,6 +111,7 @@ impl MiiSelector { unsafe { ctru_sys::miiSelectorBlacklistGuestMii(self.config.as_mut(), index) } } + /// Whitelist a user Mii pub fn whitelist_user_mii(&mut self, mii_index: MiiConfigIndex) { let index = match mii_index { MiiConfigIndex::Index(i) => i, @@ -90,6 +121,7 @@ impl MiiSelector { unsafe { ctru_sys::miiSelectorWhitelistUserMii(self.config.as_mut(), index) } } + /// Blacklist a user Mii pub fn blacklist_user_mii(&mut self, mii_index: MiiConfigIndex) { let index = match mii_index { MiiConfigIndex::Index(i) => i, @@ -99,12 +131,17 @@ impl MiiSelector { unsafe { ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), index) } } - // This function is static inline in libctru - // https://github.com/devkitPro/libctru/blob/af5321c78ee5c72a55b526fd2ed0d95ca1c05af9/libctru/include/3ds/applets/miiselector.h#L155 + + /// Set where the cursor will be. + /// If there's no Mii at that index, the cursor will start at the Mii with the index 0 pub fn set_initial_index(&mut self, index: u32) { + // This function is static inline in libctru + // https://github.com/devkitPro/libctru/blob/af5321c78ee5c72a55b526fd2ed0d95ca1c05af9/libctru/include/3ds/applets/miiselector.h#L155 self.config.initial_index = index } + /// Launch the Mii Selector. + /// Returns an error when the checksum of the Mii is invalid. pub fn launch(&mut self) -> Result { let mut return_val = Box::::default(); unsafe { ctru_sys::miiSelectorLaunch(self.config.as_mut(), return_val.as_mut()) } diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index b969c78..b40c836 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -1,3 +1,9 @@ +//! Mii Data +//! +//! This module contains the structs that represent all the data of a Mii. +//! This data is given by the [``MiiSelector``] + +/// Represents the region lock of the console #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum RegionLock { None, @@ -6,6 +12,7 @@ pub enum RegionLock { Europe, } +/// Represent the charset of the console #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Charset { JapanUSAEurope, @@ -14,6 +21,7 @@ pub enum Charset { Taiwan, } +/// Represents the options of the Mii #[derive(Copy, Clone, Debug)] pub struct MiiDataOptions { pub is_copying_allowed: bool, @@ -22,12 +30,14 @@ pub struct MiiDataOptions { pub charset: Charset, } +/// Represents the position that the Mii has on the selector #[derive(Copy, Clone, Debug)] pub struct SelectorPosition { pub page_index: u8, pub slot_index: u8, } +/// Represents the kind of origin console #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum OriginConsole { ConsoleWii, @@ -36,17 +46,20 @@ pub enum OriginConsole { ConsoleWiiUSwitch, } +/// Represents the identity of the origin console #[derive(Copy, Clone, Debug)] pub struct ConsoleIdentity { pub origin_console: OriginConsole, } +/// Represents the sex of the Mii #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum MiiSex { Male, Female, } +/// Represents the details of the Mii #[derive(Copy, Clone, Debug)] pub struct Details { pub sex: MiiSex, @@ -56,6 +69,7 @@ pub struct Details { pub is_favorite: bool, } +/// Represents the face style of the Mii #[derive(Copy, Clone, Debug)] pub struct FaceStyle { pub is_sharing_enabled: bool, @@ -63,18 +77,21 @@ pub struct FaceStyle { pub skin_color: u8, } +/// Represents the face details of the Mii #[derive(Copy, Clone, Debug)] pub struct FaceDetails { pub wrinkles: u8, pub makeup: u8, } +/// Represents the hair details of the Mii #[derive(Copy, Clone, Debug)] pub struct HairDetails { pub color: u8, pub is_flipped: bool, } +/// Represents the eye details of the Mii #[derive(Copy, Clone, Debug)] pub struct EyeDetails { pub style: u8, @@ -82,10 +99,12 @@ pub struct EyeDetails { pub scale: u8, pub y_scale: u8, pub rotation: u8, + /// Spacing between the eyes pub x_spacing: u8, pub y_position: u8, } +/// Represents the eyebrow details of the Mii #[derive(Copy, Clone, Debug)] pub struct EyebrowDetails { pub style: u8, @@ -93,10 +112,12 @@ pub struct EyebrowDetails { pub scale: u8, pub y_scale: u8, pub rotation: u8, + /// Spacing between the eyebrows pub x_spacing: u8, pub y_position: u8, } +/// Represents the details of the nose #[derive(Copy, Clone, Debug)] pub struct NoseDetails { pub style: u8, @@ -104,6 +125,7 @@ pub struct NoseDetails { pub y_position: u8, } +/// Represents the details of the mouth #[derive(Copy, Clone, Debug)] pub struct MouthDetails { pub style: u8, @@ -112,12 +134,14 @@ pub struct MouthDetails { pub y_scale: u8, } +/// Represents the details of the mustache #[derive(Copy, Clone, Debug)] pub struct MustacheDetails { pub mouth_y_position: u8, pub mustache_style: u8, } +/// Represents the details of the beard #[derive(Copy, Clone, Debug)] pub struct BeardDetails { pub style: u8, @@ -126,6 +150,7 @@ pub struct BeardDetails { pub y_position: u8, } +/// Represents the details of the glass #[derive(Copy, Clone, Debug)] pub struct GlassDetails { pub style: u8, @@ -134,6 +159,7 @@ pub struct GlassDetails { pub y_position: u8, } +/// Represents the details of the mole #[derive(Copy, Clone, Debug)] pub struct MoleDetails { pub is_enabled: bool, @@ -142,12 +168,14 @@ pub struct MoleDetails { pub y_position: u8, } +/// Represents all the data of a Mii #[derive(Clone, Debug)] pub struct MiiData { pub options: MiiDataOptions, pub selector_position: SelectorPosition, pub console_identity: ConsoleIdentity, + /// Unique system ID, not dependant on the MAC address pub system_id: [u8; 8], pub mac_address: [u8; 6], @@ -411,11 +439,12 @@ fn vec_bit_to_u8(data: [bool; 8]) -> u8 { .fold(0, |result, bit| (result << 1) ^ u8::from(bit)) } +/// Given a series of LE bits, they are filled until a full LE u8 is reached fn partial_u8_bits_to_u8(data: &[bool]) -> u8 { let leading_zeroes_to_add = 8 - data.len(); let leading_zeroes = vec![false; leading_zeroes_to_add]; - let mut val = [data, leading_zeroes.as_slice()].concat(); - vec_bit_to_u8(val.try_into().unwrap()) + + vec_bit_to_u8([data, &leading_zeroes].concat().try_into().unwrap()) } /// UTF-16 Strings are give in pairs of bytes (u8), this converts them into an _actual_ string From 441ab930cf9ca58fb8ea1185490a5507b8c47642 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Mon, 24 Oct 2022 14:46:21 +0200 Subject: [PATCH 19/27] Document values sometimes not being straight forward --- ctru-rs/src/mii.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index b40c836..2c2f845 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -169,6 +169,9 @@ pub struct MoleDetails { } /// Represents all the data of a Mii +/// +/// Some values are not ordered _like_ the Mii Editor UI. The mapped values can be seen here: +/// #[derive(Clone, Debug)] pub struct MiiData { pub options: MiiDataOptions, From f8baff0819d1631b650c678b20ff590fac3e1650 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Mon, 24 Oct 2022 14:48:29 +0200 Subject: [PATCH 20/27] Cargo fmt --- ctru-rs/src/applets/mii_selector.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index 342b797..c90e8ad 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -131,7 +131,6 @@ impl MiiSelector { unsafe { ctru_sys::miiSelectorBlacklistUserMii(self.config.as_mut(), index) } } - /// Set where the cursor will be. /// If there's no Mii at that index, the cursor will start at the Mii with the index 0 pub fn set_initial_index(&mut self, index: u32) { From 1310507fcb0886a29fc20c819ce8ee46ea40373d Mon Sep 17 00:00:00 2001 From: TechiePi Date: Mon, 24 Oct 2022 14:51:30 +0200 Subject: [PATCH 21/27] Document ``set_title`` input and NUL bytes --- ctru-rs/src/applets/mii_selector.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index c90e8ad..3bbcedc 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -76,7 +76,9 @@ impl MiiSelector { Self { config } } - /// Set the title of the Mii Selector + /// Set the title of the Mii Selector. + /// + /// This function would panic if the given ``&str`` contains NUL bytes. pub fn set_title(&mut self, text: &str) { // This can only fail if the text contains NUL bytes in the string... which seems // unlikely and is documented From 4d88ba990112ad2a80bc76ec1929995fdb16f359 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Mon, 24 Oct 2022 15:17:34 +0200 Subject: [PATCH 22/27] Fix rustdoc link --- ctru-rs/src/mii.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index 2c2f845..2f3fc51 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -1,7 +1,7 @@ //! Mii Data //! //! This module contains the structs that represent all the data of a Mii. -//! This data is given by the [``MiiSelector``] +//! This data is given by the [``MiiSelector``](crate::applets::mii_selector::MiiSelector) /// Represents the region lock of the console #[derive(Copy, Clone, Debug, Eq, PartialEq)] From 7c2440432acfcc1f6dfbc92e8d81a5651929ca9a Mon Sep 17 00:00:00 2001 From: TechiePi Date: Mon, 24 Oct 2022 18:19:20 +0200 Subject: [PATCH 23/27] Move some styles inside the their details struct for consistency --- ctru-rs/src/mii.rs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index 2f3fc51..85ac191 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -80,6 +80,7 @@ pub struct FaceStyle { /// Represents the face details of the Mii #[derive(Copy, Clone, Debug)] pub struct FaceDetails { + pub style: FaceStyle, pub wrinkles: u8, pub makeup: u8, } @@ -87,6 +88,7 @@ pub struct FaceDetails { /// Represents the hair details of the Mii #[derive(Copy, Clone, Debug)] pub struct HairDetails { + pub style: u8, pub color: u8, pub is_flipped: bool, } @@ -188,11 +190,7 @@ pub struct MiiData { pub height: u8, pub width: u8, - pub face_style: FaceStyle, pub face_details: FaceDetails, - - pub hair_style: u8, - pub hair_details: HairDetails, pub eye_details: EyeDetails, pub eyebrow_details: EyebrowDetails, @@ -239,7 +237,6 @@ impl From for MiiData { let width = raw_mii_data[0x2F]; let raw_face_style = vec_bit(raw_mii_data[0x30]); let raw_face_details = vec_bit(raw_mii_data[0x31]); - let hair_style = raw_mii_data[0x32]; let raw_hair_details = vec_bit(raw_mii_data[0x33]); let raw_eye_details: [bool; 32] = get_and_concat_vec_bit(&raw_mii_data, &[0x34, 0x35, 0x36, 0x37]) @@ -322,18 +319,18 @@ impl From for MiiData { is_favorite: raw_details[14], }; - let face_style = FaceStyle { - is_sharing_enabled: !raw_face_style[1], - shape: partial_u8_bits_to_u8(&raw_face_style[1..4]), - skin_color: partial_u8_bits_to_u8(&raw_face_style[5..7]), - }; - let face_details = FaceDetails { + style: FaceStyle { + is_sharing_enabled: !raw_face_style[1], + shape: partial_u8_bits_to_u8(&raw_face_style[1..4]), + skin_color: partial_u8_bits_to_u8(&raw_face_style[5..7]), + }, wrinkles: partial_u8_bits_to_u8(&raw_face_details[0..3]), makeup: partial_u8_bits_to_u8(&raw_face_details[4..7]), }; let hair_details = HairDetails { + style: raw_mii_data[0x32], color: partial_u8_bits_to_u8(&raw_hair_details[0..2]), is_flipped: raw_hair_details[3], }; @@ -407,9 +404,7 @@ impl From for MiiData { name: mii_name, height, width, - face_style, face_details, - hair_style, hair_details, eye_details, eyebrow_details, From 16f8de4376eff55ada9fc74393d0b562e92f08e2 Mon Sep 17 00:00:00 2001 From: TechiePi Date: Wed, 26 Oct 2022 17:05:23 +0200 Subject: [PATCH 24/27] Address comments - Make ``MiiSelectorReturn`` ``non_exhaustive`` - Remove ``checksum`` from ``MiiSelectorReturn`` - Change ``From`` impl for ``MiiSelectorReturn`` - Rename ``OriginConsole`` members - Fix ranges --- ctru-rs/examples/mii-selector.rs | 1 - ctru-rs/src/applets/mii_selector.rs | 10 ++- ctru-rs/src/mii.rs | 107 ++++++++++++++-------------- 3 files changed, 58 insertions(+), 60 deletions(-) diff --git a/ctru-rs/examples/mii-selector.rs b/ctru-rs/examples/mii-selector.rs index 1b89532..975fda0 100644 --- a/ctru-rs/examples/mii-selector.rs +++ b/ctru-rs/examples/mii-selector.rs @@ -18,7 +18,6 @@ fn main() { println!("Is Mii selected?: {:?}", result.is_mii_selected); println!("Mii type: {:?}", result.mii_type); - println!("Mii checksum: {:?}", result.checksum); println!("Name: {:?}", result.mii_data.name); println!("Author: {:?}", result.mii_data.author_name); println!( diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index 3bbcedc..fef106b 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -52,12 +52,12 @@ pub struct MiiSelector { } /// Return value from a MiiSelector's launch +#[non_exhaustive] #[derive(Clone, Debug)] pub struct MiiSelectorReturn { pub mii_data: MiiData, pub is_mii_selected: bool, pub mii_type: MiiType, - pub checksum: u16, } /// Error type for the Mii selector @@ -148,16 +148,15 @@ impl MiiSelector { unsafe { ctru_sys::miiSelectorLaunch(self.config.as_mut(), return_val.as_mut()) } if unsafe { ctru_sys::miiSelectorChecksumIsValid(return_val.as_mut()) } { - Ok(return_val.into()) + Ok((*return_val).into()) } else { Err(MiiLaunchError::InvalidChecksum) } } } -impl From> for MiiSelectorReturn { - fn from(ret: Box) -> Self { - let checksum = ret.checksum; +impl From for MiiSelectorReturn { + fn from(ret: ctru_sys::MiiSelectorReturn) -> Self { let raw_mii_data = ret.mii; let no_mii_selected = ret.no_mii_selected; let guest_mii_index_clone = ret.guest_mii_index; @@ -178,7 +177,6 @@ impl From> for MiiSelectorReturn { } else { MiiType::User }, - checksum, } } } diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index 85ac191..e4ebcb4 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -40,10 +40,11 @@ pub struct SelectorPosition { /// Represents the kind of origin console #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum OriginConsole { - ConsoleWii, - ConsoleDSi, - Console3DS, - ConsoleWiiUSwitch, + Wii, + DSi, + /// Both New 3DS and Old 3DS + N3DS, + WiiUSwitch, } /// Represents the identity of the origin console @@ -291,17 +292,17 @@ impl From for MiiData { }; let position = SelectorPosition { - page_index: partial_u8_bits_to_u8(&raw_position[0..3]), - slot_index: partial_u8_bits_to_u8(&raw_position[4..7]), + page_index: partial_u8_bits_to_u8(&raw_position[0..=3]), + slot_index: partial_u8_bits_to_u8(&raw_position[4..=7]), }; let device = ConsoleIdentity { origin_console: { match (raw_device[6], raw_device[5], raw_device[4]) { - (false, false, true) => OriginConsole::ConsoleWii, - (false, true, false) => OriginConsole::ConsoleDSi, - (false, true, true) => OriginConsole::Console3DS, - _ => OriginConsole::ConsoleWiiUSwitch, + (false, false, true) => OriginConsole::Wii, + (false, true, false) => OriginConsole::DSi, + (false, true, true) => OriginConsole::N3DS, + _ => OriginConsole::WiiUSwitch, } }, }; @@ -313,85 +314,85 @@ impl From for MiiData { false => MiiSex::Male, } }, - birthday_month: partial_u8_bits_to_u8(&raw_details[1..4]), - birthday_day: partial_u8_bits_to_u8(&raw_details[5..9]), - shirt_color: partial_u8_bits_to_u8(&raw_details[10..13]), + birthday_month: partial_u8_bits_to_u8(&raw_details[1..=4]), + birthday_day: partial_u8_bits_to_u8(&raw_details[5..=9]), + shirt_color: partial_u8_bits_to_u8(&raw_details[10..=13]), is_favorite: raw_details[14], }; let face_details = FaceDetails { style: FaceStyle { - is_sharing_enabled: !raw_face_style[1], - shape: partial_u8_bits_to_u8(&raw_face_style[1..4]), - skin_color: partial_u8_bits_to_u8(&raw_face_style[5..7]), + is_sharing_enabled: !raw_face_style[0], + shape: partial_u8_bits_to_u8(&raw_face_style[1..=4]), + skin_color: partial_u8_bits_to_u8(&raw_face_style[5..=7]), }, - wrinkles: partial_u8_bits_to_u8(&raw_face_details[0..3]), - makeup: partial_u8_bits_to_u8(&raw_face_details[4..7]), + wrinkles: partial_u8_bits_to_u8(&raw_face_details[0..=3]), + makeup: partial_u8_bits_to_u8(&raw_face_details[4..=7]), }; let hair_details = HairDetails { style: raw_mii_data[0x32], - color: partial_u8_bits_to_u8(&raw_hair_details[0..2]), + color: partial_u8_bits_to_u8(&raw_hair_details[0..=2]), is_flipped: raw_hair_details[3], }; let eye_details = EyeDetails { - style: partial_u8_bits_to_u8(&raw_eye_details[0..5]), - color: partial_u8_bits_to_u8(&raw_eye_details[6..8]), - scale: partial_u8_bits_to_u8(&raw_eye_details[9..12]), - y_scale: partial_u8_bits_to_u8(&raw_eye_details[13..15]), - rotation: partial_u8_bits_to_u8(&raw_eye_details[16..20]), - x_spacing: partial_u8_bits_to_u8(&raw_eye_details[21..24]), - y_position: partial_u8_bits_to_u8(&raw_eye_details[25..29]), + style: partial_u8_bits_to_u8(&raw_eye_details[0..=5]), + color: partial_u8_bits_to_u8(&raw_eye_details[6..=8]), + scale: partial_u8_bits_to_u8(&raw_eye_details[9..=12]), + y_scale: partial_u8_bits_to_u8(&raw_eye_details[13..=15]), + rotation: partial_u8_bits_to_u8(&raw_eye_details[16..=20]), + x_spacing: partial_u8_bits_to_u8(&raw_eye_details[21..=24]), + y_position: partial_u8_bits_to_u8(&raw_eye_details[25..=29]), }; let eyebrow_details = EyebrowDetails { - style: partial_u8_bits_to_u8(&raw_eyebrow_details[0..4]), - color: partial_u8_bits_to_u8(&raw_eyebrow_details[5..7]), - scale: partial_u8_bits_to_u8(&raw_eyebrow_details[8..11]), - y_scale: partial_u8_bits_to_u8(&raw_eyebrow_details[12..14]), - rotation: partial_u8_bits_to_u8(&raw_eyebrow_details[16..19]), - x_spacing: partial_u8_bits_to_u8(&raw_eyebrow_details[21..24]), - y_position: partial_u8_bits_to_u8(&raw_eyebrow_details[25..29]), + style: partial_u8_bits_to_u8(&raw_eyebrow_details[0..=4]), + color: partial_u8_bits_to_u8(&raw_eyebrow_details[5..=7]), + scale: partial_u8_bits_to_u8(&raw_eyebrow_details[8..=11]), + y_scale: partial_u8_bits_to_u8(&raw_eyebrow_details[12..=14]), + rotation: partial_u8_bits_to_u8(&raw_eyebrow_details[16..=19]), + x_spacing: partial_u8_bits_to_u8(&raw_eyebrow_details[21..=24]), + y_position: partial_u8_bits_to_u8(&raw_eyebrow_details[25..=29]), }; let nose_details = NoseDetails { - style: partial_u8_bits_to_u8(&raw_nose_details[0..4]), - scale: partial_u8_bits_to_u8(&raw_nose_details[5..8]), - y_position: partial_u8_bits_to_u8(&raw_nose_details[9..13]), + style: partial_u8_bits_to_u8(&raw_nose_details[0..=4]), + scale: partial_u8_bits_to_u8(&raw_nose_details[5..=8]), + y_position: partial_u8_bits_to_u8(&raw_nose_details[9..=13]), }; let mouth_details = MouthDetails { - style: partial_u8_bits_to_u8(&raw_mouth_details[0..5]), - color: partial_u8_bits_to_u8(&raw_mouth_details[6..8]), - scale: partial_u8_bits_to_u8(&raw_mouth_details[9..12]), - y_scale: partial_u8_bits_to_u8(&raw_mouth_details[13..15]), + style: partial_u8_bits_to_u8(&raw_mouth_details[0..=5]), + color: partial_u8_bits_to_u8(&raw_mouth_details[6..=8]), + scale: partial_u8_bits_to_u8(&raw_mouth_details[9..=12]), + y_scale: partial_u8_bits_to_u8(&raw_mouth_details[13..=15]), }; let mustache_details = MustacheDetails { - mouth_y_position: partial_u8_bits_to_u8(&raw_mustache_details[0..4]), - mustache_style: partial_u8_bits_to_u8(&raw_mustache_details[5..7]), + mouth_y_position: partial_u8_bits_to_u8(&raw_mustache_details[0..=4]), + mustache_style: partial_u8_bits_to_u8(&raw_mustache_details[5..=7]), }; let beard_details = BeardDetails { - style: partial_u8_bits_to_u8(&raw_beard_details[0..2]), - color: partial_u8_bits_to_u8(&raw_beard_details[3..5]), - scale: partial_u8_bits_to_u8(&raw_beard_details[6..9]), - y_position: partial_u8_bits_to_u8(&raw_beard_details[10..14]), + style: partial_u8_bits_to_u8(&raw_beard_details[0..=2]), + color: partial_u8_bits_to_u8(&raw_beard_details[3..=5]), + scale: partial_u8_bits_to_u8(&raw_beard_details[6..=9]), + y_position: partial_u8_bits_to_u8(&raw_beard_details[10..=14]), }; let glass_details = GlassDetails { - style: partial_u8_bits_to_u8(&raw_glass_details[0..3]), - color: partial_u8_bits_to_u8(&raw_glass_details[4..6]), - scale: partial_u8_bits_to_u8(&raw_glass_details[7..10]), - y_position: partial_u8_bits_to_u8(&raw_glass_details[11..15]), + style: partial_u8_bits_to_u8(&raw_glass_details[0..=3]), + color: partial_u8_bits_to_u8(&raw_glass_details[4..=6]), + scale: partial_u8_bits_to_u8(&raw_glass_details[7..=10]), + y_position: partial_u8_bits_to_u8(&raw_glass_details[11..=15]), }; let mole_details = MoleDetails { is_enabled: raw_mole_details[0], - scale: partial_u8_bits_to_u8(&raw_mole_details[1..4]), - x_position: partial_u8_bits_to_u8(&raw_mole_details[5..9]), - y_position: partial_u8_bits_to_u8(&raw_mole_details[10..14]), + scale: partial_u8_bits_to_u8(&raw_mole_details[1..=4]), + x_position: partial_u8_bits_to_u8(&raw_mole_details[5..=9]), + y_position: partial_u8_bits_to_u8(&raw_mole_details[10..=14]), }; MiiData { From accf99b2bb6ac264430f4f04e263fd27243e851a Mon Sep 17 00:00:00 2001 From: TechiePi Date: Wed, 26 Oct 2022 17:19:38 +0200 Subject: [PATCH 25/27] Apply cargo check suggestions Apply nightly lints I updated my toolchain to 1.66-nightly to fix all the files --- ctru-rs/build.rs | 2 +- ctru-rs/examples/file-explorer.rs | 14 +++++++------- ctru-rs/examples/hashmaps.rs | 2 +- ctru-rs/examples/network-sockets.rs | 10 +++++----- ctru-rs/examples/software-keyboard.rs | 2 +- ctru-rs/examples/time-rtc.rs | 4 ++-- ctru-rs/src/error.rs | 6 +++--- ctru-rs/src/services/fs.rs | 2 +- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/ctru-rs/build.rs b/ctru-rs/build.rs index ecdef33..f65d194 100644 --- a/ctru-rs/build.rs +++ b/ctru-rs/build.rs @@ -28,5 +28,5 @@ fn main() { println!("cargo:rustc-cfg=romfs_exists"); } - println!("cargo:rerun-if-changed={}", manifest_dir); + println!("cargo:rerun-if-changed={manifest_dir}"); } diff --git a/ctru-rs/examples/file-explorer.rs b/ctru-rs/examples/file-explorer.rs index ff6296f..1cff988 100644 --- a/ctru-rs/examples/file-explorer.rs +++ b/ctru-rs/examples/file-explorer.rs @@ -96,7 +96,7 @@ impl<'a> FileExplorer<'a> { } } Err(e) => { - println!("Failed to read {}: {}", self.path.display(), e) + println!("Failed to read {}: {e}", self.path.display()) } }; @@ -110,7 +110,7 @@ impl<'a> FileExplorer<'a> { for (i, entry) in dir_listing.enumerate() { match entry { Ok(entry) => { - println!("{:2} - {}", i, entry.file_name().to_string_lossy()); + println!("{i:2} - {}", entry.file_name().to_string_lossy()); self.entries.push(entry); if (i + 1) % 20 == 0 { @@ -118,7 +118,7 @@ impl<'a> FileExplorer<'a> { } } Err(e) => { - println!("{} - Error: {}", i, e); + println!("{i} - Error: {e}"); } } } @@ -132,7 +132,7 @@ impl<'a> FileExplorer<'a> { println!("{0:->80}", ""); } Err(err) => { - println!("Error reading file: {}", err); + println!("Error reading file: {err}"); } } } @@ -175,7 +175,7 @@ impl<'a> FileExplorer<'a> { unreachable!() } Err(e) => { - panic!("Error: {:?}", e) + panic!("Error: {e:?}") } } } @@ -184,7 +184,7 @@ impl<'a> FileExplorer<'a> { let next_path_index: usize = match next_path_index.parse() { Ok(index) => index, Err(e) => { - println!("Number parsing error: {}", e); + println!("Number parsing error: {e}"); return; } }; @@ -205,7 +205,7 @@ impl<'a> FileExplorer<'a> { fn set_exact_path(&mut self, new_path_str: String) { let new_path = Path::new(&new_path_str); if !new_path.is_dir() { - println!("Not a directory: {}", new_path_str); + println!("Not a directory: {new_path_str}"); return; } diff --git a/ctru-rs/examples/hashmaps.rs b/ctru-rs/examples/hashmaps.rs index 3218dc5..694752c 100644 --- a/ctru-rs/examples/hashmaps.rs +++ b/ctru-rs/examples/hashmaps.rs @@ -17,7 +17,7 @@ fn main() { map.insert("Another key?", 543); map.remove("A Key!"); - println!("{:#?}", map); + println!("{map:#?}"); while apt.main_loop() { gfx.flush_buffers(); diff --git a/ctru-rs/examples/network-sockets.rs b/ctru-rs/examples/network-sockets.rs index a0b64d0..89a1212 100644 --- a/ctru-rs/examples/network-sockets.rs +++ b/ctru-rs/examples/network-sockets.rs @@ -25,19 +25,19 @@ fn main() { match server.accept() { Ok((mut stream, socket_addr)) => { - println!("Got connection from {}", socket_addr); + println!("Got connection from {socket_addr}"); let mut buf = [0u8; 4096]; match stream.read(&mut buf) { Ok(_) => { let req_str = String::from_utf8_lossy(&buf); - println!("{}", req_str); + println!("{req_str}"); } Err(e) => { if e.kind() == io::ErrorKind::WouldBlock { println!("Note: Reading the connection returned ErrorKind::WouldBlock.") } else { - println!("Unable to read stream: {}", e) + println!("Unable to read stream: {e}") } } } @@ -45,7 +45,7 @@ fn main() { let response = b"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\nHello world\r\n"; if let Err(e) = stream.write(response) { - println!("Error writing http response: {}", e); + println!("Error writing http response: {e}"); } stream.shutdown(Shutdown::Both).unwrap(); @@ -53,7 +53,7 @@ fn main() { Err(e) => match e.kind() { std::io::ErrorKind::WouldBlock => {} _ => { - println!("Error accepting connection: {}", e); + println!("Error accepting connection: {e}"); std::thread::sleep(Duration::from_secs(2)); } }, diff --git a/ctru-rs/examples/software-keyboard.rs b/ctru-rs/examples/software-keyboard.rs index b830811..888a157 100644 --- a/ctru-rs/examples/software-keyboard.rs +++ b/ctru-rs/examples/software-keyboard.rs @@ -29,7 +29,7 @@ fn main() { // Raise the software keyboard. You can perform different actions depending on which // software button the user pressed match keyboard.get_utf8(&mut text) { - Ok(Button::Right) => println!("You entered: {}", text), + Ok(Button::Right) => println!("You entered: {text}"), Ok(Button::Left) => println!("Cancelled"), Ok(Button::Middle) => println!("How did you even press this?"), Err(_) => println!("Oh noes, an error happened!"), diff --git a/ctru-rs/examples/time-rtc.rs b/ctru-rs/examples/time-rtc.rs index 1554a21..219abda 100644 --- a/ctru-rs/examples/time-rtc.rs +++ b/ctru-rs/examples/time-rtc.rs @@ -33,8 +33,8 @@ fn main() { let day = cur_time.day(); let year = cur_time.year(); - println!("\x1b[1;1H{:0>2}:{:0>2}:{:0>2}", hours, minutes, seconds); - println!("{} {} {} {}", weekday, month, day, year); + println!("\x1b[1;1H{hours:0>2}:{minutes:0>2}:{seconds:0>2}"); + println!("{weekday} {month} {day} {year}"); // Flush and swap framebuffers gfx.flush_buffers(); diff --git a/ctru-rs/src/error.rs b/ctru-rs/src/error.rs index 650ab7a..5054ada 100644 --- a/ctru-rs/src/error.rs +++ b/ctru-rs/src/error.rs @@ -87,7 +87,7 @@ impl fmt::Debug for Error { match self { &Self::Os(err) => f .debug_struct("Error") - .field("raw", &format_args!("{:#08X}", err)) + .field("raw", &format_args!("{err:#08X}")) .field("description", &R_DESCRIPTION(err)) .field("module", &R_MODULE(err)) .field("summary", &R_SUMMARY(err)) @@ -106,8 +106,8 @@ impl fmt::Debug for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - &Self::Os(err) => write!(f, "libctru result code: 0x{:08X}", err), - Self::Libc(err) => write!(f, "{}", err), + &Self::Os(err) => write!(f, "libctru result code: 0x{err:08X}"), + Self::Libc(err) => write!(f, "{err}"), Self::ServiceAlreadyActive => write!(f, "Service already active"), Self::OutputAlreadyRedirected => { write!(f, "output streams are already redirected to 3dslink") diff --git a/ctru-rs/src/services/fs.rs b/ctru-rs/src/services/fs.rs index d86cb55..64da9c3 100644 --- a/ctru-rs/src/services/fs.rs +++ b/ctru-rs/src/services/fs.rs @@ -669,7 +669,7 @@ impl<'a> DirEntry<'a> { /// The full path is created by joining the original path to `read_dir` /// with the filename of this entry. pub fn path(&self) -> PathBuf { - self.root.join(&self.file_name()) + self.root.join(self.file_name()) } /// Return the metadata for the file that this entry points at. From 01e9c7062972ba888d02f8ddb7bfd938039b560c Mon Sep 17 00:00:00 2001 From: TechiePi Date: Wed, 2 Nov 2022 21:20:46 +0100 Subject: [PATCH 26/27] Address comments --- ctru-rs/src/applets/mii_selector.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ctru-rs/src/applets/mii_selector.rs b/ctru-rs/src/applets/mii_selector.rs index fef106b..d324055 100644 --- a/ctru-rs/src/applets/mii_selector.rs +++ b/ctru-rs/src/applets/mii_selector.rs @@ -158,16 +158,14 @@ impl MiiSelector { impl From for MiiSelectorReturn { fn from(ret: ctru_sys::MiiSelectorReturn) -> Self { let raw_mii_data = ret.mii; - let no_mii_selected = ret.no_mii_selected; - let guest_mii_index_clone = ret.guest_mii_index; let mut guest_mii_name = ret.guest_mii_name; MiiSelectorReturn { mii_data: raw_mii_data.into(), - is_mii_selected: no_mii_selected == 0, - mii_type: if guest_mii_index_clone != 0xFFFFFFFF { + is_mii_selected: ret.no_mii_selected == 0, + mii_type: if ret.guest_mii_index != 0xFFFFFFFF { MiiType::Guest { - index: guest_mii_index_clone, + index: ret.guest_mii_index, name: { let utf16_be = &mut guest_mii_name; utf16_be.reverse(); From 42345d10a803396c532a6fb80b015ff49004f50e Mon Sep 17 00:00:00 2001 From: TechiePi Date: Wed, 9 Nov 2022 10:24:36 +0100 Subject: [PATCH 27/27] Rename a few variables and document skipped bits --- ctru-rs/src/mii.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ctru-rs/src/mii.rs b/ctru-rs/src/mii.rs index e4ebcb4..17cb9bf 100644 --- a/ctru-rs/src/mii.rs +++ b/ctru-rs/src/mii.rs @@ -175,6 +175,8 @@ pub struct MoleDetails { /// /// Some values are not ordered _like_ the Mii Editor UI. The mapped values can be seen here: /// +/// +/// This struct is returned by the [``MiiSelector``](crate::applets::mii_selector::MiiSelector) #[derive(Clone, Debug)] pub struct MiiData { pub options: MiiDataOptions, @@ -222,7 +224,7 @@ impl From for MiiData { raw_mii_data[0xA], raw_mii_data[0xB], ]; - let creator_mac = [ + let mac_address = [ raw_mii_data[0x10], raw_mii_data[0x11], raw_mii_data[0x12], @@ -267,7 +269,7 @@ impl From for MiiData { .unwrap(); let raw_utf16_author = &raw_mii_data[0x48..0x5C]; - let mii_name = utf16_byte_pairs_to_string(raw_utf16_name); + let name = utf16_byte_pairs_to_string(raw_utf16_name); let author_name = utf16_byte_pairs_to_string(raw_utf16_author); let options = MiiDataOptions { @@ -291,12 +293,12 @@ impl From for MiiData { }, }; - let position = SelectorPosition { + let selector_position = SelectorPosition { page_index: partial_u8_bits_to_u8(&raw_position[0..=3]), slot_index: partial_u8_bits_to_u8(&raw_position[4..=7]), }; - let device = ConsoleIdentity { + let console_identity = ConsoleIdentity { origin_console: { match (raw_device[6], raw_device[5], raw_device[4]) { (false, false, true) => OriginConsole::Wii, @@ -350,6 +352,8 @@ impl From for MiiData { style: partial_u8_bits_to_u8(&raw_eyebrow_details[0..=4]), color: partial_u8_bits_to_u8(&raw_eyebrow_details[5..=7]), scale: partial_u8_bits_to_u8(&raw_eyebrow_details[8..=11]), + // Bits are skipped here, following the 3dbrew wiki: + // https://www.3dbrew.org/wiki/Mii#Mii_format offset 0x38 y_scale: partial_u8_bits_to_u8(&raw_eyebrow_details[12..=14]), rotation: partial_u8_bits_to_u8(&raw_eyebrow_details[16..=19]), x_spacing: partial_u8_bits_to_u8(&raw_eyebrow_details[21..=24]), @@ -397,12 +401,12 @@ impl From for MiiData { MiiData { options, - selector_position: position, - console_identity: device, + selector_position, + console_identity, system_id, - mac_address: creator_mac, + mac_address, details, - name: mii_name, + name, height, width, face_details,