Skip to main content

rkg_utils/header/mii/
head.rs

1use std::convert::Infallible;
2
3use crate::byte_handler::{ByteHandlerError, FromByteHandler};
4
5/// Represents the head customization options of a Mii,
6/// including face shape, skin tone, and facial feature overlay.
7#[derive(Clone, Copy)]
8pub struct Head {
9    /// Face/head shape.
10    shape: HeadShape,
11    /// Skin tone.
12    skin_tone: SkinTone,
13    /// Facial feature overlay applied to the face.
14    face_features: FaceFeatures,
15}
16
17/// Errors that can occur while deserializing a [`Head`].
18#[derive(thiserror::Error, Debug)]
19pub enum HeadError {
20    /// The head shape byte did not map to a known [`HeadShape`] variant.
21    #[error("Shape is invalid")]
22    ShapeInvalid,
23    /// The skin tone byte did not map to a known [`SkinTone`] variant.
24    #[error("SkinTone is invalid")]
25    SkinToneInvalid,
26    /// The face features byte did not map to a known [`FaceFeatures`] variant.
27    #[error("FaceFeatures is invalid")]
28    FaceFeaturesInvalid,
29    /// A `ByteHandler` operation failed.
30    #[error("ByteHandler Error: {0}")]
31    ByteHandlerError(#[from] ByteHandlerError),
32    /// Infallible conversion error; cannot occur at runtime.
33    #[error("")]
34    Infallible(#[from] Infallible),
35}
36
37impl Head {
38    /// Creates a new [`Head`] from its individual components.
39    ///
40    /// # Arguments
41    ///
42    /// * `shape` - Face/head shape.
43    /// * `skin_tone` - Skin tone.
44    /// * `face_features` - Facial feature overlay.
45    pub fn new(shape: HeadShape, skin_tone: SkinTone, face_features: FaceFeatures) -> Self {
46        Self {
47            shape,
48            skin_tone,
49            face_features,
50        }
51    }
52
53    /// Returns the face/head shape.
54    pub fn shape(&self) -> HeadShape {
55        self.shape
56    }
57
58    /// Returns the skin tone.
59    pub fn skin_tone(&self) -> SkinTone {
60        self.skin_tone
61    }
62
63    /// Returns the facial feature overlay.
64    pub fn face_features(&self) -> FaceFeatures {
65        self.face_features
66    }
67
68    /// Sets the face/head shape.
69    pub fn set_shape(&mut self, shape: HeadShape) {
70        self.shape = shape;
71    }
72
73    /// Sets the skin tone.
74    pub fn set_skin_tone(&mut self, skin_tone: SkinTone) {
75        self.skin_tone = skin_tone;
76    }
77
78    /// Sets the facial feature overlay.
79    pub fn set_face_features(&mut self, face_features: FaceFeatures) {
80        self.face_features = face_features;
81    }
82}
83
84/// Deserializes a [`Head`] from a `ByteHandler`.
85///
86/// The handler is shifted right by 5 bits to align the head shape, then shifted
87/// right by 1 more bit to extract the skin tone and face features from the
88/// packed Mii binary format using bit masks.
89impl FromByteHandler for Head {
90    type Err = HeadError;
91    fn from_byte_handler<T>(handler: T) -> Result<Self, Self::Err>
92    where
93        T: TryInto<crate::byte_handler::ByteHandler>,
94        Self::Err: From<T::Error>,
95    {
96        let mut handler = handler.try_into()?;
97        handler.shift_right(5);
98        let shape_value = handler.copy_byte(0);
99        handler.shift_right(1);
100        let byte = handler.copy_byte(1);
101
102        Ok(Head {
103            shape: HeadShape::try_from(shape_value).map_err(|_| HeadError::ShapeInvalid)?,
104            skin_tone: SkinTone::try_from((byte >> 4) & 0x07)
105                .map_err(|_| HeadError::SkinToneInvalid)?,
106            face_features: FaceFeatures::try_from(byte & 0x0F)
107                .map_err(|_| HeadError::FaceFeaturesInvalid)?,
108        })
109    }
110}
111
112/// Face/head shape options available in the Mii editor.
113#[derive(Clone, Copy, PartialEq, Debug)]
114pub enum HeadShape {
115    Sharp,
116    Rounded,
117    SharpRoundedSmall,
118    Large,
119    SharpSmall,
120    Flat,
121    Angular,
122    FlatRounded,
123}
124
125/// Converts a raw byte value from the Mii data format into a [`HeadShape`].
126///
127/// Returns `Err(())` if the byte does not correspond to any known head shape.
128impl TryFrom<u8> for HeadShape {
129    type Error = ();
130    fn try_from(value: u8) -> Result<Self, Self::Error> {
131        match value {
132            0x00 => Ok(Self::Sharp),
133            0x01 => Ok(Self::Rounded),
134            0x02 => Ok(Self::SharpRoundedSmall),
135            0x03 => Ok(Self::Large),
136            0x04 => Ok(Self::SharpSmall),
137            0x05 => Ok(Self::Flat),
138            0x06 => Ok(Self::Angular),
139            0x07 => Ok(Self::FlatRounded),
140            _ => Err(()),
141        }
142    }
143}
144
145/// Converts a [`HeadShape`] into its raw byte representation for the Mii data format.
146impl From<HeadShape> for u8 {
147    fn from(value: HeadShape) -> Self {
148        match value {
149            HeadShape::Sharp => 0x00,
150            HeadShape::Rounded => 0x01,
151            HeadShape::SharpRoundedSmall => 0x02,
152            HeadShape::Large => 0x03,
153            HeadShape::SharpSmall => 0x04,
154            HeadShape::Flat => 0x05,
155            HeadShape::Angular => 0x06,
156            HeadShape::FlatRounded => 0x07,
157        }
158    }
159}
160
161/// Skin tone options available in the Mii editor.
162#[derive(Clone, Copy, PartialEq, Debug)]
163pub enum SkinTone {
164    Beige,
165    Natural,
166    WarmIvory,
167    Ivory,
168    Honey,
169    Chestnut,
170}
171
172/// Converts a raw byte value from the Mii data format into a [`SkinTone`].
173///
174/// Returns `Err(())` if the byte does not correspond to any known skin tone.
175impl TryFrom<u8> for SkinTone {
176    type Error = ();
177    fn try_from(value: u8) -> Result<Self, Self::Error> {
178        match value {
179            0x00 => Ok(Self::Beige),
180            0x01 => Ok(Self::Natural),
181            0x02 => Ok(Self::WarmIvory),
182            0x03 => Ok(Self::Ivory),
183            0x04 => Ok(Self::Honey),
184            0x05 => Ok(Self::Chestnut),
185            _ => Err(()),
186        }
187    }
188}
189
190/// Converts a [`SkinTone`] into its raw byte representation for the Mii data format.
191impl From<SkinTone> for u8 {
192    fn from(value: SkinTone) -> Self {
193        match value {
194            SkinTone::Beige => 0x00,
195            SkinTone::Natural => 0x01,
196            SkinTone::WarmIvory => 0x02,
197            SkinTone::Ivory => 0x03,
198            SkinTone::Honey => 0x04,
199            SkinTone::Chestnut => 0x05,
200        }
201    }
202}
203
204/// Facial feature overlays available in the Mii editor.
205///
206/// These are decorative markings drawn on top of the Mii's face shape.
207#[derive(Clone, Copy, PartialEq, Debug)]
208pub enum FaceFeatures {
209    /// No facial feature overlay.
210    None,
211    /// Porcelain-style cheek blush.
212    CheekPorcelain,
213    /// Porcelain-style cheek blush with blue eye shadow.
214    CheekPorcelainEyeShadowBlue,
215    Freckles,
216    /// Dark circles or bags under the eyes.
217    UnderTheEyes,
218    /// Marks suggesting facial pain or stress lines.
219    FacialPain,
220    /// Rounded cheek blush marks.
221    Cheeks,
222    /// Chin shading or cleft.
223    Chin,
224    /// Drooping brow lines.
225    BrowDroop,
226    /// Lion's mane-style beard overlay.
227    LionsManeBeard,
228    /// Downturned mouth frown lines.
229    MouthFrown,
230    /// Nasolabial folds with crow's feet and frown lines.
231    FoldsCrowsFrown,
232}
233
234/// Converts a raw byte value from the Mii data format into a [`FaceFeatures`] variant.
235///
236/// Returns `Err(())` if the byte does not correspond to any known face features value.
237impl TryFrom<u8> for FaceFeatures {
238    type Error = ();
239    fn try_from(value: u8) -> Result<Self, Self::Error> {
240        match value {
241            0x00 => Ok(Self::None),
242            0x01 => Ok(Self::CheekPorcelain),
243            0x02 => Ok(Self::CheekPorcelainEyeShadowBlue),
244            0x03 => Ok(Self::Freckles),
245            0x04 => Ok(Self::UnderTheEyes),
246            0x05 => Ok(Self::FacialPain),
247            0x06 => Ok(Self::Cheeks),
248            0x07 => Ok(Self::Chin),
249            0x08 => Ok(Self::BrowDroop),
250            0x09 => Ok(Self::LionsManeBeard),
251            0x0A => Ok(Self::MouthFrown),
252            0x0B => Ok(Self::FoldsCrowsFrown),
253            _ => Err(()),
254        }
255    }
256}
257
258/// Converts a [`FaceFeatures`] variant into its raw byte representation for the Mii data format.
259impl From<FaceFeatures> for u8 {
260    fn from(value: FaceFeatures) -> Self {
261        match value {
262            FaceFeatures::None => 0x00,
263            FaceFeatures::CheekPorcelain => 0x01,
264            FaceFeatures::CheekPorcelainEyeShadowBlue => 0x02,
265            FaceFeatures::Freckles => 0x03,
266            FaceFeatures::UnderTheEyes => 0x04,
267            FaceFeatures::FacialPain => 0x05,
268            FaceFeatures::Cheeks => 0x06,
269            FaceFeatures::Chin => 0x07,
270            FaceFeatures::BrowDroop => 0x08,
271            FaceFeatures::LionsManeBeard => 0x09,
272            FaceFeatures::MouthFrown => 0x0A,
273            FaceFeatures::FoldsCrowsFrown => 0x0B,
274        }
275    }
276}