diff options
| author | Alexander Kassouni <alex@kassouni.net> | 2026-09-05 22:43:52 -0700 |
|---|---|---|
| committer | Alexander Kassouni <alex@kassouni.net> | 2026-09-05 22:43:52 -0700 |
| commit | 24ec2a92b56ca4296fe65b70524e36a8e1c67c2e (patch) | |
| tree | 51fd27e87cbd419ab74f016852fbaa2d86babd9a /src/tokenizer | |
| parent | 7f5dd54c60c70f29d0c8abedc28dd5ed199de94d (diff) | |
phonemes
Diffstat (limited to 'src/tokenizer')
| -rw-r--r-- | src/tokenizer/mod.rs | 2 | ||||
| -rw-r--r-- | src/tokenizer/tokens.rs | 202 |
2 files changed, 147 insertions, 57 deletions
diff --git a/src/tokenizer/mod.rs b/src/tokenizer/mod.rs index 54ea9a3..1651f76 100644 --- a/src/tokenizer/mod.rs +++ b/src/tokenizer/mod.rs @@ -1,2 +1,2 @@ mod tokens; -pub use tokens::VoiceCode; +pub use tokens::{ControlCode, Digit, Letter, VoiceCode}; diff --git a/src/tokenizer/tokens.rs b/src/tokenizer/tokens.rs index fa1d7e7..d013497 100644 --- a/src/tokenizer/tokens.rs +++ b/src/tokenizer/tokens.rs @@ -1,74 +1,164 @@ -#[derive(Debug, PartialEq, Eq)] +//! 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 { - Melody, // 0x80 - Space, // 0x81 - Question, // 0x82 - Bang, // 0x83 - Newline, // 0x84 - Punct, // 0x85 - Bebe, // 0x86, - Control, // -1 + /// ♪ — 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, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Letter { - 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 + 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, } -#[derive(Debug, PartialEq, Eq)] +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, // 0x53 - D1, // 0x54 - D2, // 0x55 - D3, // 0x56 - D4, // 0x57 - D5, // 0x58 - D6, // 0x59 - D7, // 0x5A - D8, // 0x5B - D9, // 0x5C + 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)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum VoiceCode { Letter(Letter), ControlCode(ControlCode), - Digit(Digit) + Digit(Digit), } - -impl Letter { - fn is_shiin(&self) -> bool { - !self.is_boin() +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(), + } } - fn is_boin(&self) -> bool { - use Letter::*; - [A, E, I, O, U, N].contains(&self) + /// `> 0x77` — the test several rules use to mean "punctuation, not a letter". + pub const fn is_punctuation(self) -> bool { + self.code() > 0x77 } } |