//! The voice-code alphabet. //! //! `mMsg_sound_voice_get` folds the game's 256-entry character set down to these 34 //! values. Everything after this point — word boundaries, capitalisation, sentence //! structure, and any letter outside `a-z0-9` — is gone. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ControlCode { /// ♪ — plays the speaker's melody and bypasses the whole engine. Melody, // 0x80 Space, // 0x81 Question, // 0x82 Bang, // 0x83 /// Injected by `mMsg_sound_voice_get2`; never present in the raw table. Newline, // 0x84 /// The catch-all for punctuation and symbols. Punct, // 0x85 /// The typing click. Only the interpunct (char 133) maps here. Bebe, // 0x86 /// An empty `b` / `c` slot in a half-built window. Empty, // 0xFF /// `mMsg_sound_voice_get` returning `-1` for `CHAR_CONTROL_CODE` / /// `CHAR_MESSAGE_TAG`. Never reaches the audio engine: `mMsg_sound_CodeVoice` /// drops it, and the `c` lookahead folds it to [`ControlCode::Empty`]. Control, // -1 } impl ControlCode { /// [`ControlCode::Control`] has no code of its own; it reports `0xFF` because that is /// what the lookahead substitutes, and it can never reach playback by any other path. pub const fn code(self) -> u8 { match self { ControlCode::Melody => 0x80, ControlCode::Space => 0x81, ControlCode::Question => 0x82, ControlCode::Bang => 0x83, ControlCode::Newline => 0x84, ControlCode::Punct => 0x85, ControlCode::Bebe => 0x86, ControlCode::Empty | ControlCode::Control => 0xFF, } } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Letter { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, } impl Letter { /// `0x5D + (letter - 'a')`. pub const fn code(self) -> u8 { use Letter::*; match self { A => 0x5D, B => 0x5E, C => 0x5F, D => 0x60, E => 0x61, F => 0x62, G => 0x63, H => 0x64, I => 0x65, J => 0x66, K => 0x67, L => 0x68, M => 0x69, N => 0x6A, O => 0x6B, P => 0x6C, Q => 0x6D, R => 0x6E, S => 0x6F, T => 0x70, U => 0x71, V => 0x72, W => 0x73, X => 0x74, Y => 0x75, Z => 0x76, } } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Digit { D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, } impl Digit { /// `0x53 + digit`. This is the *voice* code, not the phoneme code — see /// `phonemes::digit_code` for what the conversion tables turn it into. pub const fn code(self) -> u8 { use Digit::*; match self { D0 => 0x53, D1 => 0x54, D2 => 0x55, D3 => 0x56, D4 => 0x57, D5 => 0x58, D6 => 0x59, D7 => 0x5A, D8 => 0x5B, D9 => 0x5C, } } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum VoiceCode { Letter(Letter), ControlCode(ControlCode), Digit(Digit), } impl VoiceCode { pub const fn code(self) -> u8 { match self { VoiceCode::Letter(l) => l.code(), VoiceCode::ControlCode(c) => c.code(), VoiceCode::Digit(d) => d.code(), } } /// `> 0x77` — the test several rules use to mean "punctuation, not a letter". pub const fn is_punctuation(self) -> bool { self.code() > 0x77 } }