/* 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<u8>) -> 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<Vec<u8>> {
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::<String>();
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);
}
}