1use 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#[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
38pub 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 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 pub fn skip_header1(&mut self) -> Result<(), ReadError> {
77 skip_compressed(&mut self.reader)?;
78 self.header1_read = true;
79 Ok(())
80 }
81
82 pub fn read_header1(&mut self) -> Result<Header1, ReadError> {
84 let (mut cursor, _) = read_compressed(&mut self.reader)?;
85
86 let map = cursor.read_string()?;
88
89 let author_name = cursor.read_string()?;
91
92 let description = cursor.read_string()?;
94
95 let author_uuid = cursor.read_uuid()?;
97
98 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 let save_time = match self.version {
114 _ if self.version >= 4 => {
115 let mut bytes = [0u8; 8]; cursor.read_exact(&mut bytes)?;
117 Some(bytes)
118 }
119 _ => None,
120 };
121
122 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 pub fn skip_header2(&mut self) -> Result<(), ReadError> {
144 skip_compressed(&mut self.reader)?;
145 self.header2_read = true;
146 Ok(())
147 }
148
149 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 let mods = cursor.read_array(|r| r.read_string())?;
159
160 let brick_assets = cursor.read_array(|r| r.read_string())?;
162
163 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 let materials = match self.version {
174 _ if self.version >= 2 => cursor.read_array(|r| r.read_string())?,
175 _ => DEFAULT_MATERIALS.clone(),
176 };
177
178 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 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 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 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 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 {
279 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 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 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 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 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
466fn 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 reader.read_exact(&mut bytes)?;
481 } else {
482 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
491fn 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}