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
use byteorder::{LittleEndian, WriteBytesExt};
use failure::{Error, ResultExt};

use crate::model::owned::OwnedBuf;

#[derive(Default, Debug)]
pub struct Arsc {
    chunks: Vec<Box<dyn OwnedBuf>>,
}

impl Arsc {
    pub fn push_owned(&mut self, chunk: Box<dyn OwnedBuf>) {
        self.chunks.push(chunk);
    }

    pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
        let mut out = Vec::new();
        let mut inner = Vec::new();
        let mut file_size = 0;

        for c in &self.chunks {
            let encoded_chunk = c.to_vec().context("could not encode a chunk")?;
            file_size += encoded_chunk.len();

            inner.extend(encoded_chunk);
        }

        // Token
        out.write_u16::<LittleEndian>(2)?;

        // Header_size
        out.write_u16::<LittleEndian>(3 * 4)?;

        // Chunk size
        out.write_u32::<LittleEndian>(file_size as u32)?;

        // TODO: Review this value
        // Package amount
        out.write_u32::<LittleEndian>(0)?;

        out.extend(inner);

        Ok(out)
    }
}

#[derive(Default, Debug)]
pub struct Xml {
    chunks: Vec<Box<dyn OwnedBuf>>,
}

impl Xml {
    pub fn push_owned(&mut self, chunk: Box<dyn OwnedBuf>) {
        self.chunks.push(chunk);
    }

    pub fn into_vec(self) -> Result<Vec<u8>, Error> {
        let mut out = Vec::new();
        let mut inner = Vec::new();
        let mut file_size = 0;

        for c in self.chunks {
            let encoded_chunk = c.to_vec().context("could not encode a chunk")?;
            file_size += encoded_chunk.len();

            inner.extend(encoded_chunk);
        }

        // Token
        out.write_u16::<LittleEndian>(3)?;

        // Header_size
        out.write_u16::<LittleEndian>(2 * 4)?;

        // Chunk size
        out.write_u32::<LittleEndian>(file_size as u32)?;

        out.extend(inner);

        Ok(out)
    }
}

#[cfg(test)]
mod tests {
    use std::io::Cursor;

    use super::{Arsc, Xml};
    use crate::{
        model::owned::{ResourcesBuf, StringTableBuf},
        test::CounterChunkVisitor,
        visitor::Executor,
    };

    #[test]
    fn it_can_generate_a_resources_arsc_file_content() {
        let arsc = Arsc::default();
        let content = arsc.to_vec().unwrap();
        let mut visitor = CounterChunkVisitor::default();

        assert_eq!(vec![2, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0], content);

        Executor::arsc(&content, &mut visitor).unwrap();

        assert_eq!(0, visitor.get_count());
    }

    #[test]
    fn it_can_generate_a_resources_arsc_file_content_with_some_chunks() {
        let mut arsc = Arsc::default();

        arsc.push_owned(Box::new(StringTableBuf::default()));
        arsc.push_owned(Box::new(StringTableBuf::default()));
        arsc.push_owned(Box::new(ResourcesBuf::default()));

        let content = arsc.to_vec().unwrap();
        let mut visitor = CounterChunkVisitor::default();

        Executor::arsc(&content, &mut visitor).unwrap();

        // Resource should be ignored as it is not a chunk that appears on an ARSC
        assert_eq!(2, visitor.get_count());
    }

    #[test]
    fn it_can_generate_a_resources_xml_file_content() {
        let xml = Xml::default();
        let content = xml.into_vec().unwrap();
        let mut visitor = CounterChunkVisitor::default();

        assert_eq!(vec![3, 0, 8, 0, 0, 0, 0, 0], content);

        Executor::xml(Cursor::new(&content), &mut visitor).unwrap();

        assert_eq!(0, visitor.get_count());
    }

    #[test]
    fn it_can_generate_a_resources_xml_file_content_with_some_chunks() {
        let mut xml = Xml::default();

        xml.push_owned(Box::new(StringTableBuf::default()));
        xml.push_owned(Box::new(StringTableBuf::default()));
        xml.push_owned(Box::new(ResourcesBuf::default()));

        let content = xml.into_vec().unwrap();
        let mut visitor = CounterChunkVisitor::default();

        let _ = Executor::xml(Cursor::new(&content), &mut visitor);

        assert_eq!(3, visitor.get_count());
    }
}