RESTRUCTURING 1

- change formats to have their own folders with mod + include files + anything they want
- change format list to be defined in formats.rs instead of in each format
- move utils to their respective formats
- some minor code changes in some formats
This commit is contained in:
theubusu
2026-02-17 17:28:59 +01:00
parent 9fe39280cd
commit aa2249f024
69 changed files with 1678 additions and 1629 deletions
+326
View File
@@ -0,0 +1,326 @@
pub const CHARLEN: [(u32,u32); 288] = [
//key, pos
(0x00000002u32, 0x00000005u32),
(0x00000003u32, 0x00000005u32),
(0x0000000au32, 0x00000006u32),
(0x00000028u32, 0x00000007u32),
(0x00000029u32, 0x00000007u32),
(0x0000002au32, 0x00000007u32),
(0x0000002bu32, 0x00000007u32),
(0x0000002cu32, 0x00000007u32),
(0x0000002du32, 0x00000007u32),
(0x0000002eu32, 0x00000007u32),
(0x0000002fu32, 0x00000007u32),
(0x0000008au32, 0x00000008u32),
(0x0000008bu32, 0x00000008u32),
(0x0000008cu32, 0x00000008u32),
(0x0000008du32, 0x00000008u32),
(0x0000008eu32, 0x00000008u32),
(0x00000030u32, 0x00000007u32),
(0x0000008fu32, 0x00000008u32),
(0x00000090u32, 0x00000008u32),
(0x00000180u32, 0x00000009u32),
(0x00000091u32, 0x00000008u32),
(0x00000181u32, 0x00000009u32),
(0x00000182u32, 0x00000009u32),
(0x00000183u32, 0x00000009u32),
(0x00000031u32, 0x00000007u32),
(0x00000092u32, 0x00000008u32),
(0x00000093u32, 0x00000008u32),
(0x00000094u32, 0x00000008u32),
(0x0000000bu32, 0x00000006u32),
(0x00000184u32, 0x00000009u32),
(0x00000185u32, 0x00000009u32),
(0x00000186u32, 0x00000009u32),
(0x0000000cu32, 0x00000006u32),
(0x00000032u32, 0x00000007u32),
(0x00000033u32, 0x00000007u32),
(0x00000095u32, 0x00000008u32),
(0x00000096u32, 0x00000008u32),
(0x00000097u32, 0x00000008u32),
(0x00000187u32, 0x00000009u32),
(0x00000188u32, 0x00000009u32),
(0x00000034u32, 0x00000007u32),
(0x00000098u32, 0x00000008u32),
(0x00000099u32, 0x00000008u32),
(0x00000189u32, 0x00000009u32),
(0x0000009au32, 0x00000008u32),
(0x0000009bu32, 0x00000008u32),
(0x0000018au32, 0x00000009u32),
(0x0000018bu32, 0x00000009u32),
(0x00000035u32, 0x00000007u32),
(0x0000009cu32, 0x00000008u32),
(0x0000009du32, 0x00000008u32),
(0x0000018cu32, 0x00000009u32),
(0x0000018du32, 0x00000009u32),
(0x0000018eu32, 0x00000009u32),
(0x0000018fu32, 0x00000009u32),
(0x00000190u32, 0x00000009u32),
(0x0000009eu32, 0x00000008u32),
(0x00000191u32, 0x00000009u32),
(0x00000192u32, 0x00000009u32),
(0x00000193u32, 0x00000009u32),
(0x00000194u32, 0x00000009u32),
(0x00000195u32, 0x00000009u32),
(0x00000196u32, 0x00000009u32),
(0x00000197u32, 0x00000009u32),
(0x00000036u32, 0x00000007u32),
(0x0000009fu32, 0x00000008u32),
(0x00000037u32, 0x00000007u32),
(0x00000038u32, 0x00000007u32),
(0x00000198u32, 0x00000009u32),
(0x00000199u32, 0x00000009u32),
(0x000000a0u32, 0x00000008u32),
(0x0000019au32, 0x00000009u32),
(0x00000039u32, 0x00000007u32),
(0x0000003au32, 0x00000007u32),
(0x0000003bu32, 0x00000007u32),
(0x000000a1u32, 0x00000008u32),
(0x000000a2u32, 0x00000008u32),
(0x000000a3u32, 0x00000008u32),
(0x0000019bu32, 0x00000009u32),
(0x0000019cu32, 0x00000009u32),
(0x0000019du32, 0x00000009u32),
(0x0000019eu32, 0x00000009u32),
(0x0000019fu32, 0x00000009u32),
(0x000001a0u32, 0x00000009u32),
(0x000001a1u32, 0x00000009u32),
(0x000003b0u32, 0x0000000au32),
(0x000003b1u32, 0x0000000au32),
(0x000003b2u32, 0x0000000au32),
(0x000001a2u32, 0x00000009u32),
(0x000001a3u32, 0x00000009u32),
(0x000001a4u32, 0x00000009u32),
(0x000003b3u32, 0x0000000au32),
(0x000001a5u32, 0x00000009u32),
(0x000003b4u32, 0x0000000au32),
(0x000003b5u32, 0x0000000au32),
(0x000003b6u32, 0x0000000au32),
(0x0000003cu32, 0x00000007u32),
(0x000000a4u32, 0x00000008u32),
(0x000000a5u32, 0x00000008u32),
(0x000000a6u32, 0x00000008u32),
(0x000000a7u32, 0x00000008u32),
(0x000000a8u32, 0x00000008u32),
(0x000001a6u32, 0x00000009u32),
(0x000003b7u32, 0x0000000au32),
(0x0000000du32, 0x00000006u32),
(0x000000a9u32, 0x00000008u32),
(0x000001a7u32, 0x00000009u32),
(0x000001a8u32, 0x00000009u32),
(0x000001a9u32, 0x00000009u32),
(0x000001aau32, 0x00000009u32),
(0x000001abu32, 0x00000009u32),
(0x000001acu32, 0x00000009u32),
(0x0000003du32, 0x00000007u32),
(0x000001adu32, 0x00000009u32),
(0x000000aau32, 0x00000008u32),
(0x000001aeu32, 0x00000009u32),
(0x000001afu32, 0x00000009u32),
(0x000001b0u32, 0x00000009u32),
(0x000003b8u32, 0x0000000au32),
(0x000003b9u32, 0x0000000au32),
(0x0000003eu32, 0x00000007u32),
(0x000001b1u32, 0x00000009u32),
(0x000001b2u32, 0x00000009u32),
(0x000001b3u32, 0x00000009u32),
(0x000003bau32, 0x0000000au32),
(0x000003bbu32, 0x0000000au32),
(0x000003bcu32, 0x0000000au32),
(0x000003bdu32, 0x0000000au32),
(0x0000003fu32, 0x00000007u32),
(0x000000abu32, 0x00000008u32),
(0x000001b4u32, 0x00000009u32),
(0x000001b5u32, 0x00000009u32),
(0x000001b6u32, 0x00000009u32),
(0x000003beu32, 0x0000000au32),
(0x000003bfu32, 0x0000000au32),
(0x000003c0u32, 0x0000000au32),
(0x000000acu32, 0x00000008u32),
(0x000000adu32, 0x00000008u32),
(0x000001b7u32, 0x00000009u32),
(0x000001b8u32, 0x00000009u32),
(0x000001b9u32, 0x00000009u32),
(0x000003c1u32, 0x0000000au32),
(0x000003c2u32, 0x0000000au32),
(0x000003c3u32, 0x0000000au32),
(0x000000aeu32, 0x00000008u32),
(0x000000afu32, 0x00000008u32),
(0x000000b0u32, 0x00000008u32),
(0x000003c4u32, 0x0000000au32),
(0x000003c5u32, 0x0000000au32),
(0x000003c6u32, 0x0000000au32),
(0x000003c7u32, 0x0000000au32),
(0x000003c8u32, 0x0000000au32),
(0x00000040u32, 0x00000007u32),
(0x000000b1u32, 0x00000008u32),
(0x000001bau32, 0x00000009u32),
(0x000001bbu32, 0x00000009u32),
(0x000003c9u32, 0x0000000au32),
(0x000003cau32, 0x0000000au32),
(0x000003cbu32, 0x0000000au32),
(0x000003ccu32, 0x0000000au32),
(0x000000b2u32, 0x00000008u32),
(0x000000b3u32, 0x00000008u32),
(0x000003cdu32, 0x0000000au32),
(0x000003ceu32, 0x0000000au32),
(0x000003cfu32, 0x0000000au32),
(0x000003d0u32, 0x0000000au32),
(0x000003d1u32, 0x0000000au32),
(0x000007deu32, 0x0000000bu32),
(0x000000b4u32, 0x00000008u32),
(0x000001bcu32, 0x00000009u32),
(0x000001bdu32, 0x00000009u32),
(0x000001beu32, 0x00000009u32),
(0x000003d2u32, 0x0000000au32),
(0x000003d3u32, 0x0000000au32),
(0x000007dfu32, 0x0000000bu32),
(0x000007e0u32, 0x0000000bu32),
(0x000000b5u32, 0x00000008u32),
(0x000003d4u32, 0x0000000au32),
(0x000003d5u32, 0x0000000au32),
(0x000007e1u32, 0x0000000bu32),
(0x000003d6u32, 0x0000000au32),
(0x000000b6u32, 0x00000008u32),
(0x000003d7u32, 0x0000000au32),
(0x000007e2u32, 0x0000000bu32),
(0x000003d8u32, 0x0000000au32),
(0x000007e3u32, 0x0000000bu32),
(0x000007e4u32, 0x0000000bu32),
(0x000007e5u32, 0x0000000bu32),
(0x000003d9u32, 0x0000000au32),
(0x000000b7u32, 0x00000008u32),
(0x000007e6u32, 0x0000000bu32),
(0x000007e7u32, 0x0000000bu32),
(0x000000b8u32, 0x00000008u32),
(0x000001bfu32, 0x00000009u32),
(0x000003dau32, 0x0000000au32),
(0x000003dbu32, 0x0000000au32),
(0x000003dcu32, 0x0000000au32),
(0x000007e8u32, 0x0000000bu32),
(0x000007e9u32, 0x0000000bu32),
(0x000007eau32, 0x0000000bu32),
(0x000001c0u32, 0x00000009u32),
(0x000001c1u32, 0x00000009u32),
(0x000003ddu32, 0x0000000au32),
(0x000007ebu32, 0x0000000bu32),
(0x000003deu32, 0x0000000au32),
(0x000007ecu32, 0x0000000bu32),
(0x000007edu32, 0x0000000bu32),
(0x000007eeu32, 0x0000000bu32),
(0x0000000eu32, 0x00000006u32),
(0x0000000fu32, 0x00000006u32),
(0x000001c2u32, 0x00000009u32),
(0x000001c3u32, 0x00000009u32),
(0x000003dfu32, 0x0000000au32),
(0x000001c4u32, 0x00000009u32),
(0x000007efu32, 0x0000000bu32),
(0x000007f0u32, 0x0000000bu32),
(0x000001c5u32, 0x00000009u32),
(0x000001c6u32, 0x00000009u32),
(0x000003e0u32, 0x0000000au32),
(0x000003e1u32, 0x0000000au32),
(0x000001c7u32, 0x00000009u32),
(0x000003e2u32, 0x0000000au32),
(0x000007f1u32, 0x0000000bu32),
(0x000007f2u32, 0x0000000bu32),
(0x00000010u32, 0x00000006u32),
(0x000001c8u32, 0x00000009u32),
(0x000001c9u32, 0x00000009u32),
(0x000003e3u32, 0x0000000au32),
(0x000003e4u32, 0x0000000au32),
(0x000001cau32, 0x00000009u32),
(0x000001cbu32, 0x00000009u32),
(0x00000041u32, 0x00000007u32),
(0x000001ccu32, 0x00000009u32),
(0x000001cdu32, 0x00000009u32),
(0x000001ceu32, 0x00000009u32),
(0x000003e5u32, 0x0000000au32),
(0x000003e6u32, 0x0000000au32),
(0x000003e7u32, 0x0000000au32),
(0x000003e8u32, 0x0000000au32),
(0x000003e9u32, 0x0000000au32),
(0x00000042u32, 0x00000007u32),
(0x000000b9u32, 0x00000008u32),
(0x000007f3u32, 0x0000000bu32),
(0x000007f4u32, 0x0000000bu32),
(0x000003eau32, 0x0000000au32),
(0x000007f5u32, 0x0000000bu32),
(0x000001cfu32, 0x00000009u32),
(0x000001d0u32, 0x00000009u32),
(0x000000bau32, 0x00000008u32),
(0x000001d1u32, 0x00000009u32),
(0x000001d2u32, 0x00000009u32),
(0x000001d3u32, 0x00000009u32),
(0x000001d4u32, 0x00000009u32),
(0x000001d5u32, 0x00000009u32),
(0x000000bbu32, 0x00000008u32),
(0x00000043u32, 0x00000007u32),
(0x00000000u32, 0x00000004u32),
(0x00000004u32, 0x00000005u32),
(0x00000011u32, 0x00000006u32),
(0x00000012u32, 0x00000006u32),
(0x00000013u32, 0x00000006u32),
(0x00000044u32, 0x00000007u32),
(0x000000bcu32, 0x00000008u32),
(0x000000bdu32, 0x00000008u32),
(0x000000beu32, 0x00000008u32),
(0x000001d6u32, 0x00000009u32),
(0x000001d7u32, 0x00000009u32),
(0x000003ebu32, 0x0000000au32),
(0x000003ecu32, 0x0000000au32),
(0x000003edu32, 0x0000000au32),
(0x000003eeu32, 0x0000000au32),
(0x000007f6u32, 0x0000000bu32),
(0x000007f7u32, 0x0000000bu32),
(0x000007f8u32, 0x0000000bu32),
(0x000007f9u32, 0x0000000bu32),
(0x000007fau32, 0x0000000bu32),
(0x000007fbu32, 0x0000000bu32),
(0x00000ff8u32, 0x0000000cu32),
(0x00000ff9u32, 0x0000000cu32),
(0x00000ffau32, 0x0000000cu32),
(0x00000ffbu32, 0x0000000cu32),
(0x00001ffcu32, 0x0000000du32),
(0x00000ffcu32, 0x0000000cu32),
(0x00001ffdu32, 0x0000000du32),
(0x00000ffdu32, 0x0000000cu32),
(0x00001ffeu32, 0x0000000du32),
(0x00001fffu32, 0x0000000du32),
(0x000000bfu32, 0x00000008u32),
];
pub const POS: [(u32,u32); 32] = [
(0x00000000u32, 0x00000002u32),
(0x00000002u32, 0x00000003u32),
(0x00000006u32, 0x00000004u32),
(0x0000000eu32, 0x00000005u32),
(0x0000000fu32, 0x00000005u32),
(0x00000010u32, 0x00000005u32),
(0x00000011u32, 0x00000005u32),
(0x00000012u32, 0x00000005u32),
(0x00000013u32, 0x00000005u32),
(0x00000014u32, 0x00000005u32),
(0x0000002au32, 0x00000006u32),
(0x0000002bu32, 0x00000006u32),
(0x0000002cu32, 0x00000006u32),
(0x0000002du32, 0x00000006u32),
(0x0000002eu32, 0x00000006u32),
(0x0000002fu32, 0x00000006u32),
(0x00000030u32, 0x00000006u32),
(0x00000031u32, 0x00000006u32),
(0x00000032u32, 0x00000006u32),
(0x00000033u32, 0x00000006u32),
(0x00000034u32, 0x00000006u32),
(0x00000035u32, 0x00000006u32),
(0x00000036u32, 0x00000006u32),
(0x00000037u32, 0x00000006u32),
(0x00000038u32, 0x00000006u32),
(0x00000039u32, 0x00000006u32),
(0x0000003au32, 0x00000006u32),
(0x0000003bu32, 0x00000006u32),
(0x0000003cu32, 0x00000006u32),
(0x0000003du32, 0x00000006u32),
(0x0000003eu32, 0x00000006u32),
(0x0000003fu32, 0x00000006u32),
];
+63
View File
@@ -0,0 +1,63 @@
use crate::utils::common;
use binrw::BinRead;
pub static HEADER_KEY: [u8; 16] = [
0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94,
0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94,
];
pub static HEADER_IV: [u8; 16] = [0x00; 16];
pub static MTK_HEADER_MAGIC: &[u8; 8] = b"#DH@FiRm";
pub static MTK_RESERVED_MAGIC: &[u8; 16] = b"reserved mtk inc";
pub static MTK_META_MAGIC: &[u8; 4] = b"iMtK";
pub static MTK_META_PAD_MAGIC: &[u8; 4] = b"iPAd";
pub static CRYPTED_HEADER_SIZE: usize = 0x30;
pub static HEADER_SIZE: usize = 0x90;
pub static PHILIPS_EXTRA_HEADER_SIZE: usize = 0x80;
pub static _PHILIPS_FOOTER_SIGNATURE_SIZE: usize = 0x100;
#[derive(BinRead)]
pub struct Header {
pub vendor_magic_bytes: [u8; 4],
_mtk_magic: [u8; 8], //#DH@FiRm
version_bytes: [u8; 60],
pub file_size: u32,
_flags: u32,
product_name_bytes: [u8; 32],
}
impl Header {
pub fn vendor_magic(&self) -> String {
common::string_from_bytes(&self.vendor_magic_bytes)
}
pub fn version(&self) -> String {
common::string_from_bytes(&self.version_bytes)
}
pub fn product_name(&self) -> String {
common::string_from_bytes(&self.product_name_bytes)
}
}
#[derive(BinRead)]
pub struct PartEntry {
name_bytes: [u8; 4],
pub flags: u32,
pub size: u32,
}
impl PartEntry {
pub fn name(&self) -> String {
common::string_from_bytes(&self.name_bytes)
}
pub fn is_valid(&self) -> bool {
self.name().is_ascii()
}
pub fn is_encrypted(&self) -> bool {
(self.flags & 1 << 0) != 0
}
pub fn is_compressed(&self) -> bool { //lzhs fs
(self.flags & 1 << 8) != 0
}
}
+342
View File
@@ -0,0 +1,342 @@
use std::fs::{File, OpenOptions};
use std::io::{Write, Cursor, Seek, SeekFrom, Read};
use binrw::{BinRead, BinReaderExt};
use std::path::{PathBuf};
use super::huffman_tables::{CHARLEN, POS};
use crate::utils::compression::{decompress_lz4};
#[derive(BinRead)]
struct LzhsHeader {
uncompressed_size: u32,
compressed_size: u32,
checksum_or_seg_idx: u16, //as checksum in normal lzhs header, as index in lzhs_fs header
padding: [u8; 6],
}
#[derive(BinRead)]
struct LzhsOldSegmentHdr {
_uncompressed_size: u32,
_compressed_size: u32,
}
pub fn decompress_lzhs_fs_file2file(mut file: &File, output_file: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
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()?;
//lz4 type uses a 4 byte checksum instead of 1(2) byte , so the padding is not 0 anymore
//maybe this method will be changed
let is_lz4 = if &lzhs_header.padding != b"\x00\x00\x00\x00\x00\x00" {true} else {false};
println!("[{}] Segment {} - Compressed size: {}, Decompressed size: {}, Expected Checksum: 0x{:02x?}",
if is_lz4 {"LZ4"} else {"LZHS"}, 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)?;
let mut out_data;
if lzhs_header.compressed_size == 0 {
//odd variant no.1: if the compressed size is 0 , the output is just zeros with the uncompressed size.
out_data = vec![0; lzhs_header.uncompressed_size as usize];
} else if (lzhs_header.uncompressed_size > segment_header.uncompressed_size) &&
(lzhs_header.compressed_size == lzhs_header.uncompressed_size) &&
lzhs_header.checksum_or_seg_idx == 0x00
{
//odd variant no.2: the lzhs header compressed size is for some reason bigger by 8 bytes (and those 8 bytes are zeros), the data is stored UNCOMPRESSED in these
//the compressed and uncompressed size are the same and the checksum is 0.
out_data = compressed_data[..segment_header.uncompressed_size as usize].to_vec();
} else {
//normal variant
println!("- Decompressing...");
if is_lz4 {
out_data = decompress_lz4(&compressed_data, lzhs_header.uncompressed_size as i32)?;
} else {
//lzhs
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)?;
//padded to 16 bytes
let pad_pos = (file.stream_position().unwrap() + 15) & !15;
file.seek(SeekFrom::Start(pad_pos))?;
}
Ok(())
}
// OLD VARIANT use in old mtk pkg
pub fn decompress_lzhs_fs_file2file_old(mut file: &File, output_file: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
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,
code: u32,
len: u32,
bitno: u8,
code_buf: [u8; 32],
code_buf_ptr: usize,
mask: u8,
}
fn get_byte<Cursor: Read>(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<u8> {
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<u8> = 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<u8> {
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
}
+168
View File
@@ -0,0 +1,168 @@
pub mod include;
pub mod lzhs;
mod huffman_tables;
use std::any::Any;
use crate::AppContext;
use std::path::Path;
use std::fs::{self, OpenOptions};
use std::io::{Write, Cursor, Seek, SeekFrom};
use binrw::BinReaderExt;
use crate::utils::common;
use crate::utils::aes::{decrypt_aes128_cbc_nopad};
use crate::keys;
use lzhs::{decompress_lzhs_fs_file2file};
use include::*;
pub struct MtkPkgContext {
is_philips_variant: bool,
decrypted_header: Vec<u8>,
}
pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
let mut encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?;
let mut header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?;
if &header[4..12] == MTK_HEADER_MAGIC {
Ok(Some(Box::new(MtkPkgContext { is_philips_variant: false, decrypted_header: header})))
} else {
// try for philips which has additional 128 bytes at beginning
encrypted_header = common::read_file(&file, PHILIPS_EXTRA_HEADER_SIZE as u64, HEADER_SIZE)?;
header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?;
if &header[4..12] == MTK_HEADER_MAGIC {
Ok(Some(Box::new(MtkPkgContext { is_philips_variant: true, decrypted_header: header })))
} else {
Ok(None)
}
}
}
pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<MtkPkgContext>().expect("Missing context");
let file_size = file.metadata()?.len();
let header = ctx.decrypted_header;
let mut hdr_reader = Cursor::new(header);
let hdr: Header = hdr_reader.read_le()?;
println!("File info:\nFile size: {}\nVendor magic: {}\nVersion info: {}\nProduct name: {}" ,
hdr.file_size, hdr.vendor_magic(), hdr.version(), hdr.product_name());
if ctx.is_philips_variant {
file.seek(SeekFrom::Start(HEADER_SIZE as u64 + PHILIPS_EXTRA_HEADER_SIZE as u64))?;
} else {
file.seek(SeekFrom::Start(HEADER_SIZE as u64))?;
}
let mut part_n = 0;
while file.stream_position()? < file_size as u64 {
part_n += 1;
let part_entry: PartEntry = file.read_le()?;
if !part_entry.is_valid() {break};
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 + CRYPTED_HEADER_SIZE)?;
if part_entry.size == 0 {
println!("- Empty entry, skipping!");
continue
}
let mut out_data;
if part_entry.is_encrypted() {
let mut matching_key: Option<[u8; 16]> = None;
let mut matching_iv: Option<[u8; 16]> = None;
let crypted_header = &data[..CRYPTED_HEADER_SIZE];
// try decrypting with vendor magic repeated 4 times (works for most)
let mut key = [0u8; 16];
for i in 0..4 {
key[i * 4..(i + 1) * 4].copy_from_slice(&hdr.vendor_magic_bytes);
}
let try_decrypt = decrypt_aes128_cbc_nopad(&crypted_header, &key, &HEADER_IV)?;
if try_decrypt.starts_with(MTK_RESERVED_MAGIC) {
println!("- Decrypting with 4xVendor magic...");
matching_key = Some(key);
matching_iv = Some(HEADER_IV);
} else {
//try decrypting with one of custom keys
for (key_hex, iv_hex, name) in keys::MTK_PKG_CUST {
let key_array: [u8; 16] = hex::decode(key_hex)?.as_slice().try_into()?;
let iv_array: [u8; 16] = hex::decode(iv_hex)?.as_slice().try_into()?;
let try_decrypt = decrypt_aes128_cbc_nopad(&crypted_header, &key_array, &iv_array)?;
if try_decrypt.starts_with(MTK_RESERVED_MAGIC) {
println!("- Decrypting with key {}...", name);
matching_key = Some(key_array);
matching_iv = Some(iv_array);
break
}
}
}
if matching_key.is_some() && matching_iv.is_some() {
let (key_array, iv_array) = (matching_key.unwrap(), matching_iv.unwrap());
//data aligned to 16 bytes is AES encrypted. the remaining unaligned data is XORed with the key
let align_len = data.len() & !15;
let (aes_enc, xor_tail) = data.split_at(align_len);
out_data = decrypt_aes128_cbc_nopad(aes_enc, &key_array, &iv_array)?;
for (i, &b) in xor_tail.iter().enumerate() {
out_data.push(b ^ key_array[i % key_array.len()]);
}
} else {
println!("- Failed to decrypt data!");
continue
}
} else {
out_data = data;
}
//strip iMtK thing and get version
let extra_header_len = if &out_data[48..52] == MTK_META_MAGIC {
let imtk_len = u32::from_le_bytes(out_data[52..56].try_into().unwrap());
if &out_data[56..60] != MTK_META_PAD_MAGIC {
let version_len = u32::from_le_bytes(out_data[56..60].try_into().unwrap());
let version = common::string_from_bytes(&out_data[60..60 + version_len as usize]);
println!("- Version: {}", version);
}
imtk_len + 8
} else {
0
};
//for compressed part create temp file
let output_path = Path::new(&app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"});
fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().write(true).read(true)/* for lzhs */.create(true).open(&output_path)?;
out_file.write_all(&out_data[CRYPTED_HEADER_SIZE + extra_header_len as usize..])?;
if part_entry.is_compressed() {
let lzhs_out_path = Path::new(&app_ctx.output_dir).join(part_entry.name() + ".bin");
match decompress_lzhs_fs_file2file(&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!");
}
println!("\nExtraction finished!");
Ok(())
}