brickadia/
read.rs

1//! Save reading.
2
3use std::{
4    cmp,
5    collections::HashMap,
6    convert::TryFrom,
7    io::{self, Cursor, Read},
8};
9
10use bitstream_io::{BitRead, BitReader};
11use byteorder::{LittleEndian, ReadBytesExt};
12use flate2::read::ZlibDecoder;
13use thiserror::Error;
14
15use crate::{ext::*, save::*, MAGIC_BYTES};
16
17lazy_static::lazy_static! {
18    static ref DEFAULT_MATERIALS: Vec<String> = vec!["BMC_Hologram", "BMC_Plastic", "BMC_Glow", "BMC_Metallic", "BMC_Glass"].into_iter().map(|s| s.into()).collect();
19}
20
21/// A read error.
22#[derive(Error, Debug)]
23pub enum ReadError {
24    #[error("generic io error: {0}")]
25    IoError(#[from] io::Error),
26    #[error("bad magic bytes (expected 'BRS')")]
27    BadHeader,
28    #[error("invalid data in header 1")]
29    InvalidDataHeader1,
30    #[error("invalid data in header 2")]
31    InvalidDataHeader2,
32    #[error("must read in sequence: header 1, header 2, [preview], bricks")]
33    BadSectionReadOrder,
34    #[error("invalid compressed section")]
35    InvalidCompression,
36}
37
38/// A save reader, which reads data from its `reader` (a `Read + Seek`).
39pub struct SaveReader<R: Read> {
40    reader: R,
41    pub version: u16,
42    pub game_version: i32,
43
44    header1_read: bool,
45    header2_read: bool,
46    preview_read: bool,
47}
48
49impl<R: Read> SaveReader<R> {
50    /// Create a new save reader from an existing `reader`, a `Read + Seek`.
51    pub fn new(mut reader: R) -> Result<Self, ReadError> {
52        let mut magic = [0u8; 3];
53        reader.read_exact(&mut magic)?;
54        if &magic != MAGIC_BYTES {
55            return Err(ReadError::BadHeader);
56        }
57
58        let version = reader.read_u16::<LittleEndian>()?;
59        let game_version = if version >= 8 {
60            reader.read_i32::<LittleEndian>()?
61        } else {
62            0
63        };
64
65        Ok(SaveReader {
66            version,
67            game_version,
68            reader,
69            header1_read: false,
70            header2_read: false,
71            preview_read: version < 8,
72        })
73    }
74
75    /// Skip the first header.
76    pub fn skip_header1(&mut self) -> Result<(), ReadError> {
77        skip_compressed(&mut self.reader)?;
78        self.header1_read = true;
79        Ok(())
80    }
81
82    /// Read the first header.
83    pub fn read_header1(&mut self) -> Result<Header1, ReadError> {
84        let (mut cursor, _) = read_compressed(&mut self.reader)?;
85
86        // match map: a string
87        let map = cursor.read_string()?;
88
89        // match author name: a string
90        let author_name = cursor.read_string()?;
91
92        // match description: a string
93        let description = cursor.read_string()?;
94
95        // match author id: a uuid
96        let author_uuid = cursor.read_uuid()?;
97
98        // match host:
99        // version >= 8: match a user (string followed by uuid)
100        //         else: not provided
101        let host = match self.version {
102            _ if self.version >= 8 => {
103                let name = cursor.read_string()?;
104                let id = cursor.read_uuid()?;
105                Some(User { name, id })
106            }
107            _ => None,
108        };
109
110        // match save time:
111        // version >= 4: match 8 bytes
112        //         else: not provided
113        let save_time = match self.version {
114            _ if self.version >= 4 => {
115                let mut bytes = [0u8; 8]; // todo: figure out how to parse this
116                cursor.read_exact(&mut bytes)?;
117                Some(bytes)
118            }
119            _ => None,
120        };
121
122        // match brick count: an i32
123        let brick_count = match cursor.read_i32::<LittleEndian>()? {
124            count if count >= 0 => count,
125            _ => return Err(ReadError::InvalidDataHeader1),
126        } as u32;
127
128        self.header1_read = true;
129        Ok(Header1 {
130            map,
131            author: User {
132                name: author_name,
133                id: author_uuid,
134            },
135            description,
136            host,
137            save_time: save_time.unwrap_or([0u8; 8]),
138            brick_count,
139        })
140    }
141
142    /// Skip the second header.
143    pub fn skip_header2(&mut self) -> Result<(), ReadError> {
144        skip_compressed(&mut self.reader)?;
145        self.header2_read = true;
146        Ok(())
147    }
148
149    /// Read the second header.
150    pub fn read_header2(&mut self) -> Result<Header2, ReadError> {
151        if !self.header1_read {
152            return Err(ReadError::BadSectionReadOrder);
153        }
154
155        let (mut cursor, _) = read_compressed(&mut self.reader)?;
156
157        // match mods: an array of strings
158        let mods = cursor.read_array(|r| r.read_string())?;
159
160        // match brick assets: an array of strings
161        let brick_assets = cursor.read_array(|r| r.read_string())?;
162
163        // match colors: an array of 4 bytes each, BGRA
164        let colors = cursor.read_array(|r| -> io::Result<Color> {
165            let mut bytes = [0u8; 4];
166            r.read_exact(&mut bytes)?;
167            Ok(Color::from_bytes_bgra(bytes))
168        })?;
169
170        // match materials:
171        // version >= 2: an array of strings
172        //         else: a list of default materials (see top of file)
173        let materials = match self.version {
174            _ if self.version >= 2 => cursor.read_array(|r| r.read_string())?,
175            _ => DEFAULT_MATERIALS.clone(),
176        };
177
178        // match brick owners:
179        // version >= 3: match brick owner:
180        //               version >= 8: a user (uuid followed by string), then an i32 for brick count
181        //                       else: a user (uuid followed by string)
182        let brick_owners = match self.version {
183            _ if self.version >= 3 => cursor.read_array(|r| -> io::Result<BrickOwner> {
184                match self.version {
185                    _ if self.version >= 8 => {
186                        let id = r.read_uuid()?;
187                        let name = r.read_string()?;
188                        let bricks = r.read_i32::<LittleEndian>()? as u32;
189                        Ok(BrickOwner { name, id, bricks })
190                    }
191                    _ => {
192                        let id = r.read_uuid()?;
193                        let name = r.read_string()?;
194                        Ok(BrickOwner::from(User { name, id }))
195                    }
196                }
197            })?,
198            _ => vec![],
199        };
200
201        // match physical materials
202        // version >= 9: an array of strings
203        //         else: not provided
204        let physical_materials = match self.version {
205            _ if self.version >= 9 => cursor.read_array(|r| r.read_string())?,
206            _ => vec![],
207        };
208
209        self.header2_read = true;
210        Ok(Header2 {
211            mods,
212            brick_assets,
213            colors,
214            materials,
215            brick_owners,
216            physical_materials,
217        })
218    }
219
220    /// Read the preview in the save.
221    ///
222    /// The preview is an `Preview`, which might not exist (Preview::None).
223    pub fn read_preview(&mut self) -> Result<Preview, ReadError> {
224        if !self.header2_read {
225            return Err(ReadError::BadSectionReadOrder);
226        }
227
228        if self.version < 8 {
229            return Ok(Preview::None);
230        }
231
232        let preview = Preview::from_reader(&mut self.reader)?;
233        self.preview_read = true;
234        Ok(preview)
235    }
236
237    /// Skip over the preview section.
238    pub fn skip_preview(&mut self) -> Result<(), ReadError> {
239        if !self.header2_read {
240            return Err(ReadError::BadSectionReadOrder);
241        }
242
243        if self.version < 8 {
244            return Ok(());
245        }
246
247        if self.reader.read_u8()? != 0 {
248            let len = self.reader.read_i32::<LittleEndian>()?;
249            io::copy(&mut self.reader.by_ref().take(len as u64), &mut io::sink())?;
250        }
251
252        self.preview_read = true;
253        Ok(())
254    }
255
256    /// Read the bricks and components from a save.
257    pub fn read_bricks(
258        &mut self,
259        header1: &Header1,
260        header2: &Header2,
261    ) -> Result<(Vec<Brick>, HashMap<String, Component>), ReadError> {
262        if !self.preview_read || !self.header2_read {
263            return Err(ReadError::BadSectionReadOrder);
264        }
265
266        let (cursor, len) = read_compressed(&mut self.reader)?;
267        let mut bits = BitReader::<_, bitstream_io::LittleEndian>::new(cursor);
268
269        let brick_asset_count = cmp::max(header2.brick_assets.len(), 2);
270        let material_count = cmp::max(header2.materials.len(), 2);
271        let physical_material_count = cmp::max(header2.physical_materials.len(), 2);
272
273        let inital_bricks_capacity = cmp::min(header1.brick_count as usize, 10_000_000);
274        let mut bricks = Vec::with_capacity(inital_bricks_capacity);
275        let mut components = HashMap::new();
276
277        // loop over each brick
278        loop {
279            // align and break out of the loop if we've seeked far enough ahead
280            bits.byte_align();
281            if bricks.len() >= header1.brick_count as usize
282                || bits.reader().unwrap().position() >= len as u64
283            {
284                break;
285            }
286
287            let asset_name_index = bits.read_uint(brick_asset_count as u32)?;
288
289            let size = match bits.read_bit()? {
290                true => Size::Procedural(
291                    bits.read_uint_packed()?,
292                    bits.read_uint_packed()?,
293                    bits.read_uint_packed()?,
294                ),
295                false => Size::Empty,
296            };
297
298            let position = (
299                bits.read_int_packed()?,
300                bits.read_int_packed()?,
301                bits.read_int_packed()?,
302            );
303
304            let orientation = bits.read_uint(24)?;
305            let direction = Direction::try_from(((orientation >> 2) % 6) as u8).unwrap();
306            let rotation = Rotation::try_from((orientation & 3) as u8).unwrap();
307
308            let collision = match self.version {
309                _ if self.version >= 10 => Collision {
310                    player: bits.read_bit()?,
311                    weapon: bits.read_bit()?,
312                    interaction: bits.read_bit()?,
313                    tool: bits.read_bit()?,
314                },
315                _ => Collision::for_all(bits.read_bit()?),
316            };
317
318            let visibility = bits.read_bit()?;
319
320            let material_index = match self.version {
321                _ if self.version >= 8 => bits.read_uint(material_count as u32)?,
322                _ => {
323                    if bits.read_bit()? {
324                        bits.read_uint_packed()?
325                    } else {
326                        1
327                    }
328                }
329            };
330
331            let physical_index = match self.version {
332                _ if self.version >= 9 => bits.read_uint(physical_material_count as u32)?,
333                _ => 0,
334            };
335
336            let material_intensity = match self.version {
337                _ if self.version >= 9 => bits.read_uint(11)?,
338                _ => 5,
339            };
340
341            let color = match bits.read_bit()? {
342                true => match self.version {
343                    _ if self.version >= 9 => {
344                        let mut bytes = [0u8; 3];
345                        bits.read_bytes(&mut bytes)?;
346                        BrickColor::Unique(Color::from_bytes_rgb(bytes))
347                    }
348                    _ => {
349                        let mut bytes = [0u8; 4];
350                        bits.read_bytes(&mut bytes)?;
351                        BrickColor::Unique(Color::from_bytes_bgra(bytes))
352                    }
353                },
354                false => BrickColor::Index(bits.read_uint(header2.colors.len() as u32)?),
355            };
356
357            let owner_index = if self.version >= 3 {
358                bits.read_uint_packed()?
359            } else {
360                0
361            };
362
363            let brick = Brick {
364                asset_name_index,
365                size,
366                position,
367                direction,
368                rotation,
369                collision,
370                visibility,
371                material_index,
372                physical_index,
373                material_intensity,
374                color,
375                owner_index,
376                components: HashMap::new(),
377            };
378
379            bricks.push(brick);
380        }
381
382        bricks.shrink_to_fit();
383        let brick_count = cmp::max(bricks.len(), 2);
384
385        // components
386        if self.version >= 8 {
387            let (mut cursor, _) = read_compressed(&mut self.reader)?;
388            let len = cursor.read_i32::<LittleEndian>()?;
389
390            for _ in 0..len {
391                let name = cursor.read_string()?;
392
393                let mut bit_bytes = vec![0u8; cursor.read_i32::<LittleEndian>()? as usize];
394                cursor.read_exact(&mut bit_bytes)?;
395                let mut bits =
396                    BitReader::endian(Cursor::new(bit_bytes), bitstream_io::LittleEndian);
397
398                let version = bits.read_i32_le()?;
399                let brick_indices = bits.read_array(|r| r.read_uint(brick_count as u32))?;
400
401                let properties = bits
402                    .read_array(|r| Ok((r.read_string()?, r.read_string()?)))?
403                    .into_iter()
404                    .collect::<Vec<_>>();
405
406                // components for each brick
407                for &i in brick_indices.iter() {
408                    let mut props = HashMap::new();
409                    for (n, ty) in properties.iter() {
410                        props.insert(n.to_owned(), bits.read_unreal_type(ty)?);
411                    }
412                    bricks[i as usize].components.insert(name.to_owned(), props);
413                }
414
415                components.insert(
416                    name,
417                    Component {
418                        version,
419                        brick_indices,
420                        properties: properties.into_iter().collect(),
421                    },
422                );
423            }
424        }
425
426        Ok((bricks, components))
427    }
428
429    /// Read all parts of a save into a `SaveData`.
430    pub fn read_all(&mut self) -> Result<SaveData, ReadError> {
431        let header1 = self.read_header1()?;
432        let header2 = self.read_header2()?;
433        let preview = self.read_preview()?;
434        let (bricks, components) = self.read_bricks(&header1, &header2)?;
435
436        Ok(SaveData {
437            version: self.version,
438            game_version: self.game_version,
439            header1,
440            header2,
441            preview,
442            bricks,
443            components,
444        })
445    }
446
447    /// Read all parts of a save (except the preview) into a `SaveData`.
448    pub fn read_all_skip_preview(&mut self) -> Result<SaveData, ReadError> {
449        let header1 = self.read_header1()?;
450        let header2 = self.read_header2()?;
451        self.skip_preview()?;
452        let (bricks, components) = self.read_bricks(&header1, &header2)?;
453
454        Ok(SaveData {
455            version: self.version,
456            game_version: self.game_version,
457            header1,
458            header2,
459            preview: Preview::None,
460            bricks,
461            components,
462        })
463    }
464}
465
466/// Read a compressed section from a `Read`, following the BRS spec for compressed sections.
467fn read_compressed(reader: &mut impl Read) -> Result<(Cursor<Vec<u8>>, i32), ReadError> {
468    let (uncompressed_size, compressed_size) = (
469        reader.read_i32::<LittleEndian>()?,
470        reader.read_i32::<LittleEndian>()?,
471    );
472    if uncompressed_size < 0 || compressed_size < 0 || compressed_size > uncompressed_size {
473        return Err(ReadError::InvalidCompression);
474    }
475
476    let mut bytes = vec![0u8; uncompressed_size as usize];
477
478    if compressed_size == 0 {
479        // no need to decompress first
480        reader.read_exact(&mut bytes)?;
481    } else {
482        // decompress first, then read
483        let mut compressed = vec![0u8; compressed_size as usize];
484        reader.read_exact(&mut compressed)?;
485        ZlibDecoder::new(&compressed[..]).read_exact(&mut bytes)?;
486    }
487
488    Ok((Cursor::new(bytes), uncompressed_size))
489}
490
491/// Read a compressed section from a `Read`, discarding its contents.
492fn skip_compressed(reader: &mut impl Read) -> Result<(), ReadError> {
493    let (uncompressed_size, compressed_size) = (
494        reader.read_i32::<LittleEndian>()?,
495        reader.read_i32::<LittleEndian>()?,
496    );
497    if uncompressed_size < 0 || compressed_size < 0 || compressed_size > uncompressed_size {
498        return Err(ReadError::InvalidCompression);
499    }
500
501    io::copy(
502        &mut reader.take(if compressed_size == 0 {
503            uncompressed_size as u64
504        } else {
505            compressed_size as u64
506        }),
507        &mut io::sink(),
508    )?;
509
510    Ok(())
511}