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