1use crate::FileWithSource;
2use acr::{
3 encryption::aes256cbc,
4 hash::{murmur3, sha256},
5};
6use dh::{recommended::*, Readable, Rw, Writable};
7use std::io::Result;
8
9pub fn create<'a>(
10 version: u8,
11 sources: Vec<FileWithSource<'a>>,
12 encryption: Option<(&str, &[u8; 16])>,
13 main_file: Option<u32>,
14 target: &'a mut dyn Rw<'a>,
15 buffer_size: u64,
16) -> Result<(u64, u32)> {
17 let encrypted = encryption.is_some();
18 let key = if encrypted {
19 sha256(
20 &mut dh::data::read_ref(encryption.unwrap().0.as_bytes()),
21 0,
22 encryption.unwrap().0.len() as u64,
23 )?
24 } else {
25 [0; 32]
26 };
27 let key_hash = if encrypted {
28 sha256(&mut dh::data::read_ref(&key), 0, 32)?
29 } else {
30 [0; 32]
31 };
32 let iv = if encrypted {
33 encryption.unwrap().1
34 } else {
35 &[0; 16]
36 };
37
38 target.write_bytes(if version == 1 { b"SFA\0" } else { b"HSSP" })?;
39 let hash_pos = target.pos()?;
40 target.write_u32le(0)?;
41 target.write_u32le(sources.len() as u32)?;
42 if encrypted {
43 target.write_bytes(&key_hash)?;
44 target.write_bytes(iv)?;
45 } else {
46 target.write_bytes(&[0; 48])?;
47 }
48 if main_file.is_some() {
49 let main_file = main_file.unwrap();
50 target.write_u32le(if main_file == (main_file % 4294967295) {
51 main_file + 1
52 } else {
53 0
54 })?;
55 } else {
56 target.write_u32le(0)?;
57 }
58
59 if version > 2 {
60 target.write_bytes(&[0; 64])?;
61 }
62
63 let body_pos = target.pos()?;
64
65 if encrypted {
66 let mut body = dh::data::rw_empty();
67 for source in sources {
68 let file = source.0;
69 let reader = source.1;
70 let path = if file.directory {
71 &(("//").to_string() + &file.path)
72 } else {
73 &file.path
74 };
75
76 body.write_u64le(file.length)?;
77 body.write_u16le(path.len() as u16)?;
78 body.write_utf8(path)?;
79 reader.copy_at(file.offset, file.length, &mut body, buffer_size)?;
80 body.write_bytes(&vec![0; path.len()])?;
81 }
82
83 let body_size = body.size()?;
84
85 let cipher = aes256cbc::encrypt(&mut body, &key, iv, 0, body_size)?;
86 target.write_bytes(&cipher)?;
87 } else {
88 for source in sources {
89 let file = source.0;
90 let reader = source.1;
91 let path = if file.directory {
92 &(("//").to_string() + &file.path)
93 } else {
94 &file.path
95 };
96
97 target.write_u64le(file.length)?;
98 target.write_u16le(path.len() as u16)?;
99 target.write_utf8(path)?;
100 reader.copy_at(
101 file.offset,
102 file.length,
103 Writable::as_trait(target),
104 buffer_size,
105 )?;
106 target.write_bytes(&vec![0; path.len()])?;
107 }
108 }
109
110 let body_size = target.pos()? - body_pos;
111
112 let hash = murmur3(Readable::as_trait(target), body_pos, body_size, 0x31082007)?;
113
114 Ok((hash_pos, hash))
115}
116
117pub fn write_hash(target: &mut dyn Writable, create_result: (u64, u32)) -> Result<()> {
119 target.write_u32le_at(create_result.0, create_result.1)
120}