pmd_code_table/
code_to_text.rs

1use std::{collections::BTreeMap, string::FromUtf16Error};
2use thiserror::Error;
3use crate::CodeTableEntryFile;
4
5/// An error that may occur while decoding a binary with [`CodeToText::decode`]
6#[derive(Debug, Error)]
7pub enum CodeToTextError {
8    #[error("The final character of this line is not a valid UTF-16 character. It end with the 16bit code point {1} (partially decoded string: {0:?}). The encoding of the input is likely invalid.")]
9    FinalInvalid(String, u16),
10    #[error("Can't decode the pair of the two number {2} and {3} as a valid UTF-16 character. The input file is likely corrupted (partially decoded string: {1:?})")]
11    CantDecodeUtf16(#[source] FromUtf16Error, String, u16, u16),
12    #[error("There are missing character at the end of the string to decode the value of a placeholder. The input file is likely corrupted")]
13    FinalNotLongEnoughtForData
14}
15
16pub struct CodeToText<'a> {
17    pub(crate) code_to_text: BTreeMap<u16, &'a CodeTableEntryFile>
18}
19
20impl<'a> CodeToText<'a> {
21    pub fn decode(&'a self, text: &[u16]) -> Result<String, CodeToTextError> {
22        let mut iterator = text.iter().map(|x| *x);
23
24        let mut result = String::new();
25
26        while let Some(point) = iterator.next() {
27            struct EncodedData<'a> {
28                entry: &'a CodeTableEntryFile,
29                encoded_value: u32,
30            }
31
32            let data: Option<EncodedData> = if let Some(entry) = self.code_to_text.get(&point) {
33                Some(EncodedData {
34                    entry: *entry,
35                    encoded_value: 0
36                })
37            } else if let Some(entry) = self.code_to_text.get(&(point & 0xFF00)) {
38                Some(EncodedData {
39                    entry: *entry,
40                    encoded_value: (point & 0x00FF) as u32 //TODO: maybe i need a +1 here
41                })
42            } else {
43                None
44            };
45
46            if let Some(mut data) = data {
47                let encoded_value_string = if data.entry.flags != 0 {
48                    if data.entry.lenght > 0 {
49                        data.encoded_value = 0;
50                        for j in 0..data.entry.lenght {
51                            let this_encoded_char = iterator.next().map_or_else(|| Err(CodeToTextError::FinalNotLongEnoughtForData), Ok)?;
52                            data.encoded_value |= (this_encoded_char as u32) << j*16;
53                        }
54                    };
55
56                    data.encoded_value.to_string()
57                } else {
58                    String::new()
59                };
60                
61                result.push('[');
62                result.push_str(&data.entry.string);
63                result.push_str(&encoded_value_string);
64                result.push(']');
65            } else {
66                if point == '[' as u16 {
67                    result.push_str("\\[");
68                } else if point == '\\' as u16 {
69                    result.push_str("\\\\");
70                } else {
71                    if let Some(point_as_char) = char::from_u32(point as u32) {
72                        result.push(point_as_char);
73                    } else if let Some(next_point) = iterator.next() {
74                        let decoded_string = String::from_utf16(&[point, next_point]).map_err(|err| CodeToTextError::CantDecodeUtf16(err, result.clone(), point, next_point))?;
75                        result.push_str(&decoded_string);
76                    } else {
77                        return Err(CodeToTextError::FinalInvalid(result, point))
78                    }
79                }
80            }
81        };
82
83        Ok(result)
84    }
85}