1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
////////////////////////////////////////////////////////////////////////////////
// This Source Code Form is subject to the terms of the Mozilla Public /
// License, v. 2.0. If a copy of the MPL was not distributed with this /
// file, You can obtain one at https://mozilla.org/MPL/2.0/. /
// /
////////////////////////////////////////////////////////////////////////////////
//! A very overengineered rust crate for compressing and decompressing data in
//! the RefPack format utilized by many EA games of the early 2000s
//!
//! # RefPack
//! RefPack, also known as QFS, is a semi-standardized compression format
//! utilized by many games published by Electronic Arts from the 90s to the late
//! 2000s. In many cases, it was deployed with a custom header format.
//!
//! ## Structure
//! RefPack shares many similarities with lz77 compression; it is a lossless
//! compression format which relies on length-distance pairs to existing bytes
//! within the decompression buffer. Where it differs from lz77 is that rather
//! than a single format for "Literal" control codes and "Pointer" control
//! codes, RefPack uses 4 distinct control codes for different sizes of pointers
//! and literal blocks. A fifth control code is also present to indicate end of
//! stream rather than requiring a size to be specified before decompression.
//!
//! ### Codes
//! RefPack utilizes one "Literal" bytes-only control code similar to lz77, but
//! with limited precision to multiples of 4. The remaining three control codes
//! are varying sizes of "Pointer" control codes, for small, medium, and large
//! back-references and lengths. The limited precision of the "Literal" control
//! code is compensated for via "Pointer" control codes also having the ability
//! to write up to 3 literal bytes to the stream
//!
//! See [Command](crate::data::control::Command) for further details.
//!
//! ## Decompression
//! Decompression simply requires reading from a stream of `RefPack` data until
//! a stopcode is reached.
//!
//! See [decompression](crate::data::decompression) for further details
//!
//!
//! ## Compression
//! Compressing via RefPack is largely similar to lz77 compression algorithms,
//! and involves a sliding window over the data to search for repeating blocks,
//! and then writing to the stream as the previously specified codes.
//!
//! See [compression](crate::data::compression) for further details
//!
//! ## Headers
//! While the actual data block of RefPack has only one known implementation,
//! multiple types of headers for the library have been identified.
//!
//! ## Other Implementations
//!
//! RefPack has been implemented in various other languages and for various
//! games:
//!
//! - [RefPack.cpp (download)](http://download.wcnews.com/files/documents/sourcecode/shadowforce/transfer/asommers/mfcapp_src/engine/compress/RefPack.cpp):
//! Original canonical implementation of RefPack by Frank Barchard for Origin
//! Software. Utilized by some early Origin Software games.
//! - [JDBPF](https://github.com/actioninja/JDBPF/blob/90644a3286580aa7676779a2d2e5a3c9de9a31ff/src/ssp/dbpf/converter/DBPFPackager.java#L398C9-L398C9):
//! Early Simcity 4 Java Library for reading DBPF files which utilize RefPack
//! - [JDBPFX](https://github.com/actioninja/JDBPF/blob/90644a3286580aa7676779a2d2e5a3c9de9a31ff/src/ssp/dbpf/converter/DBPFPackager.java#L398C9-L398C9):
//! Later currently maintained fork of JDBPF
//! - [DBPFSharp](https://github.com/0xC0000054/DBPFSharp/blob/3038b9c15b0ddd3ccfb4b72bc6ac4541eee677fb/src/DBPFSharp/QfsCompression.cs#L100):
//! Simcity 4 DBPF Library written in C#
//! - [Sims2Tools](https://github.com/whoward69/Sims2Tools/blob/0baaf2dce985474215cf0f64096a8dd9950c2757/DbpfLibrary/Utils/Decompressor.cs#L54C1-L54C1):
//! Sims 2 DBPF Library written in C#
//!
//!
//! # This Crate
//!
//! This crate is a rust implementation designed to compress and decompress
//! refpack data with any header format. It uses generics to support arbitrary
//! header formats to allow pure usage of this library without having to write
//! "glue" code to parse header info.
//!
//! Put simply, this means that you get the benefit of being able to use any
//! format however you like without any performance overhead from dynamic
//! dispatch, as well as being able to implement your own arbitrary formats that
//! are still compatible with the same compression algorithms.
//!
//! # Usage
//!
//! `refpack-rs` exposes two functions: `compress` and `decompress`, along with
//! `easy` variants with easier but less flexible of usage.
//!
//! `compress` and `decompress` take mutable references to a buffer to read and
//! write from, that implements `std::io::Read` and `std::io::Write`,
//! respectively.
//!
//! `decompress` will read from the buffer until it encounters a stopcode (byte
//! within (0xFC..=0xFF)), while `compress` will read in the provided length.
//!
//! all compression and decompression functions accept one generic argument
//! constrained to the [Format](crate::format::Format) trait. Implementations
//! be "unconstructable" types, with the recommended type being an empty enum.
//!
//! ## Implementations
//!
//! | Format | Games | Header |
//! |--------|-------|--------|
//! | [Reference](crate::format::Reference) | Various 90s Origin Software and EA games | [Reference](crate::header::Reference) |
//! | [Maxis](crate::format::Maxis) | The Sims, The Sims Online, Simcity 4, The Sims 2 | [Maxis](crate::header::Maxis) |
//! | [SimEA](crate::format::SimEA) | The Sims 3, The Sims 4 | [SimEA](crate::header::SimEA) |
//!
//!
//! ### Example
//!
//! ```
//! use std::io::{Cursor, Seek};
//!
//! use refpack::format::Reference;
//!
//! # fn main() {
//! let mut source_reader = Cursor::new(b"Hello World!".to_vec());
//! let mut out_buf = Cursor::new(vec![]);
//! refpack::compress::<Reference>(
//! source_reader.get_ref().len(),
//! &mut source_reader,
//! &mut out_buf,
//! refpack::CompressionOptions::Optimal,
//! )
//! .unwrap();
//! # }
//! ```
//!
//! The easy variants are `compress_easy` and `decompress_easy`, which take a
//! `&[u8]` and return a `Result<Vec<u8>, RefPackError>`.
//!
//! Internally they simply call `compress` and `decompress` with a `Cursor` to
//! the input and output buffers, however they are more convenient to use in
//! many cases.
// I like clippy to yell at me about everything!
// Due to the high amount of byte conversions, sometimes intentional lossy conversions are
// necessary.
// same as above
// and above
// above
// Annoying and wrong, RefPack is a compression scheme.
// Default::default() is more idiomatic imo
// too many lines is a dumb metric
// causes weirdness with header and reader
// all uses of #[inline(always)] have been benchmarked thoroughly
pub use crate;
pub use crate;
pub use crate;