roxify 1.12.0 → 1.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +81 -55
- package/dist/roxify_native +0 -0
- package/dist/roxify_native.exe +0 -0
- package/dist/stub-progress.d.ts +10 -5
- package/dist/stub-progress.js +20 -7
- package/dist/utils/rust-cli-wrapper.d.ts +4 -1
- package/dist/utils/rust-cli-wrapper.js +69 -3
- package/package.json +5 -3
- package/roxify_native-x86_64-pc-windows-msvc.node +0 -0
- package/Cargo.toml +0 -98
- package/native/archive.rs +0 -220
- package/native/audio.rs +0 -151
- package/native/bench_hybrid.rs +0 -145
- package/native/bwt.rs +0 -56
- package/native/context_mixing.rs +0 -117
- package/native/core.rs +0 -382
- package/native/crypto.rs +0 -204
- package/native/encoder.rs +0 -690
- package/native/gpu.rs +0 -116
- package/native/hybrid.rs +0 -287
- package/native/image_utils.rs +0 -82
- package/native/lib.rs +0 -489
- package/native/main.rs +0 -534
- package/native/mtf.rs +0 -106
- package/native/packer.rs +0 -447
- package/native/png_utils.rs +0 -538
- package/native/pool.rs +0 -101
- package/native/progress.rs +0 -43
- package/native/rans.rs +0 -149
- package/native/rans_byte.rs +0 -286
- package/native/reconstitution.rs +0 -623
- package/native/streaming.rs +0 -214
- package/native/streaming_decode.rs +0 -338
- package/native/streaming_encode.rs +0 -494
- package/native/test_small_bwt.rs +0 -31
- package/native/test_stages.rs +0 -70
package/native/streaming.rs
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
use std::io::{Write, BufWriter};
|
|
2
|
-
use std::fs::File;
|
|
3
|
-
use std::path::Path;
|
|
4
|
-
|
|
5
|
-
const PNG_HEADER: &[u8] = &[137, 80, 78, 71, 13, 10, 26, 10];
|
|
6
|
-
const PIXEL_MAGIC: &[u8] = b"PXL1";
|
|
7
|
-
const MARKER_START: [(u8, u8, u8); 3] = [(255, 0, 0), (0, 255, 0), (0, 0, 255)];
|
|
8
|
-
const MARKER_END: [(u8, u8, u8); 3] = [(0, 0, 255), (0, 255, 0), (255, 0, 0)];
|
|
9
|
-
const MARKER_ZSTD: (u8, u8, u8) = (0, 255, 0);
|
|
10
|
-
const MAGIC: &[u8] = b"ROX1";
|
|
11
|
-
|
|
12
|
-
pub fn encode_to_png_file(
|
|
13
|
-
data: &[u8],
|
|
14
|
-
output_path: &Path,
|
|
15
|
-
compression_level: i32,
|
|
16
|
-
passphrase: Option<&str>,
|
|
17
|
-
encrypt_type: Option<&str>,
|
|
18
|
-
name: Option<&str>,
|
|
19
|
-
file_list: Option<&str>,
|
|
20
|
-
dict: Option<&[u8]>,
|
|
21
|
-
) -> anyhow::Result<()> {
|
|
22
|
-
let compressed = crate::core::zstd_compress_with_prefix(data, compression_level, dict, MAGIC)
|
|
23
|
-
.map_err(|e| anyhow::anyhow!("Compression failed: {}", e))?;
|
|
24
|
-
|
|
25
|
-
let encrypted = if let Some(pass) = passphrase {
|
|
26
|
-
match encrypt_type.unwrap_or("aes") {
|
|
27
|
-
"xor" => crate::crypto::encrypt_xor(&compressed, pass),
|
|
28
|
-
"aes" => crate::crypto::encrypt_aes(&compressed, pass)?,
|
|
29
|
-
_ => crate::crypto::encrypt_aes(&compressed, pass)?,
|
|
30
|
-
}
|
|
31
|
-
} else {
|
|
32
|
-
crate::crypto::no_encryption(&compressed)
|
|
33
|
-
};
|
|
34
|
-
drop(compressed);
|
|
35
|
-
|
|
36
|
-
let meta_pixel = build_meta_pixel(&encrypted, name, file_list)?;
|
|
37
|
-
drop(encrypted);
|
|
38
|
-
|
|
39
|
-
let raw_payload_len = PIXEL_MAGIC.len() + meta_pixel.len();
|
|
40
|
-
let padding_needed = (3 - (raw_payload_len % 3)) % 3;
|
|
41
|
-
let padded_len = raw_payload_len + padding_needed;
|
|
42
|
-
|
|
43
|
-
let marker_start_len = 12;
|
|
44
|
-
let data_with_markers_len = marker_start_len + padded_len;
|
|
45
|
-
let data_pixels = (data_with_markers_len + 2) / 3;
|
|
46
|
-
let end_marker_pixels = 3;
|
|
47
|
-
let total_pixels = data_pixels + end_marker_pixels;
|
|
48
|
-
|
|
49
|
-
let side = (total_pixels as f64).sqrt().ceil() as usize;
|
|
50
|
-
let side = side.max(end_marker_pixels);
|
|
51
|
-
let width = side;
|
|
52
|
-
let height = side;
|
|
53
|
-
let row_bytes = width * 3;
|
|
54
|
-
let total_data_bytes = width * height * 3;
|
|
55
|
-
let marker_end_pos = (height - 1) * width * 3 + (width - end_marker_pixels) * 3;
|
|
56
|
-
|
|
57
|
-
let flat = build_flat_buffer(&meta_pixel, padding_needed, marker_end_pos, total_data_bytes);
|
|
58
|
-
drop(meta_pixel);
|
|
59
|
-
|
|
60
|
-
let stride = row_bytes + 1;
|
|
61
|
-
let scanlines_total = height * stride;
|
|
62
|
-
|
|
63
|
-
let mut scanlines = vec![0u8; scanlines_total];
|
|
64
|
-
for row in 0..height {
|
|
65
|
-
let flat_start = row * row_bytes;
|
|
66
|
-
let flat_end = (flat_start + row_bytes).min(flat.len());
|
|
67
|
-
let copy_len = flat_end.saturating_sub(flat_start);
|
|
68
|
-
if copy_len > 0 {
|
|
69
|
-
let dst = row * stride + 1;
|
|
70
|
-
scanlines[dst..dst + copy_len].copy_from_slice(&flat[flat_start..flat_end]);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
drop(flat);
|
|
74
|
-
|
|
75
|
-
let adler = crate::core::adler32_bytes(&scanlines);
|
|
76
|
-
|
|
77
|
-
const MAX_BLOCK: usize = 65535;
|
|
78
|
-
let num_blocks = (scanlines_total + MAX_BLOCK - 1) / MAX_BLOCK;
|
|
79
|
-
let idat_len = 2 + num_blocks * 5 + scanlines_total + 4;
|
|
80
|
-
|
|
81
|
-
let f = File::create(output_path)?;
|
|
82
|
-
let mut w = BufWriter::with_capacity(16 * 1024 * 1024, f);
|
|
83
|
-
|
|
84
|
-
w.write_all(PNG_HEADER)?;
|
|
85
|
-
|
|
86
|
-
let mut ihdr = [0u8; 13];
|
|
87
|
-
ihdr[0..4].copy_from_slice(&(width as u32).to_be_bytes());
|
|
88
|
-
ihdr[4..8].copy_from_slice(&(height as u32).to_be_bytes());
|
|
89
|
-
ihdr[8] = 8;
|
|
90
|
-
ihdr[9] = 2;
|
|
91
|
-
write_chunk_small(&mut w, b"IHDR", &ihdr)?;
|
|
92
|
-
|
|
93
|
-
write_idat_direct(&mut w, &scanlines, idat_len, adler)?;
|
|
94
|
-
drop(scanlines);
|
|
95
|
-
|
|
96
|
-
if let Some(fl) = file_list {
|
|
97
|
-
write_chunk_small(&mut w, b"rXFL", fl.as_bytes())?;
|
|
98
|
-
}
|
|
99
|
-
write_chunk_small(&mut w, b"IEND", &[])?;
|
|
100
|
-
w.flush()?;
|
|
101
|
-
|
|
102
|
-
Ok(())
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
fn write_idat_direct<W: Write>(
|
|
106
|
-
w: &mut W,
|
|
107
|
-
scanlines: &[u8],
|
|
108
|
-
idat_len: usize,
|
|
109
|
-
adler: u32,
|
|
110
|
-
) -> anyhow::Result<()> {
|
|
111
|
-
const MAX_BLOCK: usize = 65535;
|
|
112
|
-
|
|
113
|
-
w.write_all(&(idat_len as u32).to_be_bytes())?;
|
|
114
|
-
w.write_all(b"IDAT")?;
|
|
115
|
-
|
|
116
|
-
let mut crc = crc32fast::Hasher::new();
|
|
117
|
-
crc.update(b"IDAT");
|
|
118
|
-
|
|
119
|
-
let zlib = [0x78u8, 0x01];
|
|
120
|
-
w.write_all(&zlib)?;
|
|
121
|
-
crc.update(&zlib);
|
|
122
|
-
|
|
123
|
-
let mut offset = 0;
|
|
124
|
-
while offset < scanlines.len() {
|
|
125
|
-
let chunk_size = (scanlines.len() - offset).min(MAX_BLOCK);
|
|
126
|
-
let is_last = offset + chunk_size >= scanlines.len();
|
|
127
|
-
let header = [
|
|
128
|
-
if is_last { 0x01 } else { 0x00 },
|
|
129
|
-
chunk_size as u8,
|
|
130
|
-
(chunk_size >> 8) as u8,
|
|
131
|
-
!chunk_size as u8,
|
|
132
|
-
(!(chunk_size >> 8)) as u8,
|
|
133
|
-
];
|
|
134
|
-
w.write_all(&header)?;
|
|
135
|
-
crc.update(&header);
|
|
136
|
-
let slice = &scanlines[offset..offset + chunk_size];
|
|
137
|
-
w.write_all(slice)?;
|
|
138
|
-
crc.update(slice);
|
|
139
|
-
offset += chunk_size;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
let adler_bytes = adler.to_be_bytes();
|
|
143
|
-
w.write_all(&adler_bytes)?;
|
|
144
|
-
crc.update(&adler_bytes);
|
|
145
|
-
|
|
146
|
-
w.write_all(&crc.finalize().to_be_bytes())?;
|
|
147
|
-
Ok(())
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
fn build_flat_buffer(
|
|
151
|
-
meta_pixel: &[u8],
|
|
152
|
-
_padding_needed: usize,
|
|
153
|
-
marker_end_pos: usize,
|
|
154
|
-
total_data_bytes: usize,
|
|
155
|
-
) -> Vec<u8> {
|
|
156
|
-
let mut flat = vec![0u8; total_data_bytes];
|
|
157
|
-
|
|
158
|
-
let mut pos = 0;
|
|
159
|
-
for m in &MARKER_START {
|
|
160
|
-
flat[pos] = m.0; flat[pos + 1] = m.1; flat[pos + 2] = m.2;
|
|
161
|
-
pos += 3;
|
|
162
|
-
}
|
|
163
|
-
flat[pos] = MARKER_ZSTD.0; flat[pos + 1] = MARKER_ZSTD.1; flat[pos + 2] = MARKER_ZSTD.2;
|
|
164
|
-
pos += 3;
|
|
165
|
-
flat[pos..pos + PIXEL_MAGIC.len()].copy_from_slice(PIXEL_MAGIC);
|
|
166
|
-
pos += PIXEL_MAGIC.len();
|
|
167
|
-
flat[pos..pos + meta_pixel.len()].copy_from_slice(meta_pixel);
|
|
168
|
-
|
|
169
|
-
if marker_end_pos + 9 <= total_data_bytes {
|
|
170
|
-
for (i, m) in MARKER_END.iter().enumerate() {
|
|
171
|
-
let off = marker_end_pos + i * 3;
|
|
172
|
-
flat[off] = m.0; flat[off + 1] = m.1; flat[off + 2] = m.2;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
flat
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
fn build_meta_pixel(payload: &[u8], name: Option<&str>, file_list: Option<&str>) -> anyhow::Result<Vec<u8>> {
|
|
180
|
-
let version = 1u8;
|
|
181
|
-
let name_bytes = name.map(|n| n.as_bytes()).unwrap_or(&[]);
|
|
182
|
-
let name_len = name_bytes.len().min(255) as u8;
|
|
183
|
-
let payload_len_bytes = (payload.len() as u32).to_be_bytes();
|
|
184
|
-
|
|
185
|
-
let mut result = Vec::with_capacity(1 + 1 + name_len as usize + 4 + payload.len() + 256);
|
|
186
|
-
result.push(version);
|
|
187
|
-
result.push(name_len);
|
|
188
|
-
if name_len > 0 {
|
|
189
|
-
result.extend_from_slice(&name_bytes[..name_len as usize]);
|
|
190
|
-
}
|
|
191
|
-
result.extend_from_slice(&payload_len_bytes);
|
|
192
|
-
result.extend_from_slice(payload);
|
|
193
|
-
|
|
194
|
-
if let Some(fl) = file_list {
|
|
195
|
-
result.extend_from_slice(b"rXFL");
|
|
196
|
-
let json_bytes = fl.as_bytes();
|
|
197
|
-
result.extend_from_slice(&(json_bytes.len() as u32).to_be_bytes());
|
|
198
|
-
result.extend_from_slice(json_bytes);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
Ok(result)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
fn write_chunk_small<W: Write>(w: &mut W, chunk_type: &[u8; 4], data: &[u8]) -> anyhow::Result<()> {
|
|
205
|
-
w.write_all(&(data.len() as u32).to_be_bytes())?;
|
|
206
|
-
w.write_all(chunk_type)?;
|
|
207
|
-
w.write_all(data)?;
|
|
208
|
-
|
|
209
|
-
let mut h = crc32fast::Hasher::new();
|
|
210
|
-
h.update(chunk_type);
|
|
211
|
-
h.update(data);
|
|
212
|
-
w.write_all(&h.finalize().to_be_bytes())?;
|
|
213
|
-
Ok(())
|
|
214
|
-
}
|
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
use std::io::Read;
|
|
2
|
-
use std::path::Path;
|
|
3
|
-
use cipher::{KeyIvInit, StreamCipher};
|
|
4
|
-
|
|
5
|
-
const PIXEL_MAGIC: &[u8] = b"PXL1";
|
|
6
|
-
const MARKER_BYTES: usize = 12;
|
|
7
|
-
|
|
8
|
-
type Aes256Ctr = ctr::Ctr64BE<aes::Aes256>;
|
|
9
|
-
|
|
10
|
-
pub fn streaming_decode_to_dir(png_path: &Path, out_dir: &Path) -> Result<Vec<String>, String> {
|
|
11
|
-
streaming_decode_to_dir_encrypted(png_path, out_dir, None)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
pub fn streaming_decode_to_dir_encrypted(
|
|
15
|
-
png_path: &Path,
|
|
16
|
-
out_dir: &Path,
|
|
17
|
-
passphrase: Option<&str>,
|
|
18
|
-
) -> Result<Vec<String>, String> {
|
|
19
|
-
let file = std::fs::File::open(png_path).map_err(|e| format!("open: {}", e))?;
|
|
20
|
-
let mmap = unsafe { memmap2::Mmap::map(&file).map_err(|e| format!("mmap: {}", e))? };
|
|
21
|
-
let data = &mmap[..];
|
|
22
|
-
|
|
23
|
-
if data.len() < 8 || &data[0..8] != &[137, 80, 78, 71, 13, 10, 26, 10] {
|
|
24
|
-
return Err("Not a PNG file".into());
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let (width, height, idat_data_start, idat_data_end) = parse_png_header(data)?;
|
|
28
|
-
|
|
29
|
-
let mut reader = DeflatePixelReader::new(data, width, height, idat_data_start, idat_data_end);
|
|
30
|
-
|
|
31
|
-
let mut marker_buf = [0u8; MARKER_BYTES];
|
|
32
|
-
reader.read_exact(&mut marker_buf).map_err(|e| format!("read markers: {}", e))?;
|
|
33
|
-
|
|
34
|
-
let mut pxl1 = [0u8; 4];
|
|
35
|
-
reader.read_exact(&mut pxl1).map_err(|e| format!("read PXL1: {}", e))?;
|
|
36
|
-
if &pxl1 != PIXEL_MAGIC {
|
|
37
|
-
return Err(format!("Expected PXL1, got {:?}", pxl1));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
let mut hdr = [0u8; 2];
|
|
41
|
-
reader.read_exact(&mut hdr).map_err(|e| format!("read hdr: {}", e))?;
|
|
42
|
-
let _version = hdr[0];
|
|
43
|
-
let name_len = hdr[1] as usize;
|
|
44
|
-
|
|
45
|
-
if name_len > 0 {
|
|
46
|
-
let mut name_buf = vec![0u8; name_len];
|
|
47
|
-
reader.read_exact(&mut name_buf).map_err(|e| format!("read name: {}", e))?;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
let mut plen_buf = [0u8; 4];
|
|
51
|
-
reader.read_exact(&mut plen_buf).map_err(|e| format!("read payload_len: {}", e))?;
|
|
52
|
-
let payload_len = u32::from_be_bytes(plen_buf) as u64;
|
|
53
|
-
|
|
54
|
-
let payload_reader = reader.take(payload_len);
|
|
55
|
-
|
|
56
|
-
let first_byte_reader = FirstByteReader::new(payload_reader);
|
|
57
|
-
let (enc_byte, remaining_reader) = first_byte_reader.into_parts()?;
|
|
58
|
-
|
|
59
|
-
match enc_byte {
|
|
60
|
-
0x00 => {
|
|
61
|
-
let mut decoder = zstd::stream::Decoder::new(remaining_reader)
|
|
62
|
-
.map_err(|e| format!("zstd decoder: {}", e))?;
|
|
63
|
-
decoder.window_log_max(31).map_err(|e| format!("zstd window_log_max: {}", e))?;
|
|
64
|
-
read_rox1_and_untar(decoder, out_dir)
|
|
65
|
-
}
|
|
66
|
-
0x03 => {
|
|
67
|
-
let pass = passphrase.ok_or("Passphrase required for AES-CTR decryption")?;
|
|
68
|
-
let mut salt = [0u8; 16];
|
|
69
|
-
let mut iv = [0u8; 16];
|
|
70
|
-
let mut r = remaining_reader;
|
|
71
|
-
r.read_exact(&mut salt).map_err(|e| format!("read salt: {}", e))?;
|
|
72
|
-
r.read_exact(&mut iv).map_err(|e| format!("read iv: {}", e))?;
|
|
73
|
-
|
|
74
|
-
let key = crate::crypto::derive_aes_ctr_key(pass, &salt);
|
|
75
|
-
let cipher = Aes256Ctr::new_from_slices(&key, &iv)
|
|
76
|
-
.map_err(|e| format!("AES-CTR init: {}", e))?;
|
|
77
|
-
|
|
78
|
-
let hmac_size = 32u64;
|
|
79
|
-
let encrypted_data_len = payload_len - 1 - 16 - 16 - hmac_size;
|
|
80
|
-
let ctr_reader = CtrDecryptReader::new(r.take(encrypted_data_len), cipher);
|
|
81
|
-
|
|
82
|
-
let mut decoder = zstd::stream::Decoder::new(ctr_reader)
|
|
83
|
-
.map_err(|e| format!("zstd decoder: {}", e))?;
|
|
84
|
-
decoder.window_log_max(31).map_err(|e| format!("zstd window_log_max: {}", e))?;
|
|
85
|
-
read_rox1_and_untar(decoder, out_dir)
|
|
86
|
-
}
|
|
87
|
-
_ => Err(format!("Unsupported encryption (enc=0x{:02x}) in streaming decode", enc_byte)),
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
fn read_rox1_and_untar<R: Read>(mut decoder: R, out_dir: &Path) -> Result<Vec<String>, String> {
|
|
92
|
-
let mut magic = [0u8; 4];
|
|
93
|
-
decoder.read_exact(&mut magic).map_err(|e| format!("read ROX1: {}", e))?;
|
|
94
|
-
if &magic != b"ROX1" {
|
|
95
|
-
return Err(format!("Expected ROX1, got {:?}", magic));
|
|
96
|
-
}
|
|
97
|
-
std::fs::create_dir_all(out_dir).map_err(|e| format!("mkdir: {}", e))?;
|
|
98
|
-
tar_unpack_from_reader(decoder, out_dir)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
fn parse_png_header(data: &[u8]) -> Result<(usize, usize, usize, usize), String> {
|
|
102
|
-
let mut pos = 8;
|
|
103
|
-
|
|
104
|
-
let mut width = 0usize;
|
|
105
|
-
let mut height = 0usize;
|
|
106
|
-
let mut idat_start = 0usize;
|
|
107
|
-
let mut idat_end = 0usize;
|
|
108
|
-
|
|
109
|
-
while pos + 12 <= data.len() {
|
|
110
|
-
let chunk_len = u32::from_be_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as usize;
|
|
111
|
-
let chunk_type = &data[pos + 4..pos + 8];
|
|
112
|
-
let chunk_data_start = pos + 8;
|
|
113
|
-
|
|
114
|
-
if chunk_type == b"IHDR" {
|
|
115
|
-
if chunk_len < 13 {
|
|
116
|
-
return Err("Invalid IHDR".into());
|
|
117
|
-
}
|
|
118
|
-
width = u32::from_be_bytes([
|
|
119
|
-
data[chunk_data_start],
|
|
120
|
-
data[chunk_data_start + 1],
|
|
121
|
-
data[chunk_data_start + 2],
|
|
122
|
-
data[chunk_data_start + 3],
|
|
123
|
-
]) as usize;
|
|
124
|
-
height = u32::from_be_bytes([
|
|
125
|
-
data[chunk_data_start + 4],
|
|
126
|
-
data[chunk_data_start + 5],
|
|
127
|
-
data[chunk_data_start + 6],
|
|
128
|
-
data[chunk_data_start + 7],
|
|
129
|
-
]) as usize;
|
|
130
|
-
} else if chunk_type == b"IDAT" {
|
|
131
|
-
idat_start = chunk_data_start;
|
|
132
|
-
idat_end = chunk_data_start + chunk_len;
|
|
133
|
-
} else if chunk_type == b"IEND" {
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
pos = chunk_data_start + chunk_len + 4;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if width == 0 || height == 0 {
|
|
141
|
-
return Err("IHDR not found".into());
|
|
142
|
-
}
|
|
143
|
-
if idat_start == 0 {
|
|
144
|
-
return Err("IDAT not found".into());
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
Ok((width, height, idat_start, idat_end))
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
struct DeflatePixelReader<'a> {
|
|
151
|
-
data: &'a [u8],
|
|
152
|
-
height: usize,
|
|
153
|
-
offset: usize,
|
|
154
|
-
idat_end: usize,
|
|
155
|
-
block_remaining: usize,
|
|
156
|
-
current_row: usize,
|
|
157
|
-
col_in_row: usize,
|
|
158
|
-
scanline_filter_pending: bool,
|
|
159
|
-
row_bytes: usize,
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
impl<'a> DeflatePixelReader<'a> {
|
|
163
|
-
fn new(data: &'a [u8], width: usize, height: usize, idat_data_start: usize, idat_data_end: usize) -> Self {
|
|
164
|
-
let row_bytes = width * 3;
|
|
165
|
-
Self {
|
|
166
|
-
data,
|
|
167
|
-
height,
|
|
168
|
-
offset: idat_data_start + 2,
|
|
169
|
-
idat_end: idat_data_end,
|
|
170
|
-
block_remaining: 0,
|
|
171
|
-
current_row: 0,
|
|
172
|
-
col_in_row: 0,
|
|
173
|
-
scanline_filter_pending: true,
|
|
174
|
-
row_bytes,
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
fn ensure_block(&mut self) -> Result<(), std::io::Error> {
|
|
179
|
-
if self.block_remaining > 0 {
|
|
180
|
-
return Ok(());
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if self.offset + 5 > self.idat_end {
|
|
184
|
-
return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "No more deflate blocks"));
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
let len_lo = self.data[self.offset + 1] as usize;
|
|
188
|
-
let len_hi = self.data[self.offset + 2] as usize;
|
|
189
|
-
self.offset += 5;
|
|
190
|
-
|
|
191
|
-
self.block_remaining = len_lo | (len_hi << 8);
|
|
192
|
-
Ok(())
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
fn copy_raw_bytes(&mut self, buf: &mut [u8], count: usize) -> Result<usize, std::io::Error> {
|
|
196
|
-
let mut written = 0;
|
|
197
|
-
while written < count {
|
|
198
|
-
self.ensure_block()?;
|
|
199
|
-
let avail = self.block_remaining.min(count - written).min(self.idat_end - self.offset);
|
|
200
|
-
if avail == 0 {
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
buf[written..written + avail].copy_from_slice(&self.data[self.offset..self.offset + avail]);
|
|
204
|
-
self.offset += avail;
|
|
205
|
-
self.block_remaining -= avail;
|
|
206
|
-
written += avail;
|
|
207
|
-
}
|
|
208
|
-
Ok(written)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
fn skip_raw_bytes(&mut self, count: usize) -> Result<(), std::io::Error> {
|
|
212
|
-
let mut remaining = count;
|
|
213
|
-
while remaining > 0 {
|
|
214
|
-
self.ensure_block()?;
|
|
215
|
-
let skip = self.block_remaining.min(remaining).min(self.idat_end - self.offset);
|
|
216
|
-
if skip == 0 {
|
|
217
|
-
break;
|
|
218
|
-
}
|
|
219
|
-
self.offset += skip;
|
|
220
|
-
self.block_remaining -= skip;
|
|
221
|
-
remaining -= skip;
|
|
222
|
-
}
|
|
223
|
-
Ok(())
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
impl<'a> Read for DeflatePixelReader<'a> {
|
|
228
|
-
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
229
|
-
let mut filled = 0;
|
|
230
|
-
|
|
231
|
-
while filled < buf.len() {
|
|
232
|
-
if self.current_row >= self.height {
|
|
233
|
-
break;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if self.scanline_filter_pending {
|
|
237
|
-
self.skip_raw_bytes(1)?;
|
|
238
|
-
self.scanline_filter_pending = false;
|
|
239
|
-
self.col_in_row = 0;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if self.col_in_row >= self.row_bytes {
|
|
243
|
-
self.current_row += 1;
|
|
244
|
-
self.scanline_filter_pending = true;
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
let remaining_in_row = self.row_bytes - self.col_in_row;
|
|
249
|
-
let remaining_in_buf = buf.len() - filled;
|
|
250
|
-
let to_read = remaining_in_row.min(remaining_in_buf);
|
|
251
|
-
|
|
252
|
-
let got = self.copy_raw_bytes(&mut buf[filled..filled + to_read], to_read)?;
|
|
253
|
-
filled += got;
|
|
254
|
-
self.col_in_row += got;
|
|
255
|
-
if got == 0 {
|
|
256
|
-
break;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
Ok(filled)
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
struct FirstByteReader<R: Read> {
|
|
265
|
-
inner: R,
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
impl<R: Read> FirstByteReader<R> {
|
|
269
|
-
fn new(inner: R) -> Self {
|
|
270
|
-
Self { inner }
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
fn into_parts(mut self) -> Result<(u8, impl Read), String> {
|
|
274
|
-
let mut byte = [0u8; 1];
|
|
275
|
-
self.inner.read_exact(&mut byte).map_err(|e| format!("read first byte: {}", e))?;
|
|
276
|
-
Ok((byte[0], self.inner))
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
struct CtrDecryptReader<R: Read> {
|
|
281
|
-
inner: R,
|
|
282
|
-
cipher: Aes256Ctr,
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
impl<R: Read> CtrDecryptReader<R> {
|
|
286
|
-
fn new(inner: R, cipher: Aes256Ctr) -> Self {
|
|
287
|
-
Self { inner, cipher }
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
impl<R: Read> Read for CtrDecryptReader<R> {
|
|
292
|
-
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
293
|
-
let n = self.inner.read(buf)?;
|
|
294
|
-
if n > 0 {
|
|
295
|
-
self.cipher.apply_keystream(&mut buf[..n]);
|
|
296
|
-
}
|
|
297
|
-
Ok(n)
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
fn tar_unpack_from_reader<R: Read>(reader: R, output_dir: &Path) -> Result<Vec<String>, String> {
|
|
302
|
-
let buf_reader = std::io::BufReader::with_capacity(8 * 1024 * 1024, reader);
|
|
303
|
-
let mut archive = tar::Archive::new(buf_reader);
|
|
304
|
-
let mut written = Vec::new();
|
|
305
|
-
let mut created_dirs = std::collections::HashSet::new();
|
|
306
|
-
|
|
307
|
-
let entries = archive.entries().map_err(|e| format!("tar entries: {}", e))?;
|
|
308
|
-
for entry in entries {
|
|
309
|
-
let mut entry = entry.map_err(|e| format!("tar entry: {}", e))?;
|
|
310
|
-
let path = entry.path().map_err(|e| format!("tar path: {}", e))?.to_path_buf();
|
|
311
|
-
|
|
312
|
-
let mut safe = std::path::PathBuf::new();
|
|
313
|
-
for comp in path.components() {
|
|
314
|
-
if let std::path::Component::Normal(osstr) = comp {
|
|
315
|
-
safe.push(osstr);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
if safe.as_os_str().is_empty() {
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
let dest = output_dir.join(&safe);
|
|
323
|
-
if let Some(parent) = dest.parent() {
|
|
324
|
-
if created_dirs.insert(parent.to_path_buf()) {
|
|
325
|
-
std::fs::create_dir_all(parent).map_err(|e| format!("mkdir {:?}: {}", parent, e))?;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
let mut f = std::io::BufWriter::with_capacity(
|
|
330
|
-
(entry.size() as usize).min(4 * 1024 * 1024).max(8192),
|
|
331
|
-
std::fs::File::create(&dest).map_err(|e| format!("create {:?}: {}", dest, e))?,
|
|
332
|
-
);
|
|
333
|
-
std::io::copy(&mut entry, &mut f).map_err(|e| format!("write {:?}: {}", dest, e))?;
|
|
334
|
-
written.push(safe.to_string_lossy().to_string());
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
Ok(written)
|
|
338
|
-
}
|