rkg_utils/input_data/face_input.rs
1/// Errors that can occur while parsing a [`FaceButton`] combination.
2#[derive(thiserror::Error, Debug)]
3pub enum FaceButtonError {
4 /// The face button byte was non-zero but did not match any known button bit.
5 #[error("Non Existent Face Button")]
6 NonExistentFaceButton,
7}
8
9/// A face button that can be active during a single input entry.
10///
11/// Multiple buttons may be simultaneously active in a single [`FaceInput`];
12/// see [`parse_face_buttons`] for the bitmask layout.
13#[derive(Clone, Copy, Debug, PartialEq)]
14pub enum FaceButton {
15 /// Accelerator button (bit 0, mask `0x01`).
16 Accelerator,
17 /// Brake button (bit 1, mask `0x02`).
18 Brake,
19 /// Drift button (bit 3, mask `0x08`).
20 Drift,
21 /// Combined brake and drift (bit 4, mask `0x10`).
22 BrakeDrift,
23 /// Item button (bit 2, mask `0x04`).
24 Item,
25 /// An unrecognized button bit was set in the upper nibble.
26 Unknown,
27}
28
29/// Parses all active [`FaceButton`]s from a packed face-button byte.
30///
31/// Each bit in the byte corresponds to one or more buttons. Bit 6 (`0x40`) is
32/// reserved for the CTGP pause flag and is ignored here. If the upper nibble
33/// has any bits set (excluding bit 6) a [`FaceButton::Unknown`] entry is
34/// appended to signal unrecognized input.
35///
36/// # Errors
37///
38/// Returns [`FaceButtonError::NonExistentFaceButton`] if the byte is non-zero,
39/// bit 6 is not set, and no known button bits were matched.
40pub fn parse_face_buttons(value: u8) -> Result<Vec<FaceButton>, FaceButtonError> {
41 let mut buttons = Vec::new();
42
43 if value & 0x01 != 0 {
44 buttons.push(FaceButton::Accelerator);
45 }
46
47 if value & 0x02 != 0 {
48 buttons.push(FaceButton::Brake);
49 }
50
51 if value & 0x08 != 0 {
52 buttons.push(FaceButton::Drift);
53 }
54
55 if value & 0x10 != 0 {
56 buttons.push(FaceButton::BrakeDrift);
57 }
58
59 if value & 0x04 != 0 {
60 buttons.push(FaceButton::Item);
61 }
62
63 // 0x40 is the CTGP pause mask and would trigger this otherwise
64 if value & 0xF0 != 0 && value & 0x40 == 0 {
65 buttons.push(FaceButton::Unknown);
66 }
67
68 if value != 0x00 && value & 0x40 == 0 && buttons.is_empty() {
69 return Err(FaceButtonError::NonExistentFaceButton);
70 }
71
72 Ok(buttons)
73}
74
75/// Errors that can occur while parsing a [`FaceInput`].
76#[derive(thiserror::Error, Debug)]
77pub enum FaceInputError {
78 /// The raw bytes did not form a valid face input entry.
79 #[error("Invalid Face Input")]
80 InvalidFaceInput,
81 /// The face button byte could not be parsed.
82 #[error("Invalid Face Button: {0}")]
83 InvalidButton(#[from] FaceButtonError),
84}
85
86/// A single encoded face-button input entry from a Mario Kart Wii ghost file.
87///
88/// Each entry records which face buttons were held and for how many consecutive
89/// frames they were held. Unlike D-pad inputs, the frame duration is stored as
90/// a single byte and does not support multi-byte encoding.
91#[derive(Debug)]
92pub struct FaceInput {
93 /// The set of face buttons active during this input entry.
94 buttons: Vec<FaceButton>,
95 /// The number of frames this button state was held.
96 frame_duration: u32,
97}
98
99impl FaceInput {
100 /// Returns the set of face buttons active during this input entry.
101 pub fn buttons(&self) -> &Vec<FaceButton> {
102 &self.buttons
103 }
104
105 /// Returns the number of frames this button state was held.
106 pub fn frame_duration(&self) -> u32 {
107 self.frame_duration
108 }
109
110 /// Sets the number of frames this button state was held.
111 pub fn set_frame_duration(&mut self, frame_duration: u32) {
112 self.frame_duration = frame_duration;
113 }
114}
115
116/// Two [`FaceInput`] values are equal if they share the same set of active
117/// buttons, regardless of frame duration.
118impl PartialEq for FaceInput {
119 fn eq(&self, other: &Self) -> bool {
120 self.buttons == other.buttons
121 }
122}
123
124/// Parses a [`FaceInput`] from a 2-byte slice.
125///
126/// # Errors
127///
128/// Returns [`FaceInputError::InvalidButton`] if the button byte does not map
129/// to any known face button combination.
130impl TryFrom<&[u8]> for FaceInput {
131 type Error = FaceInputError;
132
133 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
134 let buttons = parse_face_buttons(value[0])?;
135 let frame_duration = value[1] as u32;
136
137 Ok(Self {
138 buttons,
139 frame_duration,
140 })
141 }
142}