diff --git a/src/encoding.rs b/src/encoding.rs index 686cf72..3b9debd 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -1,34 +1,81 @@ +/// 5 bits +#[derive(Clone, Copy)] struct ZChar(u8); -struct ZsciiChar(u8); -struct ZsciiString(Vec); +/// technically 10 bits, but top two unused so they are dropped +#[derive(PartialEq, Clone, Copy)] +pub struct ZsciiChar(u8); + +pub type ZsciiString = Vec; /// Returns: /// - a result that wraps a ZsciiString, erroring if the slice terminates before the string ends /// - a usize indicating how many bytes were consumed - -pub fn decode_zchars(zchars: &[u8]) -> Option<(ZsciiString, usize)> { +pub fn decode_zchars( + zchars: &[u8], + alphabet_table: Option<&[u8]>, + abbreviations_table: &[u8], +) -> Option<(ZsciiString, usize)> { fn cut_string(zchars: &[u8]) -> Option> { - for (i, word) in zchars.chunks_exact(2).map(|c| u16::from_be_bytes([c[0], c[1]])).enumerate() { + let mut out = Vec::new(); + for word in zchars.chunks_exact(2).map(|c| u16::from_be_bytes([c[0], c[1]])) { + out.push(word); + if 0x8000 & word != 0 { + return Some(out); + } + } + None + } + + /// requires: alphabet_num < 3, 5 < char_idx < 32, !(alphabet_num = 2 AND char_idx = 6) + fn index_alphabet( + alphabet_table: Option<&[u8]>, + alphabet_number: usize, + ZChar(char_idx): ZChar, + zchars: &mut impl Iterator, + ) -> Option { + let default_alphabet: [[ZsciiChar; 26]; 3] = [ + b"abcdefghijklmnopqrstuvwxyz".map(ZsciiChar), + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ".map(ZsciiChar), + br#" 0123456789.,!?_#'"/\-:()"#.map(ZsciiChar) + ]; + if alphabet_number == 2 && char_idx == 7 { + Some(ZsciiChar(13)) + } else if alphabet_number == 2 && char_idx == 6 { + todo!() + } else if let Some(alphabet_table) = alphabet_table { + todo!() + } else { + Some(default_alphabet[alphabet_number][char_idx as usize - 6]) + } + } + + let zwords = cut_string(zchars)?; + let consumed_length = zwords.len() * 2; + let mut zchars = zwords.iter() + .flat_map(|word| [ + (word >> 10) & 0x1f, + (word >> 5) & 0x1f, + word & 0x1f + ]) + .map(|word| ZChar(word as u8)) + .peekable(); + + let mut out = Vec::new(); + let mut current_alphabet = 0; + while let Some(char) = zchars.next() { + match char { + ZChar(0) => out.push(ZsciiChar(32)), + ZChar(1..=3) => todo!("abbreviations"), + ZChar(4..=5) => if zchars.peek().is_some_and(|&ZChar(n)| n > 5) { + if let Some(zc) = index_alphabet(alphabet_table, char.0 as usize - 3, zchars.next().unwrap(), &mut zchars) { out.push(zc) } + }, + ZChar(_) => if let Some(zc) = index_alphabet(alphabet_table, 0, char, &mut zchars) { + out.push(zc) + } } } - let zchars = slice_string(zchars); - // let mut index: usize = 0; - // let mut out = Vec::new(); - // loop { - // let word = u16::from_be_bytes([zchars[index], zchars[index + 1]]); - // let end_bit_set = word & 0x8000 != 0; - // let chars = [ - // (word & (0b11111 << 10)) >> 10, - // (word & (0b11111 << 5)) >> 5, - // (word & (0b11111 << 0)) >> 0, - // ].map(|c| ZChar(c as u8)); - // out.extend(chars); - // index += 2; - // if index >= zchars.len() - 1 { break (Err(out), index); } - // if end_bit_set { break (Ok(out), index); } - // // TODO: finish - // } + Some((out, consumed_length)) }