RFLCharData
Appearance
#[bitsize(4)]
#[repr(u8)]
#[derive(FromBits, Debug, PartialEq)]
pub enum FavoriteColor {
Red = 0,
Orange = 1,
Yellow = 2,
YellowGreen = 3,
Green = 4,
Blue = 5,
SkyBlue = 6,
Pink = 7,
Purple = 8,
Brown = 9,
White = 10,
Black = 11,
#[fallback]
Invalid(u4),
}
impl FavoriteColor {
fn as_u8(&self) -> u8 {
match self {
FavoriteColor::Red => 0,
FavoriteColor::Orange => 1,
FavoriteColor::Yellow => 2,
FavoriteColor::YellowGreen => 3,
FavoriteColor::Green => 4,
FavoriteColor::Blue => 5,
FavoriteColor::SkyBlue => 6,
FavoriteColor::Pink => 7,
FavoriteColor::Purple => 8,
FavoriteColor::Brown => 9,
FavoriteColor::White => 10,
FavoriteColor::Black => 11,
FavoriteColor::Invalid(n) => n.as_u8(),
}
}
}
#[bitsize(1)]
#[derive(FromBits, Debug, PartialEq)]
pub enum Gender {
Male,
Female,
}
#[bitfield(16)]
pub struct PersonalInfoField {
pub padding: u1,
/// Originally named sex
pub gender: Gender,
pub birth_month: u4,
/// 0 = unset, Counts from 1-31
/// Birth month and day must be set together if set.
pub birth_day: u5,
pub favorite_color: FavoriteColor,
pub favorite: bool,
}
#[bitfield(16)]
pub struct FaceField {
pub face_type: u3,
pub face_color: u3,
pub face_tex: u4,
pub padding2: u3,
pub localonly: u1,
/// Set to 1 if downloaded from "Check Mii Out Channel".
pub type_: u2,
}
#[bitfield(16)]
pub struct HairField {
pub hair_type: u7,
pub hair_color: u3,
pub hair_flip: u1,
pub padding3: u5,
}
#[bitfield(32)]
pub struct EyebrowField {
pub eyebrow_type: u5,
pub eyebrow_rotate: u5,
pub padding4: u6,
pub eyebrow_color: u3,
pub eyebrow_scale: u4,
pub eyebrow_y: u5,
pub eyebrow_x: u4,
}
#[bitfield(32)]
pub struct EyeField {
pub eye_type: u6,
pub eye_rotate: u5,
pub eye_y: u5,
pub eye_color: u3,
pub eye_scale: u4,
pub eye_x: u4,
pub padding5: u5,
}
#[bitfield(16)]
pub struct NoseField {
pub nose_type: u4,
pub nose_scale: u4,
pub nose_y: u5,
pub padding6: u3,
}
#[bitfield(16)]
pub struct MouthField {
pub mouth_type: u5,
pub mouth_color: u2,
pub mouth_scale: u4,
pub mouth_y: u5,
}
#[bitfield(16)]
pub struct GlassField {
pub glass_type: u4,
pub glass_color: u3,
pub glass_scale: u4,
pub glass_y: u5,
}
#[bitfield(16)]
pub struct FaceHairField {
pub mustache_type: u2,
pub beard_type: u2,
pub beard_color: u3,
pub beard_scale: u4,
pub beard_y: u5,
}
#[bitfield(16)]
pub struct MoleField {
pub mole_type: u1,
pub mole_scale: u4,
pub mole_y: u5,
pub mole_x: u5,
pub padding8: u1,
}
/// `flags` contain information about where the Char was created,
/// and some other miscellaneous state. See [RvlCreateIdFlags::platform].
///
/// `create_date_offset` contains an offset timestamp of creation date.
/// See [Self::create_date_timestamp]'s implementation for more information.
///
/// `addr_low` is built from the Mac address of the Rvl/Ntr console.
/// It contains a checksum of the first three bytes, and the last three bytes.
///
/// ```rust
/// fn build_addr_low(mac: &[u8; 6]) -> [u8; 4] {
/// let checksum = mac.iter().take(3).fold(0u8, |sum, &b| sum.wrapping_add(b)) & 0x7F;
/// [checksum, mac[3], mac[4], mac[5]]
/// }
/// ```
///
/// It is unknown how the checksum byte is calculated on Ntr targets.
///
/// In order for the Char to be considered as created from the
/// same console on Rvl (RFLiIsMyHomeID), the CreateId has to
/// be non-null, not from Ntr, and the `addr_low` has to match.
///
#[bitfield(64)]
pub struct CreateId {
pub flags: CreateIdFlags,
pub create_date_offset: u28,
pub addr_low: [u8; 4],
}
impl CreateId {
/// Outputs the creation date timestamp from the offset encoded.
/// It is not known if this implementation is accurate.
pub fn create_date_timestamp(&self) -> u32 {
const JAN_1_2006: u32 = 1136073600;
let offset = self.create_date_offset().as_u32();
(offset * 4) + JAN_1_2006
}
}
/// `Etc` can be either targets: Cafe, Nx, Miitomo
#[bitsize(2)]
#[derive(FromBits, Debug, PartialEq)]
pub enum CreateIdPlatform {
Rvl = 0b00,
Ctr = 0b01,
Ntr = 0b10,
Etc = 0b11,
}
/// These flags can be used to derive the creation platform,
/// see [Self::platform].
///
#[bitfield(4)]
pub struct CreateIdFlags {
/// Cleared = Special, Set = Normal
pub normal: bool,
/// Cleared on Wii and 3DS, set on DS and Wii U.
pub field_1: bool,
/// Given to random Miis and seen in some games' CPU Miis.
pub temporary: bool,
/// Cleared on Wii and DS, set on 3DS and Wii U.
pub field_3: bool,
}
impl CreateIdFlags {
/// Outputs the creation platform for the Char.
/// Uses `field_1` and `field_3`.
pub fn platform(&self) -> CreateIdPlatform {
let bits = u2::from_u8(match (self.field_1(), self.field_3()) {
(false, false) => 0b00_u8,
(true, false) => 0b10,
(false, true) => 0b01,
(true, true) => 0b11,
});
CreateIdPlatform::from(bits)
}
}
/// A packed character info format.
/// This structure has a lot of bitfields.
/// These fields have been given speculative names.
///
/// This format is commonly known as `.{r,n}cd`.
/// Rvl and Ntr only differ by endian-ness.
#[derive(Debug)]
#[brw(big)]
pub struct RvlCharData {
pub personal_info: PersonalInfoField,
pub name: FixedLengthWideString<10>,
pub height: u8,
pub build: u8,
pub create_id: CreateId,
pub face: FaceField,
pub hair: HairField,
pub eyebrow: EyebrowField,
pub eye: EyeField,
pub nose: NoseField,
pub mouth: MouthField,
pub glass: GlassField,
pub face_hair: FaceHairField,
pub mole: MoleField,
pub creator_name: FixedLengthWideString<10>,
}