Skip to content

Commit fc82b02

Browse files
committed
Initial commit
0 parents  commit fc82b02

11 files changed

+1346
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/target
2+
**/*.rs.bk
3+
Cargo.lock
4+
.vscode

Cargo.toml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "brs"
3+
version = "0.1.0"
4+
authors = ["ns <[email protected]>"]
5+
edition = "2018"
6+
description = "Read and write Brickadia save files."
7+
keywords = ["brickadia", "parser", "file", "binary"]
8+
categories = ["encoding"]
9+
readme = "README.md"
10+
11+
[dependencies]
12+
byteorder = "1"
13+
libflate = "0.1"
14+
uuid = "0.7"
15+
chrono = "0.4"

README.md

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
[![brs on crates.io][cratesio-image]][cratesio]
2+
[![brs on docs.rs][docsrs-image]][docsrs]
3+
4+
[cratesio-image]: https://img.shields.io/crates/v/brs.svg
5+
[cratesio]: https://crates.io/crates/brs
6+
[docsrs-image]: https://docs.rs/brs/badge.svg
7+
[docsrs]: https://docs.rs/brs
8+
9+
Interfaces for reading and writing Brickadia save files.
10+
11+
Aims to be able to read all previous versions just like the game,
12+
but only write the newest version of the format.
13+
14+
# Usage
15+
16+
## Reading
17+
18+
First, create a reader from any
19+
[`Read`](https://doc.rust-lang.org/std/io/trait.Read.html)
20+
source, such as a file or buffer.
21+
22+
```rust
23+
let reader = brs::Reader::new(File::open("village.brs")?)?;
24+
```
25+
26+
Brickadia save files have information split into sections ordered
27+
such that one can extract simple information
28+
without needing to parse the entire file.
29+
30+
This library surfaces this by strictly enforcing the way that data is read
31+
and made available at the type level; you can't go wrong.
32+
33+
To continue, reading the first header gets you basic information.
34+
For details on what is available, see
35+
[`HasHeader1`](https://docs.rs/brs/*/brs/read/trait.HasHeader1.html).
36+
37+
```rust
38+
use brs::HasHeader1;
39+
let reader = reader.read_header1();
40+
println!("Brick count: {}", reader.brick_count());
41+
println!("Map: {}", reader.map());
42+
```
43+
44+
The next header contains data less likely to be relevant for simpler
45+
introspection, but rather things such as tables for loading bricks.
46+
See [`HasHeader2`](https://docs.rs/brs/*/brs/read/trait.HasHeader2.html).
47+
48+
```rust
49+
use brs::HasHeader2;
50+
let reader = reader.read_header2();
51+
println!("Mods: {:?}", reader.mods());
52+
println!("Color count: {}", reader.colors().len());
53+
// Properties from header 1 are still available:
54+
println!("Description: {}", reader.description();
55+
```
56+
57+
After both headers have been read, you may now iterate over the bricks.
58+
See [`Brick`](https://docs.rs/brs/*/brs/struct.Brick.html).
59+
60+
```rust
61+
for brick in reader.iter_bricks()? {
62+
let brick = brick?;
63+
println!("{:?}", brick);
64+
}
65+
```
66+
67+
You may retain access to the header information while getting the iterator:
68+
69+
```rust
70+
let (rdr, bricks) = reader.iter_bricks_and_reader()?;
71+
```
72+
73+
## Writing
74+
75+
Writing save files isn't as fancy, for now you simply just put all the data
76+
in the [`WriteData`](https://docs.rs/brs/*/brs/struct.WriteData.html) struct and pass it to
77+
[`write_save`](https://docs.rs/brs/*/brs/fn.write_save.html) along with a
78+
[`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) destination.
79+
80+
```rust
81+
let data = brs::WriteData {
82+
map: String::from("Plate"),
83+
description: String::from("A quaint park full of ducks and turkeys."),
84+
// ...
85+
};
86+
brs::write_save(&mut File::create("park.brs")?, &data)?;
87+
```

src/bin/debug.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use brs::{HasHeader1, HasHeader2};
2+
use std::env::args;
3+
use std::fs::File;
4+
5+
fn main() -> std::io::Result<()> {
6+
let path = args().nth(1).expect("missing path");
7+
let reader = brs::Reader::new(File::open(path)?)?;
8+
let reader = reader.read_header1()?;
9+
let reader = reader.read_header2()?;
10+
dbg!(reader.header1());
11+
dbg!(reader.header2());
12+
let mut first_brick = None;
13+
for brick in reader.iter_bricks()? {
14+
let brick = brick?;
15+
first_brick.get_or_insert(brick);
16+
}
17+
dbg!(first_brick);
18+
Ok(())
19+
}

src/bin/rewrite.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use std::env::args;
2+
use std::fs::File;
3+
4+
fn main() -> std::io::Result<()> {
5+
let path = args().nth(1).expect("missing path");
6+
let reader = brs::Reader::new(File::open(&path)?)?;
7+
let reader = reader.read_header1()?;
8+
let reader = reader.read_header2()?;
9+
let data = reader.into_write_data()?;
10+
let mut new_path = path.clone();
11+
new_path.push_str(".rewrite.brs");
12+
brs::write_save(&mut File::create(new_path)?, &data)?;
13+
Ok(())
14+
}

src/bit_reader.rs

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Currently panics on EOF.
2+
// Needs improvement.
3+
4+
use std::io::{self, prelude::*};
5+
6+
pub struct BitReader {
7+
buf: Vec<u8>,
8+
pos: usize,
9+
}
10+
11+
impl BitReader {
12+
pub fn new(buf: Vec<u8>) -> Self {
13+
Self { buf, pos: 0 }
14+
}
15+
16+
// ReadBit
17+
#[inline(always)]
18+
pub fn read_bit(&mut self) -> bool {
19+
let bit = (self.buf[self.pos >> 3] & (1 << (self.pos & 7))) != 0;
20+
self.pos += 1;
21+
bit
22+
}
23+
24+
// SerializeBits
25+
pub fn read_bits(&mut self, dst: &mut [u8], len: usize) {
26+
for bit in 0..len {
27+
let byte = &mut dst[bit >> 3];
28+
let shift = bit & 7;
29+
*byte = (*byte & !(1 << shift)) | (u8::from(self.read_bit()) << shift);
30+
}
31+
self.pos += len;
32+
}
33+
34+
// SerializeInt
35+
pub fn read_int(&mut self, max: u32) -> u32 {
36+
let mut value = 0;
37+
let mut mask = 1;
38+
39+
while (value + mask) < max && mask != 0 {
40+
if self.read_bit() {
41+
value |= mask;
42+
}
43+
mask *= 2;
44+
}
45+
46+
value
47+
}
48+
49+
// SerializeIntPacked
50+
pub fn read_int_packed(&mut self) -> u32 {
51+
/*
52+
let mut src = &self.buf[..];
53+
let bit_count_used_in_byte = self.pos & 7;
54+
let bit_count_left_in_byte = 8 - (self.pos & 7);
55+
let src_mask_byte_0 = ((1 << bit_count_left_in_byte) - 1) as u8;
56+
let src_mask_byte_1 = ((1 << bit_count_used_in_byte) - 1) as u8;
57+
let next_src_index = (bit_count_used_in_byte != 0) as usize;
58+
59+
let mut value: u32 = 0;
60+
61+
let mut it = 0;
62+
let mut shift_count = 0;
63+
64+
while it < 5 {
65+
self.pos += 8;
66+
67+
let byte = ((src[0] >> bit_count_used_in_byte) & src_mask_byte_0)
68+
| ((src[next_src_index] & src_mask_byte_1) << (bit_count_left_in_byte & 7));
69+
let next_byte_indicator = byte & 1;
70+
let byte_as_word = (byte >> 1) as u32;
71+
value = (byte_as_word << shift_count) | value;
72+
src = &src[1..];
73+
74+
if next_byte_indicator == 0 {
75+
break;
76+
}
77+
78+
it += 1;
79+
shift_count += 7;
80+
}
81+
82+
value
83+
*/
84+
85+
let mut value = 0;
86+
87+
for i in 0..5 {
88+
let has_next = self.read_bit();
89+
let mut part = 0;
90+
for bit_shift in 0..7 {
91+
part |= (self.read_bit() as u32) << bit_shift;
92+
}
93+
value |= part << (7 * i);
94+
if !has_next {
95+
break;
96+
}
97+
}
98+
99+
value
100+
}
101+
102+
// EatByteAlign
103+
pub fn eat_byte_align(&mut self) {
104+
self.pos = (self.pos + 7) & !0x07;
105+
}
106+
107+
// SerializeIntVectorPacked
108+
pub fn read_int_vector_packed(&mut self) -> (i32, i32, i32) {
109+
(self.rivp_item(), self.rivp_item(), self.rivp_item())
110+
}
111+
112+
#[inline(always)]
113+
fn rivp_item(&mut self) -> i32 {
114+
let value = self.read_int_packed();
115+
(value >> 1) as i32 * if value & 1 != 0 { 1 } else { -1 }
116+
}
117+
118+
// SerializePositiveIntVectorPacked
119+
pub fn read_positive_int_vector_packed(&mut self) -> (u32, u32, u32) {
120+
(
121+
self.read_int_packed(),
122+
self.read_int_packed(),
123+
self.read_int_packed(),
124+
)
125+
}
126+
}
127+
128+
impl Read for BitReader {
129+
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
130+
self.read_bits(dst, dst.len() * 8);
131+
Ok(dst.len())
132+
}
133+
}

src/bit_writer.rs

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
use std::io::{self, prelude::*};
2+
3+
pub struct BitWriter<W: Write> {
4+
w: W,
5+
cur: u8,
6+
bit: u8,
7+
}
8+
9+
impl<W: Write> BitWriter<W> {
10+
pub fn new(w: W) -> Self {
11+
Self { w, cur: 0, bit: 0 }
12+
}
13+
14+
pub fn finish(mut self) -> io::Result<W> {
15+
self.flush_byte()?;
16+
Ok(self.w)
17+
}
18+
19+
#[inline]
20+
fn flush_byte(&mut self) -> io::Result<()> {
21+
if self.bit > 0 {
22+
let src = [self.cur];
23+
if self.w.write(&src)? != 1 {
24+
return Err(io::Error::from(io::ErrorKind::WriteZero));
25+
}
26+
self.cur = 0;
27+
self.bit = 0;
28+
}
29+
Ok(())
30+
}
31+
32+
pub fn byte_align(&mut self) -> io::Result<()> {
33+
self.flush_byte()
34+
}
35+
36+
#[inline]
37+
pub fn write_bit(&mut self, bit: bool) -> io::Result<bool> {
38+
self.cur |= (bit as u8) << self.bit;
39+
self.bit += 1;
40+
if self.bit >= 8 {
41+
self.flush_byte()?;
42+
}
43+
Ok(bit)
44+
}
45+
46+
pub fn write_bits(&mut self, src: &[u8], len: usize) -> io::Result<()> {
47+
for bit in 0..len {
48+
self.write_bit((src[bit >> 3] & (1 << (bit & 7))) != 0)?;
49+
}
50+
Ok(())
51+
}
52+
53+
pub fn write_int(&mut self, value: u32, max: u32) -> io::Result<()> {
54+
assert!(max >= 2);
55+
56+
if value >= max {
57+
return Err(io::Error::from(io::ErrorKind::InvalidInput));
58+
}
59+
60+
let mut new_value = 0;
61+
let mut mask = 1;
62+
63+
while (new_value + mask) < max && mask != 0 {
64+
self.write_bit(value & mask != 0)?;
65+
if value & mask != 0 {
66+
new_value |= mask;
67+
}
68+
mask *= 2;
69+
}
70+
71+
Ok(())
72+
}
73+
74+
pub fn write_int_packed(&mut self, mut value: u32) -> io::Result<()> {
75+
loop {
76+
let src = [(value & 0b111_1111) as u8];
77+
value >>= 7;
78+
self.write_bit(value != 0)?;
79+
self.write_bits(&src, 7)?;
80+
if value == 0 {
81+
break;
82+
}
83+
}
84+
Ok(())
85+
}
86+
87+
pub fn write_positive_int_vector_packed(&mut self, v: (u32, u32, u32)) -> io::Result<()> {
88+
self.write_int_packed(v.0)?;
89+
self.write_int_packed(v.1)?;
90+
self.write_int_packed(v.2)
91+
}
92+
93+
pub fn write_int_vector_packed(&mut self, v: (i32, i32, i32)) -> io::Result<()> {
94+
fn map(x: i32) -> u32 {
95+
((x.abs() as u32) << 1) | (x.is_positive() as u32)
96+
}
97+
self.write_int_packed(map(v.0))?;
98+
self.write_int_packed(map(v.1))?;
99+
self.write_int_packed(map(v.2))
100+
}
101+
}
102+
103+
impl<W: Write> Write for BitWriter<W> {
104+
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
105+
self.write_bits(src, 8 * src.len())?;
106+
Ok(src.len())
107+
}
108+
109+
fn flush(&mut self) -> io::Result<()> {
110+
self.w.flush()
111+
}
112+
}

0 commit comments

Comments
 (0)