Skip to main content

rkg_utils/header/mii/
nose.rs

1use std::convert::Infallible;
2
3use crate::byte_handler::{ByteHandlerError, FromByteHandler};
4
5/// Represents the nose customization options of a Mii,
6/// including nose style, size, and vertical position.
7#[derive(Clone, Copy)]
8pub struct Nose {
9    /// Vertical position of the nose (0–18).
10    y: u8,
11    /// Nose size (0–8).
12    size: u8,
13    /// Nose shape/style.
14    nose_type: NoseType,
15}
16
17impl Nose {
18    /// Creates a new [`Nose`] from its individual components.
19    ///
20    /// # Arguments
21    ///
22    /// * `y` - Vertical position of the nose (0–18).
23    /// * `size` - Nose size (0–8).
24    /// * `nose_type` - Nose shape/style.
25    ///
26    /// # Errors
27    ///
28    /// Returns [`NoseError::SizeInvalid`] if `size` exceeds 8.
29    /// Returns [`NoseError::YInvalid`] if `y` exceeds 18.
30    pub fn new(y: u8, size: u8, nose_type: NoseType) -> Result<Self, NoseError> {
31        if size > 8 {
32            return Err(NoseError::SizeInvalid);
33        }
34        if y > 18 {
35            return Err(NoseError::YInvalid);
36        }
37
38        Ok(Self { y, size, nose_type })
39    }
40
41    /// Returns the vertical position of the nose (0–18).
42    pub fn y(&self) -> u8 {
43        self.y
44    }
45
46    /// Returns the nose size (0–8).
47    pub fn size(&self) -> u8 {
48        self.size
49    }
50
51    /// Returns the nose shape/style.
52    pub fn nose_type(&self) -> NoseType {
53        self.nose_type
54    }
55
56    /// Sets the vertical position of the nose.
57    ///
58    /// # Errors
59    ///
60    /// Returns [`NoseError::YInvalid`] if `y` exceeds 18.
61    pub fn set_y(&mut self, y: u8) -> Result<(), NoseError> {
62        if y > 18 {
63            return Err(NoseError::YInvalid);
64        }
65        self.y = y;
66        Ok(())
67    }
68
69    /// Sets the nose size.
70    ///
71    /// # Errors
72    ///
73    /// Returns [`NoseError::SizeInvalid`] if `size` exceeds 8.
74    pub fn set_size(&mut self, size: u8) -> Result<(), NoseError> {
75        if size > 8 {
76            return Err(NoseError::SizeInvalid);
77        }
78        self.size = size;
79        Ok(())
80    }
81
82    /// Sets the nose shape/style.
83    pub fn set_nose_type(&mut self, nose_type: NoseType) {
84        self.nose_type = nose_type;
85    }
86}
87
88/// Deserializes a [`Nose`] from a `ByteHandler`.
89///
90/// Extracts the nose type from the upper nibble of the first byte, the size from
91/// the lower nibble of the first byte, and the vertical position from the upper
92/// 5 bits of the second byte.
93impl FromByteHandler for Nose {
94    type Err = NoseError;
95    fn from_byte_handler<T>(handler: T) -> Result<Self, Self::Err>
96    where
97        T: TryInto<crate::byte_handler::ByteHandler>,
98        Self::Err: From<T::Error>,
99    {
100        let handler = handler.try_into()?;
101
102        let nose_type =
103            NoseType::try_from(handler.copy_byte(0) >> 4).map_err(|_| NoseError::TypeInvalid)?;
104        let size = handler.copy_byte(0) & 0x0F;
105        let y = handler.copy_byte(1) >> 3;
106
107        Self::new(y, size, nose_type)
108    }
109}
110
111/// Errors that can occur while constructing or deserializing a [`Nose`].
112#[derive(thiserror::Error, Debug)]
113pub enum NoseError {
114    /// The nose type byte did not map to a known [`NoseType`] variant.
115    #[error("Type is invalid")]
116    TypeInvalid,
117    /// The size value exceeds the maximum of 8.
118    #[error("Size is invalid")]
119    SizeInvalid,
120    /// The vertical position exceeds the maximum of 18.
121    #[error("Y position is invalid")]
122    YInvalid,
123    /// A `ByteHandler` operation failed.
124    #[error("ByteHandler Error: {0}")]
125    ByteHandlerError(#[from] ByteHandlerError),
126    /// Infallible conversion error; cannot occur at runtime.
127    #[error("")]
128    Infallible(#[from] Infallible),
129}
130
131/// Nose shape options available in the Mii editor.
132#[derive(Clone, Copy, Debug, PartialEq)]
133pub enum NoseType {
134    Normal,
135    Rounded,
136    Dot,
137    Arrow,
138    /// Straight, prominent Roman-style nose.
139    Roman,
140    Triangle,
141    Button,
142    RoundedInverted,
143    Potato,
144    /// Long, straight Grecian-style nose.
145    Grecian,
146    /// Short, upturned snub nose.
147    Snub,
148    /// Curved, downward-pointing aquiline nose.
149    Aquiline,
150}
151
152/// Converts a raw byte value from the Mii data format into a [`NoseType`].
153///
154/// Returns `Err(())` if the byte does not correspond to any known nose type.
155impl TryFrom<u8> for NoseType {
156    type Error = ();
157    fn try_from(value: u8) -> Result<Self, Self::Error> {
158        match value {
159            0x01 => Ok(Self::Normal),
160            0x0A => Ok(Self::Rounded),
161            0x02 => Ok(Self::Dot),
162            0x03 => Ok(Self::Arrow),
163            0x06 => Ok(Self::Roman),
164            0x00 => Ok(Self::Triangle),
165            0x05 => Ok(Self::Button),
166            0x04 => Ok(Self::RoundedInverted),
167            0x08 => Ok(Self::Potato),
168            0x09 => Ok(Self::Grecian),
169            0x07 => Ok(Self::Snub),
170            0x0B => Ok(Self::Aquiline),
171            _ => Err(()),
172        }
173    }
174}
175
176/// Converts a [`NoseType`] into its raw byte representation for the Mii data format.
177impl From<NoseType> for u8 {
178    fn from(value: NoseType) -> Self {
179        match value {
180            NoseType::Normal => 0x01,
181            NoseType::Rounded => 0x0A,
182            NoseType::Dot => 0x02,
183            NoseType::Arrow => 0x03,
184            NoseType::Roman => 0x06,
185            NoseType::Triangle => 0x00,
186            NoseType::Button => 0x05,
187            NoseType::RoundedInverted => 0x04,
188            NoseType::Potato => 0x08,
189            NoseType::Grecian => 0x09,
190            NoseType::Snub => 0x07,
191            NoseType::Aquiline => 0x0B,
192        }
193    }
194}