From eb216b1691202ab05646fdbb57cc23367440bf83 Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Thu, 7 May 2026 18:42:47 +0200 Subject: [PATCH] mtk_pkg: add ZSTD decompression support, decomp part in memory, pfl_upg: add Q5521 key --- Cargo.lock | 29 +++++++++ Cargo.toml | 3 +- src/formats/mtk_pkg/include.rs | 4 +- src/formats/mtk_pkg/lzhs.rs | 112 +++++++++++++++++++-------------- src/formats/mtk_pkg/mod.rs | 24 +++---- src/formats/mtk_pkg_new/mod.rs | 24 +++---- src/formats/mtk_pkg_old/mod.rs | 25 +++----- src/formats/pfl_upg/mod.rs | 6 +- src/keys.rs | 10 ++- src/utils/compression.rs | 8 +++ 10 files changed, 146 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e56f42..3519e31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,6 +937,7 @@ dependencies = [ "sha1", "simd-adler32", "tar", + "zstd", ] [[package]] @@ -1087,3 +1088,31 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 758963e..b5c299b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,5 @@ prost = "0.14.1" prost-types = "0.14.1" bzip2 = "0.6.1" liblzma = "0.4.5" -des = "0.8.1" \ No newline at end of file +des = "0.8.1" +zstd = "0.13.3" \ No newline at end of file diff --git a/src/formats/mtk_pkg/include.rs b/src/formats/mtk_pkg/include.rs index 7b01dfd..0fef891 100644 --- a/src/formats/mtk_pkg/include.rs +++ b/src/formats/mtk_pkg/include.rs @@ -25,7 +25,7 @@ pub struct Header { _mtk_magic: [u8; 8], //#DH@FiRm version_bytes: [u8; 60], pub file_size: u32, - _flags: u32, + _flags: [u8; 4], // 3rd is possibly the compression type in new mtk pkg product_name_bytes: [u8; 32], } impl Header { @@ -57,7 +57,7 @@ impl PartEntry { pub fn is_encrypted(&self) -> bool { (self.flags & 1 << 0) != 0 } - pub fn is_compressed(&self) -> bool { //lzhs fs + pub fn is_compressed(&self) -> bool { (self.flags & 1 << 8) != 0 } } \ No newline at end of file diff --git a/src/formats/mtk_pkg/lzhs.rs b/src/formats/mtk_pkg/lzhs.rs index 634218e..f736eb2 100644 --- a/src/formats/mtk_pkg/lzhs.rs +++ b/src/formats/mtk_pkg/lzhs.rs @@ -1,17 +1,25 @@ -use std::fs::{File, OpenOptions}; +use std::fs::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}; +use crate::utils::common; +use crate::utils::compression::{decompress_lz4, decompress_zstd}; + +#[derive(PartialEq, Debug)] +enum CompressionType { + LZHS, + LZ4, + ZSTD, +} #[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], + checksum_or_seg_idx: u32, //as checksum in normal lzhs header, as index in lzhs_fs header + _padding: [u8; 4], } #[derive(BinRead)] @@ -20,36 +28,42 @@ struct LzhsOldSegmentHdr { _compressed_size: u32, } -pub fn decompress_lzhs_fs_file2file(mut file: &File, output_file: PathBuf) -> Result<(), Box> { - let file_size = file.metadata()?.len(); +pub fn decompress_mtk_to_file(data: &[u8], output_file: &PathBuf) -> Result<(), Box> { let mut out_file = OpenOptions::new().append(true).create(true).open(&output_file)?; - file.seek(SeekFrom::Start(0))?; + let mut data_reader = Cursor::new(data); - let mut uncompressed_heading = vec![0u8; 0x100000]; //first 1mb is uncompressed - file.read_exact(&mut uncompressed_heading)?; + let uncompressed_heading = common::read_exact(&mut data_reader, 0x100000)?; //first 1mb is uncompressed 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()?; + while data_reader.stream_position().unwrap() < data_reader.get_ref().len() as u64{ + let segment_header: LzhsHeader = data_reader.read_le()?; + let comp_header: LzhsHeader = data_reader.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!("[cmp] Segment {} - Compressed size: {}, Decompressed size: {}", + segment_header.checksum_or_seg_idx, comp_header.compressed_size, comp_header.uncompressed_size); - 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 compressed_data = common::read_exact(&mut data_reader, comp_header.compressed_size as usize)?; + + //set comp method + let compression_type: CompressionType; + if comp_header.checksum_or_seg_idx & 0xFFFFFF00 != 0 { //LZHS uses only a 8 bit checksum, and other use 32 bit(although the type is unknown). + if compressed_data.starts_with(b"\x28\xB5\x2F\xFD") { //ZSTD magic + compression_type = CompressionType::ZSTD; + } else { + compression_type = CompressionType::LZ4; + } + } else { + compression_type = CompressionType::LZHS; + } - 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 { + if comp_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]; + out_data = vec![0; comp_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 + } else if (comp_header.uncompressed_size > segment_header.uncompressed_size) && + (comp_header.compressed_size == comp_header.uncompressed_size) && + comp_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. @@ -57,56 +71,58 @@ pub fn decompress_lzhs_fs_file2file(mut file: &File, output_file: PathBuf) -> Re } else { //normal variant - println!("- Decompressing..."); - if is_lz4 { - out_data = decompress_lz4(&compressed_data, lzhs_header.uncompressed_size as i32)?; - } else { - //lzhs + println!("- Decompressing {:?}...", compression_type); + if compression_type == CompressionType::LZ4 { + out_data = decompress_lz4(&compressed_data, comp_header.uncompressed_size as i32)?; + } + else if compression_type == CompressionType::ZSTD { + out_data = decompress_zstd(&compressed_data)?; + } + else if compression_type == CompressionType::LZHS { let out_huff = unhuff(&compressed_data); - out_data = unlzss(&out_huff, lzhs_header.uncompressed_size as usize); + out_data = unlzss(&out_huff, comp_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()); + if u32::from(checksum) != comp_header.checksum_or_seg_idx { + println!("--- Checksum mismatch! Expected: 0x{:02x?}, Got: 0x{:02x?}!", comp_header.checksum_or_seg_idx, checksum); + return Err("LZHS checksum mismatch".into()); } else { println!("--- Checksum OK!") } } - + else { + return Err("undefined compression type".into()); + } } 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))?; + let pad_pos = (data_reader.stream_position().unwrap() + 15) & !15; + data_reader.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> { - let file_size = file.metadata()?.len(); +pub fn decompress_mtk_to_file_old(data: &[u8], output_file: &PathBuf) -> Result<(), Box> { let mut out_file = OpenOptions::new().append(true).create(true).open(&output_file)?; - file.seek(SeekFrom::Start(0))?; + let mut data_reader = Cursor::new(data); - let mut uncompressed_heading = vec![0u8; 0x200]; //first 512b is uncompressed - file.read_exact(&mut uncompressed_heading)?; + let uncompressed_heading = common::read_exact(&mut data_reader, 0x200)?; //first 1mb is uncompressed 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()?; + while data_reader.stream_position().unwrap() < data_reader.get_ref().len() as u64{ + let _segment_header: LzhsOldSegmentHdr = data_reader.read_le()?; + let lzhs_header: LzhsHeader = data_reader.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 compressed_data = common::read_exact(&mut data_reader, lzhs_header.compressed_size as usize)?; let mut out_data; let out_huff = unhuff(&compressed_data); out_data = unlzss(&out_huff, lzhs_header.uncompressed_size as usize); @@ -114,9 +130,9 @@ pub fn decompress_lzhs_fs_file2file_old(mut file: &File, output_file: PathBuf) - let checksum = calc_checksum(&out_data); println!("-- Calculated checksum: 0x{:02x?}", checksum); - if u16::from(checksum) != lzhs_header.checksum_or_seg_idx { + if u32::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()); + return Err("LZHS checksum mismatch".into()); } else { println!("--- Checksum OK!") } diff --git a/src/formats/mtk_pkg/mod.rs b/src/formats/mtk_pkg/mod.rs index f8b1ef4..cdf1bcf 100644 --- a/src/formats/mtk_pkg/mod.rs +++ b/src/formats/mtk_pkg/mod.rs @@ -13,7 +13,7 @@ use crate::utils::common; use crate::utils::global::opt_dump_dec_hdr; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::keys; -use lzhs::{decompress_lzhs_fs_file2file}; +use lzhs::{decompress_mtk_to_file}; use include::*; pub struct MtkPkgContext { @@ -140,31 +140,27 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo } else { 0 }; + let fin_data = &out_data[CRYPTED_HEADER_SIZE + extra_header_len as usize..]; - //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"}); + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", part_entry.name())); 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) { + match decompress_mtk_to_file(&fin_data, &output_path) { Ok(()) => { - println!("- Decompressed Successfully!"); - if !app_ctx.has_option("mtk_pkg:no_del_comp") { - //after successfull decompression remove the temporary .lzhs file - fs::remove_file(&output_path)?; - } + println!("-- Decompressed Successfully, Saved file!"); + continue }, Err(e) => { eprintln!("Failed to decompress partition!, Error: {}. Saving compressed data...", e); - //if the decompression is not successfull leave out compressed data. } - } + } } + let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?; + out_file.write_all(&fin_data)?; println!("-- Saved file!"); + } Ok(()) diff --git a/src/formats/mtk_pkg_new/mod.rs b/src/formats/mtk_pkg_new/mod.rs index f0fb23f..9762a2b 100644 --- a/src/formats/mtk_pkg_new/mod.rs +++ b/src/formats/mtk_pkg_new/mod.rs @@ -11,7 +11,7 @@ use crate::utils::common; use crate::utils::global::opt_dump_dec_hdr; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::keys; -use crate::formats::mtk_pkg::lzhs::{decompress_lzhs_fs_file2file}; +use crate::formats::mtk_pkg::lzhs::{decompress_mtk_to_file}; use crate::formats::mtk_pkg::include::{Header, PartEntry, MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC}; use include::*; @@ -110,29 +110,25 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box) -> Result<() 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"}); + let fin_data = &out_data[48 + extra_header_len as usize..]; + + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", part_entry.name())); 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[48 + 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) { + match decompress_mtk_to_file(&fin_data, &output_path) { Ok(()) => { - println!("-- Decompressed Successfully!"); - if !app_ctx.has_option("mtk_pkg:no_del_comp") { - //after successfull decompression remove the temporary .lzhs file - fs::remove_file(&output_path)?; - } + println!("-- Decompressed Successfully, Saved file!"); + continue }, Err(e) => { eprintln!("Failed to decompress partition!, Error: {}. Saving compressed data...", e); - //if the decompression is not successfull leave out compressed data. } - } + } } + let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?; + out_file.write_all(&fin_data)?; println!("-- Saved file!"); } diff --git a/src/formats/mtk_pkg_old/mod.rs b/src/formats/mtk_pkg_old/mod.rs index 4eaa79f..f96cf6c 100644 --- a/src/formats/mtk_pkg_old/mod.rs +++ b/src/formats/mtk_pkg_old/mod.rs @@ -10,7 +10,7 @@ use binrw::BinReaderExt; use crate::utils::common; use crate::utils::global::opt_dump_dec_hdr; -use crate::formats::mtk_pkg::lzhs::{decompress_lzhs_fs_file2file_old}; +use crate::formats::mtk_pkg::lzhs::{decompress_mtk_to_file_old}; use crate::formats::mtk_pkg::include::{PartEntry, MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC}; use mtk_crypto::{decrypt}; use include::*; @@ -85,30 +85,25 @@ pub fn extract_mtk_pkg_old(app_ctx: &AppContext, ctx: Box) -> Result<() } 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"}); + let fin_data = &out_data[extra_header_len as usize..]; + + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", part_entry.name())); 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[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_old(&out_file, lzhs_out_path) { + match decompress_mtk_to_file_old(&fin_data, &output_path) { Ok(()) => { - println!("- Decompressed Successfully!"); - if !app_ctx.has_option("mtk_pkg:no_del_comp") { - //after successfull decompression remove the temporary .lzhs file - fs::remove_file(&output_path)?; - } + println!("-- Decompressed Successfully, Saved file!"); + continue }, Err(e) => { eprintln!("Failed to decompress partition!, Error: {}. Saving compressed data...", e); - //if the decompression is not successfull leave out compressed data. } - } + } } + let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?; + out_file.write_all(&fin_data)?; println!("-- Saved file!"); } diff --git a/src/formats/pfl_upg/mod.rs b/src/formats/pfl_upg/mod.rs index 97c8314..5f4339c 100644 --- a/src/formats/pfl_upg/mod.rs +++ b/src/formats/pfl_upg/mod.rs @@ -3,7 +3,7 @@ use std::any::Any; use crate::{AppContext, InputTarget}; use std::path::Path; -use std::io::{Cursor, Write}; +use std::io::{Cursor, Seek, SeekFrom, Write}; use std::fs::{self, File, OpenOptions}; use binrw::BinReaderExt; @@ -28,7 +28,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), B let signature = common::read_exact(&mut file, 128)?; let _ = common::read_exact(&mut file, 32)?; //unknown - let version_bytes = common::read_exact(&mut file, header.header_size as usize - 704)?; //704 is base header size + let version_bytes = common::read_exact(&mut file, 28)?; let version = common::string_from_bytes(&version_bytes); println!("\nVersion: {}", version); @@ -38,6 +38,8 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), B } println!("Data size: {}", header.data_size); + file.seek(SeekFrom::Start(header.header_size as u64))?; + let mut data; if header.is_encrypted() { println!("\nFile is encrypted."); diff --git a/src/keys.rs b/src/keys.rs index 4989472..cd28864 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -48,12 +48,16 @@ pub static RUF: &[(&str, &str)] = &[ //pfl upg pubkeys pub static PFLUPG: &[(&str, &str)] = &[ - ("TV520_1", "C41BE92C212BAC76B48261E2A1704028287DD7E121C11DA25F709E864FBDC1BD8C7F226F57605A4B42D768CDD629AF9E54011A0967AFC2826331406FB1E90321620738526EA0BEA59F1A0E612AE891C396112F13531F423DF02F94D1C871429549F4D5B30D9CA3EFDCC6D7A96849F7C1788DE8FAAEDD36560337008DF06D612F"), - ("TV520_2", "AFAF89062747CBB29343C4E4EA775E4CFDF5FAFCD92C9DD858A8725201BA54AB973BEFEE04EBF3046910FBBC78B10120AE16D80BA734931E97248BC6B1D4A909F087D37BC0A9C2210FF8A2BE44C00F31E4DD8713A364623637FF75EBBCE9D3A840DB67E0FA910F127F679496F6C21112E3E3AD4ACA459FDE1CC58E300682E6F9"), + ("TV520_1", "AFAF89062747CBB29343C4E4EA775E4CFDF5FAFCD92C9DD858A8725201BA54AB973BEFEE04EBF3046910FBBC78B10120AE16D80BA734931E97248BC6B1D4A909F087D37BC0A9C2210FF8A2BE44C00F31E4DD8713A364623637FF75EBBCE9D3A840DB67E0FA910F127F679496F6C21112E3E3AD4ACA459FDE1CC58E300682E6F9"), + ("TV520_2", "C41BE92C212BAC76B48261E2A1704028287DD7E121C11DA25F709E864FBDC1BD8C7F226F57605A4B42D768CDD629AF9E54011A0967AFC2826331406FB1E90321620738526EA0BEA59F1A0E612AE891C396112F13531F423DF02F94D1C871429549F4D5B30D9CA3EFDCC6D7A96849F7C1788DE8FAAEDD36560337008DF06D612F"), + ("TV543_Q543", "C5FD937B301A5CEF4B6C25F187728C99636515D058895FEA469496E2B24907FC7721648841F8AE4C618E215673D0C029752FA970B6F9A7F48C9331293D3B1D43E4DFC7B52914973642CD3E4EE0AD11F5254505038F95CACE0DF21FC769B34E134435D88AB617D2981F66EF45BBC7796CFB1086C5D5672E837204991FE53BC1CF"), ("TV543_Q548", "9E7DA389815251E82A84A1182807702E72A0B0E0FF707C8E73E2EA71F79D5FAAFFC6E0B90ED16E13A4289C78A7D3BDA90626162AAE169D7BE28D6A635585CC10639C4E312E288EB8F7C5A44518B7E8A26A45C5023C5078A972A4CC219CA020BAF524F7429257B7AD76E1B15390879064C6ED59CC1F20CC04EEC26C9CF7FC0727"), ("TV543_Q549", "F28ED197C5C40B7F7356D4EDD5526BC238C812EB59400087BBA2D6402B41BBE695EAAE5F6051F1B5EBAC1756F17FED9DA0678F2E6EE59ABF530847DB9249DD025D00FFC67B2ECFF320E99F5DA8A46B1E41479BA3B5CDC110D247B7AF31E887463C8E7A9929BE511C724FB7B4BACBF055F276ED53687E24543815D5437405B0CB"), - ("TV550", "D7EE8ED11AC048ED225D4F5F53F8509E55C5D94256A703C79E4CA78AE93ECE1639FE466363AD962BD6D6DE0C46FD19F363687C1D8A21820740A6E7FF87F41C4900DCE1E26EC122E5D4DFA76BFC8F296816B8D0910325E9DC5CBCC9579CF15FC0253EF7CF4919B7613491A5D7BF75DC1888531C458967FD1CF64B33139550BAA5"), + + ("TV550_Q552", "DEA689548D2C3542E1395063B9ED14F2A779B34237265E408B43DDBD57FC86260B7865C6BFA3A5F7D249F6BC82B5D8BD0FA31C8C094D3376241ADE1BD81D26354A4366F170073588BA23676CC7DCCD686C91C99C38290E2D27DA4BAE7E49FA3665DF2895D0BF81824287EDF131771E73380F45A4F6C5FF1C669154A6DD063BEB"), + ("TV550_Q555", "D7EE8ED11AC048ED225D4F5F53F8509E55C5D94256A703C79E4CA78AE93ECE1639FE466363AD962BD6D6DE0C46FD19F363687C1D8A21820740A6E7FF87F41C4900DCE1E26EC122E5D4DFA76BFC8F296816B8D0910325E9DC5CBCC9579CF15FC0253EF7CF4919B7613491A5D7BF75DC1888531C458967FD1CF64B33139550BAA5"), + ("Fusion", "ACD684155C7CCCB04372A8808514489FA9EE75D305987D1337420241FDBE0AE1F7CDFBB931C9D56C91D36F2CE79D222695B484FF42BCA12CE362C7C9ABBDEEC8E5D6107FADCF2D4DA5DF0693E13ACE54A18AEB21C051F6B62C075A1791985547C1CFF4FB5B6EA7E0A9405A1B2BB71EB89A9B209E0F62BF9794D673179C0E60F1"), //sony diff --git a/src/utils/compression.rs b/src/utils/compression.rs index 3e5fbe9..2841920 100644 --- a/src/utils/compression.rs +++ b/src/utils/compression.rs @@ -6,6 +6,7 @@ use lzma_rs::lzma_decompress; use lz4::block::decompress; use bzip2::read::BzDecoder; use liblzma::read::XzDecoder; +use zstd::stream::read::Decoder; use crate::utils::common; @@ -65,4 +66,11 @@ pub fn decompress_xz(compressed: &[u8]) -> std::io::Result> { let mut decompressed = Vec::new(); decoder.read_to_end(&mut decompressed)?; Ok(decompressed) +} + +pub fn decompress_zstd(compressed: &[u8]) -> io::Result> { + let mut decoder = Decoder::new(compressed)?; + let mut output = Vec::new(); + decoder.read_to_end(&mut output)?; + Ok(output) } \ No newline at end of file