Browse Source

Merge pull request #78 from Techie-Pi/feat/mii-selector

Implement Mii Selector applet
pull/84/head
Meziu 2 years ago committed by GitHub
parent
commit
4e221667dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      ctru-rs/build.rs
  2. 14
      ctru-rs/examples/file-explorer.rs
  3. 2
      ctru-rs/examples/hashmaps.rs
  4. 43
      ctru-rs/examples/mii-selector.rs
  5. 10
      ctru-rs/examples/network-sockets.rs
  6. 2
      ctru-rs/examples/software-keyboard.rs
  7. 4
      ctru-rs/examples/time-rtc.rs
  8. 186
      ctru-rs/src/applets/mii_selector.rs
  9. 1
      ctru-rs/src/applets/mod.rs
  10. 6
      ctru-rs/src/error.rs
  11. 1
      ctru-rs/src/lib.rs
  12. 466
      ctru-rs/src/mii.rs
  13. 2
      ctru-rs/src/services/fs.rs

2
ctru-rs/build.rs

@ -28,5 +28,5 @@ fn main() { @@ -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}");
}

14
ctru-rs/examples/file-explorer.rs

@ -96,7 +96,7 @@ impl<'a> FileExplorer<'a> { @@ -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> { @@ -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> { @@ -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> { @@ -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> { @@ -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> { @@ -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> { @@ -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;
}

2
ctru-rs/examples/hashmaps.rs

@ -17,7 +17,7 @@ fn main() { @@ -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();

43
ctru-rs/examples/mii-selector.rs

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
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().unwrap();
println!("Is Mii selected?: {:?}", result.is_mii_selected);
println!("Mii type: {:?}", result.mii_type);
println!("Name: {:?}", result.mii_data.name);
println!("Author: {:?}", result.mii_data.author_name);
println!(
"Does the Mii have moles?: {:?}",
result.mii_data.mole_details.is_enabled
);
// 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();
}
}

10
ctru-rs/examples/network-sockets.rs

@ -25,19 +25,19 @@ fn main() { @@ -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() { @@ -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\n<html><body>Hello world</body></html>\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() { @@ -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));
}
},

2
ctru-rs/examples/software-keyboard.rs

@ -29,7 +29,7 @@ fn main() { @@ -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!"),

4
ctru-rs/examples/time-rtc.rs

@ -33,8 +33,8 @@ fn main() { @@ -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();

186
ctru-rs/src/applets/mii_selector.rs

@ -0,0 +1,186 @@ @@ -0,0 +1,186 @@
//! 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 },
User,
}
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<ctru_sys::MiiSelectorConf>,
}
/// 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,
}
/// 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::<ctru_sys::MiiSelectorConf>::default();
unsafe {
ctru_sys::miiSelectorInit(config.as_mut());
}
Self { config }
}
/// 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
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());
}
}
/// 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,
MiiConfigIndex::All => ctru_sys::MIISELECTOR_GUESTMII_SLOTS,
};
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,
MiiConfigIndex::All => ctru_sys::MIISELECTOR_GUESTMII_SLOTS,
};
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,
MiiConfigIndex::All => ctru_sys::MIISELECTOR_USERMII_SLOTS,
};
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,
MiiConfigIndex::All => ctru_sys::MIISELECTOR_USERMII_SLOTS,
};
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) {
// 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<MiiSelectorReturn, MiiLaunchError> {
let mut return_val = Box::<ctru_sys::MiiSelectorReturn>::default();
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())
} else {
Err(MiiLaunchError::InvalidChecksum)
}
}
}
impl From<ctru_sys::MiiSelectorReturn> for MiiSelectorReturn {
fn from(ret: ctru_sys::MiiSelectorReturn) -> Self {
let raw_mii_data = ret.mii;
let mut guest_mii_name = ret.guest_mii_name;
MiiSelectorReturn {
mii_data: raw_mii_data.into(),
is_mii_selected: ret.no_mii_selected == 0,
mii_type: if ret.guest_mii_index != 0xFFFFFFFF {
MiiType::Guest {
index: ret.guest_mii_index,
name: {
let utf16_be = &mut guest_mii_name;
utf16_be.reverse();
String::from_utf16(utf16_be.as_slice()).unwrap()
},
}
} else {
MiiType::User
},
}
}
}
impl From<u32> for MiiConfigIndex {
fn from(v: u32) -> Self {
Self::Index(v)
}
}

1
ctru-rs/src/applets/mod.rs

@ -1 +1,2 @@ @@ -1 +1,2 @@
pub mod mii_selector;
pub mod swkbd;

6
ctru-rs/src/error.rs

