Skip to main content

rkg_utils/input_data/
dpad_input.rs

1/// Errors that can occur while parsing a [`DPadButton`].
2#[derive(thiserror::Error, Debug)]
3pub enum DPadButtonError {
4    /// The extracted button value did not correspond to any known D-pad direction.
5    #[error("Non Existent DPad Button")]
6    NonExistentDPadButton,
7}
8
9/// A D-pad button state for a single input entry in a Mario Kart Wii ghost file.
10#[derive(Clone, Copy, Debug, PartialEq)]
11pub enum DPadButton {
12    /// No D-pad button is pressed.
13    None,
14    Up,
15    Down,
16    Left,
17    Right,
18}
19
20/// Extracts a [`DPadButton`] from the upper nibble of a packed input byte.
21///
22/// The button ID is stored in bits 4–6 of the byte (mask `0x70`).
23///
24/// # Errors
25///
26/// Returns [`DPadButtonError::NonExistentDPadButton`] if the extracted 3-bit
27/// value does not map to a known D-pad direction (valid range is 0–4).
28pub fn parse_dpad_button(value: u8) -> Result<DPadButton, DPadButtonError> {
29    let button = (value & 0x70) >> 4;
30
31    match button {
32        0 => Ok(DPadButton::None),
33        1 => Ok(DPadButton::Up),
34        2 => Ok(DPadButton::Down),
35        3 => Ok(DPadButton::Left),
36        4 => Ok(DPadButton::Right),
37        _ => Err(DPadButtonError::NonExistentDPadButton),
38    }
39}
40
41/// Errors that can occur while parsing a [`DPadInput`].
42#[derive(thiserror::Error, Debug)]
43pub enum DPadInputError {
44    /// The raw bytes did not form a valid D-pad input entry.
45    #[error("Invalid DPad Input")]
46    InvalidDpadInput,
47    /// The D-pad button value could not be parsed.
48    #[error("Invalid DPad Button: {0}")]
49    InvalidButton(#[from] DPadButtonError),
50}
51
52/// A single run-length encoded D-pad input entry from a Mario Kart Wii ghost file.
53///
54/// Each entry records which D-pad button was held and for how many consecutive
55/// frames it was held. Frame durations exceeding 255 frames are encoded across
56/// two fields: the lower nibble of the first byte stores the number of
57/// additional 256-frame intervals, and the second byte stores the remainder.
58#[derive(Debug)]
59pub struct DPadInput {
60    /// The D-pad button that was held during this input entry.
61    button: DPadButton,
62    /// The number of frames this D-pad state was held.
63    frame_duration: u32,
64}
65
66impl DPadInput {
67    /// Returns the D-pad button held during this input entry.
68    pub fn button(&self) -> DPadButton {
69        self.button
70    }
71
72    /// Returns the number of frames this D-pad state was held.
73    pub fn frame_duration(&self) -> u32 {
74        self.frame_duration
75    }
76}
77
78/// Two [`DPadInput`] values are equal if they share the same button state,
79/// regardless of frame duration.
80impl PartialEq for DPadInput {
81    fn eq(&self, other: &DPadInput) -> bool {
82        self.button == other.button
83    }
84}
85
86/// Parses a [`DPadInput`] from a 2-byte slice.
87///
88/// # Errors
89///
90/// Returns [`DPadInputError::InvalidButton`] if the button bits do not map to
91/// a known D-pad direction.
92impl TryFrom<&[u8]> for DPadInput {
93    type Error = DPadInputError;
94
95    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
96        let button = parse_dpad_button(value[0])?;
97        // 0x0F bit mask used if this input state was held longer than 255 frames, gives amount of 256-frame intervals to add to frame duration
98        let previous_full_byte_presses: u32 = (value[0] & 0x0F).into();
99        let frame_duration = value[1] as u32 + (previous_full_byte_presses * 256);
100
101        Ok(Self {
102            button,
103            frame_duration,
104        })
105    }
106}