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
use crate::errors::*;
use crate::{CalcCrcChecksum, Entry, GetStoredSize, Header, ReadFrom, FILE_MAGIC};
use byteorder::{LittleEndian, ReadBytesExt};

use std::fs::File;
use std::io;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;

pub struct ArchiveReader {
    file: File,
    pub header: Header,
}

impl ArchiveReader {
    pub fn new<P: AsRef<Path>>(archive: P) -> Result<Self> {
        let mut file = File::open(archive)?;

        let header = Header::read_from(&mut file)?;
        if &header.magic_number != FILE_MAGIC {
            return Err(Error::InvalidFileType);
        }
        Ok(Self { file, header })
    }

    pub fn entries(&self) -> Entries {
        Entries::new(self)
    }

    /// `offset`: offset to `content_offset`
    /// absolute offset = `offset` + `content_offset`
    pub fn retrieve_content(&mut self, offset: u64, size: u64) -> ContentReader {
        ContentReader::new(&mut self.file, self.header.content_offset, offset, size)
    }
}

pub struct ContentReader<'a> {
    file: &'a mut File,
    left_size: u64,
    position: u64,
}

impl<'a> Read for ContentReader<'a> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if self.file.stream_position()? != self.position {
            self.file.seek(SeekFrom::Start(self.position))?;
        }
        let read_size = self.file.take(self.left_size).read(buf)?;
        self.left_size -= read_size as u64;
        self.position += read_size as u64;

        Ok(read_size)
    }
}

impl<'a> ContentReader<'a> {
    fn new(file: &'a mut File, content_offset: u64, offset: u64, size: u64) -> Self {
        Self {
            file,
            left_size: size,
            position: content_offset + offset,
        }
    }
}

pub struct Entries {
    position: u64,
    count: u64,
    total_count: u64,
    file: File,
}

impl Entries {
    fn new(outer: &ArchiveReader) -> Self {
        let entries_start_pos = outer.header.stored_size() as u64;
        let file = outer.file.try_clone().unwrap();

        Self {
            position: entries_start_pos,
            count: 0,
            total_count: outer.header.entry_count,
            file,
        }
    }
}

impl Iterator for Entries {
    type Item = Result<Entry>;

    fn next(&mut self) -> Option<Self::Item> {
        fn try_next(s: &mut Entries) -> Result<Entry> {
            if s.file.stream_position()? != s.position {
                s.file.seek(SeekFrom::Start(s.position))?;
            }
            let result = Entry::read_from(&mut s.file);
            let checksum = s.file.read_u32::<LittleEndian>()?;
            s.position = s.file.stream_position()?;

            let entry = result?;
            if entry.crc_checksum() != checksum {
                return Err(Error::Checksum(entry));
            }

            Ok(entry)
        }

        if self.count == self.total_count {
            return None;
        }

        let result = try_next(self);
        self.count += 1;
        Some(result)
    }
}