Skip to main content

rkg_utils/header/mii/
eyes.rs

1use std::convert::Infallible;
2
3use crate::byte_handler::{ByteHandlerError, FromByteHandler};
4
5/// Represents the eye customization options of a Mii.
6///
7/// All positional and size values are validated against the ranges permitted
8/// by the Mii data format on construction.
9#[derive(Clone, Copy)]
10pub struct Eyes {
11    /// Eye rotation (0–7).
12    rotation: u8,
13    /// Eye size (0–7).
14    size: u8,
15    /// Horizontal position of the eyes (0–12).
16    x: u8,
17    /// Vertical position of the eyes (0–18).
18    y: u8,
19    /// Eye color.
20    eye_color: EyeColor,
21    /// Eye shape/style.
22    eye_type: EyeType,
23}
24
25impl Eyes {
26    /// Creates a new [`Eyes`] from its individual components.
27    ///
28    /// # Arguments
29    ///
30    /// * `rotation` - Eye rotation (0–7).
31    /// * `size` - Eye size (0–7).
32    /// * `x` - Horizontal position (0–12).
33    /// * `y` - Vertical position (0–18).
34    /// * `eye_color` - Eye color.
35    /// * `eye_type` - Eye shape/style.
36    ///
37    /// # Errors
38    ///
39    /// Returns [`EyesError::SizeInvalid`] if `size` exceeds 7.
40    /// Returns [`EyesError::RotationInvalid`] if `rotation` exceeds 7.
41    /// Returns [`EyesError::YInvalid`] if `y` exceeds 18.
42    /// Returns [`EyesError::XInvalid`] if `x` exceeds 12.
43    pub fn new(
44        rotation: u8,
45        size: u8,
46        x: u8,
47        y: u8,
48        eye_color: EyeColor,
49        eye_type: EyeType,
50    ) -> Result<Self, EyesError> {
51        if size > 7 {
52            return Err(EyesError::SizeInvalid);
53        }
54        if rotation > 7 {
55            return Err(EyesError::RotationInvalid);
56        }
57        if y > 18 {
58            return Err(EyesError::YInvalid);
59        }
60        if x > 12 {
61            return Err(EyesError::XInvalid);
62        }
63
64        Ok(Self {
65            rotation,
66            size,
67            x,
68            y,
69            eye_color,
70            eye_type,
71        })
72    }
73
74    /// Returns the eye rotation (0–7).
75    pub fn rotation(&self) -> u8 {
76        self.rotation
77    }
78
79    /// Returns the eye size (0–7).
80    pub fn size(&self) -> u8 {
81        self.size
82    }
83
84    /// Returns the horizontal position of the eyes (0–12).
85    pub fn x(&self) -> u8 {
86        self.x
87    }
88
89    /// Returns the vertical position of the eyes (0–18).
90    pub fn y(&self) -> u8 {
91        self.y
92    }
93
94    /// Returns the eye color.
95    pub fn eye_color(&self) -> EyeColor {
96        self.eye_color
97    }
98
99    /// Returns the eye shape/style.
100    pub fn eye_type(&self) -> EyeType {
101        self.eye_type
102    }
103
104    /// Sets the eye rotation.
105    ///
106    /// # Errors
107    ///
108    /// Returns [`EyesError::RotationInvalid`] if `rotation` exceeds 7.
109    pub fn set_rotation(&mut self, rotation: u8) -> Result<(), EyesError> {
110        if rotation > 7 {
111            return Err(EyesError::RotationInvalid);
112        }
113        self.rotation = rotation;
114        Ok(())
115    }
116
117    /// Sets the eye size.
118    ///
119    /// # Errors
120    ///
121    /// Returns [`EyesError::SizeInvalid`] if `size` exceeds 7.
122    pub fn set_size(&mut self, size: u8) -> Result<(), EyesError> {
123        if size > 7 {
124            return Err(EyesError::SizeInvalid);
125        }
126        self.size = size;
127        Ok(())
128    }
129
130    /// Sets the horizontal position of the eyes.
131    ///
132    /// # Errors
133    ///
134    /// Returns [`EyesError::XInvalid`] if `x` exceeds 12.
135    pub fn set_x(&mut self, x: u8) -> Result<(), EyesError> {
136        if x > 12 {
137            return Err(EyesError::XInvalid);
138        }
139        self.x = x;
140        Ok(())
141    }
142
143    /// Sets the vertical position of the eyes.
144    ///
145    /// # Errors
146    ///
147    /// Returns [`EyesError::YInvalid`] if `y` exceeds 18.
148    pub fn set_y(&mut self, y: u8) -> Result<(), EyesError> {
149        if y > 18 {
150            return Err(EyesError::YInvalid);
151        }
152        self.y = y;
153        Ok(())
154    }
155
156    /// Sets the eye color.
157    pub fn set_eye_color(&mut self, eye_color: EyeColor) {
158        self.eye_color = eye_color;
159    }
160
161    /// Sets the eye shape/style.
162    pub fn set_eye_type(&mut self, eye_type: EyeType) {
163        self.eye_type = eye_type;
164    }
165}
166
167/// Errors that can occur while constructing or deserializing [`Eyes`].
168#[derive(thiserror::Error, Debug)]
169pub enum EyesError {
170    /// The eye type byte did not map to a known [`EyeType`] variant.
171    #[error("Type is invalid")]
172    TypeInvalid,
173    /// The eye color byte did not map to a known [`EyeColor`] variant.
174    #[error("Color is invalid")]
175    ColorInvalid,
176    /// The rotation value exceeds the maximum of 7.
177    #[error("Rotation is invalid")]
178    RotationInvalid,
179    /// The size value exceeds the maximum of 7.
180    #[error("Size is invalid")]
181    SizeInvalid,
182    /// The vertical position exceeds the maximum of 18.
183    #[error("Y position is invalid")]
184    YInvalid,
185    /// The horizontal position exceeds the maximum of 12.
186    #[error("X position is invalid")]
187    XInvalid,
188    /// A `ByteHandler` operation failed.
189    #[error("ByteHandler Error: {0}")]
190    ByteHandlerError(#[from] ByteHandlerError),
191    /// Infallible conversion error; cannot occur at runtime.
192    #[error("")]
193    Infallible(#[from] Infallible),
194}
195
196/// Deserializes [`Eyes`] from a `ByteHandler`.
197///
198/// Extracts and unpacks the eye type, color, vertical position, rotation, horizontal
199/// position, and size from the packed Mii binary format using a series of bit shifts and masks.
200impl FromByteHandler for Eyes {
201    type Err = EyesError;
202    fn from_byte_handler<T>(handler: T) -> Result<Self, Self::Err>
203    where
204        T: TryInto<crate::byte_handler::ByteHandler>,
205        Self::Err: From<T::Error>,
206    {
207        let mut handler = handler.try_into()?;
208
209        let eye_type =
210            EyeType::try_from(handler.copy_byte(0) >> 2).map_err(|_| EyesError::TypeInvalid)?;
211        let eye_color =
212            EyeColor::try_from(handler.copy_byte(2) >> 5).map_err(|_| EyesError::ColorInvalid)?;
213        let y = handler.copy_byte(1) & 0x1F;
214        handler.shift_right(5);
215        let rotation = handler.copy_byte(1) & 0x1F;
216        let x = handler.copy_byte(3) & 0x0F;
217        let size = handler.copy_byte(3) >> 4;
218
219        Self::new(rotation, size, x, y, eye_color, eye_type)
220    }
221}
222
223/// Eye color options available in the Mii editor.
224#[derive(Clone, Copy, PartialEq, Debug)]
225pub enum EyeColor {
226    Black,
227    Gray,
228    Brown,
229    Hazel,
230    Blue,
231    Green,
232}
233
234/// Converts a raw byte value from the Mii data format into an [`EyeColor`].
235///
236/// Returns `Err(())` if the byte does not correspond to any known eye color.
237impl TryFrom<u8> for EyeColor {
238    type Error = ();
239    fn try_from(value: u8) -> Result<Self, Self::Error> {
240        match value {
241            0x00 => Ok(Self::Black),
242            0x01 => Ok(Self::Gray),
243            0x02 => Ok(Self::Brown),
244            0x03 => Ok(Self::Hazel),
245            0x04 => Ok(Self::Blue),
246            0x05 => Ok(Self::Green),
247            _ => Err(()),
248        }
249    }
250}
251
252/// Converts an [`EyeColor`] into its raw byte representation for the Mii data format.
253impl From<EyeColor> for u8 {
254    fn from(value: EyeColor) -> Self {
255        match value {
256            EyeColor::Black => 0x00,
257            EyeColor::Gray => 0x01,
258            EyeColor::Brown => 0x02,
259            EyeColor::Hazel => 0x03,
260            EyeColor::Blue => 0x04,
261            EyeColor::Green => 0x05,
262        }
263    }
264}
265
266/// All eye shapes available in the Mii editor.
267#[derive(Clone, Copy, PartialEq, Debug)]
268pub enum EyeType {
269    Normal,
270    NormalLash,
271    WhiteLash,
272    WhiteNoBottom,
273    OvalAngledWhite,
274    AngryWhite,
275    DotLashType1,
276    Line,
277    DotLine,
278    OvalWhite,
279    RoundedWhite,
280    NormalShadow,
281    CircleWhite,
282    Circle,
283    CircleWhiteStroke,
284    NormalOvalNoBottom,
285    NormalOvalLarge,
286    NormalRoundedNoBottom,
287    SmallLash,
288    Small,
289    TwoSmall,
290    NormalLongLash,
291    WhiteTwoLashes,
292    WhiteThreeLashes,
293    DotAngry,
294    DotAngled,
295    Oval,
296    SmallWhite,
297    WhiteAngledNoBottom,
298    WhiteAngledNoLeft,
299    SmallWhiteTwoLashes,
300    LeafWhiteLash,
301    WhiteLargeNoBottom,
302    Dot,
303    DotLashType2,
304    DotThreeLashes,
305    WhiteOvalTop,
306    WhiteOvalBottom,
307    WhiteOvalBottomFlat,
308    WhiteOvalTwoLashes,
309    WhiteOvalThreeLashes,
310    WhiteOvalNoBottomTwoLashes,
311    DotWhite,
312    WhiteOvalTopFlat,
313    WhiteThinLeaf,
314    StarThreeLashes,
315    LineTwoLashes,
316    CrowsFeet,
317}
318
319/// Converts a raw byte value from the Mii data format into an [`EyeType`].
320///
321/// Returns `Err(())` if the byte does not correspond to any known eye type.
322impl TryFrom<u8> for EyeType {
323    type Error = ();
324    fn try_from(value: u8) -> Result<Self, Self::Error> {
325        match value {
326            0x02 => Ok(Self::Normal),
327            0x04 => Ok(Self::NormalLash),
328            0x00 => Ok(Self::WhiteLash),
329            0x08 => Ok(Self::WhiteNoBottom),
330            0x27 => Ok(Self::OvalAngledWhite),
331            0x11 => Ok(Self::AngryWhite),
332            0x01 => Ok(Self::DotLashType1),
333            0x1A => Ok(Self::Line),
334            0x10 => Ok(Self::DotLine),
335            0x0F => Ok(Self::OvalWhite),
336            0x1B => Ok(Self::RoundedWhite),
337            0x14 => Ok(Self::NormalShadow),
338            0x21 => Ok(Self::CircleWhite),
339            0x0B => Ok(Self::Circle),
340            0x13 => Ok(Self::CircleWhiteStroke),
341            0x20 => Ok(Self::NormalOvalNoBottom),
342            0x09 => Ok(Self::NormalOvalLarge),
343            0x0C => Ok(Self::NormalRoundedNoBottom),
344            0x17 => Ok(Self::SmallLash),
345            0x22 => Ok(Self::Small),
346            0x15 => Ok(Self::TwoSmall),
347            0x19 => Ok(Self::NormalLongLash),
348            0x28 => Ok(Self::WhiteTwoLashes),
349            0x23 => Ok(Self::WhiteThreeLashes),
350            0x05 => Ok(Self::DotAngry),
351            0x29 => Ok(Self::DotAngled),
352            0x0D => Ok(Self::Oval),
353            0x24 => Ok(Self::SmallWhite),
354            0x25 => Ok(Self::WhiteAngledNoBottom),
355            0x06 => Ok(Self::WhiteAngledNoLeft),
356            0x18 => Ok(Self::SmallWhiteTwoLashes),
357            0x1E => Ok(Self::LeafWhiteLash),
358            0x1F => Ok(Self::WhiteLargeNoBottom),
359            0x12 => Ok(Self::Dot),
360            0x1C => Ok(Self::DotLashType2),
361            0x2E => Ok(Self::DotThreeLashes),
362            0x07 => Ok(Self::WhiteOvalTop),
363            0x2C => Ok(Self::WhiteOvalBottom),
364            0x26 => Ok(Self::WhiteOvalBottomFlat),
365            0x2A => Ok(Self::WhiteOvalTwoLashes),
366            0x2D => Ok(Self::WhiteOvalThreeLashes),
367            0x1D => Ok(Self::WhiteOvalNoBottomTwoLashes),
368            0x03 => Ok(Self::DotWhite),
369            0x2B => Ok(Self::WhiteOvalTopFlat),
370            0x16 => Ok(Self::WhiteThinLeaf),
371            0x0A => Ok(Self::StarThreeLashes),
372            0x0E => Ok(Self::LineTwoLashes),
373            0x2F => Ok(Self::CrowsFeet),
374            _ => Err(()),
375        }
376    }
377}
378
379/// Converts an [`EyeType`] into its raw byte representation for the Mii data format.
380impl From<EyeType> for u8 {
381    fn from(value: EyeType) -> Self {
382        match value {
383            EyeType::Normal => 0x02,
384            EyeType::NormalLash => 0x04,
385            EyeType::WhiteLash => 0x00,
386            EyeType::WhiteNoBottom => 0x08,
387            EyeType::OvalAngledWhite => 0x27,
388            EyeType::AngryWhite => 0x11,
389            EyeType::DotLashType1 => 0x01,
390            EyeType::Line => 0x1A,
391            EyeType::DotLine => 0x10,
392            EyeType::OvalWhite => 0x0F,
393            EyeType::RoundedWhite => 0x1B,
394            EyeType::NormalShadow => 0x14,
395            EyeType::CircleWhite => 0x21,
396            EyeType::Circle => 0x0B,
397            EyeType::CircleWhiteStroke => 0x13,
398            EyeType::NormalOvalNoBottom => 0x20,
399            EyeType::NormalOvalLarge => 0x09,
400            EyeType::NormalRoundedNoBottom => 0x0C,
401            EyeType::SmallLash => 0x17,
402            EyeType::Small => 0x22,
403            EyeType::TwoSmall => 0x15,
404            EyeType::NormalLongLash => 0x19,
405            EyeType::WhiteTwoLashes => 0x28,
406            EyeType::WhiteThreeLashes => 0x23,
407            EyeType::DotAngry => 0x05,
408            EyeType::DotAngled => 0x29,
409            EyeType::Oval => 0x0D,
410            EyeType::SmallWhite => 0x24,
411            EyeType::WhiteAngledNoBottom => 0x25,
412            EyeType::WhiteAngledNoLeft => 0x06,
413            EyeType::SmallWhiteTwoLashes => 0x18,
414            EyeType::LeafWhiteLash => 0x1E,
415            EyeType::WhiteLargeNoBottom => 0x1F,
416            EyeType::Dot => 0x12,
417            EyeType::DotLashType2 => 0x1C,
418            EyeType::DotThreeLashes => 0x2E,
419            EyeType::WhiteOvalTop => 0x07,
420            EyeType::WhiteOvalBottom => 0x2C,
421            EyeType::WhiteOvalBottomFlat => 0x26,
422            EyeType::WhiteOvalTwoLashes => 0x2A,
423            EyeType::WhiteOvalThreeLashes => 0x2D,
424            EyeType::WhiteOvalNoBottomTwoLashes => 0x1D,
425            EyeType::DotWhite => 0x03,
426            EyeType::WhiteOvalTopFlat => 0x2B,
427            EyeType::WhiteThinLeaf => 0x16,
428            EyeType::StarThreeLashes => 0x0A,
429            EyeType::LineTwoLashes => 0x0E,
430            EyeType::CrowsFeet => 0x2F,
431        }
432    }
433}