Skip to main content

rkg_utils/header/
ghost_type.rs

1use crate::byte_handler::{ByteHandlerError, FromByteHandler};
2use std::fmt::Display;
3
4/// Errors that can occur while constructing a [`GhostType`].
5#[derive(thiserror::Error, Debug)]
6pub enum GhostTypeError {
7    /// The ghost type byte did not map to any known [`GhostType`] variant.
8    #[error("Nonexistent Ghost Type")]
9    NonexistentGhostType,
10    /// A `ByteHandler` operation failed.
11    #[error("ByteHandler Error: {0}")]
12    ByteHandlerError(#[from] ByteHandlerError),
13}
14
15/// All possible Ghost Types representable in a ghost file.
16///
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub enum GhostType {
19    PlayerBest,
20    WorldRecord,
21    ContinentalRecord,
22    /// A rival ghost matched against the player's time.
23    Rival,
24    /// A special ghost (e.g. a Nintendo event ghost).
25    /// More info on special ghosts here: <https://tcrf.net/User:B_squo#About_Mario_Kart_Wii.27s_Special_Ghosts>
26    Special,
27    /// A ghost downloaded from the Mario Kart Channel's "Ghost Race" gamemode.
28    GhostRace,
29    Friend1,
30    Friend2,
31    Friend3,
32    Friend4,
33    Friend5,
34    Friend6,
35    Friend7,
36    Friend8,
37    Friend9,
38    Friend10,
39    Friend11,
40    Friend12,
41    Friend13,
42    Friend14,
43    Friend15,
44    Friend16,
45    Friend17,
46    Friend18,
47    Friend19,
48    Friend20,
49    Friend21,
50    Friend22,
51    Friend23,
52    Friend24,
53    Friend25,
54    Friend26,
55    Friend27,
56    Friend28,
57    Friend29,
58    Friend30,
59    NormalStaff,
60    ExpertStaff,
61}
62
63impl Display for GhostType {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        match self {
66            GhostType::PlayerBest => write!(f, "Player's best time"),
67            GhostType::WorldRecord => write!(f, "World record ghost"),
68            GhostType::ContinentalRecord => write!(f, "Continental record ghost"),
69            GhostType::Rival => write!(f, "Rival ghost"),
70            GhostType::Special => write!(f, "Special ghost"),
71            GhostType::GhostRace => write!(f, "Ghost Race ghost"),
72            GhostType::Friend1 => write!(f, "Friend ghost #1"),
73            GhostType::Friend2 => write!(f, "Friend ghost #2"),
74            GhostType::Friend3 => write!(f, "Friend ghost #3"),
75            GhostType::Friend4 => write!(f, "Friend ghost #4"),
76            GhostType::Friend5 => write!(f, "Friend ghost #5"),
77            GhostType::Friend6 => write!(f, "Friend ghost #6"),
78            GhostType::Friend7 => write!(f, "Friend ghost #7"),
79            GhostType::Friend8 => write!(f, "Friend ghost #8"),
80            GhostType::Friend9 => write!(f, "Friend ghost #9"),
81            GhostType::Friend10 => write!(f, "Friend ghost #10"),
82            GhostType::Friend11 => write!(f, "Friend ghost #11"),
83            GhostType::Friend12 => write!(f, "Friend ghost #12"),
84            GhostType::Friend13 => write!(f, "Friend ghost #13"),
85            GhostType::Friend14 => write!(f, "Friend ghost #14"),
86            GhostType::Friend15 => write!(f, "Friend ghost #15"),
87            GhostType::Friend16 => write!(f, "Friend ghost #16"),
88            GhostType::Friend17 => write!(f, "Friend ghost #17"),
89            GhostType::Friend18 => write!(f, "Friend ghost #18"),
90            GhostType::Friend19 => write!(f, "Friend ghost #19"),
91            GhostType::Friend20 => write!(f, "Friend ghost #20"),
92            GhostType::Friend21 => write!(f, "Friend ghost #21"),
93            GhostType::Friend22 => write!(f, "Friend ghost #22"),
94            GhostType::Friend23 => write!(f, "Friend ghost #23"),
95            GhostType::Friend24 => write!(f, "Friend ghost #24"),
96            GhostType::Friend25 => write!(f, "Friend ghost #25"),
97            GhostType::Friend26 => write!(f, "Friend ghost #26"),
98            GhostType::Friend27 => write!(f, "Friend ghost #27"),
99            GhostType::Friend28 => write!(f, "Friend ghost #28"),
100            GhostType::Friend29 => write!(f, "Friend ghost #29"),
101            GhostType::Friend30 => write!(f, "Friend ghost #30"),
102            GhostType::NormalStaff => write!(f, "Normal staff ghost"),
103            GhostType::ExpertStaff => write!(f, "Expert staff ghost"),
104        }
105    }
106}
107
108/// Converts a raw byte value from the RKG header into a [`GhostType`].
109///
110/// # Errors
111///
112/// Returns [`GhostTypeError::NonexistentGhostType`] if the byte does not
113/// correspond to any known ghost type (valid range is `0x01`–`0x26`).
114impl TryFrom<u8> for GhostType {
115    type Error = GhostTypeError;
116    fn try_from(value: u8) -> Result<Self, Self::Error> {
117        match value {
118            0x01 => Ok(Self::PlayerBest),
119            0x02 => Ok(Self::WorldRecord),
120            0x03 => Ok(Self::ContinentalRecord),
121            0x04 => Ok(Self::Rival),
122            0x05 => Ok(Self::Special),
123            0x06 => Ok(Self::GhostRace),
124            0x07 => Ok(Self::Friend1),
125            0x08 => Ok(Self::Friend2),
126            0x09 => Ok(Self::Friend3),
127            0x0A => Ok(Self::Friend4),
128            0x0B => Ok(Self::Friend5),
129            0x0C => Ok(Self::Friend6),
130            0x0D => Ok(Self::Friend7),
131            0x0E => Ok(Self::Friend8),
132            0x0F => Ok(Self::Friend9),
133            0x10 => Ok(Self::Friend10),
134            0x11 => Ok(Self::Friend11),
135            0x12 => Ok(Self::Friend12),
136            0x13 => Ok(Self::Friend13),
137            0x14 => Ok(Self::Friend14),
138            0x15 => Ok(Self::Friend15),
139            0x16 => Ok(Self::Friend16),
140            0x17 => Ok(Self::Friend17),
141            0x18 => Ok(Self::Friend18),
142            0x19 => Ok(Self::Friend19),
143            0x1A => Ok(Self::Friend20),
144            0x1B => Ok(Self::Friend21),
145            0x1C => Ok(Self::Friend22),
146            0x1D => Ok(Self::Friend23),
147            0x1E => Ok(Self::Friend24),
148            0x1F => Ok(Self::Friend25),
149            0x20 => Ok(Self::Friend26),
150            0x21 => Ok(Self::Friend27),
151            0x22 => Ok(Self::Friend28),
152            0x23 => Ok(Self::Friend29),
153            0x24 => Ok(Self::Friend30),
154            0x25 => Ok(Self::NormalStaff),
155            0x26 => Ok(Self::ExpertStaff),
156            _ => Err(GhostTypeError::NonexistentGhostType),
157        }
158    }
159}
160
161/// Converts a [`GhostType`] into its raw byte representation for the RKG header.
162impl From<GhostType> for u8 {
163    fn from(value: GhostType) -> Self {
164        match value {
165            GhostType::PlayerBest => 0x01,
166            GhostType::WorldRecord => 0x02,
167            GhostType::ContinentalRecord => 0x03,
168            GhostType::Rival => 0x04,
169            GhostType::Special => 0x05,
170            GhostType::GhostRace => 0x06,
171            GhostType::Friend1 => 0x07,
172            GhostType::Friend2 => 0x08,
173            GhostType::Friend3 => 0x09,
174            GhostType::Friend4 => 0x0A,
175            GhostType::Friend5 => 0x0B,
176            GhostType::Friend6 => 0x0C,
177            GhostType::Friend7 => 0x0D,
178            GhostType::Friend8 => 0x0E,
179            GhostType::Friend9 => 0x0F,
180            GhostType::Friend10 => 0x10,
181            GhostType::Friend11 => 0x11,
182            GhostType::Friend12 => 0x12,
183            GhostType::Friend13 => 0x13,
184            GhostType::Friend14 => 0x14,
185            GhostType::Friend15 => 0x15,
186            GhostType::Friend16 => 0x16,
187            GhostType::Friend17 => 0x17,
188            GhostType::Friend18 => 0x18,
189            GhostType::Friend19 => 0x19,
190            GhostType::Friend20 => 0x1A,
191            GhostType::Friend21 => 0x1B,
192            GhostType::Friend22 => 0x1C,
193            GhostType::Friend23 => 0x1D,
194            GhostType::Friend24 => 0x1E,
195            GhostType::Friend25 => 0x1F,
196            GhostType::Friend26 => 0x20,
197            GhostType::Friend27 => 0x21,
198            GhostType::Friend28 => 0x22,
199            GhostType::Friend29 => 0x23,
200            GhostType::Friend30 => 0x24,
201            GhostType::NormalStaff => 0x25,
202            GhostType::ExpertStaff => 0x26,
203        }
204    }
205}
206
207/// Deserializes a [`GhostType`] from 2 bytes at header offset `0x0C..=0x0D`.
208impl FromByteHandler for GhostType {
209    type Err = GhostTypeError;
210    fn from_byte_handler<T>(handler: T) -> Result<Self, Self::Err>
211    where
212        T: TryInto<crate::byte_handler::ByteHandler>,
213        Self::Err: From<T::Error>,
214    {
215        let mut handler = handler.try_into()?;
216        handler.shift_right(2);
217        handler.copy_byte(1).try_into()
218    }
219}