@ -87,7 +87,7 @@ impl fmt::Debug for Error { @@ -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 { @@ -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")

1
ctru-rs/src/lib.rs

@ -76,6 +76,7 @@ pub mod applets; @@ -76,6 +76,7 @@ pub mod applets;
pub mod console;
pub mod error;
pub mod gfx;
pub mod mii;
pub mod prelude;
pub mod services;

466
ctru-rs/src/mii.rs

@ -0,0 +1,466 @@ @@ -0,0 +1,466 @@
//! Mii Data
//!
//! This module contains the structs that represent all the data of a Mii.
//! 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)]
pub enum RegionLock {
None,
Japan,
USA,
Europe,
}
/// Represent the charset of the console
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Charset {
JapanUSAEurope,
China,
Korea,
Taiwan,
}
/// Represents the options of the Mii
#[derive(Copy, Clone, Debug)]
pub struct MiiDataOptions {
pub is_copying_allowed: bool,
pub is_profanity_flag_enabled: bool,
pub region_lock: RegionLock,
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 {
Wii,
DSi,
/// Both New 3DS and Old 3DS
N3DS,
WiiUSwitch,
}
/// 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,
pub birthday_month: u8,
pub birthday_day: u8,
pub shirt_color: u8,
pub is_favorite: bool,
}
/// Represents the face style of the Mii
#[derive(Copy, Clone, Debug)]
pub struct FaceStyle {
pub is_sharing_enabled: bool,
pub shape: u8,
pub skin_color: u8,
}
/// Represents the face details of the Mii
#[derive(Copy, Clone, Debug)]
pub struct FaceDetails {
pub style: FaceStyle,
pub wrinkles: u8,
pub makeup: u8,
}
/// Represents the hair details of the Mii
#[derive(Copy, Clone, Debug)]
pub struct HairDetails {
pub style: u8,
pub color: u8,
pub is_flipped: bool,
}
/// Represents the eye details of the Mii
#[derive(Copy, Clone, Debug)]
pub struct EyeDetails {
pub style: u8,
pub color: u8,
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,
pub color: u8,
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,
pub scale: u8,
pub y_position: u8,
}
/// Represents the details of the mouth
#[derive(Copy, Clone, Debug)]
pub struct MouthDetails {
pub style: u8,
pub color: u8,
pub scale: u8,
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,
pub color: u8,
pub scale: u8,
pub y_position: u8,
}
/// Represents the details of the glass
#[derive(Copy, Clone, Debug)]
pub struct GlassDetails {
pub style: u8,
pub color: u8,
pub scale: u8,
pub y_position: u8,
}
/// Represents the details of the mole
#[derive(Copy, Clone, Debug)]
pub struct MoleDetails {
pub is_enabled: bool,
pub scale: u8,
pub x_position: u8,
pub y_position: u8,
}
/// Represents all the data of a Mii
///
/// Some values are not ordered _like_ the Mii Editor UI. The mapped values can be seen here:
/// <https://www.3dbrew.org/wiki/Mii#Mapped_Editor_.3C-.3E_Hex_values>
///
/// This struct is returned by the [``MiiSelector``](crate::applets::mii_selector::MiiSelector)
#[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],
pub details: Details,
pub name: String,
pub height: u8,
pub width: u8,
pub face_details: FaceDetails,
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<ctru_sys::MiiData> 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]);
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 mac_address = [
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, &[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];
let raw_face_style = vec_bit(raw_mii_data[0x30]);
let raw_face_details = vec_bit(raw_mii_data[0x31]);
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])
.try_into()
.unwrap();
let raw_eyebrow_details: [bool; 32] =
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_utf16_author = &raw_mii_data[0x48..0x5C];
let 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[0],
is_profanity_flag_enabled: raw_options[1],
region_lock: {
match (raw_options[3], raw_options[2]) {
(false, false) => RegionLock::None,
(false, true) => RegionLock::Japan,
(true, false) => RegionLock::USA,
(true, true) => RegionLock::Europe,
}
},
charset: {
match (raw_options[5], raw_options[4]) {
(false, false) => Charset::JapanUSAEurope,
(false, true) => Charset::China,
(true, false) => Charset::Korea,
(true, true) => Charset::Taiwan,
}
},
};
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 console_identity = ConsoleIdentity {
origin_console: {
match (raw_device[6], raw_device[5], raw_device[4]) {
(false, false, true) => OriginConsole::Wii,
(false, true, false) => OriginConsole::DSi,
(false, true, true) => OriginConsole::N3DS,
_ => OriginConsole::WiiUSwitch,
}
},
};
let details = Details {
sex: {
match raw_details[0] {
true => MiiSex::Female,
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]),
is_favorite: raw_details[14],
};
let face_details = FaceDetails {
style: FaceStyle {
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]),
};
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],
};
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]),
};
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]),
// 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]),
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]),
};
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]),
};
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]),
};
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]),
};
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]),
};
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]),
};
MiiData {
options,
selector_position,
console_identity,
system_id,
mac_address,
details,
name,
height,
width,
face_details,
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::<Vec<bool>>()
.try_into()
.unwrap()
}
/// 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))
}
/// 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];
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
fn utf16_byte_pairs_to_string(data: &[u8]) -> String {
let raw_utf16_composed = data
.chunks_exact(2)
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
.collect::<Vec<u16>>();
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<bool> {
get_values.iter().flat_map(|v| vec_bit(data[*v])).collect()
}

2
ctru-rs/src/services/fs.rs

@ -669,7 +669,7 @@ impl<'a> DirEntry<'a> { @@ -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.

Loading…
Cancel
Save