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
use std::io::Read;
use std::fs::File;
use std::fs;
use byteorder::LittleEndian;
use byteorder::WriteBytesExt;
use std::io;
use std::io::Write;
use std::path::Path;
pub struct PackedFile {
relative_path: String,
data: Vec<u8>
}
impl From<io::Error> for ::BuildPackError {
fn from(_: io::Error) -> Self {
::BuildPackError::IOError
}
}
pub fn build_pack(input_directory: &Path, output_file: &mut File, version: u32, bitmask: u32) -> Result<(), ::BuildPackError> {
if version != 4 && version != 5 {
return Err(::BuildPackError::UnsupportedPFHVersionError)
}
let (_content_size, index_size, files) = traverse_directory(input_directory, "".to_string())?;
if files.len() < 1 {
return Err(::BuildPackError::EmptyInputError)
}
write_header(output_file, version, bitmask, index_size+4, &files)?;
write_index(output_file, &files)?;
write_content(output_file, &files)?;
Ok(())
}
fn traverse_directory(directory: &Path, prefix: String) -> Result<(u32, u32, Vec<PackedFile>), ::BuildPackError> {
let mut files = vec!();
let mut content_size = 0;
let mut index_size = 0;
for entry in fs::read_dir(directory)? {
let entry = entry?;
let path = entry.path();
let metadata = fs::metadata(&path)?;
let relative_path = if prefix.len() > 0 {
prefix.clone() + &"\\".to_string() + &entry.file_name().into_string().unwrap()
} else {
entry.file_name().into_string().unwrap()
};
if metadata.is_dir() {
let (child_content_size, child_index_size, child_files) = traverse_directory(&path, relative_path)?;
content_size += child_content_size;
index_size += child_index_size;
files.extend(child_files)
} else if metadata.is_file() {
let mut file = File::open(path)?;
let mut buf = vec!();
file.read_to_end(&mut buf)?;
content_size += buf.len() as u32;
index_size += relative_path.len() as u32 + 1;
files.push(PackedFile {
relative_path: relative_path,
data: buf
})
}
}
Ok((content_size, index_size, files))
}
fn write_header(output_file: &mut File, version: u32, bitmask: u32, index_size: u32, files: &Vec<PackedFile>) -> Result<(), ::BuildPackError> {
if version == 4 {
output_file.write_u32::<LittleEndian>(::PFH4_PREAMBLE)?;
output_file.write_u32::<LittleEndian>(bitmask)?;
output_file.write_u32::<LittleEndian>(0)?;
output_file.write_u32::<LittleEndian>(0)?;
output_file.write_u32::<LittleEndian>(files.len() as u32)?;
output_file.write_u32::<LittleEndian>(index_size as u32)?;
output_file.write_u32::<LittleEndian>(0)?;
} else if version == 5 {
output_file.write_u32::<LittleEndian>(::PFH5_PREAMBLE)?;
output_file.write_u32::<LittleEndian>(bitmask)?;
output_file.write_u32::<LittleEndian>(0)?;
output_file.write_u32::<LittleEndian>(0)?;
output_file.write_u32::<LittleEndian>(files.len() as u32)?;
output_file.write_u32::<LittleEndian>(index_size as u32)?;
output_file.write_u32::<LittleEndian>(0)?;
output_file.write_u32::<LittleEndian>(0)?;
output_file.write_u32::<LittleEndian>(0)?;
output_file.write_u32::<LittleEndian>(0)?;
output_file.write_u32::<LittleEndian>(0)?;
output_file.write_u32::<LittleEndian>(0)?;
} else {
panic!();
}
Ok(())
}
fn write_index(output_file: &mut File, files: &Vec<PackedFile>) -> Result<(), ::BuildPackError> {
for file in files {
output_file.write_u32::<LittleEndian>(file.data.len() as u32)?;
output_file.write_all(file.relative_path.as_ref())?;
output_file.write_u8(0)?;
}
Ok(())
}
fn write_content(output_file: &mut File, files: &Vec<PackedFile>) -> Result<(), ::BuildPackError> {
for file in files {
output_file.write_all(&file.data)?;
}
Ok(())
}