1extern crate std;
2
3use std::{io::Read, iter::Peekable, str::FromStr, vec::Vec};
4
5use crate::lexer::{LineToken, Token, TokenIterator};
6use crate::repr::{
7 Alignment, Brush, Edict, Entity, Point, Quake2SurfaceExtension, QuakeMap,
8 Surface,
9};
10use crate::{TextParseError, TextParseResult};
11
12const CELL_EXPECT: &str = "Expected cell value";
13
14type TokenPeekable<R> = Peekable<TokenIterator<R>>;
15
16trait Extract {
17 fn extract(&mut self) -> TextParseResult<Option<LineToken>>;
18}
19
20impl<R> Extract for TokenPeekable<R>
21where
22 R: Read,
23{
24 fn extract(&mut self) -> Result<Option<LineToken>, TextParseError> {
25 self.next()
26 .transpose()
27 .map_err(|e| e.into_inner().expect(CELL_EXPECT))
28 }
29}
30
31const MIN_BRUSH_SURFACES: usize = 4;
32
33pub fn parse<R: Read>(reader: &mut R) -> TextParseResult<QuakeMap> {
39 let mut entities: Vec<Entity> = Vec::new();
40 let mut peekable_tokens = TokenIterator::new(reader).peekable();
41
42 while peekable_tokens.peek().is_some() {
43 let entity = parse_entity(&mut peekable_tokens)?;
44 entities.push(entity);
45 }
46
47 Ok(QuakeMap { entities })
48}
49
50fn parse_entity<R: Read>(
51 tokens: &mut TokenPeekable<R>,
52) -> TextParseResult<Entity> {
53 expect_token(&tokens.extract()?, Token::OpenCurly)?;
54
55 let edict = parse_edict(tokens)?;
56 let brushes = parse_brushes(tokens)?;
57
58 expect_token(&tokens.extract()?, Token::CloseCurly)?;
59
60 Ok(Entity { edict, brushes })
61}
62
63fn parse_edict<R: Read>(
64 tokens: &mut TokenPeekable<R>,
65) -> TextParseResult<Edict> {
66 let mut edict = Edict::new();
67
68 while let Some(tok_res) = tokens.peek() {
69 if tok_res
70 .as_ref()
71 .map_err(|e| e.take().expect(CELL_EXPECT))?
72 .is_quoted()
73 {
74 let key = tokens.extract()?.unwrap().into_bare_cstring();
75 let maybe_value = tokens.extract()?;
76 expect_quoted(&maybe_value)?;
77 let value = maybe_value.unwrap().into_bare_cstring();
78 edict.push((key, value));
79 } else {
80 break;
81 }
82 }
83
84 Ok(edict)
85}
86
87fn parse_brushes<R: Read>(
88 tokens: &mut TokenPeekable<R>,
89) -> TextParseResult<Vec<Brush>> {
90 let mut brushes = Vec::new();
91
92 while let Some(tok_res) = tokens.peek() {
93 if tok_res
94 .as_ref()
95 .map_err(|e| e.take().expect(CELL_EXPECT))?
96 .token
97 == Token::OpenCurly
98 {
99 brushes.push(parse_brush(tokens)?);
100 } else {
101 break;
102 }
103 }
104
105 Ok(brushes)
106}
107
108fn parse_brush<R: Read>(
109 tokens: &mut TokenPeekable<R>,
110) -> TextParseResult<Brush> {
111 let mut surfaces = Vec::with_capacity(MIN_BRUSH_SURFACES);
112 expect_token(&tokens.extract()?, Token::OpenCurly)?;
113
114 while let Some(tok_res) = tokens.peek() {
115 if tok_res
116 .as_ref()
117 .map_err(|e| e.take().expect(CELL_EXPECT))?
118 .token
119 == Token::OpenParen
120 {
121 surfaces.push(parse_surface(tokens)?);
122 } else {
123 break;
124 }
125 }
126
127 expect_token_or(&tokens.extract()?, Token::CloseCurly, b"(")?;
128 Ok(surfaces)
129}
130
131fn parse_surface<R: Read>(
132 tokens: &mut TokenPeekable<R>,
133) -> TextParseResult<Surface> {
134 let pt1 = parse_point(tokens)?;
135 let pt2 = parse_point(tokens)?;
136 let pt3 = parse_point(tokens)?;
137
138 let half_space = [pt1, pt2, pt3];
139
140 let texture_token = tokens.extract()?.ok_or_else(TextParseError::eof)?;
141
142 let texture = texture_token.into_bare_cstring();
143
144 let alignment = if let Some(tok_res) = tokens.peek() {
145 if tok_res
146 .as_ref()
147 .map_err(|e| e.take().expect(CELL_EXPECT))?
148 .token
149 == Token::OpenSquare
150 {
151 parse_valve_alignment(tokens)?
152 } else {
153 parse_legacy_alignment(tokens)?
154 }
155 } else {
156 return Err(TextParseError::eof());
157 };
158
159 let q2ext = if let Some(tok_res) = tokens.peek() {
160 if matches!(
161 tok_res
162 .as_ref()
163 .map_err(|e| e.take().expect(CELL_EXPECT))?
164 .token,
165 Token::BareString(_),
166 ) {
167 parse_q2_ext(tokens)?
168 } else {
169 Default::default()
170 }
171 } else {
172 return Err(TextParseError::eof());
173 };
174
175 Ok(Surface {
176 half_space,
177 texture,
178 alignment,
179 q2ext,
180 })
181}
182
183fn parse_point<R: Read>(
184 tokens: &mut TokenPeekable<R>,
185) -> TextParseResult<Point> {
186 expect_token(&tokens.extract()?, Token::OpenParen)?;
187 let x = expect_float(&tokens.extract()?)?;
188 let y = expect_float(&tokens.extract()?)?;
189 let z = expect_float(&tokens.extract()?)?;
190 expect_token(&tokens.extract()?, Token::CloseParen)?;
191
192 Ok([x, y, z])
193}
194
195fn parse_legacy_alignment<R: Read>(
196 tokens: &mut TokenPeekable<R>,
197) -> TextParseResult<Alignment> {
198 let offset_x = expect_float(&tokens.extract()?)?;
199 let offset_y = expect_float(&tokens.extract()?)?;
200 let rotation = expect_float(&tokens.extract()?)?;
201 let scale_x = expect_float(&tokens.extract()?)?;
202 let scale_y = expect_float(&tokens.extract()?)?;
203
204 Ok(Alignment {
205 offset: [offset_x, offset_y],
206 rotation,
207 scale: [scale_x, scale_y],
208 axes: None,
209 })
210}
211
212fn parse_q2_ext<R: Read>(
213 tokens: &mut TokenPeekable<R>,
214) -> TextParseResult<Quake2SurfaceExtension> {
215 let content_flags = expect_int(&tokens.extract()?)?;
216 let surface_flags = expect_int(&tokens.extract()?)?;
217 let surface_value = expect_float(&tokens.extract()?)?;
218
219 Ok(Quake2SurfaceExtension {
220 content_flags,
221 surface_flags,
222 surface_value,
223 })
224}
225
226fn parse_valve_alignment<R: Read>(
227 tokens: &mut TokenPeekable<R>,
228) -> TextParseResult<Alignment> {
229 expect_token(&tokens.extract()?, Token::OpenSquare)?;
230 let u_x = expect_float(&tokens.extract()?)?;
231 let u_y = expect_float(&tokens.extract()?)?;
232 let u_z = expect_float(&tokens.extract()?)?;
233 let offset_x = expect_float(&tokens.extract()?)?;
234 expect_token(&tokens.extract()?, Token::CloseSquare)?;
235
236 expect_token(&tokens.extract()?, Token::OpenSquare)?;
237 let v_x = expect_float(&tokens.extract()?)?;
238 let v_y = expect_float(&tokens.extract()?)?;
239 let v_z = expect_float(&tokens.extract()?)?;
240 let offset_y = expect_float(&tokens.extract()?)?;
241 expect_token(&tokens.extract()?, Token::CloseSquare)?;
242
243 let rotation = expect_float(&tokens.extract()?)?;
244 let scale_x = expect_float(&tokens.extract()?)?;
245 let scale_y = expect_float(&tokens.extract()?)?;
246
247 Ok(Alignment {
248 offset: [offset_x, offset_y],
249 rotation,
250 scale: [scale_x, scale_y],
251 axes: Some([[u_x, u_y, u_z], [v_x, v_y, v_z]]),
252 })
253}
254
255fn expect_token(
256 line_token: &Option<LineToken>,
257 token: Token,
258) -> TextParseResult<()> {
259 match line_token.as_ref() {
260 Some(payload) if payload.token == token => Ok(()),
261 Some(payload) => Err(TextParseError::from_parser(
262 format!("Expected `{}`, got `{}`", token, payload.token),
263 payload.line_number,
264 )),
265 _ => Err(TextParseError::eof()),
266 }
267}
268
269fn expect_token_or(
270 line_token: &Option<LineToken>,
271 token: Token,
272 rest: &[u8],
273) -> TextParseResult<()> {
274 match line_token.as_ref() {
275 Some(payload) if payload.token == token => Ok(()),
276 Some(payload) => {
277 let rest_str = rest
278 .iter()
279 .copied()
280 .map(|b| format!("`{}`", char::from(b)))
281 .collect::<Vec<_>>()[..]
282 .join(", ");
283
284 Err(TextParseError::from_parser(
285 format!(
286 "Expected {} or `{}`, got `{}`",
287 rest_str, token, payload.token
288 ),
289 payload.line_number,
290 ))
291 }
292 _ => Err(TextParseError::eof()),
293 }
294}
295
296fn expect_quoted(token: &Option<LineToken>) -> TextParseResult<()> {
297 match token.as_ref() {
298 Some(payload) if payload.is_quoted() => Ok(()),
299 Some(payload) => Err(TextParseError::from_parser(
300 format!("Expected quoted, got `{}`", payload.token),
301 payload.line_number,
302 )),
303 _ => Err(TextParseError::eof()),
304 }
305}
306
307fn expect_float(token: &Option<LineToken>) -> TextParseResult<f64> {
308 match token.as_ref() {
309 Some(payload) => match f64::from_str(payload.token.as_number_text()) {
310 Ok(num) => Ok(num),
311 Err(_) => Err(TextParseError::from_parser(
312 format!("Expected number, got `{}`", payload.token),
313 payload.line_number,
314 )),
315 },
316 None => Err(TextParseError::eof()),
317 }
318}
319
320fn expect_int(token: &Option<LineToken>) -> TextParseResult<i32> {
321 match token.as_ref() {
322 Some(payload) => match i32::from_str(payload.token.as_number_text()) {
323 Ok(num) => Ok(num),
324 Err(_) => Err(TextParseError::from_parser(
325 format!("Expected integer, got `{}`", payload.token),
326 payload.line_number,
327 )),
328 },
329 None => Err(TextParseError::eof()),
330 }
331}