From f993a3df70d05d85cb74227f72a70c475dd992b2 Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Wed, 10 Dec 2025 20:27:32 +0100 Subject: [PATCH] main: Add auto folder creation, mtk_pkg: add custom key support + some fixes., general: message costistency --- src/formats/mtk_pkg.rs | 64 ++++++++++++++++++++++--------- src/formats/mtk_upgrade_loader.rs | 2 +- src/formats/novatek.rs | 11 +++++- src/formats/nvt_timg.rs | 2 +- src/formats/rvp.rs | 2 +- src/formats/sony_bdp.rs | 2 +- src/keys.rs | 10 +++++ src/main.rs | 15 +++++--- 8 files changed, 78 insertions(+), 30 deletions(-) diff --git a/src/formats/mtk_pkg.rs b/src/formats/mtk_pkg.rs index f52200c..0f52165 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::keys; #[derive(BinRead)] struct Header { @@ -26,6 +27,7 @@ impl Header { fn product_name(&self) -> String { common::string_from_bytes(&self.product_name_bytes) } + } #[derive(BinRead)] @@ -38,6 +40,12 @@ impl PartEntry { fn name(&self) -> String { common::string_from_bytes(&self.name_bytes) } + fn is_valid(&self) -> bool { + self.name().is_ascii() + } + fn is_encrypted(&self) -> bool { + (self.flags & 1 << 0) == 1 << 0 + } } static HEADER_KEY: [u8; 16] = [ @@ -66,6 +74,7 @@ pub fn is_mtk_pkg_file(file: &File) -> bool { } pub fn extract_mtk_pkg(mut file: &File, output_folder: &str) -> Result<(), Box> { + let file_size = file.metadata()?.len(); let encrypted_header = common::read_exact(&mut file, 144)?; let header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?; let mut hdr_reader = Cursor::new(header); @@ -75,32 +84,56 @@ pub fn extract_mtk_pkg(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Resul let part_entry: PartEntry = file.read_le()?; let is_encrypted = if (part_entry.flags & 1 << 0) == 1 << 0 {true} else {false}; - println!("\n{} - {}, Size: {} {}", part_n, part_entry.name(), part_entry.size, if is_encrypted {"[ENCRYPTED]"} else {""} ); + println!("\n#{} - {}, Size: {} {}", part_n, part_entry.name(), part_entry.size, if is_encrypted {"[ENCRYPTED]"} else {""} ); let data = common::read_exact(&mut file, part_entry.size as usize)?; diff --git a/src/formats/novatek.rs b/src/formats/novatek.rs index 460c4a7..083255e 100644 --- a/src/formats/novatek.rs +++ b/src/formats/novatek.rs @@ -12,11 +12,18 @@ struct Header { #[br(count = 4)] _magic_bytes: Vec, #[br(count = 4)] _flags: Vec, _header_size: u32, - #[br(count = 40)] _unknown1: Vec, + _unused: u32, + #[br(count = 16)] firmware_name_bytes: Vec, + #[br(count = 20)] _unknown1: Vec, part_count: u32, _first_part_offset: u32, #[br(count = 116)] _unknown2: Vec, } +impl Header { + fn firmware_name(&self) -> String { + common::string_from_bytes(&self.firmware_name_bytes) + } +} #[derive(BinRead)] struct PartEntry { @@ -38,7 +45,7 @@ pub fn is_novatek_file(file: &File) -> bool { pub fn extract_novatek(mut file: &File, output_folder: &str) -> Result<(), Box> { let header: Header = file.read_le()?; - println!("Part count: {}", header.part_count); + println!("File info:\nFirmware name: {}\nPart count: {}", header.firmware_name(), header.part_count); let mut entries: Vec = Vec::new(); for _i in 0..header.part_count { diff --git a/src/formats/nvt_timg.rs b/src/formats/nvt_timg.rs index c4dbe11..386fad3 100644 --- a/src/formats/nvt_timg.rs +++ b/src/formats/nvt_timg.rs @@ -51,7 +51,7 @@ pub fn extract_nvt_timg(mut file: &File, output_folder: &str) -> Result<(), Box< }; let data = common::read_exact(&mut file, pimg.size as usize)?; - println!("\n({}) - {}, Size: {}, Dest: {}, Compression: {}", pimg_i, pimg.name(), pimg.size, pimg.dest_dev(), pimg.comp_type()); + println!("\n#{} - {}, Size: {}, Dest: {}, Compression: {}", pimg_i, pimg.name(), pimg.size, pimg.dest_dev(), pimg.comp_type()); let out_data; diff --git a/src/formats/rvp.rs b/src/formats/rvp.rs index 77dec12..9400fb7 100644 --- a/src/formats/rvp.rs +++ b/src/formats/rvp.rs @@ -46,7 +46,7 @@ pub fn extract_rvp(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box< continue } - println!("\n({}) - Offset: {}, Size: {}", i, entry.offset, entry.size); + println!("\n#{} - Offset: {}, Size: {}", i, entry.offset, entry.size); if i == 1 { first_entry_offset = entry.offset as u64; } diff --git a/src/keys.rs b/src/keys.rs index 8942e70..5f8ec9b 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -87,4 +87,14 @@ pub static PANA_DVD_KEYONLY: &[&str] = &[ //pana dvd keys (PANAEUSB pair (AES + cust)) pub static PANA_DVD_AESPAIR: &[(&str, &str)] = &[ ("62A39E1C5594AE09244EB326EF7938FA", "06C943F3B997F7E0"), //# 9 firmwares, ~2018 +]; + +//mtk pkg custom keys +//github.com/openlgtv/epk2extract +//key, iv, desc +pub static MTK_PKG_CUST: &[(&str, &str, &str)] = &[ + ("D378EAF81D378A801B556985789A7C31", "73079FD19183715E130858588479C652", "Philips 2012"), + ("47FBF8CAD62BB95AF3AD9509E5C2175D", "63120FB321B0410F216D6DC2D8641A11", "Philips 2013"), + ("1B569AA7D2E4CCE66584A7A3D8A45679", "A0E88D5D52A813260D3A34A14AA89416", "TPV_MTK2K17PLF_EU0"), + ("135AFB6DE91CD56496244BC7C0E08D63", "C6A38C89F0AF5637EB6E19D35E12E257", "TPV_MTK2K14PLF_EU1"), ]; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index bb4a484..5cfa10e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ use std::fs::{self, File}; #[derive(Parser, Debug)] struct Args { input_target: String, - output_folder: String, + output_folder: Option, } fn main() -> Result<(), Box> { @@ -19,8 +19,14 @@ fn main() -> Result<(), Box> { let target_path = args.input_target; println!("Input target: {}", target_path); + let path = PathBuf::from(target_path); - let output_path = args.output_folder; + + let output_path = if args.output_folder.is_some() { + args.output_folder.unwrap() + } else { + format!("_{}", path.file_name().and_then(|s| s.to_str()).unwrap()) + }; println!("Output folder: {}\n", output_path); let output_folder_path = PathBuf::from(&output_path); @@ -28,14 +34,13 @@ fn main() -> Result<(), Box> { if output_folder_path.is_dir() { let is_empty = fs::read_dir(&output_folder_path)?.next().is_none(); if !is_empty { - println!("Warning: Output folder exists and is NOT empty! Files may be overwritten!"); + println!("Warning: Output folder already exists and is NOT empty! Files may be overwritten!"); println!("Press Enter if you want to continue..."); io::stdin().read_line(&mut String::new())?; } } } - - let path = PathBuf::from(target_path); + if path.is_dir() { if formats::samsung_old::is_samsung_old_dir(&path) { println!("Samsung old firmware dir detected!\n");