mtk_pkg: add ZSTD decompression support, decomp part in memory, pfl_upg: add Q5521 key

This commit is contained in:
theubusu
2026-05-07 18:42:47 +02:00
parent b6a1454d76
commit eb216b1691
10 changed files with 146 additions and 99 deletions
Generated
+29
View File
@@ -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",
]
+2 -1
View File
@@ -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"
des = "0.8.1"
zstd = "0.13.3"
+2 -2
View File
@@ -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
}
}
+64 -48
View File
@@ -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<dyn std::error::Error>> {
let file_size = file.metadata()?.len();
pub fn decompress_mtk_to_file(data: &[u8], output_file: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
let file_size = file.metadata()?.len();
pub fn decompress_mtk_to_file_old(data: &[u8], output_file: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
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!")
}
+10 -14
View File
@@ -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<dyn Any>) -> 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(())
+10 -14
View File
@@ -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<dyn Any>) -> 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!");
}
+10 -15
View File
@@ -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<dyn Any>) -> 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!");
}
+4 -2
View File
@@ -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<dyn Any>) -> 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<dyn Any>) -> 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.");
+7 -3
View File
@@ -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
+8
View File
@@ -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<Vec<u8>> {
let mut decompressed = Vec::new();
decoder.read_to_end(&mut decompressed)?;
Ok(decompressed)
}
pub fn decompress_zstd(compressed: &[u8]) -> io::Result<Vec<u8>> {
let mut decoder = Decoder::new(compressed)?;
let mut output = Vec::new();
decoder.read_to_end(&mut output)?;
Ok(output)
}