Upload files to "/"
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "apk"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "apk"
|
||||
path = "src/main.rs"
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
panic = "abort"
|
||||
@@ -0,0 +1,278 @@
|
||||
const RES_XML_TYPE: u16 = 0x0003;
|
||||
const RES_STRING_POOL_TYPE: u16 = 0x0001;
|
||||
const RES_XML_RESOURCE_MAP_TYPE: u16 = 0x0180;
|
||||
const RES_XML_START_NAMESPACE_TYPE: u16 = 0x0100;
|
||||
const RES_XML_END_NAMESPACE_TYPE: u16 = 0x0101;
|
||||
const RES_XML_START_ELEMENT_TYPE: u16 = 0x0102;
|
||||
const RES_XML_END_ELEMENT_TYPE: u16 = 0x0103;
|
||||
|
||||
const NO_COMMENT: u32 = 0;
|
||||
|
||||
const ATTR_RES_IDS: &[(&str, u32)] = &[
|
||||
("versionCode", 0x0101021b),
|
||||
("versionName", 0x0101021c),
|
||||
("minSdkVersion", 0x0101020c),
|
||||
("targetSdkVersion", 0x01010270),
|
||||
("label", 0x01010001),
|
||||
("name", 0x01010003),
|
||||
("exported", 0x01010010),
|
||||
("hasCode", 0x0101000c),
|
||||
("configChanges", 0x0101001f),
|
||||
];
|
||||
|
||||
pub const TYPE_STRING: u8 = 0x03;
|
||||
pub const TYPE_INT_DEC: u8 = 0x10;
|
||||
pub const TYPE_INT_BOOL: u8 = 0x12;
|
||||
|
||||
struct StringPool {
|
||||
strings: Vec<String>,
|
||||
index: std::collections::HashMap<String, u32>,
|
||||
}
|
||||
|
||||
impl StringPool {
|
||||
fn new() -> Self {
|
||||
StringPool {
|
||||
strings: Vec::new(),
|
||||
index: std::collections::HashMap::new(),
|
||||
}
|
||||
}
|
||||
fn intern(&mut self, s: &str) -> u32 {
|
||||
if let Some(&i) = self.index.get(s) {
|
||||
return i;
|
||||
}
|
||||
let i = self.strings.len() as u32;
|
||||
self.strings.push(s.to_string());
|
||||
self.index.insert(s.to_string(), i);
|
||||
i
|
||||
}
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
const UTF8_FLAG: u32 = 1 << 8;
|
||||
|
||||
let mut offsets = Vec::new();
|
||||
let mut string_data = Vec::new();
|
||||
for s in &self.strings {
|
||||
offsets.push(string_data.len() as u32);
|
||||
let utf16_units = s.encode_utf16().count();
|
||||
let bytes = s.as_bytes();
|
||||
push_utf8_len(&mut string_data, utf16_units);
|
||||
push_utf8_len(&mut string_data, bytes.len());
|
||||
string_data.extend_from_slice(bytes);
|
||||
string_data.push(0);
|
||||
}
|
||||
while string_data.len() % 4 != 0 {
|
||||
string_data.push(0);
|
||||
}
|
||||
|
||||
let string_count = self.strings.len() as u32;
|
||||
let header_size = 28u32;
|
||||
let offsets_size = string_count * 4;
|
||||
let strings_start = header_size + offsets_size;
|
||||
let chunk_size = strings_start + string_data.len() as u32;
|
||||
|
||||
let mut out = Vec::new();
|
||||
push_u16(&mut out, RES_STRING_POOL_TYPE);
|
||||
push_u16(&mut out, 28);
|
||||
push_u32(&mut out, chunk_size);
|
||||
push_u32(&mut out, string_count);
|
||||
push_u32(&mut out, 0);
|
||||
push_u32(&mut out, UTF8_FLAG);
|
||||
push_u32(&mut out, strings_start);
|
||||
push_u32(&mut out, 0);
|
||||
for o in offsets {
|
||||
push_u32(&mut out, o);
|
||||
}
|
||||
out.extend_from_slice(&string_data);
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
struct Attr {
|
||||
ns: Option<u32>,
|
||||
name: u32,
|
||||
raw_value: i32,
|
||||
typ: u8,
|
||||
data: u32,
|
||||
}
|
||||
|
||||
pub struct AxmlBuilder {
|
||||
pool: StringPool,
|
||||
body: Vec<u8>,
|
||||
android_ns_uri: u32,
|
||||
android_ns_prefix: u32,
|
||||
line: u32,
|
||||
}
|
||||
|
||||
impl AxmlBuilder {
|
||||
pub fn new() -> Self {
|
||||
AxmlBuilder {
|
||||
pool: StringPool::new(),
|
||||
body: Vec::new(),
|
||||
android_ns_uri: 0xFFFF_FFFF,
|
||||
android_ns_prefix: 0xFFFF_FFFF,
|
||||
line: 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn ns_uri(&mut self) -> u32 {
|
||||
if self.android_ns_uri == 0xFFFF_FFFF {
|
||||
self.android_ns_uri = self.pool.intern("http://schemas.android.com/apk/res/android");
|
||||
}
|
||||
self.android_ns_uri
|
||||
}
|
||||
|
||||
pub fn intern(&mut self, s: &str) -> u32 {
|
||||
self.pool.intern(s)
|
||||
}
|
||||
|
||||
pub const EMIT_NAMESPACE_CHUNKS: bool = false;
|
||||
|
||||
pub fn start(&mut self) {
|
||||
if !Self::EMIT_NAMESPACE_CHUNKS {
|
||||
return;
|
||||
}
|
||||
let mut c = Vec::new();
|
||||
push_u16(&mut c, RES_XML_START_NAMESPACE_TYPE);
|
||||
push_u16(&mut c, 16);
|
||||
push_u32(&mut c, 24);
|
||||
push_u32(&mut c, self.line);
|
||||
push_u32(&mut c, NO_COMMENT);
|
||||
push_u32(&mut c, self.android_ns_prefix);
|
||||
push_u32(&mut c, self.android_ns_uri);
|
||||
self.body.extend_from_slice(&c);
|
||||
}
|
||||
|
||||
pub fn end(&mut self) {
|
||||
if !Self::EMIT_NAMESPACE_CHUNKS {
|
||||
return;
|
||||
}
|
||||
let mut c = Vec::new();
|
||||
push_u16(&mut c, RES_XML_END_NAMESPACE_TYPE);
|
||||
push_u16(&mut c, 16);
|
||||
push_u32(&mut c, 24);
|
||||
push_u32(&mut c, self.line);
|
||||
push_u32(&mut c, 0xFFFF_FFFF);
|
||||
push_u32(&mut c, self.android_ns_prefix);
|
||||
push_u32(&mut c, self.android_ns_uri);
|
||||
self.body.extend_from_slice(&c);
|
||||
}
|
||||
|
||||
pub fn open(&mut self, tag: u32, attrs: &[(u32, u8, u32, i32)]) {
|
||||
let with_ns: Vec<(u32, u8, u32, i32, bool)> =
|
||||
attrs.iter().map(|&(n, t, d, r)| (n, t, d, r, true)).collect();
|
||||
self.open_ns(tag, &with_ns);
|
||||
}
|
||||
|
||||
pub fn open_ns(&mut self, tag: u32, attrs: &[(u32, u8, u32, i32, bool)]) {
|
||||
let uri = if attrs.iter().any(|a| a.4) { Some(self.ns_uri()) } else { None };
|
||||
let attr_structs: Vec<Attr> = attrs
|
||||
.iter()
|
||||
.map(|&(name, typ, data, raw, android_ns)| Attr {
|
||||
ns: if android_ns { uri } else { None },
|
||||
name,
|
||||
raw_value: raw,
|
||||
typ,
|
||||
data,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut c = Vec::new();
|
||||
let attr_count = attr_structs.len() as u32;
|
||||
let chunk_size = 16 + 20 + attr_count * 20;
|
||||
push_u16(&mut c, RES_XML_START_ELEMENT_TYPE);
|
||||
push_u16(&mut c, 16);
|
||||
push_u32(&mut c, chunk_size);
|
||||
push_u32(&mut c, self.line);
|
||||
push_u32(&mut c, NO_COMMENT);
|
||||
push_u32(&mut c, 0);
|
||||
push_u32(&mut c, tag);
|
||||
push_u16(&mut c, 20);
|
||||
push_u16(&mut c, 20);
|
||||
push_u16(&mut c, attr_count as u16);
|
||||
push_u16(&mut c, 0);
|
||||
push_u16(&mut c, 0);
|
||||
push_u16(&mut c, 0);
|
||||
|
||||
for a in &attr_structs {
|
||||
push_u32(&mut c, a.ns.unwrap_or(0xFFFF_FFFF));
|
||||
push_u32(&mut c, a.name);
|
||||
push_u32(&mut c, a.raw_value as u32);
|
||||
push_u16(&mut c, 8);
|
||||
c.push(0);
|
||||
c.push(a.typ);
|
||||
push_u32(&mut c, a.data);
|
||||
}
|
||||
self.body.extend_from_slice(&c);
|
||||
}
|
||||
|
||||
pub fn close(&mut self, tag: u32) {
|
||||
let mut c = Vec::new();
|
||||
push_u16(&mut c, RES_XML_END_ELEMENT_TYPE);
|
||||
push_u16(&mut c, 16);
|
||||
push_u32(&mut c, 24);
|
||||
push_u32(&mut c, self.line);
|
||||
push_u32(&mut c, NO_COMMENT);
|
||||
push_u32(&mut c, 0);
|
||||
push_u32(&mut c, tag);
|
||||
self.body.extend_from_slice(&c);
|
||||
}
|
||||
|
||||
pub fn res_id_for(name: &str) -> Option<u32> {
|
||||
ATTR_RES_IDS.iter().find(|(n, _)| *n == name).map(|(_, id)| *id)
|
||||
}
|
||||
|
||||
pub fn finish(self, attr_name_to_resid: &[(u32, u32)]) -> Vec<u8> {
|
||||
let pool_chunk = self.pool.encode();
|
||||
|
||||
let max_attr_idx = attr_name_to_resid
|
||||
.iter()
|
||||
.map(|&(str_idx, _)| str_idx as usize)
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
let mut resmap = vec![0u32; max_attr_idx + 1];
|
||||
for &(str_idx, resid) in attr_name_to_resid {
|
||||
if (str_idx as usize) < resmap.len() {
|
||||
resmap[str_idx as usize] = resid;
|
||||
}
|
||||
}
|
||||
let mut resmap_chunk = Vec::new();
|
||||
let resmap_size = 8 + resmap.len() as u32 * 4;
|
||||
push_u16(&mut resmap_chunk, RES_XML_RESOURCE_MAP_TYPE);
|
||||
push_u16(&mut resmap_chunk, 8);
|
||||
push_u32(&mut resmap_chunk, resmap_size);
|
||||
for r in &resmap {
|
||||
push_u32(&mut resmap_chunk, *r);
|
||||
}
|
||||
|
||||
let inner_len = pool_chunk.len() + resmap_chunk.len() + self.body.len();
|
||||
let total = 8 + inner_len;
|
||||
let mut out = Vec::new();
|
||||
push_u16(&mut out, RES_XML_TYPE);
|
||||
push_u16(&mut out, 8);
|
||||
push_u32(&mut out, total as u32);
|
||||
out.extend_from_slice(&pool_chunk);
|
||||
out.extend_from_slice(&resmap_chunk);
|
||||
out.extend_from_slice(&self.body);
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
pub mod val {
|
||||
#[allow(unused_imports)]
|
||||
pub use super::{TYPE_INT_BOOL, TYPE_INT_DEC, TYPE_STRING};
|
||||
}
|
||||
|
||||
fn push_u16(v: &mut Vec<u8>, x: u16) {
|
||||
v.extend_from_slice(&x.to_le_bytes());
|
||||
}
|
||||
fn push_u32(v: &mut Vec<u8>, x: u32) {
|
||||
v.extend_from_slice(&x.to_le_bytes());
|
||||
}
|
||||
|
||||
fn push_utf8_len(v: &mut Vec<u8>, len: usize) {
|
||||
if len < 0x80 {
|
||||
v.push(len as u8);
|
||||
} else {
|
||||
v.push(((len >> 8) & 0x7f) as u8 | 0x80);
|
||||
v.push((len & 0xff) as u8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BigUint {
|
||||
limbs: Vec<u32>,
|
||||
}
|
||||
|
||||
impl BigUint {
|
||||
pub fn zero() -> Self {
|
||||
BigUint { limbs: vec![] }
|
||||
}
|
||||
|
||||
pub fn from_u32(x: u32) -> Self {
|
||||
if x == 0 {
|
||||
BigUint::zero()
|
||||
} else {
|
||||
BigUint { limbs: vec![x] }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_be_bytes(bytes: &[u8]) -> Self {
|
||||
let mut b = bytes.to_vec();
|
||||
while b.len() > 1 && b[0] == 0 {
|
||||
b.remove(0);
|
||||
}
|
||||
let mut limbs = Vec::new();
|
||||
let mut i = b.len();
|
||||
while i > 0 {
|
||||
let start = i.saturating_sub(4);
|
||||
let mut limb = 0u32;
|
||||
for (k, &byte) in b[start..i].iter().enumerate() {
|
||||
let shift = (b[start..i].len() - 1 - k) * 8;
|
||||
limb |= (byte as u32) << shift;
|
||||
}
|
||||
limbs.push(limb);
|
||||
i = start;
|
||||
}
|
||||
let mut r = BigUint { limbs };
|
||||
r.normalize();
|
||||
r
|
||||
}
|
||||
|
||||
pub fn to_be_bytes(&self) -> Vec<u8> {
|
||||
if self.limbs.is_empty() {
|
||||
return vec![0];
|
||||
}
|
||||
let mut out = Vec::new();
|
||||
for &limb in self.limbs.iter().rev() {
|
||||
out.extend_from_slice(&limb.to_be_bytes());
|
||||
}
|
||||
while out.len() > 1 && out[0] == 0 {
|
||||
out.remove(0);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub fn to_be_bytes_padded(&self, len: usize) -> Vec<u8> {
|
||||
let mut b = self.to_be_bytes();
|
||||
if b.len() > len {
|
||||
b = b[b.len() - len..].to_vec();
|
||||
}
|
||||
while b.len() < len {
|
||||
b.insert(0, 0);
|
||||
}
|
||||
b
|
||||
}
|
||||
|
||||
fn normalize(&mut self) {
|
||||
while let Some(&0) = self.limbs.last() {
|
||||
self.limbs.pop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.limbs.is_empty()
|
||||
}
|
||||
|
||||
fn bit_len(&self) -> usize {
|
||||
if self.limbs.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
let top = *self.limbs.last().unwrap();
|
||||
(self.limbs.len() - 1) * 32 + (32 - top.leading_zeros() as usize)
|
||||
}
|
||||
|
||||
fn bit(&self, i: usize) -> bool {
|
||||
let limb = i / 32;
|
||||
let off = i % 32;
|
||||
if limb >= self.limbs.len() {
|
||||
return false;
|
||||
}
|
||||
(self.limbs[limb] >> off) & 1 == 1
|
||||
}
|
||||
|
||||
fn cmp(&self, other: &BigUint) -> std::cmp::Ordering {
|
||||
use std::cmp::Ordering::*;
|
||||
if self.limbs.len() != other.limbs.len() {
|
||||
return self.limbs.len().cmp(&other.limbs.len());
|
||||
}
|
||||
for i in (0..self.limbs.len()).rev() {
|
||||
match self.limbs[i].cmp(&other.limbs[i]) {
|
||||
Equal => continue,
|
||||
ord => return ord,
|
||||
}
|
||||
}
|
||||
Equal
|
||||
}
|
||||
|
||||
fn sub(&self, other: &BigUint) -> BigUint {
|
||||
let mut out = Vec::new();
|
||||
let mut borrow = 0i64;
|
||||
for i in 0..self.limbs.len() {
|
||||
let a = self.limbs[i] as i64;
|
||||
let b = *other.limbs.get(i).unwrap_or(&0) as i64;
|
||||
let mut diff = a - b - borrow;
|
||||
if diff < 0 {
|
||||
diff += 1 << 32;
|
||||
borrow = 1;
|
||||
} else {
|
||||
borrow = 0;
|
||||
}
|
||||
out.push(diff as u32);
|
||||
}
|
||||
let mut r = BigUint { limbs: out };
|
||||
r.normalize();
|
||||
r
|
||||
}
|
||||
|
||||
fn shl1(&self) -> BigUint {
|
||||
let mut out = Vec::with_capacity(self.limbs.len() + 1);
|
||||
let mut carry = 0u32;
|
||||
for &limb in &self.limbs {
|
||||
let new = (limb << 1) | carry;
|
||||
carry = limb >> 31;
|
||||
out.push(new);
|
||||
}
|
||||
if carry != 0 {
|
||||
out.push(carry);
|
||||
}
|
||||
let mut r = BigUint { limbs: out };
|
||||
r.normalize();
|
||||
r
|
||||
}
|
||||
|
||||
fn mul(&self, other: &BigUint) -> BigUint {
|
||||
if self.is_zero() || other.is_zero() {
|
||||
return BigUint::zero();
|
||||
}
|
||||
let mut out = vec![0u64; self.limbs.len() + other.limbs.len()];
|
||||
for (i, &a) in self.limbs.iter().enumerate() {
|
||||
let mut carry = 0u64;
|
||||
for (j, &b) in other.limbs.iter().enumerate() {
|
||||
let cur = out[i + j] + (a as u64) * (b as u64) + carry;
|
||||
out[i + j] = cur & 0xFFFF_FFFF;
|
||||
carry = cur >> 32;
|
||||
}
|
||||
out[i + other.limbs.len()] += carry;
|
||||
}
|
||||
let limbs: Vec<u32> = out.iter().map(|&x| x as u32).collect();
|
||||
let mut r = BigUint { limbs };
|
||||
r.normalize();
|
||||
r
|
||||
}
|
||||
|
||||
fn rem(&self, m: &BigUint) -> BigUint {
|
||||
use std::cmp::Ordering::*;
|
||||
if m.is_zero() {
|
||||
panic!("mod by zero");
|
||||
}
|
||||
if self.cmp(m) == Less {
|
||||
return self.clone();
|
||||
}
|
||||
let mut rem = BigUint::zero();
|
||||
for i in (0..self.bit_len()).rev() {
|
||||
rem = rem.shl1();
|
||||
if self.bit(i) {
|
||||
if rem.limbs.is_empty() {
|
||||
rem.limbs.push(1);
|
||||
} else {
|
||||
rem.limbs[0] |= 1;
|
||||
}
|
||||
}
|
||||
if rem.cmp(m) != Less {
|
||||
rem = rem.sub(m);
|
||||
}
|
||||
}
|
||||
rem.normalize();
|
||||
rem
|
||||
}
|
||||
|
||||
fn mulmod(&self, other: &BigUint, m: &BigUint) -> BigUint {
|
||||
self.mul(other).rem(m)
|
||||
}
|
||||
|
||||
pub fn modpow(&self, exp: &BigUint, m: &BigUint) -> BigUint {
|
||||
if m.cmp(&BigUint::from_u32(1)) == std::cmp::Ordering::Equal {
|
||||
return BigUint::zero();
|
||||
}
|
||||
let mut result = BigUint::from_u32(1);
|
||||
let mut base = self.rem(m);
|
||||
let bits = exp.bit_len();
|
||||
for i in 0..bits {
|
||||
if exp.bit(i) {
|
||||
result = result.mulmod(&base, m);
|
||||
}
|
||||
base = base.mulmod(&base, m);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use crate::keydata::SPKI_DER;
|
||||
|
||||
pub fn build_min_cert() -> Vec<u8> {
|
||||
let sig_alg = der_seq(&der_oid(&[0x2a]));
|
||||
|
||||
let name = der_seq(&[]);
|
||||
|
||||
let not_before = der_tlv(0x17, b"000101000000Z");
|
||||
let not_after = der_tlv(0x17, b"490101000000Z");
|
||||
let validity = der_seq(&[not_before, not_after].concat());
|
||||
|
||||
let cert_spki = SPKI_DER.to_vec();
|
||||
let tbs = der_seq(
|
||||
&[
|
||||
der_int(&[1]),
|
||||
sig_alg.clone(),
|
||||
name.clone(),
|
||||
validity,
|
||||
name,
|
||||
cert_spki,
|
||||
]
|
||||
.concat(),
|
||||
);
|
||||
|
||||
let sig_bitstring = der_tlv(0x03, &[0x00]);
|
||||
|
||||
der_seq(&[tbs, sig_alg, sig_bitstring].concat())
|
||||
}
|
||||
|
||||
fn der_len(len: usize) -> Vec<u8> {
|
||||
if len < 0x80 {
|
||||
vec![len as u8]
|
||||
} else {
|
||||
let mut b = Vec::new();
|
||||
let mut l = len;
|
||||
while l > 0 {
|
||||
b.insert(0, (l & 0xff) as u8);
|
||||
l >>= 8;
|
||||
}
|
||||
let mut out = vec![0x80 | b.len() as u8];
|
||||
out.extend_from_slice(&b);
|
||||
out
|
||||
}
|
||||
}
|
||||
fn der_tlv(tag: u8, body: &[u8]) -> Vec<u8> {
|
||||
let mut out = vec![tag];
|
||||
out.extend_from_slice(&der_len(body.len()));
|
||||
out.extend_from_slice(body);
|
||||
out
|
||||
}
|
||||
fn der_seq(body: &[u8]) -> Vec<u8> {
|
||||
der_tlv(0x30, body)
|
||||
}
|
||||
fn der_oid(bytes: &[u8]) -> Vec<u8> {
|
||||
der_tlv(0x06, bytes)
|
||||
}
|
||||
fn der_int(bytes: &[u8]) -> Vec<u8> {
|
||||
let mut b = bytes.to_vec();
|
||||
if !b.is_empty() && b[0] & 0x80 != 0 {
|
||||
b.insert(0, 0x00);
|
||||
}
|
||||
der_tlv(0x02, &b)
|
||||
}
|
||||
+657
@@ -0,0 +1,657 @@
|
||||
enum Token {
|
||||
Lit(u8),
|
||||
Match { len: u16, dist: u16 },
|
||||
}
|
||||
|
||||
pub fn deflate(input: &[u8]) -> Vec<u8> {
|
||||
let mut best: Option<Vec<u8>> = None;
|
||||
let consider = |block: Vec<u8>, best: &mut Option<Vec<u8>>| {
|
||||
if best.as_ref().map_or(true, |b| block.len() < b.len()) {
|
||||
*best = Some(block);
|
||||
}
|
||||
};
|
||||
|
||||
let lazy = lz77(input);
|
||||
consider(emit_dynamic_block(&lazy), &mut best);
|
||||
consider(emit_fixed_block(&lazy), &mut best);
|
||||
|
||||
let matches = build_match_table(input);
|
||||
let default_parse = lz77_optimal(input, &matches, &default_costs());
|
||||
consider(emit_dynamic_block(&default_parse), &mut best);
|
||||
consider(emit_fixed_block(&default_parse), &mut best);
|
||||
|
||||
for seed in [&default_parse, &lazy] {
|
||||
let mut last = lz77_optimal(input, &matches, &costs_from_tokens(seed));
|
||||
for _ in 0..30 {
|
||||
consider(emit_dynamic_block(&last), &mut best);
|
||||
consider(emit_fixed_block(&last), &mut best);
|
||||
let next = lz77_optimal(input, &matches, &costs_from_tokens(&last));
|
||||
last = next;
|
||||
}
|
||||
}
|
||||
|
||||
best.unwrap()
|
||||
}
|
||||
|
||||
struct Costs {
|
||||
lit: [u32; 288],
|
||||
dist: [u32; 30],
|
||||
}
|
||||
|
||||
fn default_costs() -> Costs {
|
||||
let mut lit = [9u32; 288];
|
||||
for i in 0..144 {
|
||||
lit[i] = 8;
|
||||
}
|
||||
for i in 256..288 {
|
||||
lit[i] = 8;
|
||||
}
|
||||
Costs { lit, dist: [5u32; 30] }
|
||||
}
|
||||
|
||||
fn costs_from_tokens(tokens: &[Token]) -> Costs {
|
||||
let (lit_freq, dist_freq) = tally(tokens);
|
||||
let lit_lens = huffman_lengths(&lit_freq, 15);
|
||||
let dist_lens = huffman_lengths(&dist_freq, 15);
|
||||
let mut lit = [0u32; 288];
|
||||
for i in 0..288 {
|
||||
lit[i] = if lit_lens[i] > 0 { lit_lens[i] as u32 } else { 15 };
|
||||
}
|
||||
let mut dist = [0u32; 30];
|
||||
for i in 0..30 {
|
||||
dist[i] = if dist_lens[i] > 0 { dist_lens[i] as u32 } else { 15 };
|
||||
}
|
||||
Costs { lit, dist }
|
||||
}
|
||||
|
||||
fn build_match_table(input: &[u8]) -> (Vec<u16>, Vec<u16>) {
|
||||
let n = input.len();
|
||||
let mut best_len = vec![0u16; n];
|
||||
let mut best_dist = vec![0u16; n];
|
||||
if n == 0 {
|
||||
return (best_len, best_dist);
|
||||
}
|
||||
let mut head = vec![usize::MAX; 1 << 15];
|
||||
let mut prev = vec![usize::MAX; n];
|
||||
let hash = |a: u8, b: u8, c: u8| -> usize {
|
||||
(((a as usize) << 10) ^ ((b as usize) << 5) ^ (c as usize)) & ((1 << 15) - 1)
|
||||
};
|
||||
for i in 0..n {
|
||||
if i + 3 <= n {
|
||||
let h = hash(input[i], input[i + 1], input[i + 2]);
|
||||
let mut j = head[h];
|
||||
let mut chain = 4096;
|
||||
let max_len = (n - i).min(258);
|
||||
let mut bl = 0;
|
||||
let mut bd = 0;
|
||||
while j != usize::MAX && chain > 0 {
|
||||
if i - j > 32768 {
|
||||
break;
|
||||
}
|
||||
let mut l = 0;
|
||||
while l < max_len && input[j + l] == input[i + l] {
|
||||
l += 1;
|
||||
}
|
||||
if l > bl {
|
||||
bl = l;
|
||||
bd = i - j;
|
||||
if l >= max_len {
|
||||
break;
|
||||
}
|
||||
}
|
||||
j = prev[j];
|
||||
chain -= 1;
|
||||
}
|
||||
best_len[i] = bl as u16;
|
||||
best_dist[i] = bd as u16;
|
||||
prev[i] = head[h];
|
||||
head[h] = i;
|
||||
}
|
||||
}
|
||||
(best_len, best_dist)
|
||||
}
|
||||
|
||||
fn lz77_optimal(input: &[u8], matches: &(Vec<u16>, Vec<u16>), costs: &Costs) -> Vec<Token> {
|
||||
let n = input.len();
|
||||
if n == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
let (best_len, best_dist) = matches;
|
||||
|
||||
let lit_cost = |b: u8| -> u32 { costs.lit[b as usize] };
|
||||
let match_cost = |len: u16, dist: u16| -> u32 {
|
||||
let (lsym, _, lex) = length_code(len);
|
||||
let (dsym, _, dex) = dist_code(dist);
|
||||
costs.lit[lsym as usize] + lex + costs.dist[dsym as usize] + dex
|
||||
};
|
||||
|
||||
let mut cost = vec![u32::MAX; n + 1];
|
||||
let mut choice_len = vec![0u16; n + 1];
|
||||
cost[n] = 0;
|
||||
for i in (0..n).rev() {
|
||||
let mut bc = lit_cost(input[i]).saturating_add(cost[i + 1]);
|
||||
let mut bl = 0u16;
|
||||
let ml = best_len[i];
|
||||
if ml >= 3 {
|
||||
let d = best_dist[i];
|
||||
let mut l = 3u16;
|
||||
while l <= ml {
|
||||
let nxt = cost[i + l as usize];
|
||||
if nxt != u32::MAX {
|
||||
let c = match_cost(l, d).saturating_add(nxt);
|
||||
if c < bc {
|
||||
bc = c;
|
||||
bl = l;
|
||||
}
|
||||
}
|
||||
l += 1;
|
||||
}
|
||||
}
|
||||
cost[i] = bc;
|
||||
choice_len[i] = bl;
|
||||
}
|
||||
|
||||
let mut tokens = Vec::new();
|
||||
let mut i = 0;
|
||||
while i < n {
|
||||
let l = choice_len[i];
|
||||
if l >= 3 {
|
||||
tokens.push(Token::Match { len: l, dist: best_dist[i] });
|
||||
i += l as usize;
|
||||
} else {
|
||||
tokens.push(Token::Lit(input[i]));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
tokens
|
||||
}
|
||||
|
||||
fn lz77(input: &[u8]) -> Vec<Token> {
|
||||
let n = input.len();
|
||||
let mut tokens = Vec::new();
|
||||
let mut head = vec![usize::MAX; 1 << 15];
|
||||
let mut prev = vec![usize::MAX; n.max(1)];
|
||||
|
||||
let hash = |a: u8, b: u8, c: u8| -> usize {
|
||||
(((a as usize) << 10) ^ ((b as usize) << 5) ^ (c as usize)) & ((1 << 15) - 1)
|
||||
};
|
||||
|
||||
let find = |p: usize, head: &[usize], prev: &[usize]| -> (usize, usize) {
|
||||
if p + 3 > n {
|
||||
return (0, 0);
|
||||
}
|
||||
let h = hash(input[p], input[p + 1], input[p + 2]);
|
||||
let mut j = head[h];
|
||||
let mut best_len = 0;
|
||||
let mut best_dist = 0;
|
||||
let mut chain = 4096;
|
||||
let max_len = (n - p).min(258);
|
||||
while j != usize::MAX && chain > 0 {
|
||||
if p - j > 32768 {
|
||||
break;
|
||||
}
|
||||
let mut l = 0;
|
||||
while l < max_len && input[j + l] == input[p + l] {
|
||||
l += 1;
|
||||
}
|
||||
if l > best_len {
|
||||
best_len = l;
|
||||
best_dist = p - j;
|
||||
if l >= max_len {
|
||||
break;
|
||||
}
|
||||
}
|
||||
j = prev[j];
|
||||
chain -= 1;
|
||||
}
|
||||
(best_len, best_dist)
|
||||
};
|
||||
let insert = |p: usize, head: &mut Vec<usize>, prev: &mut Vec<usize>| {
|
||||
if p + 3 <= n {
|
||||
let h = hash(input[p], input[p + 1], input[p + 2]);
|
||||
prev[p] = head[h];
|
||||
head[h] = p;
|
||||
}
|
||||
};
|
||||
|
||||
let mut i = 0;
|
||||
let mut inserted = 0usize;
|
||||
macro_rules! insert_before {
|
||||
($before:expr) => {{
|
||||
let before = $before;
|
||||
while inserted < before && inserted < n {
|
||||
insert(inserted, &mut head, &mut prev);
|
||||
inserted += 1;
|
||||
}
|
||||
}};
|
||||
}
|
||||
while i < n {
|
||||
insert_before!(i);
|
||||
let (mlen, mdist) = find(i, &head, &prev);
|
||||
|
||||
if mlen >= 3 {
|
||||
if i + 1 < n {
|
||||
insert_before!(i + 1);
|
||||
let (next_len, _) = find(i + 1, &head, &prev);
|
||||
if next_len > mlen {
|
||||
tokens.push(Token::Lit(input[i]));
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
tokens.push(Token::Match { len: mlen as u16, dist: mdist as u16 });
|
||||
i += mlen;
|
||||
} else {
|
||||
tokens.push(Token::Lit(input[i]));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
tokens
|
||||
}
|
||||
|
||||
fn length_code(len: u16) -> (u16, u32, u32) {
|
||||
const BASE: [(u16, u16, u32); 29] = [
|
||||
(257, 3, 0), (258, 4, 0), (259, 5, 0), (260, 6, 0), (261, 7, 0),
|
||||
(262, 8, 0), (263, 9, 0), (264, 10, 0), (265, 11, 1), (266, 13, 1),
|
||||
(267, 15, 1), (268, 17, 1), (269, 19, 2), (270, 23, 2), (271, 27, 2),
|
||||
(272, 31, 2), (273, 35, 3), (274, 43, 3), (275, 51, 3), (276, 59, 3),
|
||||
(277, 67, 4), (278, 83, 4), (279, 99, 4), (280, 115, 4), (281, 131, 5),
|
||||
(282, 163, 5), (283, 195, 5), (284, 227, 5), (285, 258, 0),
|
||||
];
|
||||
let mut chosen = BASE[0];
|
||||
for &e in BASE.iter() {
|
||||
if e.1 <= len {
|
||||
chosen = e;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let (sym, base, ebits) = chosen;
|
||||
(sym, (len - base) as u32, ebits)
|
||||
}
|
||||
|
||||
fn dist_code(dist: u16) -> (u16, u32, u32) {
|
||||
const BASE: [(u16, u16, u32); 30] = [
|
||||
(0, 1, 0), (1, 2, 0), (2, 3, 0), (3, 4, 0), (4, 5, 1), (5, 7, 1),
|
||||
(6, 9, 2), (7, 13, 2), (8, 17, 3), (9, 25, 3), (10, 33, 4), (11, 49, 4),
|
||||
(12, 65, 5), (13, 97, 5), (14, 129, 6), (15, 193, 6), (16, 257, 7),
|
||||
(17, 385, 7), (18, 513, 8), (19, 769, 8), (20, 1025, 9), (21, 1537, 9),
|
||||
(22, 2049, 10), (23, 3073, 10), (24, 4097, 11), (25, 6145, 11),
|
||||
(26, 8193, 12), (27, 12289, 12), (28, 16385, 13), (29, 24577, 13),
|
||||
];
|
||||
let mut chosen = BASE[0];
|
||||
for &e in BASE.iter() {
|
||||
if e.1 <= dist {
|
||||
chosen = e;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let (sym, base, ebits) = chosen;
|
||||
(sym, (dist - base) as u32, ebits)
|
||||
}
|
||||
|
||||
fn length_extra_bits(sym: u16) -> u32 {
|
||||
length_code_extra(sym)
|
||||
}
|
||||
fn length_code_extra(sym: u16) -> u32 {
|
||||
match sym {
|
||||
257..=264 => 0,
|
||||
265..=268 => 1,
|
||||
269..=272 => 2,
|
||||
273..=276 => 3,
|
||||
277..=280 => 4,
|
||||
281..=284 => 5,
|
||||
285 => 0,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
fn dist_extra_bits(sym: u16) -> u32 {
|
||||
match sym {
|
||||
0..=3 => 0,
|
||||
s => ((s / 2) - 1) as u32,
|
||||
}
|
||||
}
|
||||
|
||||
fn huffman_lengths(freqs: &[u32], max_bits: usize) -> Vec<u8> {
|
||||
let n = freqs.len();
|
||||
let mut lens = vec![0u8; n];
|
||||
|
||||
let used: Vec<usize> = (0..n).filter(|&i| freqs[i] > 0).collect();
|
||||
if used.is_empty() {
|
||||
return lens;
|
||||
}
|
||||
if used.len() == 1 {
|
||||
lens[used[0]] = 1;
|
||||
return lens;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Node {
|
||||
freq: u64,
|
||||
sym: i32,
|
||||
left: i32,
|
||||
right: i32,
|
||||
}
|
||||
let mut nodes: Vec<Node> = Vec::new();
|
||||
let mut heap: Vec<usize> = Vec::new();
|
||||
for &s in &used {
|
||||
nodes.push(Node { freq: freqs[s] as u64, sym: s as i32, left: -1, right: -1 });
|
||||
heap.push(nodes.len() - 1);
|
||||
}
|
||||
let pop_min = |heap: &mut Vec<usize>, nodes: &Vec<Node>| -> usize {
|
||||
let mut mi = 0;
|
||||
for k in 1..heap.len() {
|
||||
if nodes[heap[k]].freq < nodes[heap[mi]].freq {
|
||||
mi = k;
|
||||
}
|
||||
}
|
||||
heap.swap_remove(mi)
|
||||
};
|
||||
while heap.len() > 1 {
|
||||
let a = pop_min(&mut heap, &nodes);
|
||||
let b = pop_min(&mut heap, &nodes);
|
||||
let f = nodes[a].freq + nodes[b].freq;
|
||||
nodes.push(Node { freq: f, sym: -1, left: a as i32, right: b as i32 });
|
||||
heap.push(nodes.len() - 1);
|
||||
}
|
||||
let root = heap[0];
|
||||
|
||||
fn depth(nodes: &[Node], idx: i32, d: u8, lens: &mut Vec<u8>) {
|
||||
let node = &nodes[idx as usize];
|
||||
if node.sym >= 0 {
|
||||
lens[node.sym as usize] = d.max(1);
|
||||
} else {
|
||||
depth(nodes, node.left, d + 1, lens);
|
||||
depth(nodes, node.right, d + 1, lens);
|
||||
}
|
||||
}
|
||||
depth(&nodes, root as i32, 0, &mut lens);
|
||||
|
||||
clamp_lengths(&mut lens, max_bits);
|
||||
lens
|
||||
}
|
||||
|
||||
fn clamp_lengths(lens: &mut [u8], max_bits: usize) {
|
||||
let maxb = max_bits as u8;
|
||||
loop {
|
||||
let mut over = false;
|
||||
for l in lens.iter() {
|
||||
if *l > maxb {
|
||||
over = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !over {
|
||||
break;
|
||||
}
|
||||
let mut long_idx = None;
|
||||
for (i, l) in lens.iter().enumerate() {
|
||||
if *l > maxb {
|
||||
long_idx = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let li = long_idx.unwrap();
|
||||
let mut donor = None;
|
||||
for (i, l) in lens.iter().enumerate() {
|
||||
if *l > 0 && *l < maxb && i != li {
|
||||
donor = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
match donor {
|
||||
Some(di) => {
|
||||
lens[li] = maxb;
|
||||
lens[di] += 1;
|
||||
}
|
||||
None => {
|
||||
lens[li] = maxb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn canonical_codes(lens: &[u8]) -> Vec<(u32, u32)> {
|
||||
let max_len = *lens.iter().max().unwrap_or(&0) as usize;
|
||||
let mut bl_count = vec![0u32; max_len + 1];
|
||||
for &l in lens {
|
||||
if l > 0 {
|
||||
bl_count[l as usize] += 1;
|
||||
}
|
||||
}
|
||||
let mut next_code = vec![0u32; max_len + 2];
|
||||
let mut code = 0u32;
|
||||
for bits in 1..=max_len {
|
||||
code = (code + bl_count[bits - 1]) << 1;
|
||||
next_code[bits] = code;
|
||||
}
|
||||
let mut codes = vec![(0u32, 0u32); lens.len()];
|
||||
for (i, &l) in lens.iter().enumerate() {
|
||||
if l > 0 {
|
||||
codes[i] = (next_code[l as usize], l as u32);
|
||||
next_code[l as usize] += 1;
|
||||
}
|
||||
}
|
||||
codes
|
||||
}
|
||||
|
||||
fn tally(tokens: &[Token]) -> ([u32; 288], [u32; 30]) {
|
||||
let mut lit = [0u32; 288];
|
||||
let mut dst = [0u32; 30];
|
||||
for t in tokens {
|
||||
match t {
|
||||
Token::Lit(b) => lit[*b as usize] += 1,
|
||||
Token::Match { len, dist } => {
|
||||
let (lc, _, _) = length_code(*len);
|
||||
lit[lc as usize] += 1;
|
||||
let (dc, _, _) = dist_code(*dist);
|
||||
dst[dc as usize] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
lit[256] += 1;
|
||||
(lit, dst)
|
||||
}
|
||||
|
||||
fn emit_dynamic_block(tokens: &[Token]) -> Vec<u8> {
|
||||
let (lit_freq, dist_freq) = tally(tokens);
|
||||
|
||||
let mut lit_lens = huffman_lengths(&lit_freq, 15);
|
||||
let mut dist_lens = huffman_lengths(&dist_freq, 15);
|
||||
|
||||
let mut hlit = 286;
|
||||
while hlit > 257 && lit_lens[hlit - 1] == 0 {
|
||||
hlit -= 1;
|
||||
}
|
||||
let mut hdist = 30;
|
||||
while hdist > 1 && dist_lens[hdist - 1] == 0 {
|
||||
hdist -= 1;
|
||||
}
|
||||
lit_lens.truncate(hlit);
|
||||
dist_lens.truncate(hdist);
|
||||
|
||||
let lit_codes = canonical_codes(&lit_lens);
|
||||
let dist_codes = canonical_codes(&dist_lens);
|
||||
|
||||
let mut cl_seq: Vec<u8> = Vec::new();
|
||||
cl_seq.extend_from_slice(&lit_lens);
|
||||
cl_seq.extend_from_slice(&dist_lens);
|
||||
let (cl_syms, cl_extra) = rle_code_lengths(&cl_seq);
|
||||
|
||||
let mut cl_freq = [0u32; 19];
|
||||
for &s in &cl_syms {
|
||||
cl_freq[s as usize] += 1;
|
||||
}
|
||||
let cl_lens_full = huffman_lengths(&cl_freq, 7);
|
||||
let cl_codes = canonical_codes(&cl_lens_full);
|
||||
|
||||
const ORDER: [usize; 19] = [
|
||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
|
||||
];
|
||||
let mut hclen = 19;
|
||||
while hclen > 4 && cl_lens_full[ORDER[hclen - 1]] == 0 {
|
||||
hclen -= 1;
|
||||
}
|
||||
|
||||
let mut bw = BitWriter::new();
|
||||
bw.write_bits(1, 1);
|
||||
bw.write_bits(0b10, 2);
|
||||
bw.write_bits((hlit - 257) as u32, 5);
|
||||
bw.write_bits((hdist - 1) as u32, 5);
|
||||
bw.write_bits((hclen - 4) as u32, 4);
|
||||
for k in 0..hclen {
|
||||
bw.write_bits(cl_lens_full[ORDER[k]] as u32, 3);
|
||||
}
|
||||
for (idx, &s) in cl_syms.iter().enumerate() {
|
||||
let (code, len) = cl_codes[s as usize];
|
||||
bw.write_bits_msb(code, len);
|
||||
match s {
|
||||
16 => bw.write_bits(cl_extra[idx], 2),
|
||||
17 => bw.write_bits(cl_extra[idx], 3),
|
||||
18 => bw.write_bits(cl_extra[idx], 7),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
emit_tokens(&mut bw, tokens, &lit_codes, &dist_codes);
|
||||
bw.finish()
|
||||
}
|
||||
|
||||
fn emit_fixed_block(tokens: &[Token]) -> Vec<u8> {
|
||||
let mut bw = BitWriter::new();
|
||||
bw.write_bits(1, 1);
|
||||
bw.write_bits(0b01, 2);
|
||||
|
||||
let lit_codes: Vec<(u32, u32)> = (0..288).map(fixed_lit_code_u).collect();
|
||||
let dist_codes: Vec<(u32, u32)> = (0..30).map(|s| (s as u32, 5u32)).collect();
|
||||
emit_tokens(&mut bw, tokens, &lit_codes, &dist_codes);
|
||||
bw.finish()
|
||||
}
|
||||
|
||||
fn fixed_lit_code_u(sym: usize) -> (u32, u32) {
|
||||
match sym {
|
||||
0..=143 => (0x30 + sym as u32, 8),
|
||||
144..=255 => (0x190 + (sym as u32 - 144), 9),
|
||||
256..=279 => (sym as u32 - 256, 7),
|
||||
280..=287 => (0xc0 + (sym as u32 - 280), 8),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_tokens(
|
||||
bw: &mut BitWriter,
|
||||
tokens: &[Token],
|
||||
lit_codes: &[(u32, u32)],
|
||||
dist_codes: &[(u32, u32)],
|
||||
) {
|
||||
for t in tokens {
|
||||
match t {
|
||||
Token::Lit(b) => {
|
||||
let (c, l) = lit_codes[*b as usize];
|
||||
bw.write_bits_msb(c, l);
|
||||
}
|
||||
Token::Match { len, dist } => {
|
||||
let (lc, lextra, lebits) = length_code(*len);
|
||||
let (c, l) = lit_codes[lc as usize];
|
||||
bw.write_bits_msb(c, l);
|
||||
if lebits > 0 {
|
||||
bw.write_bits(lextra, lebits);
|
||||
}
|
||||
let (dc, dextra, debits) = dist_code(*dist);
|
||||
let (c2, l2) = dist_codes[dc as usize];
|
||||
bw.write_bits_msb(c2, l2);
|
||||
if debits > 0 {
|
||||
bw.write_bits(dextra, debits);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let (c, l) = lit_codes[256];
|
||||
bw.write_bits_msb(c, l);
|
||||
let _ = (length_extra_bits, length_code_extra, dist_extra_bits);
|
||||
}
|
||||
|
||||
fn rle_code_lengths(lens: &[u8]) -> (Vec<u8>, Vec<u32>) {
|
||||
let mut syms = Vec::new();
|
||||
let mut extra = Vec::new();
|
||||
let n = lens.len();
|
||||
let mut i = 0;
|
||||
while i < n {
|
||||
let cur = lens[i];
|
||||
let mut run = 1;
|
||||
while i + run < n && lens[i + run] == cur {
|
||||
run += 1;
|
||||
}
|
||||
if cur == 0 {
|
||||
while run >= 11 {
|
||||
let take = run.min(138);
|
||||
syms.push(18);
|
||||
extra.push((take - 11) as u32);
|
||||
run -= take;
|
||||
i += take;
|
||||
}
|
||||
while run >= 3 {
|
||||
let take = run.min(10);
|
||||
syms.push(17);
|
||||
extra.push((take - 3) as u32);
|
||||
run -= take;
|
||||
i += take;
|
||||
}
|
||||
for _ in 0..run {
|
||||
syms.push(0);
|
||||
extra.push(0);
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
syms.push(cur);
|
||||
extra.push(0);
|
||||
i += 1;
|
||||
let mut rem = run - 1;
|
||||
while rem >= 3 {
|
||||
let take = rem.min(6);
|
||||
syms.push(16);
|
||||
extra.push((take - 3) as u32);
|
||||
rem -= take;
|
||||
i += take;
|
||||
}
|
||||
for _ in 0..rem {
|
||||
syms.push(cur);
|
||||
extra.push(0);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
(syms, extra)
|
||||
}
|
||||
|
||||
struct BitWriter {
|
||||
out: Vec<u8>,
|
||||
bitbuf: u32,
|
||||
bitcnt: u32,
|
||||
}
|
||||
impl BitWriter {
|
||||
fn new() -> Self {
|
||||
BitWriter { out: Vec::new(), bitbuf: 0, bitcnt: 0 }
|
||||
}
|
||||
fn write_bits(&mut self, val: u32, n: u32) {
|
||||
self.bitbuf |= (val & ((1u32 << n) - 1)) << self.bitcnt;
|
||||
self.bitcnt += n;
|
||||
while self.bitcnt >= 8 {
|
||||
self.out.push((self.bitbuf & 0xff) as u8);
|
||||
self.bitbuf >>= 8;
|
||||
self.bitcnt -= 8;
|
||||
}
|
||||
}
|
||||
fn write_bits_msb(&mut self, code: u32, n: u32) {
|
||||
for k in (0..n).rev() {
|
||||
self.write_bits((code >> k) & 1, 1);
|
||||
}
|
||||
}
|
||||
fn finish(mut self) -> Vec<u8> {
|
||||
if self.bitcnt > 0 {
|
||||
self.out.push((self.bitbuf & 0xff) as u8);
|
||||
}
|
||||
self.out
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pub const RSA_N: &[u8] = &[0xbe,0xfe,0x5e,0x63,0x8e,0x82,0x60,0xe5,0x16,0x75,0x36,0x8f,0x02,0xbd,0xaf,0x7e,0x8d,0x53,0xd6,0x9a,0xe4,0x25,0xa6,0xde,0xbd,0x4b,0xca,0x8a,0x0f,0x1a,0x11,0xca,0xc2,0x27,0xfa,0xc0,0x13,0x34,0x4d,0xbb,0x55,0x37,0x7d,0x99,0x2d,0xaf,0x58,0x9b,0x10,0xc7,0xfe,0x16,0x24,0xbe,0x23,0x06,0x0a,0xaf,0x45,0xa4,0x5c,0x80,0x7f,0xbd,];
|
||||
|
||||
pub const RSA_D: &[u8] = &[0x7f,0x54,0x3e,0xed,0x09,0xac,0x40,0x98,0xb9,0xa3,0x79,0xb4,0xac,0x7e,0x74,0xff,0x08,0xe2,0x8f,0x11,0xed,0x6e,0x6f,0x3f,0x28,0xdd,0x31,0xb1,0x5f,0x66,0xb6,0x86,0x03,0xe7,0xd0,0x29,0x0c,0x6a,0xe9,0x33,0x4c,0x74,0x11,0xfa,0xe1,0x7f,0xdd,0x53,0x9f,0xf5,0xc5,0x19,0x62,0x3b,0xd9,0x25,0x60,0xb4,0x92,0x9d,0xa8,0x2c,0xe8,0x93,];
|
||||
|
||||
pub const SPKI_DER: &[u8] = &[0x30,0x5a,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x03,0x49,0x00,0x30,0x46,0x02,0x41,0x00,0xbe,0xfe,0x5e,0x63,0x8e,0x82,0x60,0xe5,0x16,0x75,0x36,0x8f,0x02,0xbd,0xaf,0x7e,0x8d,0x53,0xd6,0x9a,0xe4,0x25,0xa6,0xde,0xbd,0x4b,0xca,0x8a,0x0f,0x1a,0x11,0xca,0xc2,0x27,0xfa,0xc0,0x13,0x34,0x4d,0xbb,0x55,0x37,0x7d,0x99,0x2d,0xaf,0x58,0x9b,0x10,0xc7,0xfe,0x16,0x24,0xbe,0x23,0x06,0x0a,0xaf,0x45,0xa4,0x5c,0x80,0x7f,0xbd,0x02,0x01,0x03,];
|
||||
@@ -0,0 +1,85 @@
|
||||
mod axml;
|
||||
mod bigint;
|
||||
mod cert;
|
||||
mod deflate;
|
||||
mod keydata;
|
||||
mod sha256;
|
||||
mod sign_v2;
|
||||
mod zip;
|
||||
|
||||
use axml::{val, AxmlBuilder};
|
||||
|
||||
fn build_manifest() -> Vec<u8> {
|
||||
let mut b = AxmlBuilder::new();
|
||||
|
||||
let a_name = b.intern("name");
|
||||
let a_hascode = b.intern("hasCode");
|
||||
let a_exported = b.intern("exported");
|
||||
let a_minsdk = b.intern("minSdkVersion");
|
||||
|
||||
let s_manifest = b.intern("manifest");
|
||||
let s_uses_sdk = b.intern("uses-sdk");
|
||||
let s_application = b.intern("application");
|
||||
let s_activity = b.intern("activity");
|
||||
let a_package = b.intern("package");
|
||||
|
||||
let v_package = b.intern("android.app");
|
||||
let v_activity = b.intern(".Activity");
|
||||
|
||||
let resmap = vec![
|
||||
(a_name, AxmlBuilder::res_id_for("name").unwrap()),
|
||||
(a_minsdk, AxmlBuilder::res_id_for("minSdkVersion").unwrap()),
|
||||
(a_hascode, AxmlBuilder::res_id_for("hasCode").unwrap()),
|
||||
(a_exported, AxmlBuilder::res_id_for("exported").unwrap()),
|
||||
];
|
||||
|
||||
b.start();
|
||||
b.open_ns(
|
||||
s_manifest,
|
||||
&[(a_package, val::TYPE_STRING, v_package, v_package as i32, false)],
|
||||
);
|
||||
b.open(s_uses_sdk, &[(a_minsdk, val::TYPE_INT_DEC, 34, -1)]);
|
||||
b.close(s_uses_sdk);
|
||||
b.open(s_application, &[(a_hascode, val::TYPE_INT_BOOL, 0, -1)]);
|
||||
b.open(
|
||||
s_activity,
|
||||
&[
|
||||
(a_name, val::TYPE_STRING, v_activity, v_activity as i32),
|
||||
(a_exported, val::TYPE_INT_BOOL, 0xFFFF_FFFF, -1),
|
||||
],
|
||||
);
|
||||
let s_intent_filter = b.intern("intent-filter");
|
||||
let s_action = b.intern("action");
|
||||
let s_category = b.intern("category");
|
||||
let v_action_main = b.intern("android.intent.action.MAIN");
|
||||
let v_cat_launcher = b.intern("android.intent.category.LAUNCHER");
|
||||
b.open(s_intent_filter, &[]);
|
||||
b.open(s_action, &[(a_name, val::TYPE_STRING, v_action_main, v_action_main as i32)]);
|
||||
b.close(s_action);
|
||||
b.open(s_category, &[(a_name, val::TYPE_STRING, v_cat_launcher, v_cat_launcher as i32)]);
|
||||
b.close(s_category);
|
||||
b.close(s_intent_filter);
|
||||
b.close(s_activity);
|
||||
b.close(s_application);
|
||||
b.close(s_manifest);
|
||||
b.end();
|
||||
b.finish(&resmap)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let manifest = build_manifest();
|
||||
|
||||
let mut z = zip::Zip::new();
|
||||
z.add_deflated("AndroidManifest.xml", manifest);
|
||||
let apk_unsigned = z.finish();
|
||||
|
||||
let apk = sign_v2::add_v2_signature(&apk_unsigned);
|
||||
|
||||
std::fs::create_dir_all("out").expect("create out dir");
|
||||
let path = "out/app.apk";
|
||||
std::fs::write(path, &apk).expect("write apk");
|
||||
|
||||
println!("{} ({} bytes)", path, apk.len());
|
||||
println!("adb install -r {}", path);
|
||||
println!("adb shell am start -n android.app/.Activity");
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
const K: [u32; 64] = [
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||
];
|
||||
|
||||
pub fn sha256(data: &[u8]) -> [u8; 32] {
|
||||
let mut h: [u32; 8] = [
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||
];
|
||||
|
||||
let ml = (data.len() as u64) * 8;
|
||||
let mut msg = data.to_vec();
|
||||
msg.push(0x80);
|
||||
while msg.len() % 64 != 56 {
|
||||
msg.push(0);
|
||||
}
|
||||
msg.extend_from_slice(&ml.to_be_bytes());
|
||||
|
||||
for chunk in msg.chunks(64) {
|
||||
let mut w = [0u32; 64];
|
||||
for i in 0..16 {
|
||||
w[i] = u32::from_be_bytes([
|
||||
chunk[i * 4],
|
||||
chunk[i * 4 + 1],
|
||||
chunk[i * 4 + 2],
|
||||
chunk[i * 4 + 3],
|
||||
]);
|
||||
}
|
||||
for i in 16..64 {
|
||||
let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
|
||||
let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
|
||||
w[i] = w[i - 16]
|
||||
.wrapping_add(s0)
|
||||
.wrapping_add(w[i - 7])
|
||||
.wrapping_add(s1);
|
||||
}
|
||||
|
||||
let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh) =
|
||||
(h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]);
|
||||
|
||||
for i in 0..64 {
|
||||
let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
|
||||
let ch = (e & f) ^ ((!e) & g);
|
||||
let t1 = hh
|
||||
.wrapping_add(s1)
|
||||
.wrapping_add(ch)
|
||||
.wrapping_add(K[i])
|
||||
.wrapping_add(w[i]);
|
||||
let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
|
||||
let maj = (a & b) ^ (a & c) ^ (b & c);
|
||||
let t2 = s0.wrapping_add(maj);
|
||||
hh = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d.wrapping_add(t1);
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1.wrapping_add(t2);
|
||||
}
|
||||
|
||||
h[0] = h[0].wrapping_add(a);
|
||||
h[1] = h[1].wrapping_add(b);
|
||||
h[2] = h[2].wrapping_add(c);
|
||||
h[3] = h[3].wrapping_add(d);
|
||||
h[4] = h[4].wrapping_add(e);
|
||||
h[5] = h[5].wrapping_add(f);
|
||||
h[6] = h[6].wrapping_add(g);
|
||||
h[7] = h[7].wrapping_add(hh);
|
||||
}
|
||||
|
||||
let mut out = [0u8; 32];
|
||||
for i in 0..8 {
|
||||
out[i * 4..i * 4 + 4].copy_from_slice(&h[i].to_be_bytes());
|
||||
}
|
||||
out
|
||||
}
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
use crate::bigint::BigUint;
|
||||
use crate::cert::build_min_cert;
|
||||
use crate::keydata::{RSA_D, RSA_N, SPKI_DER};
|
||||
use crate::sha256::sha256;
|
||||
|
||||
const APK_SIG_BLOCK_MAGIC: &[u8; 16] = b"APK Sig Block 42";
|
||||
const V2_BLOCK_ID: u32 = 0x7109_871a;
|
||||
const SIG_ALG_RSA_PKCS1_SHA256: u32 = 0x0103;
|
||||
|
||||
const CHUNK_SIZE: usize = 1024 * 1024;
|
||||
|
||||
fn v2_sig_len() -> usize {
|
||||
RSA_N.len()
|
||||
}
|
||||
|
||||
pub fn add_v2_signature(zip: &[u8]) -> Vec<u8> {
|
||||
let eocd_pos = find_eocd(zip).expect("EOCD not found");
|
||||
let cd_offset = read_u32(zip, eocd_pos + 16) as usize;
|
||||
let cd_size = read_u32(zip, eocd_pos + 12) as usize;
|
||||
|
||||
let section1 = &zip[0..cd_offset];
|
||||
let section2 = &zip[cd_offset..cd_offset + cd_size];
|
||||
let eocd = &zip[eocd_pos..];
|
||||
|
||||
let sig_len = v2_sig_len();
|
||||
let dummy_digest = [0u8; 32];
|
||||
let dummy_block = build_signing_block(&dummy_digest, &vec![0u8; sig_len]);
|
||||
let block_len = dummy_block.len();
|
||||
|
||||
let mut eocd_for_digest = eocd.to_vec();
|
||||
write_u32(&mut eocd_for_digest, 16, cd_offset as u32);
|
||||
|
||||
let mut eocd_final = eocd.to_vec();
|
||||
write_u32(&mut eocd_final, 16, (cd_offset + block_len) as u32);
|
||||
|
||||
let digest = chunked_sha256(&[section1, section2, &eocd_for_digest]);
|
||||
|
||||
let (real_block, _) = build_signed_block(&digest);
|
||||
assert_eq!(
|
||||
real_block.len(),
|
||||
block_len,
|
||||
"signing block length changed between passes"
|
||||
);
|
||||
|
||||
let mut out = Vec::with_capacity(zip.len() + block_len);
|
||||
out.extend_from_slice(section1);
|
||||
out.extend_from_slice(&real_block);
|
||||
out.extend_from_slice(section2);
|
||||
out.extend_from_slice(&eocd_final);
|
||||
out
|
||||
}
|
||||
|
||||
fn build_signed_block(content_digest: &[u8; 32]) -> (Vec<u8>, Vec<u8>) {
|
||||
let signed_data = build_signed_data(content_digest);
|
||||
|
||||
let signature = rsa_sign_sha256(&signed_data);
|
||||
|
||||
let block = build_signing_block(content_digest, &signature);
|
||||
(block, signature)
|
||||
}
|
||||
|
||||
fn build_signed_data(content_digest: &[u8]) -> Vec<u8> {
|
||||
let mut digest_entry = Vec::new();
|
||||
write_u32_push(&mut digest_entry, SIG_ALG_RSA_PKCS1_SHA256);
|
||||
push_len_prefixed(&mut digest_entry, content_digest);
|
||||
let mut digests = Vec::new();
|
||||
push_len_prefixed(&mut digests, &digest_entry);
|
||||
|
||||
let cert = build_min_cert();
|
||||
let mut certs = Vec::new();
|
||||
push_len_prefixed(&mut certs, &cert);
|
||||
|
||||
let mut sd = Vec::new();
|
||||
push_len_prefixed(&mut sd, &digests);
|
||||
push_len_prefixed(&mut sd, &certs);
|
||||
let additional: Vec<u8> = Vec::new();
|
||||
push_len_prefixed(&mut sd, &additional);
|
||||
sd
|
||||
}
|
||||
|
||||
fn build_signing_block(content_digest: &[u8; 32], signature: &[u8]) -> Vec<u8> {
|
||||
let signed_data = build_signed_data(content_digest);
|
||||
|
||||
let mut sig_entry = Vec::new();
|
||||
write_u32_push(&mut sig_entry, SIG_ALG_RSA_PKCS1_SHA256);
|
||||
push_len_prefixed(&mut sig_entry, signature);
|
||||
let mut signatures = Vec::new();
|
||||
push_len_prefixed(&mut signatures, &sig_entry);
|
||||
|
||||
let mut signer = Vec::new();
|
||||
push_len_prefixed(&mut signer, &signed_data);
|
||||
push_len_prefixed(&mut signer, &signatures);
|
||||
push_len_prefixed(&mut signer, SPKI_DER);
|
||||
|
||||
let mut signers = Vec::new();
|
||||
push_len_prefixed(&mut signers, &signer);
|
||||
|
||||
let mut v2_value = Vec::new();
|
||||
push_len_prefixed(&mut v2_value, &signers);
|
||||
|
||||
let pair_payload_len = 4 + v2_value.len();
|
||||
let mut pairs = Vec::new();
|
||||
write_u64_push(&mut pairs, pair_payload_len as u64);
|
||||
write_u32_push(&mut pairs, V2_BLOCK_ID);
|
||||
pairs.extend_from_slice(&v2_value);
|
||||
|
||||
let size_of_block = (pairs.len() + 8 + 16) as u64;
|
||||
let mut block = Vec::new();
|
||||
write_u64_push(&mut block, size_of_block);
|
||||
block.extend_from_slice(&pairs);
|
||||
write_u64_push(&mut block, size_of_block);
|
||||
block.extend_from_slice(APK_SIG_BLOCK_MAGIC);
|
||||
block
|
||||
}
|
||||
|
||||
fn chunked_sha256(sections: &[&[u8]]) -> [u8; 32] {
|
||||
let mut chunk_digests: Vec<[u8; 32]> = Vec::new();
|
||||
for sec in sections {
|
||||
let mut off = 0;
|
||||
while off < sec.len() {
|
||||
let end = (off + CHUNK_SIZE).min(sec.len());
|
||||
let chunk = &sec[off..end];
|
||||
let mut buf = Vec::with_capacity(1 + 4 + chunk.len());
|
||||
buf.push(0xa5);
|
||||
buf.extend_from_slice(&(chunk.len() as u32).to_le_bytes());
|
||||
buf.extend_from_slice(chunk);
|
||||
chunk_digests.push(sha256(&buf));
|
||||
off = end;
|
||||
}
|
||||
}
|
||||
let mut top = Vec::with_capacity(1 + 4 + chunk_digests.len() * 32);
|
||||
top.push(0x5a);
|
||||
top.extend_from_slice(&(chunk_digests.len() as u32).to_le_bytes());
|
||||
for d in &chunk_digests {
|
||||
top.extend_from_slice(d);
|
||||
}
|
||||
sha256(&top)
|
||||
}
|
||||
|
||||
fn rsa_sign_sha256(message: &[u8]) -> Vec<u8> {
|
||||
let n = BigUint::from_be_bytes(RSA_N);
|
||||
let d = BigUint::from_be_bytes(RSA_D);
|
||||
let k = RSA_N.len();
|
||||
|
||||
let digest = sha256(message);
|
||||
let sha256_prefix: &[u8] = &[
|
||||
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
|
||||
0x05, 0x00, 0x04, 0x20,
|
||||
];
|
||||
let mut t = Vec::new();
|
||||
t.extend_from_slice(sha256_prefix);
|
||||
t.extend_from_slice(&digest);
|
||||
|
||||
let ps_len = k - t.len() - 3;
|
||||
let mut em = Vec::with_capacity(k);
|
||||
em.push(0x00);
|
||||
em.push(0x01);
|
||||
em.extend(std::iter::repeat(0xFF).take(ps_len));
|
||||
em.push(0x00);
|
||||
em.extend_from_slice(&t);
|
||||
|
||||
let m = BigUint::from_be_bytes(&em);
|
||||
let s = m.modpow(&d, &n);
|
||||
s.to_be_bytes_padded(k)
|
||||
}
|
||||
|
||||
fn push_len_prefixed(out: &mut Vec<u8>, data: &[u8]) {
|
||||
out.extend_from_slice(&(data.len() as u32).to_le_bytes());
|
||||
out.extend_from_slice(data);
|
||||
}
|
||||
fn write_u32_push(out: &mut Vec<u8>, x: u32) {
|
||||
out.extend_from_slice(&x.to_le_bytes());
|
||||
}
|
||||
fn write_u64_push(out: &mut Vec<u8>, x: u64) {
|
||||
out.extend_from_slice(&x.to_le_bytes());
|
||||
}
|
||||
fn read_u32(buf: &[u8], pos: usize) -> u32 {
|
||||
u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]])
|
||||
}
|
||||
fn write_u32(buf: &mut [u8], pos: usize, x: u32) {
|
||||
buf[pos..pos + 4].copy_from_slice(&x.to_le_bytes());
|
||||
}
|
||||
|
||||
fn find_eocd(buf: &[u8]) -> Option<usize> {
|
||||
if buf.len() < 22 {
|
||||
return None;
|
||||
}
|
||||
let mut pos = buf.len() - 22;
|
||||
loop {
|
||||
if read_u32(buf, pos) == 0x0605_4b50 {
|
||||
return Some(pos);
|
||||
}
|
||||
if pos == 0 {
|
||||
return None;
|
||||
}
|
||||
pos -= 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
use crate::deflate::deflate;
|
||||
|
||||
pub struct Entry {
|
||||
pub name: String,
|
||||
stored: Vec<u8>,
|
||||
uncompressed_size: u32,
|
||||
method: u16,
|
||||
offset: u32,
|
||||
crc: u32,
|
||||
}
|
||||
|
||||
pub struct Zip {
|
||||
entries: Vec<Entry>,
|
||||
}
|
||||
|
||||
impl Zip {
|
||||
pub fn new() -> Self {
|
||||
Zip { entries: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn add(&mut self, name: &str, data: Vec<u8>) {
|
||||
let crc = crc32(&data);
|
||||
let size = data.len() as u32;
|
||||
self.entries.push(Entry {
|
||||
name: name.to_string(),
|
||||
stored: data,
|
||||
uncompressed_size: size,
|
||||
method: 0,
|
||||
offset: 0,
|
||||
crc,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn add_deflated(&mut self, name: &str, data: Vec<u8>) {
|
||||
let crc = crc32(&data);
|
||||
let uncompressed_size = data.len() as u32;
|
||||
let compressed = deflate(&data);
|
||||
if compressed.len() < data.len() {
|
||||
self.entries.push(Entry {
|
||||
name: name.to_string(),
|
||||
stored: compressed,
|
||||
uncompressed_size,
|
||||
method: 8,
|
||||
offset: 0,
|
||||
crc,
|
||||
});
|
||||
} else {
|
||||
self.add(name, data);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(mut self) -> Vec<u8> {
|
||||
let mut out = Vec::new();
|
||||
|
||||
for e in self.entries.iter_mut() {
|
||||
e.offset = out.len() as u32;
|
||||
write_local_header(&mut out, e);
|
||||
out.extend_from_slice(&e.stored);
|
||||
}
|
||||
|
||||
let cd_start = out.len() as u32;
|
||||
for e in &self.entries {
|
||||
write_central_header(&mut out, e);
|
||||
}
|
||||
let cd_size = out.len() as u32 - cd_start;
|
||||
|
||||
write_eocd(&mut out, self.entries.len() as u16, cd_size, cd_start);
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
fn write_local_header(out: &mut Vec<u8>, e: &Entry) {
|
||||
out.extend_from_slice(&0x0403_4b50u32.to_le_bytes());
|
||||
out.extend_from_slice(&20u16.to_le_bytes());
|
||||
out.extend_from_slice(&0u16.to_le_bytes());
|
||||
out.extend_from_slice(&e.method.to_le_bytes());
|
||||
out.extend_from_slice(&0u16.to_le_bytes());
|
||||
out.extend_from_slice(&0x21u16.to_le_bytes());
|
||||
out.extend_from_slice(&e.crc.to_le_bytes());
|
||||
out.extend_from_slice(&(e.stored.len() as u32).to_le_bytes());
|
||||
out.extend_from_slice(&e.uncompressed_size.to_le_bytes());
|
||||
out.extend_from_slice(&(e.name.len() as u16).to_le_bytes());
|
||||
out.extend_from_slice(&0u16.to_le_bytes());
|
||||
out.extend_from_slice(e.name.as_bytes());
|
||||
}
|
||||
|
||||
fn write_central_header(out: &mut Vec<u8>, e: &Entry) {
|
||||
out.extend_from_slice(&0x0201_4b50u32.to_le_bytes());
|
||||
out.extend_from_slice(&20u16.to_le_bytes());
|
||||
out.extend_from_slice(&20u16.to_le_bytes());
|
||||
out.extend_from_slice(&0u16.to_le_bytes());
|
||||
out.extend_from_slice(&e.method.to_le_bytes());
|
||||
out.extend_from_slice(&0u16.to_le_bytes());
|
||||
out.extend_from_slice(&0x21u16.to_le_bytes());
|
||||
out.extend_from_slice(&e.crc.to_le_bytes());
|
||||
out.extend_from_slice(&(e.stored.len() as u32).to_le_bytes());
|
||||
out.extend_from_slice(&e.uncompressed_size.to_le_bytes());
|
||||
out.extend_from_slice(&(e.name.len() as u16).to_le_bytes());
|
||||
out.extend_from_slice(&0u16.to_le_bytes());
|
||||
out.extend_from_slice(&0u16.to_le_bytes());
|
||||
out.extend_from_slice(&0u16.to_le_bytes());
|
||||
out.extend_from_slice(&0u16.to_le_bytes());
|
||||
out.extend_from_slice(&0u32.to_le_bytes());
|
||||
out.extend_from_slice(&e.offset.to_le_bytes());
|
||||
out.extend_from_slice(e.name.as_bytes());
|
||||
}
|
||||
|
||||
fn write_eocd(out: &mut Vec<u8>, count: u16, cd_size: u32, cd_offset: u32) {
|
||||
out.extend_from_slice(&0x0605_4b50u32.to_le_bytes());
|
||||
out.extend_from_slice(&0u16.to_le_bytes());
|
||||
out.extend_from_slice(&0u16.to_le_bytes());
|
||||
out.extend_from_slice(&count.to_le_bytes());
|
||||
out.extend_from_slice(&count.to_le_bytes());
|
||||
out.extend_from_slice(&cd_size.to_le_bytes());
|
||||
out.extend_from_slice(&cd_offset.to_le_bytes());
|
||||
out.extend_from_slice(&0u16.to_le_bytes());
|
||||
}
|
||||
|
||||
pub fn crc32(data: &[u8]) -> u32 {
|
||||
let mut crc: u32 = 0xFFFF_FFFF;
|
||||
for &b in data {
|
||||
crc ^= b as u32;
|
||||
for _ in 0..8 {
|
||||
let mask = (crc & 1).wrapping_neg();
|
||||
crc = (crc >> 1) ^ (0xEDB8_8320 & mask);
|
||||
}
|
||||
}
|
||||
!crc
|
||||
}
|
||||
Reference in New Issue
Block a user