1use boytacean_common::{
2 data::{read_bytes, read_string, read_u32, write_bytes, write_string, write_u32},
3 error::Error,
4};
5use boytacean_hashing::crc32c::crc32c;
6use std::{
7 collections::HashSet,
8 convert::TryInto,
9 default,
10 hash::Hash,
11 io::{Cursor, Read, Write},
12 iter::FromIterator,
13 mem::size_of,
14};
15
16use crate::{
17 codec::Codec,
18 huffman::{decode_huffman, encode_huffman},
19 rc4::{decrypt_rc4, encrypt_rc4},
20 rle::{decode_rle, encode_rle},
21};
22
23pub const ZIPPY_MAGIC: &str = "ZIPY";
24
25pub const ZIPPY_MAGIC_UINT: u32 = 0x5a495059;
26
27pub const ZIPPY_CIPHER_TEST: &[u8; 22] = b"ZIPPY_CIPHER_SIGNATURE";
28
29#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
30pub enum ZippyFeatures {
31 Crc32,
32 EncryptedRc4,
33 Other,
34}
35
36impl From<ZippyFeatures> for &str {
37 fn from(value: ZippyFeatures) -> Self {
38 match value {
39 ZippyFeatures::Crc32 => "crc32",
40 ZippyFeatures::EncryptedRc4 => "encrypted_rc4",
41 ZippyFeatures::Other => "other",
42 }
43 }
44}
45
46impl From<&ZippyFeatures> for &str {
47 fn from(value: &ZippyFeatures) -> Self {
48 match value {
49 ZippyFeatures::Crc32 => "crc32",
50 ZippyFeatures::EncryptedRc4 => "encrypted_rc4",
51 ZippyFeatures::Other => "other",
52 }
53 }
54}
55
56impl From<u32> for ZippyFeatures {
57 fn from(value: u32) -> Self {
58 match value {
59 0 => Self::Crc32,
60 1 => Self::EncryptedRc4,
61 _ => Self::Other,
62 }
63 }
64}
65
66impl From<&str> for ZippyFeatures {
67 fn from(value: &str) -> Self {
68 match value {
69 "crc32" => Self::Crc32,
70 "encrypted_rc4" => Self::EncryptedRc4,
71 _ => Self::Other,
72 }
73 }
74}
75
76#[derive(Default)]
77pub struct Zippy {
78 name: String,
79 description: String,
80 features: HashSet<ZippyFeatures>,
81 options: ZippyOptions,
82 crc32: u32,
83 data: Vec<u8>,
84}
85
86#[derive(Clone, Debug, Eq, Hash, PartialEq)]
87pub struct ZippyOptions {
88 crc32: bool,
89 key: Option<String>,
90}
91
92impl ZippyOptions {
93 pub fn new(crc32: bool, key: Option<String>) -> Self {
94 Self { crc32, key }
95 }
96}
97
98impl default::Default for ZippyOptions {
99 fn default() -> Self {
100 Self {
101 crc32: true,
102 key: None,
103 }
104 }
105}
106
107pub struct ZippyEncodeOptions {
108 name: Option<String>,
109 description: Option<String>,
110 features: Option<Vec<ZippyFeatures>>,
111 options: Option<ZippyOptions>,
112}
113
114pub struct ZippyDecodeOptions {
115 options: Option<ZippyOptions>,
116}
117
118impl Zippy {
119 pub fn build(
120 data: &[u8],
121 name: String,
122 description: String,
123 features: Option<Vec<ZippyFeatures>>,
124 options: Option<ZippyOptions>,
125 ) -> Result<Self, Error> {
126 let features = features.unwrap_or(vec![ZippyFeatures::Crc32]);
127 let options = options.unwrap_or_default();
128 let is_crc32 = options.crc32;
129 Ok(Self {
130 name,
131 description,
132 features: HashSet::from_iter(features.iter().cloned()),
133 options,
134 crc32: if is_crc32 { crc32c(data) } else { 0xffffffff },
135 data: data.to_vec(),
136 })
137 }
138
139 pub fn encode_data(&self) -> Result<Vec<u8>, Error> {
140 let mut buffer = Cursor::new(vec![]);
141 let mut encoded = encode_huffman(&encode_rle(&self.data)?)?;
142
143 if self.has_feature(ZippyFeatures::EncryptedRc4) {
144 encrypt_rc4(&mut encoded, self.key()?)?;
145 }
146
147 write_u32(&mut buffer, ZIPPY_MAGIC_UINT)?;
148
149 Self::write_string(&mut buffer, &self.name)?;
150 Self::write_string(&mut buffer, &self.description)?;
151
152 self.write_features(&mut buffer)?;
153
154 Self::write_buffer(&mut buffer, &encoded)?;
155
156 Ok(buffer.into_inner())
157 }
158
159 pub fn decode_data(data: &[u8], options: Option<ZippyOptions>) -> Result<Zippy, Error> {
160 let options = options.unwrap_or_default();
161
162 let mut data = Cursor::new(data);
163
164 let magic = read_u32(&mut data)?;
165 if magic != ZIPPY_MAGIC_UINT {
166 return Err(Error::InvalidData);
167 }
168
169 let name = Self::read_string(&mut data)?;
170 let description = Self::read_string(&mut data)?;
171
172 let mut instance = Self {
173 name,
174 description,
175 features: HashSet::new(),
176 options,
177 crc32: 0xffffffff,
178 data: vec![],
179 };
180
181 instance.read_features(&mut data)?;
182
183 let mut buffer = Self::read_buffer(&mut data)?;
184 if instance.has_feature(ZippyFeatures::EncryptedRc4) {
185 decrypt_rc4(&mut buffer, instance.key()?)?;
186 }
187
188 let decoded = decode_rle(&decode_huffman(&buffer)?)?;
189 instance.data = decoded;
190
191 Ok(instance)
192 }
193
194 pub fn is_zippy(data: &[u8]) -> Result<bool, Error> {
195 let mut data = Cursor::new(data);
196
197 let mut buffer = [0x00; size_of::<u32>()];
198 data.read_exact(&mut buffer)?;
199 let magic = u32::from_le_bytes(buffer);
200
201 Ok(magic == ZIPPY_MAGIC_UINT)
202 }
203
204 pub fn check_crc32(&self) -> bool {
205 self.crc32 == crc32c(&self.data)
206 }
207
208 pub fn crc32(&self) -> u32 {
209 self.crc32
210 }
211
212 pub fn data(&self) -> &[u8] {
213 &self.data
214 }
215
216 pub fn has_feature(&self, feature: ZippyFeatures) -> bool {
217 self.features.contains(&feature)
218 }
219
220 #[inline(always)]
221 fn read_string<R: Read>(reader: &mut R) -> Result<String, Error> {
222 let count = read_u32(reader)?;
223 read_string(reader, count as usize)
224 }
225
226 #[inline(always)]
227 fn read_buffer<R: Read>(reader: &mut R) -> Result<Vec<u8>, Error> {
228 let count = read_u32(reader)?;
229 read_bytes(reader, count as usize)
230 }
231
232 #[inline(always)]
233 fn read_features<R: Read>(&mut self, reader: &mut R) -> Result<(), Error> {
234 let num_features = read_u32(reader)?;
235 for _ in 0..num_features {
236 let feature_str = Self::read_string(reader)?;
237 let feature = ZippyFeatures::from(feature_str.as_str());
238 match feature {
239 ZippyFeatures::Crc32 => self.read_crc32_feature(reader)?,
240 ZippyFeatures::EncryptedRc4 => self.read_rc4_feature(reader)?,
241 _ => self.read_empty_feature(reader)?,
242 };
243 self.features.insert(feature);
244 }
245 Ok(())
246 }
247
248 #[inline(always)]
249 fn read_crc32_feature<R: Read>(&mut self, reader: &mut R) -> Result<(), Error> {
250 let payload = Self::read_buffer(reader)?;
251 if payload.len() != size_of::<u32>() {
252 return Err(Error::InvalidData);
253 }
254 let payload: [u8; 4] = payload.try_into().unwrap();
255 self.crc32 = u32::from_le_bytes(payload);
256 Ok(())
257 }
258
259 #[inline(always)]
260 fn read_rc4_feature<R: Read>(&mut self, reader: &mut R) -> Result<(), Error> {
261 let mut test_data = Self::read_buffer(reader)?;
262 decrypt_rc4(&mut test_data, self.key()?)?;
263 if test_data != ZIPPY_CIPHER_TEST {
264 return Err(Error::InvalidKey);
265 }
266 Ok(())
267 }
268
269 #[inline(always)]
270 fn read_empty_feature<R: Read>(&mut self, reader: &mut R) -> Result<(), Error> {
271 Self::read_buffer(reader)?;
272 Ok(())
273 }
274
275 #[inline(always)]
276 fn write_string<W: Write>(writer: &mut W, value: &str) -> Result<(), Error> {
277 write_u32(writer, value.len() as u32)?;
278 write_string(writer, value)?;
279 Ok(())
280 }
281
282 #[inline(always)]
283 fn write_buffer<W: Write>(writer: &mut W, value: &[u8]) -> Result<(), Error> {
284 write_u32(writer, value.len() as u32)?;
285 write_bytes(writer, value)?;
286 Ok(())
287 }
288
289 #[inline(always)]
290 fn write_features<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
291 write_u32(writer, self.features.len() as u32)?;
292 for feature in &self.features {
293 match feature {
294 ZippyFeatures::Crc32 => self.write_crc32_feature(writer)?,
295 ZippyFeatures::EncryptedRc4 => self.write_rc4_feature(writer)?,
296 _ => self.write_empty_feature(writer, feature.into())?,
297 }
298 }
299 Ok(())
300 }
301
302 #[inline(always)]
303 fn write_crc32_feature<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
304 Self::write_string(writer, ZippyFeatures::Crc32.into())?;
305 write_u32(writer, size_of::<u32>() as u32)?;
306 write_u32(writer, self.crc32)?;
307 Ok(())
308 }
309
310 #[inline(always)]
311 fn write_rc4_feature<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
312 let mut test_data = ZIPPY_CIPHER_TEST.to_vec();
313 encrypt_rc4(&mut test_data, self.key()?)?;
314 Self::write_string(writer, ZippyFeatures::EncryptedRc4.into())?;
315 Self::write_buffer(writer, &test_data)?;
316 Ok(())
317 }
318
319 #[inline(always)]
320 fn write_empty_feature<W: Write>(&self, writer: &mut W, name: &str) -> Result<(), Error> {
321 Self::write_string(writer, name)?;
322 write_u32(writer, 0)?;
323 Ok(())
324 }
325
326 fn key(&self) -> Result<&[u8], Error> {
327 Ok(self
328 .options
329 .key
330 .as_ref()
331 .ok_or(Error::MissingOption(String::from("key")))?
332 .as_bytes())
333 }
334}
335
336impl Codec for Zippy {
337 type EncodeOptions = ZippyEncodeOptions;
338 type DecodeOptions = ZippyDecodeOptions;
339
340 fn encode(data: &[u8], options: &Self::EncodeOptions) -> Result<Vec<u8>, Error> {
341 Self::build(
342 data,
343 options.name.clone().unwrap_or_default(),
344 options.description.clone().unwrap_or_default(),
345 options.features.clone(),
346 options.options.clone(),
347 )?
348 .encode_data()
349 }
350
351 fn decode(data: &[u8], options: &Self::DecodeOptions) -> Result<Vec<u8>, Error> {
352 Ok(Zippy::decode_data(data, options.options.clone())?
353 .data()
354 .to_vec())
355 }
356}
357
358pub fn encode_zippy(
359 data: &[u8],
360 features: Option<Vec<ZippyFeatures>>,
361 options: Option<ZippyOptions>,
362) -> Result<Vec<u8>, Error> {
363 Zippy::encode(
364 data,
365 &ZippyEncodeOptions {
366 name: None,
367 description: None,
368 features,
369 options,
370 },
371 )
372}
373
374pub fn decode_zippy(data: &[u8], options: Option<ZippyOptions>) -> Result<Vec<u8>, Error> {
375 Zippy::decode(data, &ZippyDecodeOptions { options })
376}
377
378#[cfg(test)]
379mod tests {
380 use boytacean_common::error::Error;
381
382 use super::{decode_zippy, encode_zippy, Zippy, ZippyFeatures, ZippyOptions};
383
384 #[test]
385 fn test_zippy_build_and_encode() {
386 let data = vec![1, 2, 3, 4, 5];
387 let name = String::from("Test");
388 let description = String::from("Test description");
389
390 let zippy = Zippy::build(&data, name.clone(), description.clone(), None, None).unwrap();
391 let encoded = zippy.encode_data().unwrap();
392
393 let decoded = Zippy::decode_data(&encoded, None).unwrap();
394 assert_eq!(decoded.name, name);
395 assert_eq!(decoded.description, description);
396 assert_eq!(decoded.data, data);
397 }
398
399 #[test]
400 fn test_zippy_decode() {
401 let data = vec![1, 2, 3, 4, 5];
402 let name = String::from("Test");
403 let description = String::from("Test description");
404
405 let zippy = Zippy::build(&data, name.clone(), description.clone(), None, None).unwrap();
406 let encoded = zippy.encode_data().unwrap();
407
408 let decoded_data = decode_zippy(&encoded, None).unwrap();
409 assert_eq!(decoded_data, data);
410 }
411
412 #[test]
413 fn test_zippy_crc32() {
414 let data = vec![1, 2, 3, 4, 5];
415 let name = String::from("Test");
416 let description = String::from("Test description");
417
418 let zippy = Zippy::build(&data, name.clone(), description.clone(), None, None).unwrap();
419 let encoded = zippy.encode_data().unwrap();
420
421 let zippy = Zippy::decode_data(&encoded, None).unwrap();
422 assert!(zippy.has_feature(ZippyFeatures::Crc32));
423 assert!(zippy.check_crc32());
424 assert_eq!(zippy.crc32(), 0x53518fab);
425 }
426
427 #[test]
428 fn test_zippy_no_crc32() {
429 let data = vec![1, 2, 3, 4, 5];
430 let name = String::from("Test");
431 let description = String::from("Test description");
432
433 let zippy = Zippy::build(
434 &data,
435 name.clone(),
436 description.clone(),
437 None,
438 Some(ZippyOptions::new(false, None)),
439 )
440 .unwrap();
441 let encoded = zippy.encode_data().unwrap();
442
443 let zippy = Zippy::decode_data(&encoded, None).unwrap();
444 assert!(zippy.has_feature(ZippyFeatures::Crc32));
445 assert!(!zippy.check_crc32());
446 assert_eq!(zippy.crc32(), 0xffffffff);
447 }
448
449 #[test]
450 fn test_zippy_decode_invalid() {
451 let decoded_data = decode_zippy(b"invalid", None);
452 assert!(decoded_data.is_err());
453 assert_eq!(decoded_data.unwrap_err(), Error::InvalidData);
454 }
455
456 #[test]
457 fn test_zippy_dummy_feature() {
458 let data = vec![1, 2, 3, 4, 5];
459 let name = String::from("Test");
460 let description = String::from("Test description");
461
462 let zippy = Zippy::build(
463 &data,
464 name.clone(),
465 description.clone(),
466 Some(vec![ZippyFeatures::Other]),
467 Some(ZippyOptions::new(false, None)),
468 )
469 .unwrap();
470 let encoded = zippy.encode_data().unwrap();
471
472 let zippy = Zippy::decode_data(&encoded, None).unwrap();
473 assert!(zippy.has_feature(ZippyFeatures::Other));
474 assert!(!zippy.has_feature(ZippyFeatures::Crc32));
475 assert!(!zippy.check_crc32());
476 assert_eq!(zippy.crc32(), 0xffffffff);
477 }
478
479 #[test]
480 fn test_zippy_encrypted() {
481 let encoded = encode_zippy(
482 b"test",
483 Some(vec![ZippyFeatures::EncryptedRc4]),
484 Some(ZippyOptions::new(false, Some(String::from("key")))),
485 )
486 .unwrap();
487 let decoded = decode_zippy(
488 &encoded,
489 Some(ZippyOptions::new(false, Some(String::from("key")))),
490 )
491 .unwrap();
492 assert_eq!(decoded, b"test");
493 }
494
495 #[test]
496 fn test_zippy_wrong_key() {
497 let encoded = encode_zippy(
498 b"test",
499 Some(vec![ZippyFeatures::EncryptedRc4]),
500 Some(ZippyOptions::new(false, Some(String::from("key")))),
501 )
502 .unwrap();
503 let decoded = decode_zippy(
504 &encoded,
505 Some(ZippyOptions::new(false, Some(String::from("wrong_key")))),
506 );
507 assert!(decoded.is_err());
508 assert_eq!(decoded.unwrap_err(), Error::InvalidKey);
509 }
510
511 #[test]
512 fn test_zippy_no_key() {
513 let encoded = encode_zippy(
514 b"test",
515 Some(vec![ZippyFeatures::EncryptedRc4]),
516 Some(ZippyOptions::new(false, Some(String::from("key")))),
517 )
518 .unwrap();
519 let decoded = decode_zippy(&encoded, Some(ZippyOptions::new(false, None)));
520 assert!(decoded.is_err());
521 assert_eq!(
522 decoded.unwrap_err(),
523 Error::MissingOption(String::from("key"))
524 );
525 }
526}