1use crate::Result;
2use byteorder::{ReadBytesExt, WriteBytesExt, LE};
3use genie_support::{read_str, write_i32_str, DecodeStringError, ReadStringError};
4use std::convert::TryFrom;
5use std::io::{Read, Write};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
8#[repr(u32)]
9pub enum AIErrorCode {
10 ConstantAlreadyDefined = 0,
12 FileOpenFailed = 1,
14 FileReadFailed = 2,
16 InvalidIdentifier = 3,
18 InvalidKeyword = 4,
20 InvalidPreprocessorDirective = 5,
22 ListFull = 6,
24 MissingArrow = 7,
26 MissingClosingParenthesis = 8,
28 MissingClosingQuote = 9,
30 MissingEndIf = 10,
32 MissingFileName = 11,
34 MissingIdentifier = 12,
36 MissingKeyword = 13,
38 MissingLHS = 14,
40 MissingOpeningParenthesis = 15,
42 MissingPreprocessorSymbol = 16,
44 MissingRHS = 17,
46 NoRules = 18,
48 PreprocessorNestingTooDeep = 19,
50 RuleTooLong = 20,
52 StringTableFull = 21,
54 UndocumentedError = 22,
56 UnexpectedElse = 23,
58 UnexpectedEndIf = 24,
60 UnexpectedError = 25,
62 UnexpectedEOF = 26,
64}
65
66#[derive(Debug, Clone)]
67pub struct AIErrorInfo {
68 filename: String,
69 line_number: i32,
70 description: String,
71 error_code: AIErrorCode,
72}
73
74fn parse_bytes(bytes: &[u8]) -> std::result::Result<String, ReadStringError> {
75 let mut bytes = bytes.to_vec();
76 if let Some(end) = bytes.iter().position(|&byte| byte == 0) {
77 bytes.truncate(end);
78 }
79 if bytes.is_empty() {
80 Ok("<empty>".to_string())
81 } else {
82 String::from_utf8(bytes).map_err(|_| ReadStringError::DecodeStringError(DecodeStringError))
83 }
84}
85
86impl AIErrorInfo {
87 pub fn read_from(mut input: impl Read) -> Result<Self> {
89 let mut filename_bytes = [0; 257];
91 input.read_exact(&mut filename_bytes)?;
92 let line_number = input.read_i32::<LE>()?;
93 let mut description_bytes = [0; 128];
94 input.read_exact(&mut description_bytes)?;
95 let error_code = AIErrorCode::try_from(input.read_u32::<LE>()?)?;
96
97 let filename = parse_bytes(&filename_bytes)?;
98 let description = parse_bytes(&description_bytes)?;
99
100 Ok(AIErrorInfo {
101 filename,
102 line_number,
103 description,
104 error_code,
105 })
106 }
107
108 pub fn write_to(&self, mut output: impl Write) -> Result<()> {
110 let mut filename_bytes = [0; 257];
112 (&mut filename_bytes[..self.filename.len()]).copy_from_slice(self.filename.as_bytes());
113 output.write_all(&filename_bytes)?;
114
115 output.write_i32::<LE>(self.line_number)?;
116
117 let mut description_bytes = [0; 128];
118 (&mut description_bytes[..self.description.len()])
119 .copy_from_slice(self.description.as_bytes());
120 output.write_all(&description_bytes)?;
121
122 output.write_u32::<LE>(self.error_code.into())?;
123
124 Ok(())
125 }
126}
127
128#[derive(Debug, Clone)]
129pub struct AIFile {
130 filename: String,
131 content: String,
132}
133
134impl AIFile {
135 pub fn read_from(mut input: impl Read) -> Result<Self> {
137 let len = input.read_i32::<LE>()? as usize;
138 let filename = read_str(&mut input, len)?.expect("missing ai file name");
139 let len = input.read_i32::<LE>()? as usize;
140 let content = read_str(&mut input, len)?.expect("empty ai file?");
141
142 Ok(Self { filename, content })
143 }
144
145 pub fn write_to(&self, mut output: impl Write) -> Result<()> {
147 write_i32_str(&mut output, &self.filename)?;
148 write_i32_str(&mut output, &self.content)?;
149 Ok(())
150 }
151}
152
153#[derive(Debug, Default, Clone)]
154pub struct AIInfo {
155 error: Option<AIErrorInfo>,
156 files: Vec<AIFile>,
157}
158
159impl AIInfo {
160 pub fn read_from(mut input: impl Read) -> Result<Option<Self>> {
161 let has_ai_files = input.read_u32::<LE>()? != 0;
162 let has_error = input.read_u32::<LE>()? != 0;
163
164 if !has_error && !has_ai_files {
165 return Ok(None);
166 }
167
168 let error = if has_error {
169 Some(AIErrorInfo::read_from(&mut input)?)
170 } else {
171 None
172 };
173
174 let num_ai_files = input.read_u32::<LE>()?;
175 let mut files = vec![];
176 for _ in 0..num_ai_files {
177 files.push(AIFile::read_from(&mut input)?);
178 }
179
180 Ok(Some(Self { error, files }))
181 }
182
183 pub fn write_to(&self, mut output: impl Write) -> Result<()> {
184 output.write_u32::<LE>(if self.files.is_empty() { 0 } else { 1 })?;
185
186 if let Some(error) = &self.error {
187 output.write_u32::<LE>(1)?;
188 error.write_to(&mut output)?;
189 } else {
190 output.write_u32::<LE>(0)?;
191 }
192
193 if !self.files.is_empty() {
194 output.write_u32::<LE>(self.files.len() as u32)?;
195 for file in &self.files {
196 file.write_to(&mut output)?;
197 }
198 }
199
200 Ok(())
201 }
202}