diff --git a/src/formats/mtk_pkg_old.rs b/src/formats/mtk_pkg_old.rs index 65ebeb5..bf23036 100644 --- a/src/formats/mtk_pkg_old.rs +++ b/src/formats/mtk_pkg_old.rs @@ -5,6 +5,7 @@ use binrw::{BinRead, BinReaderExt}; use crate::utils::common; use crate::utils::mtk_crypto::{decrypt}; +use crate::utils::lzhs::{decompress_lzhs_fs_file2file_old}; static KEY: u32 = 0x94102909; //09 29 10 94 // first 4 bytes of header and content are additionally XORed, they have different masks although only differ by half a byte @@ -47,6 +48,9 @@ impl PartEntry { fn is_encrypted(&self) -> bool { (self.flags & 1 << 0) == 1 << 0 } + fn is_compressed(&self) -> bool { //lzhs fs + (self.flags & 1 << 8) != 0 + } } pub fn is_mtk_pkg_old_file(mut file: &File) -> bool { @@ -82,7 +86,8 @@ pub fn extract_mtk_pkg_old(mut file: &File, output_folder: &str) -> Result<(), B part_n += 1; let part_entry: PartEntry = file.read_le()?; - println!("\n#{} - {}, Size: {} {}", part_n, part_entry.name(), part_entry.size, if part_entry.is_encrypted() {"[ENCRYPTED]"} else {""} ); + println!("\n#{} - {}, Size: {}{} {}", + part_n, part_entry.name(), part_entry.size, if part_entry.is_compressed() {" [COMPRESSED]"} else {""}, if part_entry.is_encrypted() {"[ENCRYPTED]"} else {""} ); let data = common::read_exact(&mut file, part_entry.size as usize)?; let out_data; @@ -108,14 +113,27 @@ pub fn extract_mtk_pkg_old(mut file: &File, output_folder: &str) -> Result<(), B 0 }; - let output_path = Path::new(&output_folder).join(part_entry.name() + ".bin"); + //for compressed part create temp file + let output_path = Path::new(&output_folder).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); fs::create_dir_all(&output_folder)?; - let mut out_file = OpenOptions::new() - .write(true) - .create(true) - .open(output_path)?; + let mut out_file = OpenOptions::new().write(true).read(true)/* for lzhs */.create(true).open(&output_path)?; out_file.write_all(&out_data[extra_header_len as usize..])?; + if part_entry.is_compressed() { + let lzhs_out_path = Path::new(&output_folder).join(part_entry.name() + ".bin"); + match decompress_lzhs_fs_file2file_old(&out_file, lzhs_out_path) { + Ok(()) => { + println!("- Decompressed Successfully!"); + //after successfull decompression remove the temporary .lzhs file + fs::remove_file(&output_path)?; + }, + Err(e) => { + eprintln!("Failed to decompress partition!, Error: {}. Saving compressed data...", e); + //if the decompression is not successfull leave out compressed data. + } + } + } + println!("-- Saved file!"); } diff --git a/src/utils/lzhs.rs b/src/utils/lzhs.rs index b8933be..db82286 100644 --- a/src/utils/lzhs.rs +++ b/src/utils/lzhs.rs @@ -14,6 +14,12 @@ struct LzhsHeader { #[br(count = 6)] padding: Vec, } +#[derive(BinRead)] +struct LzhsOldSegmentHdr { + _uncompressed_size: u32, + _compressed_size: u32, +} + 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)?; @@ -82,6 +88,45 @@ pub fn decompress_lzhs_fs_file2file(mut file: &File, output_file: PathBuf) -> Re Ok(()) } +// OLD VARIANT use in old mtk pkg +pub fn decompress_lzhs_fs_file2file_old(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; 0x200]; //first 512b is uncompressed + file.read_exact(&mut uncompressed_heading)?; + out_file.write_all(&uncompressed_heading)?; + + while file.stream_position().unwrap() < file_size { + let _segment_header: LzhsOldSegmentHdr = file.read_le()?; + let lzhs_header: LzhsHeader = file.read_le()?; + + println!("[LZHS] Segment - Compressed size: {}, Decompressed size: {}, Expected Checksum: 0x{:02x?}", + 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)?; + let mut out_data; + let out_huff = unhuff(&compressed_data); + 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); + return Err("Checksum mismatch!".into()); + } else { + println!("--- Checksum OK!") + } + + out_file.write_all(&out_data)?; + } + + Ok(()) +} + #[derive(Debug)] struct HuffmanCtx { c: u8,