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