diff --git a/src/formats/mtk_pkg.rs b/src/formats/mtk_pkg.rs index e555978..5c8f8de 100644 --- a/src/formats/mtk_pkg.rs +++ b/src/formats/mtk_pkg.rs @@ -5,6 +5,7 @@ use binrw::{BinRead, BinReaderExt}; use crate::utils::common; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; +use crate::utils::lzhs::{decompress_lzhs_fs_file2file}; use crate::keys; #[derive(BinRead)] @@ -156,11 +157,19 @@ pub fn extract_mtk_pkg(mut file: &File, output_folder: &str) -> Result<(), Box, +} + +pub fn decompress_lzhs_fs_file2file(mut file: &File, output_file: PathBuf) -> Result<(), Box> { + let file_size = file.metadata()?.len(); + let mut out_file = OpenOptions::new().append(true).create(true).open(output_file)?; + file.seek(SeekFrom::Start(0))?; + + let mut uncompressed_heading = vec![0u8; 0x100000]; //first 1mb is uncompressed + file.read_exact(&mut uncompressed_heading)?; + out_file.write_all(&uncompressed_heading)?; + + while file.stream_position().unwrap() < file_size { + let segment_header: LzhsHeader = file.read_le()?; + let lzhs_header: LzhsHeader = file.read_le()?; + println!("[LZHS] Segment {} - Compressed size: {}, Decompressed size: {}, Expected Checksum: 0x{:02x?}", + segment_header.checksum_or_seg_idx, lzhs_header.compressed_size, lzhs_header.uncompressed_size, lzhs_header.checksum_or_seg_idx); + + let mut compressed_data = vec![0u8; lzhs_header.compressed_size as usize]; + file.read_exact(&mut compressed_data)?; + + println!("- Decompressing..."); + let out_huff = unhuff(&compressed_data); + let mut out_data = unlzss(&out_huff, lzhs_header.uncompressed_size as usize); + arm_thumb_convert(&mut out_data, 0, false); + + let checksum = calc_checksum(&out_data); + println!("-- Calculated checksum: 0x{:02x?}", checksum); + if u16::from(checksum) != lzhs_header.checksum_or_seg_idx { + println!("--- Checksum mismatch! Expected: 0x{:02x?}, Got: 0x{:02x?}!", lzhs_header.checksum_or_seg_idx, checksum); + } else { + println!("--- Checksum OK!") + } + + out_file.write_all(&out_data)?; + + //padded to 16 bytes + let pad_pos = (file.stream_position().unwrap() + 15) & !15; + file.seek(SeekFrom::Start(pad_pos))?; + } + + Ok(()) +} + +#[derive(Debug)] +struct HuffmanCtx { + c: u8, + code: u32, + len: u32, + bitno: u8, + code_buf: [u8; 32], + code_buf_ptr: usize, + mask: u8, +} + +fn get_byte(ctx: &mut HuffmanCtx, input: &mut Cursor) -> bool { + if ctx.bitno > 7 { + ctx.bitno = 0; + let mut buf = [0u8; 1]; + if input.read_exact(&mut buf).is_err() { + return false; + } + ctx.c = buf[0]; + } + let bit = ((ctx.c >> (7 - ctx.bitno)) & 1) as u32; + ctx.code = (ctx.code << 1) | bit; + ctx.len += 1; + ctx.bitno += 1; + true +} + +fn unhuff(data: &[u8]) -> Vec { + let charlen = &CHARLEN; + let pos = &POS; + + let mut ctx = HuffmanCtx { + c: 0, + code: 0, + len: 0, + bitno: 8, + code_buf: [0u8; 32], + code_buf_ptr: 1, + mask: 1, + }; + let mut lookup_charlen = vec![-1i16; 131072]; + let mut lookup_charpos = vec![-1i16; 512]; + + let mut in_cur = Cursor::new(data); + let mut out: Vec = Vec::with_capacity(data.len()); + + loop { + if !get_byte(&mut ctx, &mut in_cur) { /*println!("break1");*/break; } + if ctx.len < 4 { continue; } + let key = (((ctx.len & 0xF) as usize) << 13) | ((ctx.code & 0x1FFF) as usize); + let mut idx = lookup_charlen[key]; + if idx == -2 { continue; } + if idx == -1 { + let mut found = false; + for (i, &(code, len)) in charlen.iter().enumerate() { + if len == ctx.len && code == ctx.code { + lookup_charlen[key] = i as i16; + idx = i as i16; found = true; break; + } + } + if !found { lookup_charlen[key] = -2; continue; } + } + let i = idx as i32; + if i > 255 { + let val = (i - 256) as u8; + if ctx.code_buf_ptr < ctx.code_buf.len() { ctx.code_buf[ctx.code_buf_ptr] = val; ctx.code_buf_ptr += 1; } + ctx.code = 0; ctx.len = 0; + + let found_j: i32; + loop { + if !get_byte(&mut ctx, &mut in_cur) { + //println!("retA"); + //flush + if ctx.code_buf_ptr > 1 { for j in 0..ctx.code_buf_ptr { out.push(ctx.code_buf[j]); } }; + return out; + } + if ctx.len < 2 { continue; } + let keyp = (((ctx.len & 0x7) as usize) << 6) | ((ctx.code & 0x3F) as usize); + let mut jdx = lookup_charpos[keyp]; + if jdx == -2 { continue; } + if jdx == -1 { + let mut found = false; + for (j, &(code, len)) in pos.iter().enumerate() { + if len == ctx.len && code == ctx.code { + lookup_charpos[keyp] = j as i16; jdx = j as i16; found = true; break; + } + } + if !found { lookup_charpos[keyp] = -2; continue; } + } + found_j = jdx as i32; + let b = ((jdx as i32) >> 1) as u8; + if ctx.code_buf_ptr < ctx.code_buf.len() { ctx.code_buf[ctx.code_buf_ptr] = b; ctx.code_buf_ptr += 1; } + break; + } + ctx.code = 0; + for _ in 0..7 { if !get_byte(&mut ctx, &mut in_cur) { + //println!("retB"); + //flush + if ctx.code_buf_ptr > 1 { for j in 0..ctx.code_buf_ptr { out.push(ctx.code_buf[j]); } }; + return out; + } + } + let combined = (ctx.code | ((found_j as u32) << 7)) as u32; + if ctx.code_buf_ptr < ctx.code_buf.len() { ctx.code_buf[ctx.code_buf_ptr] = (combined & 0xFF) as u8; ctx.code_buf_ptr += 1; } + ctx.code = 0; ctx.len = 0; + } else { + ctx.code_buf[0] |= ctx.mask; + if ctx.code_buf_ptr < ctx.code_buf.len() { ctx.code_buf[ctx.code_buf_ptr] = i as u8; ctx.code_buf_ptr += 1; } + ctx.code = 0; ctx.len = 0; + } + ctx.mask = ctx.mask.wrapping_shl(1); + if ctx.mask == 0 { + for j in 0..ctx.code_buf_ptr { out.push(ctx.code_buf[j]); } + ctx.code_buf[0] = 0; ctx.code_buf_ptr = 1; ctx.mask = 1; + } + } + if ctx.code_buf_ptr > 1 { for j in 0..ctx.code_buf_ptr { out.push(ctx.code_buf[j]); } } + out +} + +fn unlzss(data: &[u8], expected_size: usize) -> Vec { + let mut window = [0u8; 0x1000]; + let mut dst = Vec::with_capacity(expected_size); + let mut src_i = 0; + let mut win_pos = 0usize; + let mut flags = 0u32; + + while dst.len() < expected_size { + flags >>= 1; + if (flags & 0x100) == 0 { + if src_i >= data.len() { + break; + } + flags = data[src_i] as u32 | 0xFF00; + src_i += 1; + } + + if (flags & 1) != 0 { + // literal + if src_i >= data.len() { + break; + } + let c = data[src_i]; + src_i += 1; + dst.push(c); + window[win_pos] = c; + win_pos = (win_pos + 1) & 0xFFF; + } else { + // backreference + if src_i + 2 >= data.len() { + break; + } + let j = data[src_i] as usize; + let off = ((data[src_i + 1] as usize) << 8) | data[src_i + 2] as usize; + src_i += 3; + + let count = j + 3; + for _ in 0..count { + if dst.len() >= expected_size { + break; + } + let c = window[(win_pos.wrapping_sub(off)) & 0xFFF]; + dst.push(c); + window[win_pos] = c; + win_pos = (win_pos + 1) & 0xFFF; + } + } + } + + dst +} + +fn arm_thumb_convert(data: &mut [u8], now_pos: u32, encoding: bool) { + let size = data.len() as u32; + let mut i: u32 = 0; + + while i + 4 <= size { + let idx = i as usize; + + if (data[idx + 1] & 0xF8) == 0xF0 && (data[idx + 3] & 0xF8) == 0xF8 { + let mut src: u32 = + ((data[idx + 1] as u32 & 0x7) << 19) | + ((data[idx + 0] as u32) << 11) | + ((data[idx + 3] as u32 & 0x7) << 8) | + (data[idx + 2] as u32); + + src <<= 1; + + let dest = if encoding { + now_pos + i + 4 + src + } else { + src.wrapping_sub(now_pos + i + 4) + } >> 1; + + data[idx + 1] = 0xF0 | ((dest >> 19) & 0x7) as u8; + data[idx + 0] = ((dest >> 11) & 0xFF) as u8; + data[idx + 3] = 0xF8 | ((dest >> 8) & 0x7) as u8; + data[idx + 2] = (dest & 0xFF) as u8; + + i += 2; + } + + i += 2; + } +} + +fn calc_checksum(data: &[u8]) -> u8 { + let mut checksum: u8 = 0; + for &bt in data { + checksum = checksum.wrapping_add(bt); + } + checksum +} \ No newline at end of file