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
use std::fs::{create_dir_all, File};
use std::io::Error;
use std::io::ErrorKind::InvalidData;
use std::io::Write;
use std::path::Path;

use super::ggpk::GGPK;

pub struct GGPKFile<'a> {
    pub ggpk: &'a GGPK,
    pub record: FileRecord,
}

impl GGPKFile<'_> {
    pub fn write_file(&self, path: &str) -> Result<usize, Error> {
        let record = &self.record;
        self.ggpk
            .mmap
            .get(record.begin..(record.begin + record.bytes as usize))
            .map(|bytes| {
                Path::new(path).parent().map(|path| create_dir_all(path));
                File::create(path).and_then(|mut file| file.write(bytes))
            })
            .unwrap_or_else(|| Err(Error::new(InvalidData, "Read outside GGPK")))
    }

    pub fn write_into(&self, dst: &mut impl Write) -> Result<usize, Error> {
        let record = &self.record;
        self.ggpk
            .mmap
            .get(record.begin..(record.begin + record.bytes as usize))
            .map(|bytes| dst.write(bytes))
            .unwrap_or_else(|| Err(Error::new(InvalidData, "Read outside GGPK")))
    }

    pub fn bytes(&self) -> &[u8] {
        let record = &self.record;
        self.ggpk
            .mmap
            .get(record.begin..(record.begin + record.bytes as usize))
            .unwrap()
    }
}

pub struct FileRecord {
    pub name: String,
    pub path: String,
    pub signature: [u8; 32],
    pub begin: usize,
    pub bytes: u32,
}

impl FileRecord {
    pub fn absolute_path(&self) -> String {
        format!("{}/{}", self.path, self.name)
    }

    pub fn clone(&self) -> FileRecord {
        FileRecord {
            name: self.name.clone(),
            path: self.path.clone(),
            signature: self.signature,
            begin: self.begin,
            bytes: self.bytes,
        }
    }
}