brs/lib.rs
1//! Interfaces for reading and writing Brickadia save files.
2//!
3//! Aims to be able to read all previous versions just like the game,
4//! but only write the newest version of the format.
5//!
6//! # Usage
7//!
8//! ## Reading
9//!
10//! First, create a reader from any
11//! [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html)
12//! source, such as a file or buffer.
13//!
14//! ```no_run
15//! # use std::fs::File;
16//! let reader = brs::Reader::new(File::open("village.brs")?)?;
17//! # Ok::<(), std::io::Error>(())
18//! ```
19//!
20//! Brickadia save files have information split into sections ordered
21//! such that one can extract simple information
22//! without needing to parse the entire file.
23//!
24//! This library surfaces this by strictly enforcing the way that data is read
25//! and made available at the type level; you can't go wrong.
26//!
27//! To continue, reading the first header gets you basic information.
28//! For details on what is available, see
29//! [`HasHeader1`](read/trait.HasHeader1.html).
30//!
31//! ```no_run
32//! use brs::HasHeader1;
33//! # let reader: brs::Reader<std::fs::File> = unimplemented!();
34//! let reader = reader.read_header1()?;
35//! println!("Brick count: {}", reader.brick_count());
36//! println!("Map: {}", reader.map());
37//! # Ok::<(), std::io::Error>(())
38//! ```
39//!
40//! The next header contains data less likely to be relevant for simpler
41//! introspection, but rather things such as tables for loading bricks.
42//! See [`HasHeader2`](read/trait.HasHeader2.html).
43//!
44//! ```no_run
45//! use brs::HasHeader2;
46//! # let reader: brs::read::ReaderAfterHeader1<std::fs::File> = unimplemented!();
47//! let reader = reader.read_header2()?;
48//! println!("Mods: {:?}", reader.mods());
49//! println!("Color count: {}", reader.colors().len());
50//! // Properties from header 1 are still available:
51//! use brs::HasHeader1;
52//! println!("Description: {}", reader.description());
53//! # Ok::<(), std::io::Error>(())
54//! ```
55//!
56//! After both headers have been read, you may now iterate over the bricks.
57//! See [`Brick`](struct.Brick.html).
58//!
59//! ```no_run
60//! # let reader: brs::read::ReaderAfterHeader2<std::fs::File> = unimplemented!();
61//! for brick in reader.iter_bricks()? {
62//! let brick = brick?;
63//! println!("{:?}", brick);
64//! }
65//! # Ok::<(), std::io::Error>(())
66//! ```
67//!
68//! You may retain access to the header information while getting the iterator:
69//!
70//! ```no_run
71//! # let reader: brs::read::ReaderAfterHeader2<std::fs::File> = unimplemented!();
72//! let (reader, bricks) = reader.iter_bricks_and_reader()?;
73//! # Ok::<(), std::io::Error>(())
74//! ```
75//!
76//! ## Writing
77//!
78//! Writing save files isn't as fancy, for now you simply just put all the data
79//! in the [`WriteData`](struct.WriteData.html) struct and pass it to
80//! [`write_save`](fn.write_save.html) along with a
81//! [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) destination.
82//!
83//! ```no_run
84//! # use std::fs::File;
85//! let data = brs::WriteData {
86//! map: "Plate".to_string(),
87//! author: brs::User {
88//! id: brs::uuid::Uuid::nil(),
89//! name: "Jensen".to_string(),
90//! },
91//! description: "A quaint park full of ducks and turkeys.".to_string(),
92//! save_time: chrono::Utc::now(),
93//!
94//! mods: Vec::new(),
95//! brick_assets: vec!["PB_DefaultBrick".to_string()],
96//! colors: vec![brs::Color::from_rgba(255, 23, 198, 255)],
97//! materials: vec!["BMC_Plastic".to_string()],
98//! brick_owners: Vec::new(),
99//!
100//! bricks: Vec::new(),
101//! };
102//! brs::write_save(&mut File::create("park.brs")?, &data)?;
103//! # Ok::<(), std::io::Error>(())
104//! ```
105
106mod bit_reader;
107mod bit_writer;
108mod save;
109
110pub mod read;
111mod write;
112
113pub use read::{HasHeader1, HasHeader2, Reader};
114pub use save::*;
115pub use write::{write_save, WriteData};
116
117pub use chrono;
118pub use uuid;
119
120use chrono::prelude::*;
121use num_enum::{IntoPrimitive, TryFromPrimitive};
122
123const MAGIC: [u8; 3] = [b'B', b'R', b'S'];
124
125/// A save file version.
126#[repr(u16)]
127#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, IntoPrimitive, TryFromPrimitive)]
128pub enum Version {
129 Initial = 1,
130 MaterialsStoredAsNames,
131 AddedOwnerData,
132 AddedDateTime,
133}
134
135/// The version that will be written.
136pub const VERSION_WRITE: Version = Version::AddedDateTime;
137
138fn ue4_date_time_base() -> DateTime<Utc> {
139 Utc.ymd(1, 1, 1).and_hms(0, 0, 0)
140}