boytacean_encoding/
zippy.rs

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}