/* NOTE: Most of this file is copypasta from huawei-modem, the only new code is in `fn main()` */ /// Lookup table for 'extended' GSM characters (characters which are encoded as `'\x1B'` plus /// whatever this table says). pub static GSM_EXTENDED_ENCODING_TABLE: [(char, u8); 9] = [ ('^', 0x14), ('{', 0x28), ('}', 0x29), ('\\', 0x2F), ('[', 0x3C), ('~', 0x3D), (']', 0x3E), ('|', 0x40), ('\u{20AC}', 0x65) ]; /// Lookup table for non-alphanumeric characters in the GSM alphabet. pub static GSM_ENCODING_TABLE: [(char, u8); 65] = [ ('@', 0x00), ('\u{00A3}', 0x01), ('$', 0x02), ('\u{00A5}', 0x03), ('è', 0x04), ('é', 0x05), ('ù', 0x06), ('ì', 0x07), ('ò', 0x08), ('\u{00C7}', 0x09), ('\n', 0x0a), ('\u{00D8}', 0x0b), ('\u{00F8}', 0x0c), ('\r', 0x0d), ('\u{00C5}', 0x0e), ('\u{00E5}', 0x0f), ('\u{0394}', 0x10), ('_', 0x11), ('\u{03A6}', 0x12), ('Γ', 0x13), ('Λ', 0x14), ('Ω', 0x15), ('Π', 0x16), ('Ψ', 0x17), ('Σ', 0x18), ('Θ', 0x19), ('Ξ', 0x1A), ('Æ', 0x1C), ('æ', 0x1D), ('ß', 0x1E), ('É', 0x1F), (' ', 0x20), ('!', 0x21), ('"', 0x22), ('#', 0x23), ('¤', 0x24), ('%', 0x25), ('&', 0x26), ('\'', 0x27), ('(', 0x28), (')', 0x29), ('*', 0x2A), ('+', 0x2B), (',', 0x2C), ('-', 0x2D), ('.', 0x2E), ('/', 0x2F), (':', 0x3A), (';', 0x3B), ('<', 0x3C), ('=', 0x3D), ('>', 0x3E), ('?', 0x3F), ('¡', 0x40), ('Ä', 0x5B), ('Ö', 0x5C), ('Ñ', 0x5D), ('Ü', 0x5E), ('§', 0x5F), ('¿', 0x60), ('ä', 0x7B), ('ö', 0x7C), ('ñ', 0x7D), ('ü', 0x7E), ('à', 0x7F) ]; /// Decode a GSM 7-bit-encoded buffer into a string. /// /// **Warning:** You need to unpack the string first; this method operates on unpacked septets, not /// packed septets. See the `pdu` module for more. /// /// This method is lossy, and doesn't complain about crap that it can't decode. pub fn gsm_decode_string(input: &[u8]) -> String { let mut ret = String::new(); let mut skip = false; for (i, b) in input.iter().enumerate() { if skip { skip = false; continue; } match *b { b'A' ... b'Z' | b'a' ... b'z' | b'0' ... b'9' => { ret.push(*b as char); }, 0x1B => { if let Some(b) = input.get(i+1) { for &(ch, val) in GSM_EXTENDED_ENCODING_TABLE.iter() { if val == *b { ret.push(ch); skip = true; } } } }, b => { for &(ch, val) in GSM_ENCODING_TABLE.iter() { if val == b { ret.push(ch); } } } } } ret } /// Tries to encode a character into the given destination buffer, returning `true` if the /// character was successfully encoded, and `false` if the character cannot be represented in the /// GSM 7-bit encoding. pub fn try_gsm_encode_char(b: char, dest: &mut Vec) -> bool { match b { 'A' ... 'Z' | 'a' ... 'z' | '0' ... '9' => { dest.push(b as u8); return true; }, b => { for &(ch, val) in GSM_ENCODING_TABLE.iter() { if b == ch { dest.push(val); return true; } } for &(ch, val) in GSM_EXTENDED_ENCODING_TABLE.iter() { if b == ch { dest.push(0x1B); dest.push(val); return true; } } } } false } /// Tries to encode a string as GSM 7-bit, returning a buffer of **unpacked** septets iff all of /// the data in `input` was representable in the 7-bit encoding. /// /// **Warning:** The output of this function is unsuitable for transmission across the network; /// you need to pack the septets first! See the `pdu` module for more. pub fn try_gsm_encode_string(input: &str) -> Option> { let mut ret = vec![]; for c in input.chars() { if !try_gsm_encode_char(c, &mut ret) { return None; } } Some(ret) } fn main() { let weird_text = "Π7Oß.ù\"GÅ&ì:Offù:åΠwùJà.ìJ6ù\"G.Π/>Ξì"; let mut garboleum = try_gsm_encode_string(&weird_text).expect("couldn't encode weird text back"); let mut garblestring = garboleum.iter().map(|x| format!("{x:07b}")).collect::(); let mut offset = 0; while !garblestring.is_empty() { let mut interpretation = String::new(); // eprint!("{garblestring} => "); for text in garblestring.as_bytes().chunks(7) { let text = str::from_utf8(text).unwrap(); // eprint!("{text} "); if text.len() < 7 && !text.is_empty() { continue; } match u8::from_str_radix(text, 2) { Ok(v) => { // eprint!("({:?}) ", gsm_decode_string(&[v])); interpretation.push_str(&gsm_decode_string(&[v])); }, _ => /*eprint!("(???)")*/{}, } } // eprintln!(); eprintln!("offset {offset}: {interpretation:?}"); offset += 1; garblestring.remove(0); } }