From c1f5b7961de020ccbe313076907eefaeedbbfaba Mon Sep 17 00:00:00 2001 From: Pari Malam Date: Fri, 5 Jun 2026 21:02:40 +0800 Subject: [PATCH] + cleanup --- .github/workflows/rust.yml | 40 --------- .gitignore | 3 +- Cargo.toml | 8 +- README.md | 24 +++--- src/formats.rs | 12 +-- src/formats/amlogic/mod.rs | 13 +-- src/formats/android_ota_payload/mod.rs | 13 +-- src/formats/bdl/mod.rs | 9 +- src/formats/cd5/mod.rs | 13 +-- src/formats/epk/mod.rs | 9 +- src/formats/epk1/mod.rs | 13 +-- src/formats/epk2/mod.rs | 23 ++--- src/formats/epk2b/mod.rs | 9 +- src/formats/epk3/mod.rs | 21 ++--- src/formats/funai_bdp/include.rs | 2 +- src/formats/funai_bdp/mod.rs | 23 ++--- src/formats/funai_mstar/mod.rs | 14 +-- src/formats/funai_upg/mod.rs | 13 +-- src/formats/funai_upg_phl/mod.rs | 11 +-- src/formats/gx_dvb/mod.rs | 7 +- src/formats/invincible_image/mod.rs | 11 +-- src/formats/msd/msd_ouith_parser_old.rs | 86 ++++++++++--------- src/formats/msd/msd_ouith_parser_tizen_1_8.rs | 86 ++++++++++--------- src/formats/msd/msd_ouith_parser_tizen_1_9.rs | 78 +++++++++-------- src/formats/msd10/mod.rs | 31 +++---- src/formats/msd11/mod.rs | 21 ++--- src/formats/mstar/mod.rs | 43 ++++------ src/formats/mstar_secure_old/mod.rs | 8 +- src/formats/mtk_bdp/include.rs | 4 +- src/formats/mtk_bdp/mod.rs | 23 ++--- src/formats/mtk_pkg/include.rs | 8 +- src/formats/mtk_pkg/lzhs.rs | 19 ++-- src/formats/mtk_pkg/mod.rs | 23 ++--- src/formats/mtk_pkg_new/mod.rs | 21 ++--- src/formats/mtk_pkg_old/mod.rs | 17 ++-- src/formats/novatek/mod.rs | 7 +- src/formats/novatek_raw/mod.rs | 17 ++-- src/formats/nvt_timg/mod.rs | 17 ++-- src/formats/onkyo/crypto.rs | 2 +- src/formats/onkyo/mod.rs | 29 ++++--- src/formats/pana_dvd/mod.rs | 50 +++++------ src/formats/pana_dvd/util.rs | 8 +- src/formats/pfl_upg/include.rs | 14 +-- src/formats/pfl_upg/mod.rs | 24 +++--- src/formats/philips_bdp/mod.rs | 21 ++--- src/formats/pup/mod.rs | 23 ++--- src/formats/roku/mod.rs | 15 ++-- src/formats/ruf/include.rs | 6 +- src/formats/ruf/mod.rs | 19 ++-- src/formats/rvp/mod.rs | 23 ++--- src/formats/samsung_old/mod.rs | 17 ++-- src/formats/sdboot/mod.rs | 19 ++-- src/formats/sddl_sec/include.rs | 10 +-- src/formats/sddl_sec/mod.rs | 43 +++++----- src/formats/sddl_sec/util.rs | 16 ++-- src/formats/sdimage/mod.rs | 7 +- src/formats/slp/mod.rs | 21 ++--- src/formats/sony_bdp/mod.rs | 18 ++-- src/formats/tsb_bin/mod.rs | 15 ++-- src/formats/vestel/mod.rs | 29 ++++--- src/main.rs | 65 +++++++++----- src/utils/common.rs | 9 +- src/utils/compression.rs | 2 +- src/utils/global.rs | 3 +- src/utils/lzop.rs | 3 +- 65 files changed, 670 insertions(+), 641 deletions(-) delete mode 100644 .github/workflows/rust.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml deleted file mode 100644 index ee59b02..0000000 --- a/.github/workflows/rust.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Rust - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - - strategy: - matrix: - include: - - os: ubuntu-latest - label: linux - - os: windows-latest - label: windows - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v4 - - name: Build - run: cargo build --release - - - uses: actions/upload-artifact@v4 - with: - name: unixtract-${{ matrix.label }}-auto - path: | - target/release/unixtract - target/release/unixtract.exe - - - - - diff --git a/.gitignore b/.gitignore index ab23b67..8a6c12d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target __pycache__/ -.vscode \ No newline at end of file +.vscode +venv/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 603b4a1..57f5bd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,11 @@ version = "0.2.0" edition = "2024" description = "Firmware extractor for various file formats from TVs and AV devices" license = "GPL-3.0" -repository = "https://github.com/theubusu/unixtract" +repository = "https://github.com/Ap0dexMe0/unixtract" + +[features] +default = ["zstd"] +zstd = ["dep:zstd"] [dependencies] clap = { version = "4.5", features = ["derive"] } @@ -27,7 +31,7 @@ prost-types = "0.14.1" bzip2 = "0.6.1" liblzma = "0.4.5" des = "0.8.1" -zstd = "0.13.3" +zstd = { version = "0.13.3", optional = true } thiserror = "2.0" log = "0.4" env_logger = "0.11" \ No newline at end of file diff --git a/README.md b/README.md index e41da4f..8663a28 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ # unixtract Extractor for various file formats. +This project is a fork of [theubusu/unixtract](https://github.com/theubusu/unixtract), adding extra features and improvements. This is a tool that is able to extract the contents of various firmware package formats, mostly from TVs and AV devices. Built in Rust, and made to not depend on any external dependancies, only Rust crates. This way it can work on Windows, Linux and MacOS and even Android. Please note that this project is still under active development and errors may occur. Feel free to make an issue in that case, or if you have any feature request. - + **PLEASE NOTE** - this program is NOT, and will never be designed for re-packing the extracted files! +## About this fork +This fork extends the upstream project with additional features and bug fixes. For the original README and format support details, see the [upstream repository](https://github.com/theubusu/unixtract). + # Installation You can download the latest auto build for Windows and Linux x86-64 from [here](https://nightly.link/theubusu/unixtract/workflows/rust/main). Or, build from source, by downloading the code or cloning the respository and running `cargo build --release`. The binary will be saved in `target/release`. @@ -15,9 +19,10 @@ Or, build from source, by downloading the code or cloning the respository and ru Arguments: `` - The target to analyze/extract. `[OUTPUT_FOLDER]` - Folder to save extracted files to. -If an output folder is not provided, extracted files will be saved in folder `_`. +If an output folder is not provided, extracted files will be saved in folder `_`. Options: `-o, --options ` - Format specific or global(for all formats that implement it) options, see the list below for format specific options. You can use this multiple times to activate multiple options. + ## Global options `dump_dec_hdrs` - For formats with an encrypted header - dump the decrypted header(s). @@ -121,6 +126,7 @@ Tip: if you have split ROM (.ROM-00 and .ROM-01), extract both into the same fol ## MediaTek PKG (New) **Used in:** Newer MediaTek-based TVs (TCL, Hisense, Sony, Philips, CVT...) **Notes:** **Depends on keys** - see keys.rs (Keys for Philips and Sony included) +**Thanks to:** https://github.com/theubusu/mtkdec **Options:** `mtk_pkg:no_del_comp` - Don't delete LZHS compressed partition file after decompressing. ※ Support `dump_dec_hdrs` option @@ -132,14 +138,6 @@ Tip: if you have split ROM (.ROM-00 and .ROM-01), extract both into the same fol `mtk_pkg:no_del_comp` - Don't delete LZHS compressed partition file after decompressing. ※ Support `dump_dec_hdrs` option -## MediaTek PKG -**Used in:** MediaTek-based TVs (Sony, Philips, Panasonic, Sharp...) -**Notes:** All files should be supported, decryption + decompression, however some Philips files use custom keys - most are included some could be missing -**Thanks to:** https://github.com/openlgtv/epk2extract -**Options:** -`mtk_pkg:no_del_comp` - Don't delete LZHS compressed partition file after decompressing. -※ Support `dump_dec_hdrs` option - ## Novatek PKG (NFWB) **Used in:** Some older Novatek-based TVs (LG, Philips) **Notes:** None, all files should be supported. @@ -204,16 +202,16 @@ Tip: if you have split ROM (.ROM-00 and .ROM-01), extract both into the same fol ## SDDL.SEC **Used in:** Panasonic TVs -**Notes:** None, all files should be supported. +**Notes:** None, all files should be supported. **Options:** `sddl_sec:save_extra` - Save SDIT.FDI and .TXT files that are not extracted by default. `sddl_sec:split_peaks` - Split PEAKS module into partitions (only on older files). This will also automatically decompress compressed partitions. -`sddl_sec:no_decomp_peaks` - Do not automatically decompress partitions when splitting PEAKS with above option. +`sddl_sec:no_decomp_peaks` - Do not automatically decompress partitions when splitting PEAKS with above option. **Base:** https://github.com/theubusu/sddl_dec ## SDImage (SDImage.bin) **Used in:** Some 2010 USA Panasonic TVs -**Notes:** Decryption is not yet supported. +**Notes:** Decryption is not yet supported. ## SLP **Used in:** Samsung Tizen-based NX series cameras diff --git a/src/formats.rs b/src/formats.rs index 6c2ca28..8c62c8c 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -52,11 +52,11 @@ pub mod gx_dvb; pub mod onkyo; pub mod philips_bdp; pub mod tsb_bin; - pub mod pup; - pub mod vestel; - pub mod novatek_raw; +pub mod pup; +pub mod vestel; +pub mod novatek_raw; - pub mod msd; +pub mod msd; pub mod msd10; pub mod msd11; @@ -77,7 +77,7 @@ pub mod mtk_bdp; /// means that container formats (which embed other formats) must appear /// before their inner formats to avoid misdetection. pub fn get_registry() -> Vec { - return vec![ + vec![ Format { name: "mstar", detector_func: crate::formats::mstar::is_mstar_file, @@ -289,4 +289,4 @@ pub fn get_registry() -> Vec { extractor_func: crate::formats::novatek_raw::extract_novatek_raw, }, ] -} \ No newline at end of file +} diff --git a/src/formats/amlogic/mod.rs b/src/formats/amlogic/mod.rs index a704c70..8f3cce2 100644 --- a/src/formats/amlogic/mod.rs +++ b/src/formats/amlogic/mod.rs @@ -10,6 +10,7 @@ use binrw::BinReaderExt; use crate::utils::common; use crate::utils::sparse::unsparse_to_file; use include::*; +use log::info; pub fn is_amlogic_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -26,7 +27,7 @@ pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Box) -> Result<(), B let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: ImageHeader = file.read_le()?; - println!("File info -\nImage size: {}\nItem align size: {}\nItem count: {}\nFormat version: {}", + info!("File info -\nImage size: {}\nItem align size: {}\nItem count: {}\nFormat version: {}", header.image_size, header.item_align_size, header.item_count, header.version); if header.version != 2 { @@ -41,13 +42,13 @@ pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Box) -> Result<(), B } for (i, item) in items.iter().enumerate() { - println!("\n({}/{}) - {}, Type: {}, Offset: {}, Size: {} {}", + info!("\n({}/{}) - {}, Type: {}, Offset: {}, Size: {} {}", i+1, header.item_count, item.name(), item.item_type(), item.offset_in_image, item.item_size, if item.is_sparse() {"[SPARSE]"} else {""}); if item.item_type() == "VERIFY" { //verify item is SHA1 of partition item let sum_bytes = common::read_file(&file, item.offset_in_image, item.item_size as usize)?; let sum = common::string_from_bytes(&sum_bytes); - println!("- Checksum for {}: {}", item.name(), sum); + info!("- Checksum for {}: {}", item.name(), sum); } else { let data = common::read_file(&file, item.offset_in_image, item.item_size as usize)?; @@ -57,15 +58,15 @@ pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Box) -> Result<(), B fs::create_dir_all(&app_ctx.output_dir)?; if item.is_sparse() { - println!("- Unsparsing..."); + info!("- Unsparsing..."); unsparse_to_file(&data, output_path)?; - println!("-- Saved file!"); + info!("-- Saved file!"); continue } else { let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; - println!("- Saved file!"); + info!("- Saved file!"); } } diff --git a/src/formats/android_ota_payload/mod.rs b/src/formats/android_ota_payload/mod.rs index c5b72ef..d450241 100644 --- a/src/formats/android_ota_payload/mod.rs +++ b/src/formats/android_ota_payload/mod.rs @@ -1,3 +1,5 @@ +use log::info; + mod include; mod android_ota_update_metadata; use std::any::Any; @@ -29,7 +31,7 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box) -> let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: Header = file.read_be()?; - println!("File info:\nFormat version: {}\nManifest size: {}", header.file_format_version, header.manifest_size); + info!("File info:\nFormat version: {}\nManifest size: {}", header.file_format_version, header.manifest_size); if header.file_format_version != 2 { return Err("Unsupported format version! (Only 2 is supported right now)".into()); @@ -42,7 +44,7 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box) -> for (i, partition) in manifest.partitions.into_iter().enumerate() { let operation_count = partition.operations.len(); - println!("\n#{} - {}, Size: {}, Operations: {}", + info!("\n#{} - {}, Size: {}, Operations: {}", i + 1, partition.partition_name, partition.new_partition_info.unwrap().size.unwrap(), operation_count); fs::create_dir_all(&app_ctx.output_dir)?; @@ -60,9 +62,8 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box) -> //because the amount of operations can reach up to the thousands, i think its best to update the current line //to not clog up the terminal and so you know what the program is actually doing - print!("\r- ({}/{}) - {}({}), Offset: {}, Size: {}", + info!("- ({}/{}) - {}({}), Offset: {}, Size: {}", i + 1, operation_count, operation_name_str, operation.r#type, offset, size); - std::io::stdout().flush()?; let data = common::read_file(&file, offset, size as usize)?; @@ -76,13 +77,13 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box) -> else if operation.r#type == 8 { //REPLACE_XZ - decompress with xz and write out_data = decompress_xz(&data)?; } else { - println!("-- Unsupported operation!"); + info!("-- Unsupported operation!"); break } out_file.write_all(&out_data)?; } - println!("\n-- Saved!"); + info!("\n-- Saved!"); } Ok(()) diff --git a/src/formats/bdl/mod.rs b/src/formats/bdl/mod.rs index 68e51ae..7cf6ae6 100644 --- a/src/formats/bdl/mod.rs +++ b/src/formats/bdl/mod.rs @@ -9,6 +9,7 @@ use binrw::BinReaderExt; use crate::utils::common; use include::*; +use log::info; pub fn is_bdl_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -26,7 +27,7 @@ pub fn extract_bdl(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box = Vec::new(); @@ -39,7 +40,7 @@ pub fn extract_bdl(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box = Vec::new(); @@ -53,7 +54,7 @@ pub fn extract_bdl(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box) -> Result<(), Box Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -32,7 +33,7 @@ pub fn extract_cd5(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box) -> Result<(), Box) -> Result<(), Box) -> Result<(), Box bool { pub fn extract_epk(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; let versions = common::read_file(&file, 1712, 36)?; let platform_version = common::string_from_bytes(&versions[4..20]); let sdk_version = common::string_from_bytes(&versions[20..36]); - println!("Platform version: {}\nSDK version: {}", platform_version, sdk_version); + info!("Platform version: {}\nSDK version: {}", platform_version, sdk_version); file.seek(std::io::SeekFrom::Start(0))?; if ctx.epk_version == 2 { - println!("EPK2 detected!\n"); + info!("EPK2 detected!\n"); formats::epk2::extract_epk2(app_ctx, Box::new(()))?; } else if ctx.epk_version == 3 { - println!("EPK3 detected!\n"); + info!("EPK3 detected!\n"); formats::epk3::extract_epk3(app_ctx, Box::new(()))?; } diff --git a/src/formats/epk1/mod.rs b/src/formats/epk1/mod.rs index 0e6e15f..81fb94f 100644 --- a/src/formats/epk1/mod.rs +++ b/src/formats/epk1/mod.rs @@ -9,6 +9,7 @@ use binrw::BinReaderExt; use crate::utils::common; use include::*; +use log::info; pub fn is_epk1_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -30,10 +31,10 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let init_pak_count = u32::from_le_bytes(init_pak_count_bytes.try_into().unwrap()); if init_pak_count > 256 { - println!("\nBig endian EPK1 detected."); + info!("\nBig endian EPK1 detected."); epk1_type = Epk1Type::BigEndian; } else if init_pak_count < 33 { - println!("\nLittle endian EPK1 detected."); + info!("\nLittle endian EPK1 detected."); epk1_type = Epk1Type::LittleEndian; } else { return Err("Unknown EPK1 variant!".into()); @@ -60,7 +61,7 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let version = common::read_exact(&mut file, 4)?; - println!("EPK info -\nData size: {}\nPak count: {}\nVersion: {:02x?}.{:02x?}.{:02x?}", + info!("EPK info -\nData size: {}\nPak count: {}\nVersion: {:02x?}.{:02x?}.{:02x?}", header.file_size, header.pak_count, version[1], version[2], version[3]); } else if epk1_type == Epk1Type::LittleEndian { @@ -91,7 +92,7 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let ota_id_bytes = common::read_exact(&mut file, 32)?; let ota_id = common::string_from_bytes(&ota_id_bytes); - println!("EPK info -\nData size: {}\nHeader size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}", + info!("EPK info -\nData size: {}\nHeader size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}", header.file_size, header_size, header.pak_count, ota_id, version[2], version[1], version[0]); } @@ -99,7 +100,7 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< file.seek(SeekFrom::Start(pak.offset as u64))?; let pak_header: PakHeader = if epk1_type == Epk1Type::BigEndian {file.read_be()?} else {file.read_le()?}; - println!("\n({}/{}) - {}, Offset: {}, Size: {}, Platform: {}", + info!("\n({}/{}) - {}, Offset: {}, Size: {}, Platform: {}", i + 1, paks.len(), pak_header.pak_name(), pak.offset, pak_header.image_size, pak_header.platform_id()); let data = common::read_exact(&mut file, pak_header.image_size as usize)?; @@ -109,7 +110,7 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; - println!("- Saved file!"); + info!("- Saved file!"); } Ok(()) diff --git a/src/formats/epk2/mod.rs b/src/formats/epk2/mod.rs index 169747c..5a32617 100644 --- a/src/formats/epk2/mod.rs +++ b/src/formats/epk2/mod.rs @@ -13,6 +13,7 @@ use crate::keys; use crate::formats::epk::find_key; use crate::utils::aes::decrypt_aes_ecb_auto; use include::*; +use log::info; pub fn is_epk2_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -37,14 +38,14 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< //check if header is encrypted let epak = &stored_header[0..4]; // epak magic if epak == b"epak" { - println!("Header is not encrypted."); + info!("Header is not encrypted."); header = stored_header; } else { - println!("Header is encrypted..."); - println!("\nFinding key..."); + info!("Header is encrypted..."); + info!("\nFinding key..."); //find the key, knowing that the header should start with "epak" if let Some((key_name, key_bytes)) = find_key(&keys::EPK, &stored_header, b"epak")? { - println!("Found valid key: {}", key_name); + info!("Found valid key: {}", key_name); matching_key = Some(key_bytes); header = decrypt_aes_ecb_auto(matching_key.as_ref().unwrap(), &stored_header)?; opt_dump_dec_hdr(app_ctx, &header, "header")?; @@ -57,7 +58,7 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let mut hdr_reader = Cursor::new(header); let hdr: Header = hdr_reader.read_le()?; - println!("\nEPK info -\nData size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}.{:02x?}\n", + info!("\nEPK info -\nData size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}.{:02x?}\n", hdr.file_size, hdr.pak_count, hdr.ota_id(), hdr.version[3], hdr.version[2], hdr.version[1], hdr.version[0]); let mut paks: Vec = Vec::new(); @@ -65,7 +66,7 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< for i in 0..hdr.pak_count { let pak: PakEntry = hdr_reader.read_le()?; //here the accounted for signature is the one at the beginning of the EPK file - println!("Pak {} - {}, offset: {}, size: {}, segment size: {}", i + 1, pak.name(), pak.offset + SIGNATURE_SIZE, pak.size, pak.segment_size); + info!("Pak {} - {}, offset: {}, size: {}, segment size: {}", i + 1, pak.name(), pak.offset + SIGNATURE_SIZE, pak.size, pak.segment_size); paks.push(Pak { offset: pak.offset + SIGNATURE_SIZE, _size: pak.size, name: pak.name() }); } @@ -82,10 +83,10 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< //the file's header was not encrypted so we dont have the key yet if matching_key.is_none() { - println!("\nFinding key..."); + info!("\nFinding key..."); //find the key, knowing that the header should start with with the paks name if let Some((key_name, key_bytes)) = find_key(&keys::EPK, &encrypted_header, pak.name.as_bytes())? { - println!("Found correct key: {}", key_name); + info!("Found correct key: {}", key_name); matching_key = Some(key_bytes); } else { return Err("No valid key found!".into()); @@ -96,7 +97,7 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let mut pak_header_reader = Cursor::new(decrypt_aes_ecb_auto(&matching_key_bytes, &encrypted_header)?); let mut pak_header: PakHeader = pak_header_reader.read_le()?; - println!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}", + info!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}", pak_n + 1, paks.len(), pak.name, pak_header.image_size, pak_header.segment_count, pak_header.platform_id()); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", pak.name)); @@ -137,14 +138,14 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< pak_header.segment_size }; - println!("- Segment {}/{} - Size: {}", i + 1, pak_header.segment_count, actual_segment_size); + info!("- Segment {}/{} - Size: {}", i + 1, pak_header.segment_count, actual_segment_size); let segment_data = common::read_exact(&mut file, actual_segment_size as usize)?; let out_data = decrypt_aes_ecb_auto(&matching_key_bytes, &segment_data)?; out_file.write_all(&out_data)?; - println!("-- Saved to file!"); + info!("-- Saved to file!"); } } diff --git a/src/formats/epk2b/mod.rs b/src/formats/epk2b/mod.rs index 3cd5602..a42e0af 100644 --- a/src/formats/epk2b/mod.rs +++ b/src/formats/epk2b/mod.rs @@ -9,6 +9,7 @@ use binrw::BinReaderExt; use crate::utils::common; use include::*; +use log::info; pub fn is_epk2b_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -26,7 +27,7 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: EpkHeader = file.read_le()?; - println!("EPK info -\nData size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}", + info!("EPK info -\nData size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}", header.file_size, header.pak_count, header.ota_id(), header.version[2], header.version[1], header.version[0]); let mut paks: Vec = Vec::new(); @@ -49,7 +50,7 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let mut all_segment_size = 0; - println!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}", + info!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}", i + 1, paks.len(), pak_header.pak_name(), pak_header.image_size, pak_header.segment_count, pak_header.platform_id()); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", pak_header.pak_name())); @@ -66,7 +67,7 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box return Err(format!("Unexpected segment index in pak header!, expected: {}, got: {}", i , pak_header.segment_index).into()); } - println!("- Segment {}/{} - Size: {}", i + 1, pak_header.segment_count, pak_header.segment_size); + info!("- Segment {}/{} - Size: {}", i + 1, pak_header.segment_count, pak_header.segment_size); let out_data = common::read_exact(&mut file, pak_header.segment_size as usize)?; all_segment_size += pak_header.segment_size; @@ -80,7 +81,7 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box out_file.write_all(&out_data[..segment_limit as usize])?; - println!("-- Saved to file!"); + info!("-- Saved to file!"); } } diff --git a/src/formats/epk3/mod.rs b/src/formats/epk3/mod.rs index ee42e61..e908b0f 100644 --- a/src/formats/epk3/mod.rs +++ b/src/formats/epk3/mod.rs @@ -13,6 +13,7 @@ use crate::keys; use crate::formats::epk::find_key; use crate::utils::aes::decrypt_aes_ecb_auto; use include::*; +use log::info; pub fn is_epk3_file(_app_ctx: &AppContext) -> Result>, Box> { Ok(None) @@ -27,11 +28,11 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let mut new_type = false; let matching_key: Option>; - println!("Finding key..."); + info!("Finding key..."); // find the key, knowing that the header should start with "EPK3" (old type 128 byte signature) if let Some((key_name, key_bytes)) = find_key(&keys::EPK, &stored_header[128..], b"EPK3")? { - println!("Found valid key: {}", key_name); + info!("Found valid key: {}", key_name); matching_key = Some(key_bytes); _header_signature = &stored_header[..128]; header = decrypt_aes_ecb_auto(matching_key.as_ref().unwrap(), &stored_header[128..])?; @@ -39,7 +40,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< //try for new format epk3 (new type 256 byte signature) } else if let Some((key_name, key_bytes)) = find_key(&keys::EPK, &stored_header[256..], b"EPK3")? { - println!("Found valid key: {}", key_name); + info!("Found valid key: {}", key_name); matching_key = Some(key_bytes); _header_signature = &stored_header[..256]; header = decrypt_aes_ecb_auto(matching_key.as_ref().unwrap(), &stored_header[256..])?; @@ -59,16 +60,16 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let mut hdr_reader = Cursor::new(header); let hdr: Header = hdr_reader.read_le()?; - println!("\nEPK info -\nEPK3 type: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}.{:02x?}\nPackage Info size: {}", + info!("\nEPK info -\nEPK3 type: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}.{:02x?}\nPackage Info size: {}", if new_type {"New"} else {"Old"}, hdr.ota_id(), hdr.version[3], hdr.version[2], hdr.version[1], hdr.version[0], hdr.package_info_size); if new_type { let ex_hdr: HeaderNewEx = hdr_reader.read_le()?; - println!("Encrypt type: {}\nUpdate type: {}\nUpdate platform version: {:.6}\nCompatible minimum version: {:.6}\nNeed to check compatible version: {}", + info!("Encrypt type: {}\nUpdate type: {}\nUpdate platform version: {:.6}\nCompatible minimum version: {:.6}\nNeed to check compatible version: {}", ex_hdr.encrypt_type(), ex_hdr.update_type(), ex_hdr.update_platform_version, ex_hdr.compatible_minimum_version, ex_hdr.need_to_check_compatible_version); } - println!(); + info!(""); let _platform_versions = common::read_exact(&mut file, 36)?; let _pkg_info_signature = common::read_exact(&mut file, signature_size)?; @@ -81,7 +82,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let mut pkg_info_reader = Cursor::new(pkg_info); let pkg_info_hdr: PkgInfoHeader = pkg_info_reader.read_le()?; - println!("Package info list size: {}\nPackage info count: {}", + info!("Package info list size: {}\nPackage info count: {}", pkg_info_hdr.package_info_list_size, pkg_info_hdr.package_info_count); if new_type {let _unknown = common::read_exact(&mut pkg_info_reader, 4)?;}; //new type has additional value @@ -90,7 +91,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< while (pkg_info_reader.position() as usize) < pkg_info_reader.get_ref().len() { let mut entry: PkgInfoEntry = pkg_info_reader.read_le()?; - println!("\n({}) - {}, Size: {}, Segments: {}", + info!("\n({}) - {}, Size: {}, Segments: {}", pak_i, entry.package_name(), entry.package_size, entry.segment_count); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", entry.package_name())); @@ -102,7 +103,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< entry = pkg_info_reader.read_le()?; } - println!("- Segment {}/{}, Size: {}", entry.segment_index + 1, entry.segment_count, entry.segment_size); + info!("- Segment {}/{}, Size: {}", entry.segment_index + 1, entry.segment_count, entry.segment_size); let _segment_signature = common::read_exact(&mut file, signature_size)?; @@ -111,7 +112,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< out_file.write_all(&out_data[extra_segment_size..])?; - println!("-- Saved to file!"); + info!("-- Saved to file!"); } pak_i += 1; } diff --git a/src/formats/funai_bdp/include.rs b/src/formats/funai_bdp/include.rs index 01da080..054f19b 100644 --- a/src/formats/funai_bdp/include.rs +++ b/src/formats/funai_bdp/include.rs @@ -40,7 +40,7 @@ pub fn is_cmpr(data: &[u8], size: u32) -> bool { if count > size || data_size > size {return false}; - return (size - 0x20 - (count*12) - 12) == data_size; + (size - 0x20 - (count*12) - 12) == data_size } //image rom diff --git a/src/formats/funai_bdp/mod.rs b/src/formats/funai_bdp/mod.rs index fd0824a..a773f75 100644 --- a/src/formats/funai_bdp/mod.rs +++ b/src/formats/funai_bdp/mod.rs @@ -12,6 +12,7 @@ use crate::formats::funai_upg::funai_des::funai_des_decrypt; use include::*; use crate::keys; use crate::utils::compression::decompress_zlib; +use log::info; pub struct FunaiBdpContext { key: u32, @@ -36,12 +37,12 @@ pub fn is_funai_bdp_file(app_ctx: &AppContext) -> Result>, B pub fn extract_funai_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; let mut data = Vec::new(); //to decrypt entire file file.read_to_end(&mut data)?; - println!("Decrypting file..."); + info!("Decrypting file..."); data = funai_des_decrypt(&data, ctx.key); let mut file_reader = Cursor::new(data); @@ -56,17 +57,17 @@ pub fn extract_funai_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), } for (i, entry) in entries.iter().enumerate() { - println!("\n({}/{}) - {}, Offset: {}, Size: {}", i +1, index_entry_count, entry.name(), entry.offset, entry.size); + info!("\n({}/{}) - {}, Offset: {}, Size: {}", i +1, index_entry_count, entry.name(), entry.offset, entry.size); file_reader.seek(SeekFrom::Start(entry.offset as u64))?; let mut data = common::read_exact(&mut file_reader, entry.size as usize)?; if is_cmpr(&data, entry.size) { - println!("- cmpr detected!, 'uncompressing' data..."); + info!("- cmpr detected!, 'uncompressing' data..."); data = uncmpr_data(&data)?; } else if entry.name().ends_with("_image_rom") { - println!("- Decompressing image ROM..."); + info!("- Decompressing image ROM..."); data = uncomp_image_rom(&data)?; } else {//strip partition name at start @@ -80,7 +81,7 @@ pub fn extract_funai_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), out_file.write_all(&data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } Ok(()) @@ -90,13 +91,13 @@ pub fn uncmpr_data(data: &[u8]) -> Result, Box> { let mut data_reader = Cursor::new(data); let _part_name = read_exact(&mut data_reader, 0x20)?; let cmpr_header: CmprHeader = data_reader.read_le()?; - println!("[cmpr] out chk: {:02x}, count: {}, data size: {}", + info!("[cmpr] out chk: {:02x}, count: {}, data size: {}", cmpr_header.out_checksum, cmpr_header.count, cmpr_header.data_size); let mut out_data: Vec = Vec::new(); for (i, entry) in cmpr_header.entries.iter().enumerate() { - println!("[cmpr] ({}/{}) size: {}, mode: {}, fill: {:02x}", + info!("[cmpr] ({}/{}) size: {}, mode: {}, fill: {:02x}", i+1, cmpr_header.count, entry.size, entry.mode, entry.fill); let mut data; @@ -120,20 +121,20 @@ pub fn uncomp_image_rom(data: &[u8]) -> Result, Box = Vec::new(); for (i, entry) in header.entries.iter().enumerate() { let offset = header.start_offset + entry.offset; - println!("[rom] ({}/{}) offset: {}, size: {}", + info!("[rom] ({}/{}) offset: {}, size: {}", i+1, header.count, offset, entry.size); data_reader.seek(SeekFrom::Start(offset as u64))?; let compr_data = read_exact(&mut data_reader, entry.size as usize)?; - println!("[rom] - Decompressing..."); + info!("[rom] - Decompressing..."); let mut decomp_data = decompress_zlib(&compr_data)?; out_data.append(&mut decomp_data); diff --git a/src/formats/funai_mstar/mod.rs b/src/formats/funai_mstar/mod.rs index 58e3286..e6b9925 100644 --- a/src/formats/funai_mstar/mod.rs +++ b/src/formats/funai_mstar/mod.rs @@ -9,6 +9,7 @@ use std::io::{Write, Seek, SeekFrom}; use crate::utils::common; use crate::formats::mstar_secure_old::{is_mstar_secure_old_file, extract_mstar_secure_old}; use include::*; +use log::info; struct FunaiMstarCtx { data_offset: u64, @@ -38,10 +39,10 @@ pub fn is_funai_mstar_file(app_ctx: &AppContext) -> Result>, pub fn extract_funai_mstar(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; let info = InfoStruct::from_str(ctx.info_str).unwrap(); - println!("File info -\nFile code: {}\nBrand name: {}\nModel name: {}\nSoC Version: {}\nFRC Version: {}", + info!("File info -\nFile code: {}\nBrand name: {}\nModel name: {}\nSoC Version: {}\nFRC Version: {}", info.file_code, info.brand_name, info.model_name, info.soc_version, info.frc_version); let payloads: Vec<(&str, usize)> = vec![("SoC", info.soc_size), ("FRC60", info.frc60_size), ("FRC120", info.frc120_size)]; @@ -53,7 +54,7 @@ pub fn extract_funai_mstar(app_ctx: &AppContext, ctx: Box) -> Result<() if size == 0 { continue } - println!("\n#{} - {}, Size: {}", p_i+1, name, size); + info!("\n#{} - {}, Size: {}", p_i+1, name, size); let data = common::read_exact(&mut file, size)?; @@ -62,7 +63,7 @@ pub fn extract_funai_mstar(app_ctx: &AppContext, ctx: Box) -> Result<() let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?; out_file.write_all(&data)?; - println!("- Saved file!"); + info!("- Saved file!"); //extract SoC which (should be) mstar_secure_old, this is just a simple container for that format ( so we will go funai_mstar -> mstar_secure_old -> mstar (DUMB?) ) if name == "SoC" { @@ -73,11 +74,12 @@ pub fn extract_funai_mstar(app_ctx: &AppContext, ctx: Box) -> Result<() options: app_ctx.options.clone(), dry_run: app_ctx.dry_run, quiet: app_ctx.quiet, + verbose: app_ctx.verbose, }; - //do check and extarct + //do check and extract if let Some(result) = is_mstar_secure_old_file(&in_ctx)? { - println!("- Extracting mstar_secure_old..."); + info!("- Extracting mstar_secure_old..."); extract_mstar_secure_old(&in_ctx, result)?; }; diff --git a/src/formats/funai_upg/mod.rs b/src/formats/funai_upg/mod.rs index 1e5d691..00ca134 100644 --- a/src/formats/funai_upg/mod.rs +++ b/src/formats/funai_upg/mod.rs @@ -12,6 +12,7 @@ use crate::utils::common; use include::*; use crate::keys; use funai_des::funai_des_decrypt; +use log::info; pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -30,7 +31,7 @@ pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), let header: Header = file.read_le()?; let mut key: Option = None; - println!("File info:\nFile size: {}\nEntry count: {}", header.file_size, header.entry_count); + info!("File info:\nFile size: {}\nEntry count: {}", header.file_size, header.entry_count); for i in 0..header.entry_count { let entry: Entry = file.read_le()?; @@ -47,7 +48,7 @@ pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), let decrypted = funai_des_decrypt(&data, key_u32); if is_valid_ver_string(&decrypted) { - println!("Matched key: {}\nFirmware info: {}", + info!("Matched key: {}\nFirmware info: {}", key_hex, common::string_from_bytes(&decrypted)); key = Some(key_u32); break @@ -55,14 +56,14 @@ pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), } } - println!("\n({}/{}) - Type: {}, Size: {}", i + 1, header.entry_count, entry.entry_type, entry.entry_size); + info!("\n({}/{}) - Type: {}, Size: {}", i + 1, header.entry_count, entry.entry_type, entry.entry_size); if entry.encryption_flag == 1 { if let Some(key_u32) = key { - println!("- Decrypting..."); + info!("- Decrypting..."); data = funai_des_decrypt(&data, key_u32); } else { - println!("- Warning! Failed to find decryption key, saving encrypted data") + info!("- Warning! Failed to find decryption key, saving encrypted data") } } @@ -72,7 +73,7 @@ pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } Ok(()) diff --git a/src/formats/funai_upg_phl/mod.rs b/src/formats/funai_upg_phl/mod.rs index e2a335d..4a06e21 100644 --- a/src/formats/funai_upg_phl/mod.rs +++ b/src/formats/funai_upg_phl/mod.rs @@ -12,6 +12,7 @@ use crate::keys; use crate::formats::funai_upg::funai_des::funai_des_decrypt; use crate::formats::funai_upg::include::is_valid_ver_string; use include::*; +use log::info; pub fn is_funai_upg_phl_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -46,7 +47,7 @@ pub fn extract_funai_upg_phl(app_ctx: &AppContext, _ctx: Box) -> Result let decrypted = funai_des_decrypt(&data, key_u32); if is_valid_ver_string(&decrypted[..16]) { - println!("Matched key: {}\nFirmware info: {}\nFirmware date: {}", + info!("Matched key: {}\nFirmware info: {}\nFirmware date: {}", key_hex, common::string_from_bytes(&decrypted[..16]), common::string_from_bytes(&decrypted[16..])); key = Some(key_u32); break @@ -54,14 +55,14 @@ pub fn extract_funai_upg_phl(app_ctx: &AppContext, _ctx: Box) -> Result } } - println!("\n#{} - Type: {}, Size: {}", i + 1, entry.body_type, entry.size); + info!("\n#{} - Type: {}, Size: {}", i + 1, entry.body_type, entry.size); if let Some(key_u32) = key { - println!("- Decrypting..."); + info!("- Decrypting..."); data = funai_des_decrypt(&data, key_u32); } else { - println!("- Warning! Failed to find decryption key, saving encrypted data") + info!("- Warning! Failed to find decryption key, saving encrypted data") } let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", entry.body_type)); @@ -70,7 +71,7 @@ pub fn extract_funai_upg_phl(app_ctx: &AppContext, _ctx: Box) -> Result let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } diff --git a/src/formats/gx_dvb/mod.rs b/src/formats/gx_dvb/mod.rs index ed9e974..f133a5d 100644 --- a/src/formats/gx_dvb/mod.rs +++ b/src/formats/gx_dvb/mod.rs @@ -8,6 +8,7 @@ use crate::AppContext; use crate::utils::common; use binrw::BinReaderExt; use include::*; +use log::info; pub fn is_gx_dvb_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -26,10 +27,10 @@ pub fn extract_gx_dvb(app_ctx: &AppContext, _ctx: Box) -> Result<(), Bo file.seek(SeekFrom::Start(TABLE_OFFSET))?; let table: PartTable = file.read_be()?; - println!("Part count: {}", table.part_count); + info!("Part count: {}", table.part_count); for (i, part) in table.part_entries.iter().enumerate() { - println!("\n({}/{}) - {}, Offset: {}, Total size: {}, Used size: {}", + info!("\n({}/{}) - {}, Offset: {}, Total size: {}, Used size: {}", i+1, table.part_count, part.name(), part.start, part.total_size, part.used_size); let data = common::read_file(&file, part.start as u64, part.total_size as usize)?; @@ -39,7 +40,7 @@ pub fn extract_gx_dvb(app_ctx: &AppContext, _ctx: Box) -> Result<(), Bo let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; - println!("- Saved file!"); + info!("- Saved file!"); } Ok(()) diff --git a/src/formats/invincible_image/mod.rs b/src/formats/invincible_image/mod.rs index 5164476..381dce4 100644 --- a/src/formats/invincible_image/mod.rs +++ b/src/formats/invincible_image/mod.rs @@ -10,6 +10,7 @@ use binrw::BinReaderExt; use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; use crate::utils::common; use include::*; +use log::info; pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -26,14 +27,14 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Box) -> Res let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: Header = file.read_le()?; - println!("File info -\nKey ID: {}\nVersion(1): {}\nVersion(2): {}\nVersion(3): {}\nVersion(4): {}\nData size: {}\nChunk count: {}\nChunk size: {}\n\nPayload Count: {}", + info!("File info -\nKey ID: {}\nVersion(1): {}\nVersion(2): {}\nVersion(3): {}\nVersion(4): {}\nData size: {}\nChunk count: {}\nChunk size: {}\n\nPayload Count: {}", header.file_infos[0], header.ver1(), header.ver2(), header.ver3(), header.ver4(), header.data_size, header.chunk_count, header.chunk_size, header.payload_count); let mut entries: Vec = Vec::new(); for i in 0..header.payload_count { let entry: Entry = file.read_le()?; - println!("{}. {} - Start offset: {}, Size: {}", + info!("{}. {} - Start offset: {}, Size: {}", i + 1, entry.name(), entry.start_offset, entry.size); entries.push(entry); } @@ -65,13 +66,13 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Box) -> Res } } - println!("\nDecrypting data..."); + info!("\nDecrypting data..."); let decrypted_data = decrypt_aes128_cbc_pcks7(&encrypted_data, &aes_key, &aes_iv)?; let mut data_reader = Cursor::new(decrypted_data); for (i , entry) in entries.iter().enumerate() { - println!("\n({}/{}) - {}, Size: {}, Start offset: {}", i+1, header.payload_count, entry.name(), entry.size, entry.start_offset); + info!("\n({}/{}) - {}, Size: {}, Start offset: {}", i+1, header.payload_count, entry.name(), entry.size, entry.start_offset); let data = common::read_exact(&mut data_reader, entry.size as usize)?; @@ -82,7 +83,7 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Box) -> Res out_file.seek(SeekFrom::Start(entry.start_offset.into()))?; out_file.write_all(&data)?; - println!("- Saved file!"); + info!("- Saved file!"); } Ok(()) diff --git a/src/formats/msd/msd_ouith_parser_old.rs b/src/formats/msd/msd_ouith_parser_old.rs index ed5781f..e65bab0 100644 --- a/src/formats/msd/msd_ouith_parser_old.rs +++ b/src/formats/msd/msd_ouith_parser_old.rs @@ -3,6 +3,8 @@ use std::io::{Seek, SeekFrom, Cursor}; use binrw::{BinRead, BinReaderExt}; +use log::info; + use crate::utils::common; #[derive(BinRead)] @@ -100,44 +102,44 @@ pub fn parse_ouith_blob(blob: &[u8], print_tree: bool) -> Result<(Vec, while reader.stream_position()? < blob.len() as u64 { chunk_n += 1; let chunk: ChunkHeader = reader.read_be()?; - if print_tree { println!("\nChunk {} - Size: {}, Value: {}", chunk_n, chunk.size, chunk.value); }; + if print_tree { info!("\nChunk {} - Size: {}, Value: {}", chunk_n, chunk.size, chunk.value); }; let chunk_end = reader.stream_position()? + chunk.size as u64; //parse top level descriptor. it can only be ID 1(OUUpgradeItemDesc) or 2(OUGroupDesc) let top_descriptor: DescriptorHeader = reader.read_be()?; if top_descriptor.tag == 0x01 { - if print_tree { println!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); }; + if print_tree { info!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); }; let item_id: u32 = reader.read_be()?; - if print_tree { println!(" Item ID: {}", item_id); }; + if print_tree { info!(" Item ID: {}", item_id); }; //REQUIRED are items in order: OUDestinationDesc(0x03), OUDataProcessingDesc(0x07), OUGroupInfoDesc(0x13). OPTIONAL items: OUDependenciesDesc(0x04), OUDataPostProcessingDesc(0x08) //In MSD files, no others seem to be used than the required ones. We will ignore all data after required descriptors. let destination_descriptor: DescriptorHeader = reader.read_be()?; if destination_descriptor.tag != 0x03 {return Err(format!("Unexpected descriptor type in OUUpgradeItemDesc, Expected: 0x03, Got: 0x{:02x}!", destination_descriptor.tag).into())} - if print_tree { println!(" OUDestinationDesc(0x03) - Size: {}", destination_descriptor.size); }; + if print_tree { info!(" OUDestinationDesc(0x03) - Size: {}", destination_descriptor.size); }; let out_size: u32 = reader.read_be()?; - if print_tree { println!(" Out data size: {}", out_size); }; + if print_tree { info!(" Out data size: {}", out_size); }; //OUDestinationDesc needs one of OUSWFileVersionDesc(0x0B), OUPartitionVersionDesc(0x0A), OUCMACDataDesc(0x11). Their structure is the same. so we can store the type let type_descriptor: DescriptorHeader = reader.read_be()?; if ![0x0B, 0xA, 0x11].contains(&type_descriptor.tag) {return Err(format!("Unexpected descriptor type in OUDestinationDesc, Expected: one of 0x0B, 0x0A, 0x11, Got: 0x{:02x}!", type_descriptor.tag).into())} - if print_tree { println!(" Type descriptor(0x{:02x}) - Size: {}", type_descriptor.tag, type_descriptor.size); }; + if print_tree { info!(" Type descriptor(0x{:02x}) - Size: {}", type_descriptor.tag, type_descriptor.size); }; let destination_info: CommonDestinationInfo = reader.read_be()?; if print_tree { - println!(" Name length: {}", destination_info.name_len); - println!(" Name: {}", destination_info.name()); - println!(" Version: {}", destination_info.version); + info!(" Name length: {}", destination_info.name_len); + info!(" Name: {}", destination_info.name()); + info!(" Version: {}", destination_info.version); }; //OUDataProcessingDesc can have OUXOREncryptionDesc(0x0D), OUAESEncryptionDesc(0x0E), OUCompressionDesc(0x0F), OUSecureHashValidationDesc(0x18), OURSAValidationDesc(0x10), OUDataCopyDesc(0x16), OUKeepCurrentDataDesc(0x1E), OUCRC32ValidationDesc(0x12) let data_processing_descriptor: DescriptorHeader = reader.read_be()?; if data_processing_descriptor.tag != 0x07 {return Err(format!("Unexpected descriptor type in OUUpgradeItemDesc, Expected: 0x07, Got: 0x{:02x}!", data_processing_descriptor.tag).into())} - if print_tree { println!(" OUDataProcessingDesc(0x07) - Size: {}", data_processing_descriptor.size); }; + if print_tree { info!(" OUDataProcessingDesc(0x07) - Size: {}", data_processing_descriptor.size); }; let heading_size: u32 = reader.read_be()?; - if print_tree { println!(" Heading size: {}", heading_size); }; + if print_tree { info!(" Heading size: {}", heading_size); }; let data_size: u32 = reader.read_be()?; - if print_tree { println!(" Data size: {}", data_size); }; + if print_tree { info!(" Data size: {}", data_size); }; let mut aes_encryption = false; //let mut crc32_hash: Option = None; @@ -149,46 +151,46 @@ pub fn parse_ouith_blob(blob: &[u8], print_tree: bool) -> Result<(Vec, if ![0x0D, 0x0E, 0x0F, 0x18, 0x10, 0x16, 0x1E, 0x12].contains(&descriptor.tag) {return Err(format!("Unexpected descriptor type in OUDataProcessingDesc, Expected: one of 0x0D, 0x0E, 0x0F, 0x18, 0x10, 0x16, 0x1E, 0x12, Got: 0x{:02x}!", descriptor.tag).into())} if descriptor.tag == 0x0E { //OUAESEncryptionDesc - if print_tree { println!(" OUAESEncryptionDesc(0x0E) - Size: {}", descriptor.size); }; + if print_tree { info!(" OUAESEncryptionDesc(0x0E) - Size: {}", descriptor.size); }; let aes_encryption_desc: OUAESEncryptionDesc = reader.read_be()?; if print_tree { - println!(" Mode: {}", aes_encryption_desc.mode); - println!(" Key size: {}", aes_encryption_desc.key_size); - println!(" Salt size: {}", aes_encryption_desc.salt_size); + info!(" Mode: {}", aes_encryption_desc.mode); + info!(" Key size: {}", aes_encryption_desc.key_size); + info!(" Salt size: {}", aes_encryption_desc.salt_size); }; aes_encryption = true; } else if descriptor.tag == 0x10 { //OURSAValidationDesc - if print_tree { println!(" OURSAValidationDesc(0x10) - Size: {}", descriptor.size); }; + if print_tree { info!(" OURSAValidationDesc(0x10) - Size: {}", descriptor.size); }; let rsa_validation_desc: OURSAValidationDesc = reader.read_be()?; if print_tree { - println!(" Mode: {}", rsa_validation_desc.mode); - println!(" Field 2: {}", rsa_validation_desc._unknown); - println!(" Signature size: {}", rsa_validation_desc.signature_size); + info!(" Mode: {}", rsa_validation_desc.mode); + info!(" Field 2: {}", rsa_validation_desc._unknown); + info!(" Signature size: {}", rsa_validation_desc.signature_size); }; } else if descriptor.tag == 0x12 { //OUCRC32ValidationDesc - if print_tree { println!(" OUCRC32ValidationDesc(0x12) - Size: {}", descriptor.size); }; + if print_tree { info!(" OUCRC32ValidationDesc(0x12) - Size: {}", descriptor.size); }; let crc32: u32 = reader.read_be()?; - if print_tree { println!(" CRC32: {:02x}", crc32); }; + if print_tree { info!(" CRC32: {:02x}", crc32); }; //crc32_hash = Some(crc32); } else if descriptor.tag == 0x18 { //OUSecureHashValidationDesc - if print_tree { println!(" OUSecureHashValidationDesc(0x18) - Size: {}", descriptor.size); }; + if print_tree { info!(" OUSecureHashValidationDesc(0x18) - Size: {}", descriptor.size); }; let secure_hash_validation_desc: OUSecureHashValidationDesc = reader.read_be()?; if print_tree { - println!(" Mode: {}", secure_hash_validation_desc.mode); - println!(" Hash size: {}", secure_hash_validation_desc.hash_size); - println!(" Hash: {}", hex::encode(&secure_hash_validation_desc.hash)); + info!(" Mode: {}", secure_hash_validation_desc.mode); + info!(" Hash size: {}", secure_hash_validation_desc.hash_size); + info!(" Hash: {}", hex::encode(&secure_hash_validation_desc.hash)); }; //secure_hash = Some(secure_hash_validation_desc.hash); } else { //type not implemented, ignore the data - if print_tree { println!(" Unimplemented descriptor(0x{:02x}) - Size: {}", descriptor.tag, descriptor.size); }; + if print_tree { info!(" Unimplemented descriptor(0x{:02x}) - Size: {}", descriptor.tag, descriptor.size); }; let _descriptor_data = common::read_exact(&mut reader, descriptor.size as usize); } } @@ -196,9 +198,9 @@ pub fn parse_ouith_blob(blob: &[u8], print_tree: bool) -> Result<(Vec, //OUGroupInfoDesc let group_info_descriptor: DescriptorHeader = reader.read_be()?; if group_info_descriptor.tag != 0x13 {return Err(format!("Unexpected descriptor type in OUUpgradeItemDesc, Expected: 0x13, Got: 0x{:02x}!", group_info_descriptor.tag).into())} - if print_tree { println!(" OUGroupInfoDesc(0x13) - Size: {}", group_info_descriptor.size); }; + if print_tree { info!(" OUGroupInfoDesc(0x13) - Size: {}", group_info_descriptor.size); }; let group_id: u32 = reader.read_be()?; - if print_tree { println!(" Group ID: {}", group_id); }; + if print_tree { info!(" Group ID: {}", group_id); }; //create the msd item with all infos let msd_item = MSDItem { @@ -220,12 +222,12 @@ pub fn parse_ouith_blob(blob: &[u8], print_tree: bool) -> Result<(Vec, } else if top_descriptor.tag == 0x02 { - if print_tree { println!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); }; + if print_tree { info!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); }; let group_desc: OUGroupDesc = reader.read_be()?; if print_tree { - println!(" Group ID: {}", group_desc.group_id); - println!(" Field 2: {}", group_desc.field_2); - println!(" Field 3: {}", group_desc.field_3); + info!(" Group ID: {}", group_desc.group_id); + info!(" Field 2: {}", group_desc.field_2); + info!(" Field 3: {}", group_desc.field_3); }; //OUGroupDesc REQUIRES one of: OUSWImageVersionDesc(0x09), OUSWImageVersionExDesc(0x19), OUOptionalDataVersionDesc(0x14), OUFirmwareVersionDesc(0x15). OPTIONALLY it can also have OUDependenciesDesc @@ -233,23 +235,23 @@ pub fn parse_ouith_blob(blob: &[u8], print_tree: bool) -> Result<(Vec, let version_descriptor: DescriptorHeader = reader.read_be()?; if ![0x09, 0x19, 0x14, 0x15].contains(&version_descriptor.tag) {return Err(format!("Unexpected descriptor type in OUGroupDesc, Expected: one of 0x09, 0x19, 0x14, 0x15, Got: 0x{:02x}!", version_descriptor.tag).into())} if version_descriptor.tag == 0x19 { - if print_tree { println!(" OUSWImageVersionExDesc(0x12) - Size: {}", version_descriptor.size); }; + if print_tree { info!(" OUSWImageVersionExDesc(0x12) - Size: {}", version_descriptor.size); }; let sw_image_version_ex_desc: OUSWImageVersionExDesc = reader.read_be()?; if print_tree { - println!(" Name length: {}", sw_image_version_ex_desc.name_len); - println!(" Name: {}", sw_image_version_ex_desc.name()); - println!(" Major version: {}", sw_image_version_ex_desc.major_ver); - println!(" Minor version: {}", sw_image_version_ex_desc.minor_ver); - println!(" Date year: {}", sw_image_version_ex_desc.date_year); - println!(" Date month: {}", sw_image_version_ex_desc.date_month); - println!(" Date day: {}", sw_image_version_ex_desc.date_day); + info!(" Name length: {}", sw_image_version_ex_desc.name_len); + info!(" Name: {}", sw_image_version_ex_desc.name()); + info!(" Major version: {}", sw_image_version_ex_desc.major_ver); + info!(" Minor version: {}", sw_image_version_ex_desc.minor_ver); + info!(" Date year: {}", sw_image_version_ex_desc.date_year); + info!(" Date month: {}", sw_image_version_ex_desc.date_month); + info!(" Date day: {}", sw_image_version_ex_desc.date_day); }; info = Some(sw_image_version_ex_desc); } else { //type not implemented, ignore the data - if print_tree { println!(" Unimplemented descriptor(0x{:02x}) - Size: {}", version_descriptor.tag, version_descriptor.size); }; + if print_tree { info!(" Unimplemented descriptor(0x{:02x}) - Size: {}", version_descriptor.tag, version_descriptor.size); }; let _descriptor_data = common::read_exact(&mut reader, version_descriptor.size as usize); } diff --git a/src/formats/msd/msd_ouith_parser_tizen_1_8.rs b/src/formats/msd/msd_ouith_parser_tizen_1_8.rs index 675c227..941cdd6 100644 --- a/src/formats/msd/msd_ouith_parser_tizen_1_8.rs +++ b/src/formats/msd/msd_ouith_parser_tizen_1_8.rs @@ -3,6 +3,8 @@ use std::io::{Cursor}; use binrw::{BinRead, BinReaderExt}; +use log::info; + use crate::utils::common; #[derive(BinRead)] @@ -116,24 +118,24 @@ pub fn parse_blob_1_8(blob: &[u8], print_tree: bool) -> Result<(Vec, Op } let top_level_descriptor_count: u32 = reader.read_be()?; //BIG ENDIAN - if print_tree { println!("\nTop level descriptor count: {}", top_level_descriptor_count); }; + if print_tree { info!("\nTop level descriptor count: {}", top_level_descriptor_count); }; for _i in 0..top_level_descriptor_count { //parse top level descriptor. it can only be ID 1(OUUpgradeItemDesc) or 2(OUGroupDesc) let top_descriptor: DescriptorHeader = reader.read_be()?; if top_descriptor.tag == 0x01 { - if print_tree { println!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); }; + if print_tree { info!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); }; let upgrade_item_desc: OUUpgradeItemDesc = reader.read_be()?; if print_tree { - println!(" Item ID: {}", upgrade_item_desc.item_id); - println!(" Unknown flag: {}", upgrade_item_desc.unk_flag); - println!(" Original size: {}", upgrade_item_desc.original_size); - println!(" Processed size: {}", upgrade_item_desc.processed_size); - println!(" Unknown: {}", upgrade_item_desc.unk); + info!(" Item ID: {}", upgrade_item_desc.item_id); + info!(" Unknown flag: {}", upgrade_item_desc.unk_flag); + info!(" Original size: {}", upgrade_item_desc.original_size); + info!(" Processed size: {}", upgrade_item_desc.processed_size); + info!(" Unknown: {}", upgrade_item_desc.unk); }; let subdesc_count: u32 = reader.read_le()?; //LITTLE ENDIAN?? - if print_tree { println!(" Subdescriptor count: {}", subdesc_count); }; + if print_tree { info!(" Subdescriptor count: {}", subdesc_count); }; let mut name: Option = None; //let mut crc32_hash: Option = None; @@ -143,66 +145,66 @@ pub fn parse_blob_1_8(blob: &[u8], print_tree: bool) -> Result<(Vec, Op for _i in 0..subdesc_count { let sub_descriptor: DescriptorHeader = reader.read_be()?; if sub_descriptor.tag == 0x0A { - if print_tree { println!(" OUPartitionVersionDesc(0x0A) - Size: {}", sub_descriptor.size); }; + if print_tree { info!(" OUPartitionVersionDesc(0x0A) - Size: {}", sub_descriptor.size); }; let partition_version_desc: OUPartitionVersionDesc = reader.read_be()?; if print_tree { - println!(" Name length: {}", partition_version_desc.name_len); - println!(" Name: {}", partition_version_desc.name()); - println!(" Version: {}", partition_version_desc.version); + info!(" Name length: {}", partition_version_desc.name_len); + info!(" Name: {}", partition_version_desc.name()); + info!(" Version: {}", partition_version_desc.version); }; name = Some(partition_version_desc.name()); } else if sub_descriptor.tag == 0x07 { - if print_tree { println!(" OUDataProcessingDesc(0x07) - Size: {}", sub_descriptor.size); }; + if print_tree { info!(" OUDataProcessingDesc(0x07) - Size: {}", sub_descriptor.size); }; let data_processing_desc: OUDataProcessingDesc = reader.read_le()?; //LITTLE ENDIAN?? - if print_tree { println!(" Subdescriptor count: {}", data_processing_desc.subdesc_count); }; + if print_tree { info!(" Subdescriptor count: {}", data_processing_desc.subdesc_count); }; for _i in 0..data_processing_desc.subdesc_count { let data_processing_sub_desc: DescriptorHeader = reader.read_be()?; if data_processing_sub_desc.tag == 0x12 { - if print_tree { println!(" OUCRC32ValidationDesc(0x12) - Size: {}", data_processing_sub_desc.size); }; + if print_tree { info!(" OUCRC32ValidationDesc(0x12) - Size: {}", data_processing_sub_desc.size); }; let crc32_validation_desc: OUCRC32ValidationDesc = reader.read_be()?; - if print_tree { println!(" CRC32: {:02x}", crc32_validation_desc.crc32); }; + if print_tree { info!(" CRC32: {:02x}", crc32_validation_desc.crc32); }; //crc32_hash = Some(crc32_validation_desc.crc32); } else if data_processing_sub_desc.tag == 0x10 { - if print_tree { println!(" OURSAValidationDesc(0x10) - Size: {}", data_processing_sub_desc.size); }; + if print_tree { info!(" OURSAValidationDesc(0x10) - Size: {}", data_processing_sub_desc.size); }; let rsa_validation_desc: OURSAValidationDesc = reader.read_be()?; if print_tree { - println!(" Signature size: {}", rsa_validation_desc.signature_size); - println!(" Public key ID: {}", rsa_validation_desc.public_key_id); - println!(" Signature: {}", hex::encode(&rsa_validation_desc.signature)); + info!(" Signature size: {}", rsa_validation_desc.signature_size); + info!(" Public key ID: {}", rsa_validation_desc.public_key_id); + info!(" Signature: {}", hex::encode(&rsa_validation_desc.signature)); }; } else if data_processing_sub_desc.tag == 0x0E { - if print_tree { println!(" OUAESEncryptionDesc(0x0E) - Size: {}", data_processing_sub_desc.size); }; + if print_tree { info!(" OUAESEncryptionDesc(0x0E) - Size: {}", data_processing_sub_desc.size); }; let aes_encryption_desc: OUAESEncryptionDesc = reader.read_be()?; if print_tree { - println!(" Private key ID: {}", aes_encryption_desc.private_key_id); - println!(" Salt size: {}", aes_encryption_desc.salt_size); - println!(" Salt: {}", hex::encode(&aes_encryption_desc.salt)); - println!(" Processed size: {}", aes_encryption_desc.processed_size); + info!(" Private key ID: {}", aes_encryption_desc.private_key_id); + info!(" Salt size: {}", aes_encryption_desc.salt_size); + info!(" Salt: {}", hex::encode(&aes_encryption_desc.salt)); + info!(" Processed size: {}", aes_encryption_desc.processed_size); }; aes_encryption = true; aes_salt = Some(aes_encryption_desc.salt); } else { - if print_tree { println!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", data_processing_sub_desc.tag, data_processing_sub_desc.size); }; + if print_tree { info!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", data_processing_sub_desc.tag, data_processing_sub_desc.size); }; let _ = common::read_exact(&mut reader, data_processing_sub_desc.size as usize - 4)?; } } } else if sub_descriptor.tag == 0x13 { - if print_tree { println!(" OUGroupInfoDesc(0x13) - Size: {}", sub_descriptor.size); }; + if print_tree { info!(" OUGroupInfoDesc(0x13) - Size: {}", sub_descriptor.size); }; let group_info_desc: OUGroupInfoDesc = reader.read_be()?; - if print_tree { println!(" Group ID: {}", group_info_desc.group_id); }; + if print_tree { info!(" Group ID: {}", group_info_desc.group_id); }; } else { - if print_tree { println!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); }; + if print_tree { info!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); }; let _ = common::read_exact(&mut reader, sub_descriptor.size as usize - 4)?; } } @@ -224,34 +226,34 @@ pub fn parse_blob_1_8(blob: &[u8], print_tree: bool) -> Result<(Vec, Op } } else if top_descriptor.tag == 0x02 { - if print_tree { println!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); }; + if print_tree { info!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); }; let group_desc: OUGroupDesc = reader.read_be()?; if print_tree { - println!(" Group ID: {}", group_desc.group_id); - println!(" Unknown: {}", group_desc.unknown); + info!(" Group ID: {}", group_desc.group_id); + info!(" Unknown: {}", group_desc.unknown); }; let subdesc_count: u32 = reader.read_le()?; //LITTLE ENDIAN?? - if print_tree { println!(" Subdescriptor count: {}", subdesc_count); }; + if print_tree { info!(" Subdescriptor count: {}", subdesc_count); }; for _i in 0..subdesc_count { let sub_descriptor: DescriptorHeader = reader.read_be()?; if sub_descriptor.tag == 0x19 { - if print_tree { println!(" OUSWImageVersionDesc(0x19) - Size: {}", sub_descriptor.size); }; + if print_tree { info!(" OUSWImageVersionDesc(0x19) - Size: {}", sub_descriptor.size); }; let sw_image_version_desc: OUSWImageVersionDesc = reader.read_be()?; if print_tree { - println!(" Name length: {}", sw_image_version_desc.name_len); - println!(" Name: {}", sw_image_version_desc.name()); - println!(" Major ver: {}", sw_image_version_desc.major_ver); - println!(" Minor ver: {}", sw_image_version_desc.minor_ver); - println!(" Year: {}", sw_image_version_desc.date_year); - println!(" Month: {}", sw_image_version_desc.date_month); - println!(" Day: {}", sw_image_version_desc.date_day); + info!(" Name length: {}", sw_image_version_desc.name_len); + info!(" Name: {}", sw_image_version_desc.name()); + info!(" Major ver: {}", sw_image_version_desc.major_ver); + info!(" Minor ver: {}", sw_image_version_desc.minor_ver); + info!(" Year: {}", sw_image_version_desc.date_year); + info!(" Month: {}", sw_image_version_desc.date_month); + info!(" Day: {}", sw_image_version_desc.date_day); }; info = Some(sw_image_version_desc); } else { - if print_tree { println!(" Unimplemented Descriptor (0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); }; + if print_tree { info!(" Unimplemented Descriptor (0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); }; let _ = common::read_exact(&mut reader, sub_descriptor.size as usize - 4)?; } } diff --git a/src/formats/msd/msd_ouith_parser_tizen_1_9.rs b/src/formats/msd/msd_ouith_parser_tizen_1_9.rs index 1f0ecc8..c90da92 100644 --- a/src/formats/msd/msd_ouith_parser_tizen_1_9.rs +++ b/src/formats/msd/msd_ouith_parser_tizen_1_9.rs @@ -3,6 +3,8 @@ use std::io::{Cursor}; use binrw::{BinRead, BinReaderExt}; +use log::info; + use crate::utils::common; #[derive(BinRead)] @@ -121,24 +123,24 @@ pub fn parse_blob_1_9(blob: &[u8], print_tree: bool) -> Result<(Vec, Op } let top_level_descriptor_count: u32 = reader.read_le()?; - if print_tree { println!("\nTop level descriptor count: {}", top_level_descriptor_count); }; + if print_tree { info!("\nTop level descriptor count: {}", top_level_descriptor_count); }; for _i in 0..top_level_descriptor_count { //parse top level descriptor. it can only be ID 1(OUUpgradeItemDesc) or 2(OUGroupDesc) or 0x37(OUSecureDowngradeDesc) let top_descriptor: DescriptorHeader = reader.read_le()?; if top_descriptor.tag == 0x01 { - if print_tree { println!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); }; + if print_tree { info!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); }; let upgrade_item_desc: OUUpgradeItemDesc = reader.read_le()?; if print_tree { - println!(" Item ID: {}", upgrade_item_desc.item_id); - println!(" Unknown flag: {}", upgrade_item_desc.unk_flag); - println!(" Original size: {}", upgrade_item_desc.original_size); - println!(" Processed size: {}", upgrade_item_desc.processed_size); - println!(" Unknown: {}", upgrade_item_desc.unk); + info!(" Item ID: {}", upgrade_item_desc.item_id); + info!(" Unknown flag: {}", upgrade_item_desc.unk_flag); + info!(" Original size: {}", upgrade_item_desc.original_size); + info!(" Processed size: {}", upgrade_item_desc.processed_size); + info!(" Unknown: {}", upgrade_item_desc.unk); }; let subdesc_count: u32 = reader.read_le()?; - if print_tree { println!(" Subdescriptor count: {}", subdesc_count); }; + if print_tree { info!(" Subdescriptor count: {}", subdesc_count); }; let mut name: Option = None; //let mut crc32_hash: Option = None; @@ -148,64 +150,64 @@ pub fn parse_blob_1_9(blob: &[u8], print_tree: bool) -> Result<(Vec, Op for _i in 0..subdesc_count { let sub_descriptor: DescriptorHeader = reader.read_le()?; if sub_descriptor.tag == 0x0A { - if print_tree { println!(" OUPartitionVersionDesc(0x0A) - Size: {}", sub_descriptor.size); }; + if print_tree { info!(" OUPartitionVersionDesc(0x0A) - Size: {}", sub_descriptor.size); }; let partition_version_desc: OUPartitionVersionDesc = reader.read_le()?; if print_tree { - println!(" Name length: {}", partition_version_desc.name_len); - println!(" Name: {}", partition_version_desc.name()); + info!(" Name length: {}", partition_version_desc.name_len); + info!(" Name: {}", partition_version_desc.name()); }; name = Some(partition_version_desc.name()); } else if sub_descriptor.tag == 0x07 { - if print_tree { println!(" OUDataProcessingDesc(0x07) - Size: {}", sub_descriptor.size); }; + if print_tree { info!(" OUDataProcessingDesc(0x07) - Size: {}", sub_descriptor.size); }; let data_processing_desc: OUDataProcessingDesc = reader.read_le()?; - if print_tree { println!(" Subdescriptor count: {}", data_processing_desc.subdesc_count); }; + if print_tree { info!(" Subdescriptor count: {}", data_processing_desc.subdesc_count); }; for _i in 0..data_processing_desc.subdesc_count { let data_processing_sub_desc: DescriptorHeader = reader.read_le()?; if data_processing_sub_desc.tag == 0x12 { - if print_tree { println!(" OUCRC32ValidationDesc(0x12) - Size: {}", data_processing_sub_desc.size); }; + if print_tree { info!(" OUCRC32ValidationDesc(0x12) - Size: {}", data_processing_sub_desc.size); }; let crc32_validation_desc: OUCRC32ValidationDesc = reader.read_le()?; - if print_tree { println!(" CRC32: {:02x}", crc32_validation_desc.crc32); }; + if print_tree { info!(" CRC32: {:02x}", crc32_validation_desc.crc32); }; //crc32_hash = Some(crc32_validation_desc.crc32); } else if data_processing_sub_desc.tag == 0x10 { - if print_tree { println!(" OURSAValidationDesc(0x10) - Size: {}", data_processing_sub_desc.size); }; + if print_tree { info!(" OURSAValidationDesc(0x10) - Size: {}", data_processing_sub_desc.size); }; let rsa_validation_desc: OURSAValidationDesc = reader.read_le()?; if print_tree { - println!(" Signature size: {}", rsa_validation_desc.signature_size); - println!(" Public key ID: {}", rsa_validation_desc.public_key_id); - println!(" Signature: {}", hex::encode(&rsa_validation_desc.signature)); + info!(" Signature size: {}", rsa_validation_desc.signature_size); + info!(" Public key ID: {}", rsa_validation_desc.public_key_id); + info!(" Signature: {}", hex::encode(&rsa_validation_desc.signature)); }; } else if data_processing_sub_desc.tag == 0x0E { - if print_tree { println!(" OUAESEncryptionDesc(0x0E) - Size: {}", data_processing_sub_desc.size); }; + if print_tree { info!(" OUAESEncryptionDesc(0x0E) - Size: {}", data_processing_sub_desc.size); }; let aes_encryption_desc: OUAESEncryptionDesc = reader.read_le()?; if print_tree { - println!(" Salt size: {}", aes_encryption_desc.salt_size); - println!(" Salt: {}", hex::encode(&aes_encryption_desc.salt)); - println!(" Processed size: {}", aes_encryption_desc.processed_size); + info!(" Salt size: {}", aes_encryption_desc.salt_size); + info!(" Salt: {}", hex::encode(&aes_encryption_desc.salt)); + info!(" Processed size: {}", aes_encryption_desc.processed_size); }; aes_encryption = true; aes_salt = Some(aes_encryption_desc.salt); } else { - if print_tree { println!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", data_processing_sub_desc.tag, data_processing_sub_desc.size); }; + if print_tree { info!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", data_processing_sub_desc.tag, data_processing_sub_desc.size); }; let _ = common::read_exact(&mut reader, data_processing_sub_desc.size as usize - 4)?; } } } else if sub_descriptor.tag == 0x13 { - if print_tree { println!(" OUGroupInfoDesc(0x13) - Size: {}", sub_descriptor.size); }; + if print_tree { info!(" OUGroupInfoDesc(0x13) - Size: {}", sub_descriptor.size); }; let group_info_desc: OUGroupInfoDesc = reader.read_le()?; - if print_tree { println!(" Group ID: {}", group_info_desc.group_id); }; + if print_tree { info!(" Group ID: {}", group_info_desc.group_id); }; } else { - if print_tree { println!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); }; + if print_tree { info!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); }; let _ = common::read_exact(&mut reader, sub_descriptor.size as usize - 4)?; } } @@ -227,36 +229,36 @@ pub fn parse_blob_1_9(blob: &[u8], print_tree: bool) -> Result<(Vec, Op } } else if top_descriptor.tag == 0x02 { - if print_tree { println!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); }; + if print_tree { info!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); }; let group_desc: OUGroupDesc = reader.read_le()?; - if print_tree { println!(" Group ID: {}", group_desc.group_id); }; + if print_tree { info!(" Group ID: {}", group_desc.group_id); }; let subdesc_count: u32 = reader.read_le()?; //LITTLE ENDIAN?? - if print_tree { println!(" Subdescriptor count: {}", subdesc_count); }; + if print_tree { info!(" Subdescriptor count: {}", subdesc_count); }; for _i in 0..subdesc_count { let sub_descriptor: DescriptorHeader = reader.read_le()?; if sub_descriptor.tag == 0x19 { - if print_tree { println!(" OUSWImageVersionDesc(0x19) - Size: {}", sub_descriptor.size); }; + if print_tree { info!(" OUSWImageVersionDesc(0x19) - Size: {}", sub_descriptor.size); }; let sw_image_version_desc: OUSWImageVersionDesc = reader.read_le()?; if print_tree { - println!(" Name length: {}", sw_image_version_desc.name_len); - println!(" Name: {}", sw_image_version_desc.name()); - println!(" Major ver: {}", sw_image_version_desc.major_ver); - println!(" Minor ver: {}", sw_image_version_desc.minor_ver); + info!(" Name length: {}", sw_image_version_desc.name_len); + info!(" Name: {}", sw_image_version_desc.name()); + info!(" Major ver: {}", sw_image_version_desc.major_ver); + info!(" Minor ver: {}", sw_image_version_desc.minor_ver); }; info = Some(sw_image_version_desc); } else { - if print_tree { println!(" Unimplemented Descriptor (0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); }; + if print_tree { info!(" Unimplemented Descriptor (0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); }; let _ = common::read_exact(&mut reader, sub_descriptor.size as usize - 4)?; } } } else if top_descriptor.tag == 0x37 { - if print_tree { println!("OUSecureDowngradeDesc(0x37) - Size: {}", top_descriptor.size); }; + if print_tree { info!("OUSecureDowngradeDesc(0x37) - Size: {}", top_descriptor.size); }; let secure_downgrade_desc: OUSecureDowngradeDesc = reader.read_le()?; - if print_tree { println!(" Image generation timestamp: {}", secure_downgrade_desc.image_generation_date); }; + if print_tree { info!(" Image generation timestamp: {}", secure_downgrade_desc.image_generation_date); }; } else { return Err(format!("Unexpected top level descriptor type 0x{:02x}!", top_descriptor.tag).into()); diff --git a/src/formats/msd10/mod.rs b/src/formats/msd10/mod.rs index 7ef3fbe..3a08529 100644 --- a/src/formats/msd10/mod.rs +++ b/src/formats/msd10/mod.rs @@ -14,6 +14,7 @@ use crate::formats::msd::{decrypt_aes_salted_old, decrypt_aes_salted_tizen, decr use crate::formats::msd::msd_ouith_parser_old::{parse_ouith_blob}; use crate::formats::msd::msd_ouith_parser_tizen_1_8::{parse_blob_1_8}; use include::*; +use log::info; pub fn is_msd10_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -30,29 +31,29 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: FileHeader = file.read_le()?; - println!("\nNumber of sections: {}", header.section_count); + info!("\nNumber of sections: {}", header.section_count); let mut sections: Vec = Vec::new(); for _i in 0..header.section_count { let section: SectionEntry = file.read_le()?; - println!("Section {}: offset: {}, size: {}", section.index, section.offset, section.size); + info!("Section {}: offset: {}, size: {}", section.index, section.offset, section.size); sections.push(section); } let _zero_padding = common::read_exact(&mut file, 4)?; let header_count: u32 = file.read_le()?; - println!("\nNumber of headers: {}", header_count); + info!("\nNumber of headers: {}", header_count); let mut headers: Vec = Vec::new(); for i in 0..header_count { let header: HeaderEntry = file.read_le()?; - println!("Header {}: {}, offset: {}, size: {}", i + 1, header.name(), header.offset, header.size); + info!("Header {}: {}, offset: {}, size: {}", i + 1, header.name(), header.offset, header.size); headers.push(header); } //use first header let firmware_name = &headers[0].name(); - println!("\nFirmware name: {}", firmware_name); + info!("\nFirmware name: {}", firmware_name); let toc_offset = headers[0].offset; let toc_size = headers[0].size; @@ -89,7 +90,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box } let (passphrase_bytes, firmware_type) = if let (Some(p), Some(t)) = (passphrase_bytes, firmware_type) { - println!("Using passphrase: {}", passphrase_name); + info!("Using passphrase: {}", passphrase_name); (p, t) } else { return Err("No matching key found!".into()); @@ -103,7 +104,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let (items, info) = parse_blob_1_8(&toc, app_ctx.has_option("msd:print_ouith"))?; if let Some(info) = info { - println!("\nImage info:\n{} {}.{} {}/{}/{}", + info!("\nImage info:\n{} {}.{} {}/{}/{}", info.name(), info.major_ver, info.minor_ver, info.date_day, info.date_month, info.date_year); } @@ -111,7 +112,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let size = sections[i as usize].size; let offset = sections[i as usize].offset; - println!("\n({}/{}) - {}, Size: {}", + info!("\n({}/{}) - {}, Size: {}", i + 1, items.len(), item.name, size); if sections[i as usize].index != item.item_id { @@ -122,7 +123,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let out_data; if item.aes_encryption { - println!("- Decrypting..."); + info!("- Decrypting..."); let salt = item.aes_salt.as_ref().ok_or("AES salt missing!")?; out_data = decrypt_aes_tizen(&stored_data, &passphrase_bytes, salt)?; } else { @@ -134,7 +135,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&out_data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } @@ -145,14 +146,14 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let (items, info) = parse_ouith_blob(&toc, app_ctx.has_option("msd:print_ouith"))?; if let Some(info) = info { - println!("\nImage info:\n{} {}.{} {}/{}/20{}", + info!("\nImage info:\n{} {}.{} {}/{}/20{}", info.name(), info.major_ver, info.minor_ver, info.date_day, info.date_month, info.date_year); } for (i, item) in items.iter().enumerate() { let offset = sections[i as usize].offset; let type_str = if item.item_type == 0x0A {"Partition"} else if item.item_type == 0x0B {"File"} else if item.item_type == 0x11 {"CMAC Data"} else {"Unknown"}; - println!("\n({}/{}) - {}, Type: {}, Size: {}", + info!("\n({}/{}) - {}, Type: {}, Size: {}", item.item_id, items.len(), item.name, type_str, item.all_size); if sections[i as usize].index != item.item_id { @@ -164,7 +165,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box if app_ctx.has_option("msd10:save_cmac") { out_filename = format!("{}.cmac", item.name); //add an additional extension, because the CMAC data has the same name as its item } else { - println!("- Skipping CMAC Data..."); + info!("- Skipping CMAC Data..."); continue } } @@ -177,7 +178,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let stored_data = common::read_exact(&mut file, item.data_size as usize)?; let out_data; if item.aes_encryption { - println!("- Decrypting..."); + info!("- Decrypting..."); out_data = decrypt_aes_salted_old(&stored_data, &passphrase_bytes)?; } else { out_data = stored_data; @@ -188,7 +189,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&out_data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } } diff --git a/src/formats/msd11/mod.rs b/src/formats/msd11/mod.rs index e3a47e9..d39ba57 100644 --- a/src/formats/msd11/mod.rs +++ b/src/formats/msd11/mod.rs @@ -13,6 +13,7 @@ use crate::keys; use crate::formats::msd::{decrypt_aes_salted_tizen, decrypt_aes_tizen}; use crate::formats::msd::msd_ouith_parser_tizen_1_9::{parse_blob_1_9}; use include::*; +use log::info; pub fn is_msd11_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -29,28 +30,28 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: FileHeader = file.read_le()?; - println!("\nNumber of sections: {}", header.section_count); + info!("\nNumber of sections: {}", header.section_count); let mut sections: Vec = Vec::new(); for _i in 0..header.section_count { let section: SectionEntry = file.read_le()?; - println!("Section {}: offset: {}, size: {}", section.index, section.offset, section.size); + info!("Section {}: offset: {}, size: {}", section.index, section.offset, section.size); sections.push(section); } let header_count: u32 = file.read_le()?; - println!("\nNumber of headers: {}", header_count); + info!("\nNumber of headers: {}", header_count); let mut headers: Vec = Vec::new(); for i in 0..header_count { let header: HeaderEntry = file.read_le()?; - println!("Header {}: {}, offset: {}, size: {}", i + 1, header.name(), header.offset, header.size); + info!("Header {}: {}, offset: {}, size: {}", i + 1, header.name(), header.offset, header.size); headers.push(header); } //use first header let firmware_name = &headers[0].name(); - println!("\nFirmware name: {}", firmware_name); + info!("\nFirmware name: {}", firmware_name); let toc_offset = headers[0].offset + 8; let toc_size = headers[0].size - 8; @@ -72,7 +73,7 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box } let passphrase_bytes = if let Some(p) = passphrase_bytes { - println!("Using passphrase: {}", passphrase_name); + info!("Using passphrase: {}", passphrase_name); p } else { return Err("No matching key found!".into()); @@ -84,7 +85,7 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let (items, info) = parse_blob_1_9(&toc, app_ctx.has_option("msd:print_ouith"))?; if let Some(info) = info { - println!("\nImage info:\n{} {}.{}", + info!("\nImage info:\n{} {}.{}", info.name(), info.major_ver, info.minor_ver); } @@ -92,7 +93,7 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let size = sections[i as usize].size; let offset = sections[i as usize].offset; - println!("\n({}/{}) - {}, Size: {}", + info!("\n({}/{}) - {}, Size: {}", i + 1, items.len(), item.name, size); if sections[i as usize].index != item.item_id { @@ -103,7 +104,7 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let out_data; if item.aes_encryption { - println!("- Decrypting..."); + info!("- Decrypting..."); let salt = item.aes_salt.as_ref().ok_or("AES salt missing!")?; out_data = decrypt_aes_tizen(&stored_data, &passphrase_bytes, salt)?; } else { @@ -115,7 +116,7 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&out_data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } Ok(()) diff --git a/src/formats/mstar/mod.rs b/src/formats/mstar/mod.rs index 13f9bdd..52ea1cb 100644 --- a/src/formats/mstar/mod.rs +++ b/src/formats/mstar/mod.rs @@ -12,6 +12,7 @@ use crate::utils::compression::{decompress_lzma, decompress_lz4}; use crate::utils::lzop::{unlzop_to_file}; use crate::utils::sparse::{unsparse_to_file}; use include::*; +use log::info; pub fn is_mstar_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -35,9 +36,9 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box } let mut script_string = String::from_utf8_lossy(&script); - if script_string == "" { + if script_string.is_empty() { //try for hisense - println!("Failed to get script at 0x0, trying 0x1000..."); + info!("Failed to get script at 0x0, trying 0x1000..."); script = common::read_file(&file, 4096, 32768)?; if let Some(pos) = script.iter().position(|x| [0x00, 0xFF].contains(x)) { @@ -46,16 +47,15 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box script_string = String::from_utf8_lossy(&script); - if script_string == "" { + if script_string.is_empty() { return Err("Failed to get script".into()); } } opt_dump_dec_hdr(app_ctx, &script, "script")?; let lines: Vec<&str> = script_string.lines().map(|l| l.trim()).collect(); - let mut i = 0; - for line in &lines { + for (i, line) in lines.iter().enumerate() { if line.starts_with("filepartload") { let parts: Vec<&str> = line.split_whitespace().collect(); let offset = parse_number(parts[3]).unwrap_or(0); @@ -113,7 +113,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box } // try to get partname from nand/mmc/ubi writes - if lines[j].starts_with("mmc write") | lines[j].starts_with("nand write") | lines[j].starts_with("ubi write"){ + if lines[j].starts_with("mmc write") || lines[j].starts_with("nand write") || lines[j].starts_with("ubi write"){ let parts: Vec<&str> = lines[j].split_whitespace().collect(); if partname == "unknown" { partname = parts[3] @@ -123,17 +123,16 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box j += 1; } - println!("\nPart - Offset: {}, Size: {} --> {}", offset, size, partname); + info!("\nPart - Offset: {}, Size: {} --> {}", offset, size, partname); - let data = common::read_file(&file, offset, size.try_into().unwrap())?; + let data = common::read_file(&file, offset, size.try_into().map_err(|_| "Size conversion failed")?)?; let out_data; let output_path = if partname == "unknown" { if app_ctx.has_option("mstar:keep_unknown") { - println!("- Warning, unknown destination - saving to _unknown_{}.bin", offset); + info!("- Warning, unknown destination - saving to _unknown_{}.bin", offset); Path::new(&app_ctx.output_dir).join(format!("_unknown_{}.bin", offset)) } else { - println!("- Warning, unknown destination - skipping..."); - i += 1; + info!("- Warning, unknown destination - skipping..."); continue; } } else { @@ -141,27 +140,25 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box }; if compression == CompressionType::Lzma { - println!("- Decompressing LZMA..."); + info!("- Decompressing LZMA..."); out_data = decompress_lzma(&data)?; } else if compression == CompressionType::DoubleLzma { - println!("- Decompressing LZMA (Pass 1)..."); + info!("- Decompressing LZMA (Pass 1)..."); let pass_1 = decompress_lzma(&data)?; - println!("- Decompressing LZMA (Pass 2)..."); + info!("- Decompressing LZMA (Pass 2)..."); out_data = decompress_lzma(&pass_1)?; } else if compression == CompressionType::Lz4 { - println!("- Decompressing lz4, expected size: {}", lz4_expect_size); + info!("- Decompressing lz4, expected size: {}", lz4_expect_size); out_data = decompress_lz4(&data, lz4_expect_size.try_into().unwrap())?; } else if compression == CompressionType::Lzo { - println!("- Decompressing LZO.."); + info!("- Decompressing LZO.."); unlzop_to_file(&data, output_path)?; - println!("-- Saved file!"); - i += 1; + info!("-- Saved file!"); continue } else if compression == CompressionType::Sparse { - println!("- Unsparsing..."); + info!("- Unsparsing..."); unsparse_to_file(&data, output_path)?; - println!("-- Saved file!"); - i += 1; + info!("-- Saved file!"); continue } else { out_data = data; @@ -170,10 +167,8 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box fs::create_dir_all(&app_ctx.output_dir)?; let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?; out_file.write_all(&out_data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } - - i += 1; } Ok(()) diff --git a/src/formats/mstar_secure_old/mod.rs b/src/formats/mstar_secure_old/mod.rs index 92e4c53..cdd925d 100644 --- a/src/formats/mstar_secure_old/mod.rs +++ b/src/formats/mstar_secure_old/mod.rs @@ -11,6 +11,7 @@ use crate::utils::common; use crate::utils::aes::decrypt_aes128_ecb; use crate::formats::mstar::{extract_mstar, is_mstar_file}; use include::*; +use log::info; pub struct MstarSecureCtx { dec_footer: Vec, @@ -34,14 +35,14 @@ pub fn is_mstar_secure_old_file(app_ctx: &AppContext) -> Result) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; let hdr: ChunkFileFooter = Cursor::new(ctx.dec_footer).read_le()?; - println!("Info -\nSegment size: {}\nFile data offset: {}\nFile data len: {}", hdr.segment_size, hdr.file_data_offset, hdr.file_data_len); + info!("Info -\nSegment size: {}\nFile data offset: {}\nFile data len: {}", hdr.segment_size, hdr.file_data_offset, hdr.file_data_len); let enc_data = common::read_file(&mut file, hdr.file_data_offset as u64, hdr.file_data_len as usize)?; - println!("- Decrypting..."); + info!("- Decrypting..."); let dec_data = decrypt_aes128_ecb(&MSTAR_DEFAULT_UPGRADE_KEY, &enc_data)?; let output_path = Path::new(&app_ctx.output_dir).join("_decrypted.bin"); @@ -57,6 +58,7 @@ pub fn extract_mstar_secure_old(app_ctx: &AppContext, ctx: Box) -> Resu options: app_ctx.options.clone(), dry_run: app_ctx.dry_run, quiet: app_ctx.quiet, + verbose: app_ctx.verbose, }; //do check just in case and extract diff --git a/src/formats/mtk_bdp/include.rs b/src/formats/mtk_bdp/include.rs index 6606f90..40af7e8 100644 --- a/src/formats/mtk_bdp/include.rs +++ b/src/formats/mtk_bdp/include.rs @@ -10,7 +10,7 @@ pub static PITIT_END_MARKER: u32 = 0x69_54_49_50; //PITi - end marker of PITIT #[derive(BinRead)] pub struct PITITPITEntry { - pub nand_size: u32, + pub nand_size: u32, pub pit_offset: u32, pub pit_size: u32, _table_id: u32, @@ -18,7 +18,7 @@ pub struct PITITPITEntry { #[derive(BinRead)] pub struct PITITBITEntry { - pub bit_offset: u32, + pub bit_offset: u32, pub bit_size: u32, _private_data_1: u32, _private_data_2: u32, diff --git a/src/formats/mtk_bdp/mod.rs b/src/formats/mtk_bdp/mod.rs index 22c9bbc..1e11a04 100644 --- a/src/formats/mtk_bdp/mod.rs +++ b/src/formats/mtk_bdp/mod.rs @@ -9,6 +9,7 @@ use binrw::BinReaderExt; use crate::utils::common; use include::*; +use log::info; pub struct MtkBdpContext { pitit_offset: u64, @@ -34,10 +35,10 @@ pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result>, Box pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; let offset = ctx.pitit_offset; - println!("\nReading PITIT at: {}", offset); + info!("\nReading PITIT at: {}", offset); file.seek(SeekFrom::Start(offset + 8))?; @@ -53,11 +54,11 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo if pitit_ver == 1 { //old PITIT does not have BIT entry, because BIT appears directly after PITIT let pitit_bit_entry: PITITBITEntry = file.read_le()?; - println!("PITIT Entry - NAND Size: {}, PIT Offset: {}, PIT Size: {}, BIT Offset: {}, BIT Size: {}", + info!("PITIT Entry - NAND Size: {}, PIT Offset: {}, PIT Size: {}, BIT Offset: {}, BIT Size: {}", pitit_pit_entry.nand_size, pitit_pit_entry.pit_offset, pitit_pit_entry.pit_size, pitit_bit_entry.bit_offset, pitit_bit_entry.bit_size); if bit_offset == 0 { bit_offset = pitit_bit_entry.bit_offset as u64 } //use the first entry in PITIT } else { - println!("PITIT Entry - NAND Size: {}, PIT Offset: {}, PIT Size: {}", + info!("PITIT Entry - NAND Size: {}, PIT Offset: {}, PIT Size: {}", pitit_pit_entry.nand_size, pitit_pit_entry.pit_offset, pitit_pit_entry.pit_size); } if pit_offset == 0 { pit_offset = pitit_pit_entry.pit_offset as u64 } //use the first entry in PITIT @@ -67,24 +68,24 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo bit_offset = file.stream_position()?; } - println!("\nReading PIT at: {}", pit_offset); //PIT is the NAND partition table. + info!("\nReading PIT at: {}", pit_offset); //PIT is the NAND partition table. file.seek(SeekFrom::Start(pit_offset))?; let mut pit_entries: Vec = Vec::new(); let pit_header: PITHeader = file.read_le()?; if pit_header.pit_magic != PIT_MAGIC { return Err("Invalid PIT magic!".into()); } - println!("PIT Info - First entry offs: {}, Entry size: {}, Entry count: {}", pit_header.first_entry_offset, pit_header.entry_size, pit_header.entry_count); + info!("PIT Info - First entry offs: {}, Entry size: {}, Entry count: {}", pit_header.first_entry_offset, pit_header.entry_size, pit_header.entry_count); file.seek(SeekFrom::Start(pit_offset + pit_header.first_entry_offset as u64))?; for i in 0..pit_header.entry_count { let pit_entry: PITEntry = file.read_le()?; - println!("{}. ID: {:02x}, Name: {}, NAND Offset: {}, NAND Size: {}", + info!("{}. ID: {:02x}, Name: {}, NAND Offset: {}, NAND Size: {}", i + 1, pit_entry.partition_id, pit_entry.name(), pit_entry.offset_on_nand, pit_entry.size_on_nand); pit_entries.push(pit_entry); } - println!("\nReading BIT at: {}", bit_offset); //BIT is the table of objects present in the update file. + info!("\nReading BIT at: {}", bit_offset); //BIT is the table of objects present in the update file. file.seek(SeekFrom::Start(bit_offset))?; let mut bit_entries: Vec = Vec::new(); let bit_magic = common::read_exact(&mut file, 20)?; @@ -96,7 +97,7 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo loop { let bit_entry: BITEntry = file.read_le()?; if bit_entry.partition_id == BIT_END_MARKER {break}; - println!("{}. ID: {:02x}, Offset: {}, Size: {}, Offset in part: {}", + info!("{}. ID: {:02x}, Offset: {}, Size: {}, Offset in part: {}", bit_i + 1, bit_entry.partition_id, bit_entry.offset, bit_entry.size, bit_entry.offset_in_target_part); bit_entries.push(bit_entry); bit_i += 1; @@ -112,7 +113,7 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo } } - println!("\n({}/{}) - {}, Offset: {}, Size: {}, Offset in partition: {}", + info!("\n({}/{}) - {}, Offset: {}, Size: {}, Offset in partition: {}", i + 1, bit_entries.len(), name, bit_entry.offset, bit_entry.size, bit_entry.offset_in_target_part); let data = common::read_file(&file, bit_entry.offset as u64, bit_entry.size as usize)?; @@ -123,7 +124,7 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo out_file.seek(SeekFrom::Start(bit_entry.offset_in_target_part as u64))?; out_file.write_all(&data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } Ok(()) diff --git a/src/formats/mtk_pkg/include.rs b/src/formats/mtk_pkg/include.rs index 0fef891..750def4 100644 --- a/src/formats/mtk_pkg/include.rs +++ b/src/formats/mtk_pkg/include.rs @@ -2,8 +2,8 @@ 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, + 0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94, + 0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94, ]; pub static HEADER_IV: [u8; 16] = [0x00; 16]; @@ -24,7 +24,7 @@ pub struct Header { pub vendor_magic_bytes: [u8; 4], _mtk_magic: [u8; 8], //#DH@FiRm version_bytes: [u8; 60], - pub file_size: u32, + pub file_size: u32, _flags: [u8; 4], // 3rd is possibly the compression type in new mtk pkg product_name_bytes: [u8; 32], } @@ -44,7 +44,7 @@ impl Header { #[derive(BinRead)] pub struct PartEntry { name_bytes: [u8; 4], - pub flags: u32, + pub flags: u32, pub size: u32, } impl PartEntry { diff --git a/src/formats/mtk_pkg/lzhs.rs b/src/formats/mtk_pkg/lzhs.rs index 6eff93c..2587aeb 100644 --- a/src/formats/mtk_pkg/lzhs.rs +++ b/src/formats/mtk_pkg/lzhs.rs @@ -1,3 +1,4 @@ +use log::info; use std::fs::OpenOptions; use std::io::{Write, Cursor, Seek, SeekFrom, Read}; use binrw::{BinRead, BinReaderExt}; @@ -39,7 +40,7 @@ pub fn decompress_mtk_to_file(data: &[u8], output_file: &PathBuf) -> Result<(), let segment_header: LzhsHeader = data_reader.read_le()?; let comp_header: LzhsHeader = data_reader.read_le()?; - println!("[cmp] Segment {} - Compressed size: {}, Decompressed size: {}", + info!("[cmp] Segment {} - Compressed size: {}, Decompressed size: {}", segment_header.checksum_or_seg_idx, comp_header.compressed_size, comp_header.uncompressed_size); let compressed_data = common::read_exact(&mut data_reader, comp_header.compressed_size as usize)?; @@ -71,7 +72,7 @@ pub fn decompress_mtk_to_file(data: &[u8], output_file: &PathBuf) -> Result<(), } else { //normal variant - println!("- Decompressing {:?}...", compression_type); + info!("- Decompressing {:?}...", compression_type); if compression_type == CompressionType::LZ4 { out_data = decompress_lz4(&compressed_data, comp_header.uncompressed_size as i32)?; } @@ -84,12 +85,12 @@ pub fn decompress_mtk_to_file(data: &[u8], output_file: &PathBuf) -> Result<(), arm_thumb_convert(&mut out_data, 0, false); let checksum = calc_checksum(&out_data); - println!("-- Calculated checksum: 0x{:02x?}", checksum); + info!("-- Calculated checksum: 0x{:02x?}", checksum); 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); + info!("--- 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!") + info!("--- Checksum OK!") } } else { @@ -119,7 +120,7 @@ pub fn decompress_mtk_to_file_old(data: &[u8], output_file: &PathBuf) -> Result< 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?}", + info!("[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 compressed_data = common::read_exact(&mut data_reader, lzhs_header.compressed_size as usize)?; @@ -129,12 +130,12 @@ pub fn decompress_mtk_to_file_old(data: &[u8], output_file: &PathBuf) -> Result< arm_thumb_convert(&mut out_data, 0, false); let checksum = calc_checksum(&out_data); - println!("-- Calculated checksum: 0x{:02x?}", checksum); + info!("-- Calculated checksum: 0x{:02x?}", checksum); 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); + info!("--- Checksum mismatch! Expected: 0x{:02x?}, Got: 0x{:02x?}!", lzhs_header.checksum_or_seg_idx, checksum); return Err("LZHS checksum mismatch".into()); } else { - println!("--- Checksum OK!") + info!("--- Checksum OK!") } out_file.write_all(&out_data)?; diff --git a/src/formats/mtk_pkg/mod.rs b/src/formats/mtk_pkg/mod.rs index 32223c3..0ef32cb 100644 --- a/src/formats/mtk_pkg/mod.rs +++ b/src/formats/mtk_pkg/mod.rs @@ -15,6 +15,7 @@ use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::keys; use lzhs::{decompress_mtk_to_file}; use include::*; +use log::{info, warn}; pub struct MtkPkgContext { is_philips_variant: bool, @@ -43,7 +44,7 @@ pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result>, Box pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; let file_size = file.metadata()?.len(); let header = ctx.decrypted_header; @@ -52,7 +53,7 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo 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: {}" , + info!("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 { @@ -67,13 +68,13 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo let part_entry: PartEntry = file.read_le()?; if !part_entry.is_valid() {break}; - println!("\n#{} - {}, Size: {}{} {}", + info!("\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!"); + info!("- Empty entry, skipping!"); continue } @@ -91,7 +92,7 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo } 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..."); + info!("- Decrypting with 4xVendor magic..."); matching_key = Some(key); matching_iv = Some(HEADER_IV); @@ -103,7 +104,7 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo 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); + info!("- Decrypting with key {}...", name); matching_key = Some(key_array); matching_iv = Some(iv_array); break @@ -121,7 +122,7 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo out_data.push(b ^ key_array[i % key_array.len()]); } } else { - println!("- Failed to decrypt data!"); + info!("- Failed to decrypt data!"); continue } } else { @@ -134,7 +135,7 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo if imtk_len != 0 && &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); + info!("- Version: {}", version); } imtk_len + 8 } else { @@ -148,18 +149,18 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo if part_entry.is_compressed() { match decompress_mtk_to_file(&fin_data, &output_path) { Ok(()) => { - println!("-- Decompressed Successfully, Saved file!"); + info!("-- Decompressed Successfully, Saved file!"); continue }, Err(e) => { - eprintln!("Failed to decompress partition!, Error: {}. Saving compressed data...", e); + warn!("Failed to decompress partition!, Error: {}. Saving compressed data...", e); } } } let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?; out_file.write_all(&fin_data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } diff --git a/src/formats/mtk_pkg_new/mod.rs b/src/formats/mtk_pkg_new/mod.rs index dd3e6d4..897b754 100644 --- a/src/formats/mtk_pkg_new/mod.rs +++ b/src/formats/mtk_pkg_new/mod.rs @@ -14,6 +14,7 @@ use crate::keys; 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::*; +use log::{info, warn}; pub struct MtkPkgNewContext { matching_key_name: String, @@ -46,12 +47,12 @@ pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result>, pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; let file_size = file.metadata()?.len(); //the key was founf, and header was decrypted at detection stage so we can reuse - println!("Using key {}", ctx.matching_key_name); + info!("Using key {}", ctx.matching_key_name); let key_array = ctx.matching_key_key; let iv_array = ctx.matching_key_iv; let header = ctx.decrypted_header; @@ -60,7 +61,7 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box) -> Result<() 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: {}" , + info!("File info:\nFile size: {}\nVendor magic: {}\nVersion info: {}\nProduct name: {}" , hdr.file_size, hdr.vendor_magic(), hdr.version(), hdr.product_name()); file.seek(SeekFrom::Start(HEADER_SIZE as u64))?; @@ -73,19 +74,19 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box) -> Result<() break } - println!("\n#{} - {}, Size: {}{} {}", + info!("\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 + 48)?; if part_entry.size == 0 { - println!("- Empty entry, skipping!"); + info!("- Empty entry, skipping!"); continue } let mut out_data; if part_entry.is_encrypted() { - println!("- Decrypting..."); + info!("- Decrypting..."); //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); @@ -103,7 +104,7 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box) -> Result<() if imtk_len != 0 && &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); + info!("- Version: {}", version); } imtk_len + 8 } else { @@ -118,18 +119,18 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box) -> Result<() if part_entry.is_compressed() { match decompress_mtk_to_file(&fin_data, &output_path) { Ok(()) => { - println!("-- Decompressed Successfully, Saved file!"); + info!("-- Decompressed Successfully, Saved file!"); continue }, Err(e) => { - eprintln!("Failed to decompress partition!, Error: {}. Saving compressed data...", e); + warn!("Failed to decompress partition!, Error: {}. Saving compressed data...", e); } } } let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?; out_file.write_all(&fin_data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } Ok(()) diff --git a/src/formats/mtk_pkg_old/mod.rs b/src/formats/mtk_pkg_old/mod.rs index 42056a9..69583da 100644 --- a/src/formats/mtk_pkg_old/mod.rs +++ b/src/formats/mtk_pkg_old/mod.rs @@ -14,6 +14,7 @@ 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::*; +use log::{info, warn}; pub struct MtkPkgOldContext { header_offset: u64, @@ -39,7 +40,7 @@ pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result>, pub fn extract_mtk_pkg_old(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; let file_size = file.metadata()?.len(); @@ -51,7 +52,7 @@ pub fn extract_mtk_pkg_old(app_ctx: &AppContext, ctx: Box) -> Result<() 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: {}" , + info!("File info:\nFile size: {}\nVendor magic: {}\nVersion info: {}\nProduct name: {}" , hdr.file_size, hdr.vendor_magic(), hdr.version(), hdr.product_name()); let mut part_n = 0; @@ -59,14 +60,14 @@ pub fn extract_mtk_pkg_old(app_ctx: &AppContext, ctx: Box) -> Result<() part_n += 1; let part_entry: PartEntry = file.read_le()?; - println!("\n#{} - {}, Size: {}{} {}", + info!("\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; if part_entry.is_encrypted() { //decrypt with the vendor magic - println!("- Decrypting..."); + info!("- Decrypting..."); let vendor_magic_u32 = u32::from_le_bytes(hdr.vendor_magic_bytes.clone().try_into().unwrap()); out_data = decrypt(&data, vendor_magic_u32, Some(CONTENT_XOR_MASK)); } else { @@ -79,7 +80,7 @@ pub fn extract_mtk_pkg_old(app_ctx: &AppContext, ctx: Box) -> Result<() if imtk_len != 0 && &out_data[8..12] != MTK_META_PAD_MAGIC { let version_len = u32::from_le_bytes(out_data[8..12].try_into().unwrap()); let version = common::string_from_bytes(&out_data[12..12 + version_len as usize]); - println!("- Version: {}", version); + info!("- Version: {}", version); } imtk_len + 8 } else { @@ -93,18 +94,18 @@ pub fn extract_mtk_pkg_old(app_ctx: &AppContext, ctx: Box) -> Result<() if part_entry.is_compressed() { match decompress_mtk_to_file_old(&fin_data, &output_path) { Ok(()) => { - println!("-- Decompressed Successfully, Saved file!"); + info!("-- Decompressed Successfully, Saved file!"); continue }, Err(e) => { - eprintln!("Failed to decompress partition!, Error: {}. Saving compressed data...", e); + warn!("Failed to decompress partition!, Error: {}. Saving compressed data...", e); } } } let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?; out_file.write_all(&fin_data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } Ok(()) diff --git a/src/formats/novatek/mod.rs b/src/formats/novatek/mod.rs index 3ef7c6f..d4bd3ba 100644 --- a/src/formats/novatek/mod.rs +++ b/src/formats/novatek/mod.rs @@ -9,6 +9,7 @@ use binrw::BinReaderExt; use crate::utils::common; use include::*; +use log::info; pub fn is_novatek_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -25,7 +26,7 @@ pub fn extract_novatek(app_ctx: &AppContext, _ctx: Box) -> Result<(), B let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: Header = file.read_le()?; - println!("File info:\nFirmware name: {}\nVersion: {}.{}\nData size: {}\nPart count: {}", + info!("File info:\nFirmware name: {}\nVersion: {}.{}\nData size: {}\nPart count: {}", header.firmware_name(), header.version_major, header.version_minor, header.data_size, header.part_count); let mut entries: Vec = Vec::new(); @@ -37,7 +38,7 @@ pub fn extract_novatek(app_ctx: &AppContext, _ctx: Box) -> Result<(), B let mut e_i = 0; for entry in &entries { e_i += 1; - println!("\n({}/{}) - ID: {}, Offset: {}, Size: {}", e_i, entries.len(), entry.id, entry.offset, entry.size); + info!("\n({}/{}) - ID: {}, Offset: {}, Size: {}", e_i, entries.len(), entry.id, entry.offset, entry.size); let data = common::read_file(&file, entry.offset as u64, entry.size as usize)?; @@ -47,7 +48,7 @@ pub fn extract_novatek(app_ctx: &AppContext, _ctx: Box) -> Result<(), B let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; - println!("- Saved file!"); + info!("- Saved file!"); } Ok(()) diff --git a/src/formats/novatek_raw/mod.rs b/src/formats/novatek_raw/mod.rs index b57b050..70f56fb 100644 --- a/src/formats/novatek_raw/mod.rs +++ b/src/formats/novatek_raw/mod.rs @@ -8,6 +8,7 @@ use std::io::Write; use crate::utils::common; use crate::utils::aes::decrypt_aes_ecb_auto; use crate::keys; +use log::info; const NVT_PARTITIONS: [(&str, u32, u32); 16] = [ ("spi_boot", 0x0000020, 0x00020000), @@ -41,10 +42,6 @@ pub fn is_novatek_raw_file(app_ctx: &AppContext) -> Result>, return Ok(Some(Box::new(()))); } - for _ in keys::NOVATEK_RAW { - return Ok(Some(Box::new(()))); - } - Ok(None) } @@ -66,28 +63,28 @@ pub fn extract_novatek_raw(app_ctx: &AppContext, _ctx: Box) -> Result<( let file_size = file.metadata()?.len() as usize; let enc_data = common::read_file(&mut file, 0, file_size)?; - println!("- File size: {} bytes", file_size); - println!("- Decrypting with AES-{}...", if key.len() == 16 { "128" } else { "256" }); + info!("- File size: {} bytes", file_size); + info!("- Decrypting with AES-{}...", if key.len() == 16 { "128" } else { "256" }); let dec_data = decrypt_aes_ecb_auto(&key, &enc_data)?; - println!("- Decryption complete"); + info!("- Decryption complete"); fs::create_dir_all(&app_ctx.output_dir)?; let output_path = Path::new(&app_ctx.output_dir).join("_decrypted.bin"); let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?; out_file.write_all(&dec_data)?; - println!("- Saved decrypted image to {}", output_path.display()); + info!("- Saved decrypted image to {}", output_path.display()); - println!("\nExtracting partitions:"); + info!("\nExtracting partitions:"); for (name, offset, size) in NVT_PARTITIONS { if (offset + size) as usize <= dec_data.len() && size > 0 { let part_data = &dec_data[offset as usize..(offset + size) as usize]; let part_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", name)); let mut part_file = OpenOptions::new().write(true).create(true).open(&part_path)?; part_file.write_all(part_data)?; - println!(" {}: offset=0x{:x}, size={}", name, offset, size); + info!(" {}: offset=0x{:x}, size={}", name, offset, size); } } diff --git a/src/formats/nvt_timg/mod.rs b/src/formats/nvt_timg/mod.rs index 677750a..a60794d 100644 --- a/src/formats/nvt_timg/mod.rs +++ b/src/formats/nvt_timg/mod.rs @@ -11,6 +11,7 @@ use crate::utils::common; use crate::utils::compression::{decompress_gzip}; use crate::utils::sparse::{unsparse_to_file}; use include::*; +use log::info; pub struct TimgContext { variant: TimgVariant, @@ -42,14 +43,14 @@ pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result>, Bo pub fn extract_nvt_timg(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; let timg: Box = match ctx.variant { TimgVariant::New => Box::new(file.read_le::()?), TimgVariant::Old => Box::new(file.read_le::()?), TimgVariant::Old2 => Box::new(file.read_le::()?), }; - println!("File info:\nVariant: {:?}\nData size: {}", ctx.variant, timg.data_size()); + info!("File info:\nVariant: {:?}\nData size: {}", ctx.variant, timg.data_size()); //position after header + data size let end = file.stream_position()? + timg.data_size() as u64; @@ -70,27 +71,27 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, ctx: Box) -> Result<(), B let data = common::read_exact(&mut file, pimg.size())?; - println!("\n#{} - {}, Size: {}, Dest: {}, Compression: {}, Comment: {}", + info!("\n#{} - {}, Size: {}, Dest: {}, Compression: {}, Comment: {}", pimg_i, pimg.name(), pimg.size(), pimg.dest_dev(), pimg.comp_type(), pimg.comment()); let out_data; let output_path = Path::new(&app_ctx.output_dir).join(pimg.name() + ".bin"); if pimg.comp_type() == "gzip" && data.starts_with(b"\x1F\x8B") { //additionally check for gzip header, because sometimes its deceptive - println!("- Decompressing gzip..."); + info!("- Decompressing gzip..."); out_data = decompress_gzip(&data)?; } else if pimg.comp_type() == "none" || pimg.comp_type() == "" { out_data = data; } else if pimg.comp_type() == "sparse" { - println!("- Unsparsing..."); + info!("- Unsparsing..."); unsparse_to_file(&data, output_path)?; - println!("-- Saved file!"); + info!("-- Saved file!"); continue } else { - println!("- Warning: unsupported compression type, saving stored data!"); + info!("- Warning: unsupported compression type, saving stored data!"); out_data = data; } @@ -98,7 +99,7 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, ctx: Box) -> Result<(), B let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&out_data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } Ok(()) diff --git a/src/formats/onkyo/crypto.rs b/src/formats/onkyo/crypto.rs index f916c1f..383c92d 100644 --- a/src/formats/onkyo/crypto.rs +++ b/src/formats/onkyo/crypto.rs @@ -1,6 +1,6 @@ use super::include::ONKYO_MAGIC; -pub fn ub_encrypte_block(input: &[u8], key: &[u8; 8]) -> Vec { +pub fn ub_encrypted_block(input: &[u8], key: &[u8; 8]) -> Vec { let mut output = Vec::with_capacity(input.len()); let state_counter = 0; let state_byte = 0; diff --git a/src/formats/onkyo/mod.rs b/src/formats/onkyo/mod.rs index 6e1ece6..071c414 100644 --- a/src/formats/onkyo/mod.rs +++ b/src/formats/onkyo/mod.rs @@ -12,6 +12,7 @@ use binrw::BinReaderExt; use crate::utils::common; use include::*; use crypto::*; +use log::info; struct OnkyoCtx { header_size: u32, @@ -21,7 +22,7 @@ pub fn is_onkyo_file(app_ctx: &AppContext) -> Result>, Box f, None => return Ok(None)}; let enc_inihdr = common::read_file(&file, 0, 20)?; - let dec_inihdr= ub_encrypte_block(&enc_inihdr, &HEADER_KEY); + let dec_inihdr= ub_encrypted_block(&enc_inihdr, &HEADER_KEY); if dec_inihdr.starts_with(ONKYO_MAGIC) { let header_size = u32::from_le_bytes(dec_inihdr[16..20].try_into().unwrap()); @@ -33,12 +34,12 @@ pub fn is_onkyo_file(app_ctx: &AppContext) -> Result>, Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; - println!("Header size: {}", ctx.header_size); + info!("Header size: {}", ctx.header_size); let enc_hdr = common::read_exact(&mut file, ctx.header_size as usize)?; - let dec_hdr = ub_encrypte_block(&enc_hdr, &HEADER_KEY); + let dec_hdr = ub_encrypted_block(&enc_hdr, &HEADER_KEY); opt_dump_dec_hdr(app_ctx, &dec_hdr, "header")?; let mut hdr_rdr = Cursor::new(dec_hdr); @@ -47,7 +48,7 @@ pub fn extract_onkyo(app_ctx: &AppContext, ctx: Box) -> Result<(), Box< //read info section hdr_rdr.seek(SeekFrom::Start(hdr.pack_info_offset as u64))?; let info: PackInfo = hdr_rdr.read_le()?; - println!("Info -\nPackage ID: {}\nVersion: {}\nEntry count: {}\nEntries in file: {}\nPack: {}/{}", + info!("Info -\nPackage ID: {}\nVersion: {}\nEntry count: {}\nEntries in file: {}\nPack: {}/{}", info.package_id(), info.package_version(), info.entry_count, info.entries_in_file, info.pack_id, info.pack_count,); if info.entries_in_file == 0 { @@ -80,7 +81,7 @@ pub fn extract_onkyo(app_ctx: &AppContext, ctx: Box) -> Result<(), Box< } act_ei += 1; - println!("\n({}/{}) - {}, Size: {}, Offset: {}, Pack location: {}", + info!("\n({}/{}) - {}, Size: {}, Offset: {}, Pack location: {}", act_ei, info.entries_in_file, entry.id(), data_entry.size, data_entry.offset, entry.pack_location); let data = common::read_file(&mut file, data_entry.offset as u64, data_entry.size as usize)?; @@ -91,33 +92,33 @@ pub fn extract_onkyo(app_ctx: &AppContext, ctx: Box) -> Result<(), Box< let mut is_pack = false; //try using standard data key - if ub_encrypte_block(&data[..16], &DATA_KEY).starts_with(ONKYO_MAGIC) { + if ub_encrypted_block(&data[..16], &DATA_KEY).starts_with(ONKYO_MAGIC) { dec_key = Some(DATA_KEY); //try header key, if success it means this is a package inside a package, and it should not be decrypted - } else if ub_encrypte_block(&data[..16], &HEADER_KEY).starts_with(ONKYO_MAGIC) { + } else if ub_encrypted_block(&data[..16], &HEADER_KEY).starts_with(ONKYO_MAGIC) { is_pack = true; //if not matched with data key, try to calc key } else { let calced_key = calc_key(&data[..8]); - if ub_encrypte_block(&data[..16], &calced_key).starts_with(ONKYO_MAGIC) { + if ub_encrypted_block(&data[..16], &calced_key).starts_with(ONKYO_MAGIC) { dec_key = Some(calced_key); } } if let Some(key) = dec_key { - println!("- Detected encrypted data, decrypting..."); - out_data = ub_encrypte_block(&data, &key); + info!("- Detected encrypted data, decrypting..."); + out_data = ub_encrypted_block(&data, &key); out_data.drain(0..16); //remove ONKYO Encryption heading } else if is_pack { - println!("- Inner pack detected!"); //maybe handle this... + info!("- Inner pack detected!"); //maybe handle this... out_data = data; } else { - println!("- Failed to decrypt data or entry is not encrypted, saving raw data..."); + info!("- Failed to decrypt data or entry is not encrypted, saving raw data..."); out_data = data; } @@ -126,7 +127,7 @@ pub fn extract_onkyo(app_ctx: &AppContext, ctx: Box) -> Result<(), Box< let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&out_data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } diff --git a/src/formats/pana_dvd/mod.rs b/src/formats/pana_dvd/mod.rs index 05a6274..270f2dd 100644 --- a/src/formats/pana_dvd/mod.rs +++ b/src/formats/pana_dvd/mod.rs @@ -1,3 +1,5 @@ +use log::info; + mod include; mod pana_dvd_crypto; mod lzss; @@ -62,7 +64,7 @@ pub fn is_pana_dvd_file(app_ctx: &AppContext) -> Result>, Bo pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let context = ctx.downcast::().expect("Missing context"); + let context = ctx.downcast::().map_err(|_| "Invalid context type")?; let matching_key = context.matching_key; let mut file_entries: Vec = Vec::new(); @@ -70,7 +72,7 @@ pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Box) -> Result<(), B //AES files can contain multiple firmwares inside of itself if context.is_aes { let (aes_key, aes_iv) = (context.aes_key.unwrap(), context.aes_iv.unwrap()); - println!("Using key: {} + AES key: {}, IV: {}", hex::encode_upper(matching_key), hex::encode_upper(aes_key), hex::encode_upper(aes_iv)); + info!("Using key: {} + AES key: {}, IV: {}", hex::encode_upper(matching_key), hex::encode_upper(aes_key), hex::encode_upper(aes_iv)); //read inner file table let file_table = common::read_exact(&mut file, 48)?; @@ -89,11 +91,11 @@ pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Box) -> Result<(), B } } else { - println!("Using key: {}", hex::encode_upper(matching_key)); + info!("Using key: {}", hex::encode_upper(matching_key)); file_entries.push(FileEntry { offset: 0, size: file.metadata()?.len() as u32, header_size: context.base_hdr_size }); } - println!("File contains {} sub-files...", file_entries.len()); + info!("File contains {} sub-files...", file_entries.len()); for (i, file_entry ) in file_entries.iter().enumerate() { let data = common::read_file(&mut file, file_entry.offset as u64, file_entry.size as usize)?; let dec_data = if context.is_aes { @@ -111,7 +113,7 @@ pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Box) -> Result<(), B &app_ctx.output_dir.join(format!("file_{}", i + 1)) }; - println!("\nExtracting file {}/{} - Offset: {}, Size: {}, Header size: {}", + info!("\nExtracting file {}/{} - Offset: {}, Size: {}, Header size: {}", i + 1, file_entries.len(), file_entry.offset, file_entry.size, file_entry.header_size); extract_file(app_ctx, &mut file_reader, file_entry.header_size as u64, matching_key, output_dir)?; @@ -133,10 +135,10 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor>, header_ for i in 0..100 { let entry: ModuleEntry = list_reader.read_le()?; if !entry.is_valid() {break}; - println!("Module {} - Name: {}, Version: {}, Model ID: {}, ID: {}, Offset: {}, Size: {}", + info!("Module {} - Name: {}, Version: {}, Model ID: {}, ID: {}, Offset: {}, Size: {}", i + 1, entry.name(), entry.version(), entry.model_id(), entry.id(), entry.offset, entry.size); if modules.iter().any(|m| m.offset == entry.offset ){ - println!("- Duplicate module, skipping!"); + info!("- Duplicate module, skipping!"); continue } @@ -146,7 +148,7 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor>, header_ let mut mod_i = 0; for module in &modules { mod_i += 1; - println!("\n({}/{}) - {}, Offset: {}, Size: {}, Checksum: {:#010x}", + info!("\n({}/{}) - {}, Offset: {}, Size: {}, Checksum: {:#010x}", mod_i, modules.len(), module.name(), module.offset, module.size, module.data_checksum); //if there is multiple modules with the same name, add the module ID to the outptut file to prevent collision @@ -160,10 +162,10 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor>, header_ //special treatment of MAIN if module.name() == "MAIN" { - println!("- Extracting MAIN..."); + info!("- Extracting MAIN..."); extract_main(file_reader, key, &output_path)?; if app_ctx.has_option("pana_dvd:split_main") { - println!("\n- Splitting MAIN..."); + info!("\n- Splitting MAIN..."); split_main_file(&output_path, output_folder)?; } continue @@ -171,12 +173,12 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor>, header_ let data = common::read_exact(file_reader, (module.size as usize + 7) & !7)?; //read to the nearest multiple of 8 (needed for unalinged data decryption) - println!("- Decrypting..."); + info!("- Decrypting..."); let mut dec_data = decrypt_data(&data, &key); dec_data.truncate(module.size as usize); //discard padding if module.name().starts_with("DRV") { - println!("- Extracting DRIVE firmware..."); + info!("- Extracting DRIVE firmware..."); dec_data = extract_drv(dec_data, &key)?; } @@ -184,7 +186,7 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor>, header_ let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&dec_data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } Ok(()) @@ -193,15 +195,15 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor>, header_ fn extract_main(file_reader: &mut Cursor>, key: [u8; 8], output_path: &PathBuf) -> Result<(), Box> { let main_list_hdr: MainListHeader = file_reader.read_le()?; if main_list_hdr.entry_count() > 200 { - println!("Unsupported MAIN data, skipping!"); + info!("Unsupported MAIN data, skipping!"); return Ok(()) } - println!("MAIN - Entry count: {}, Decompressed part size: {}", main_list_hdr.entry_count(), main_list_hdr.decompressed_part_size); + info!("MAIN - Entry count: {}, Decompressed part size: {}", main_list_hdr.entry_count(), main_list_hdr.decompressed_part_size); let mut main_entries: Vec = Vec::new(); for i in 0..main_list_hdr.entry_count() { let main_entry: MainListEntry = file_reader.read_le()?; - println!("- Entry {}/{} - Size: {}, Checksum: {:#010x}", + info!("- Entry {}/{} - Size: {}, Checksum: {:#010x}", i + 1, main_list_hdr.entry_count(), main_entry.size, main_entry.checksum); main_entries.push(main_entry); } @@ -225,12 +227,12 @@ fn extract_main(file_reader: &mut Cursor>, key: [u8; 8], output_path: &P data.copy_from_slice(&decrypted); } - print!("\nMAIN ({}/{}) - ", maine_i, main_entries.len()); + info!("MAIN ({}/{}) - ", maine_i, main_entries.len()); let decompressed_data = decompress_data(&data)?; main_out_file.write_all(&decompressed_data)?; - println!("-- Saved to MAIN!"); + info!("-- Saved to MAIN!"); } Ok(()) @@ -241,22 +243,22 @@ fn decompress_data(data: &[u8]) -> Result, Box> { let header: CompressedFileHeader = data_reader.read_le()?; let compression_type = CompressionType::from(header.compression_type); - println!("Compressed size: {}, Decompressed size: {}, Compression type: {:?}({})", + info!("Compressed size: {}, Decompressed size: {}, Compression type: {:?}({})", header.src_size, header.dest_size, compression_type, header.compression_type); let compressed_data = common::read_exact(&mut data_reader, header.src_size as usize)?; let mut decompressed_data; if compression_type == CompressionType::Gzip { - println!("- Decompressing GZIP..."); + info!("- Decompressing GZIP..."); let (decompressed_gzip, gzip_filename) = decompress_gzip_get_filename(&compressed_data)?; if let Some(gzip_filename) = gzip_filename { - println!("- GZIP filename: {}", gzip_filename); + info!("- GZIP filename: {}", gzip_filename); } decompressed_data = decompressed_gzip; } else if compression_type == CompressionType::Lzss { - println!("- Decompressing LZSS..."); + info!("- Decompressing LZSS..."); decompressed_data = decompress_lzss(&compressed_data); if decompressed_data.len() != header.dest_size as usize { return Err("Decompressed size does not match size in header, decompression failed!".into()); @@ -267,7 +269,7 @@ fn decompress_data(data: &[u8]) -> Result, Box> { //GzipAndLzss is not used in this context. } else { - println!("- Unknown compression method!"); + info!("- Unknown compression method!"); decompressed_data = compressed_data; } @@ -293,7 +295,7 @@ fn extract_drv(mut data: Vec, key: &[u8; 8]) -> Result, Box Result<(), Box Result<(), Box Result<(), Box String { @@ -58,8 +58,8 @@ impl Header { pub struct FileHeader { file_name_bytes: [u8; 60], pub real_size: u32, - pub stored_size: u32, - pub header_size: u32, + pub stored_size: u32, + pub header_size: u32, pub attributes: [u8; 4], } impl FileHeader { diff --git a/src/formats/pfl_upg/mod.rs b/src/formats/pfl_upg/mod.rs index a517d52..8a0c183 100644 --- a/src/formats/pfl_upg/mod.rs +++ b/src/formats/pfl_upg/mod.rs @@ -10,6 +10,7 @@ use binrw::BinReaderExt; use crate::utils::common; use crate::utils::aes::decrypt_aes256_ecb; use include::*; +use log::info; pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -32,24 +33,24 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), B let version_bytes = common::read_exact(&mut file, 28)?; let version = common::string_from_bytes(&version_bytes); - println!("\nVersion: {}", version); + info!("\nVersion: {}", version); if header.description() != "" { //look ugly when empty - println!("--- Description --- \n{}", header.description()); - println!("-------------------"); + info!("--- Description --- \n{}", header.description()); + info!("-------------------"); } - println!("Data size: {}", header.data_size); + info!("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."); + info!("\nFile is encrypted."); //get some data as test ciphertext for key finding let ciphertext = common::read_file(&mut file, header.header_size as u64, 64)?; let aes_key; if let Some((key_name, key)) = try_find_key(&signature, &ciphertext)? { - println!("Matched pubkey: {}, AES key: {}", key_name, hex::encode(key)); + info!("Matched pubkey: {}, AES key: {}", key_name, hex::encode(key)); aes_key = key; } else { return Err("Matching key not found, cannot decrypt data".into()); @@ -58,7 +59,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), B //need to align to 16 bytes for AES blocksize let encrypted_data = common::read_exact(&mut file, (header.data_size as usize + 0xf) & !0xf)?; - println!("Decrypting data..."); + info!("Decrypting data..."); data = decrypt_aes256_ecb(&aes_key, &encrypted_data)?; data.truncate(header.data_size as usize); //discard padding @@ -76,7 +77,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), B let ex_header_bytes = common::read_exact(&mut data_reader, ex_header_size as usize)?; if file_header.is_folder() { - println!("\nFolder - {}", file_header.file_name()); + info!("\nFolder - {}", file_header.file_name()); let output_path = Path::new(&app_ctx.output_dir).join(file_header.file_name().trim_start_matches('/')); fs::create_dir_all(output_path)?; continue @@ -88,7 +89,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), B file_header.file_name() }; - println!("\nFile - {}, Size: {}", file_name, file_header.real_size); + info!("\nFile - {}, Size: {}", file_name, file_header.real_size); let data = common::read_exact(&mut data_reader, file_header.stored_size as usize)?; let output_path = Path::new(&app_ctx.output_dir).join(file_name.trim_start_matches('/')); @@ -100,7 +101,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), B //pfl upg inside pfl upg! DUMB code! if file_header.is_package() && !app_ctx.has_option("pfl_upg:no_extract_inner_upg"){ - println!("- Extracting inner UPG..."); + info!("- Extracting inner UPG..."); //save this as temp file let temp_path = Path::new(&app_ctx.output_dir).join("inner_upg_temp"); @@ -115,6 +116,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), B options: app_ctx.options.clone(), dry_run: app_ctx.dry_run, quiet: app_ctx.quiet, + verbose: app_ctx.verbose, }; //do check just in case and extract @@ -132,7 +134,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), B let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data[..file_header.real_size as usize])?; - println!("- Saved file!"); + info!("- Saved file!"); } Ok(()) diff --git a/src/formats/philips_bdp/mod.rs b/src/formats/philips_bdp/mod.rs index cd29e24..854aa26 100644 --- a/src/formats/philips_bdp/mod.rs +++ b/src/formats/philips_bdp/mod.rs @@ -9,6 +9,7 @@ use binrw::BinReaderExt; use crate::utils::common; use include::*; +use log::info; struct PhilipsBdpCtx { header_type: HeaderType, @@ -31,7 +32,7 @@ pub fn is_philips_bdp_file(app_ctx: &AppContext) -> Result>, pub fn extract_philips_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; let header: Box = match ctx.header_type { HeaderType::Old => Box::new(file.read_le::()?), @@ -40,7 +41,7 @@ pub fn extract_philips_bdp(app_ctx: &AppContext, ctx: Box) -> Result<() let header_size = file.stream_position()?; - println!("File info -\nName: {}\nVersion: {}\nTarget size: {}\nEntry count: {}\nHeader type: {:?}\nHeader size: {}", + info!("File info -\nName: {}\nVersion: {}\nTarget size: {}\nEntry count: {}\nHeader type: {:?}\nHeader size: {}", header.name(), header.version(), header.target_size(), header.target_num(), ctx.header_type, header_size); for (i, entry) in header.entries().iter().enumerate() { @@ -48,14 +49,14 @@ pub fn extract_philips_bdp(app_ctx: &AppContext, ctx: Box) -> Result<() break } - println!("\n#{} - ID: {:x}, IIC: {:x}, Version: {}, Offset: {}, Size: {}", + info!("\n#{} - ID: {:x}, IIC: {:x}, Version: {}, Offset: {}, Size: {}", i+1, entry.id, entry.iic, entry.version(), entry.offset, entry.size); let data = common::read_file(&file, entry.offset as u64 + header_size, entry.size as usize)?; let out_data; if entry.id == 0 && app_ctx.has_option("philips_bdp:decrypt") { - println!("- Decrypting..."); + info!("- Decrypting..."); out_data = bebin_decrypt_aes256cfb(&data, &KEY1, &IV1); } else { out_data = data; @@ -67,23 +68,23 @@ pub fn extract_philips_bdp(app_ctx: &AppContext, ctx: Box) -> Result<() let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?; out_file.write_all(&out_data)?; - println!("- Saved file!"); + info!("- Saved file!"); //ID 0 should be the main MTK bdp file, since this is just an extra container for that format (like Sony BDP), so we can try to extract it here. if entry.id == 0 { - println!("Checking if it's also MTK BDP..."); + info!("Checking if it's also MTK BDP..."); let new_file = File::open(&output_path)?; - let mtk_ctx: AppContext = AppContext { input: InputTarget::File(new_file), output_dir: app_ctx.output_dir.join("0"), options: app_ctx.options.clone(), dry_run: app_ctx.dry_run, quiet: app_ctx.quiet }; + let mtk_ctx: AppContext = AppContext { input: InputTarget::File(new_file), output_dir: app_ctx.output_dir.join("0"), options: app_ctx.options.clone(), dry_run: app_ctx.dry_run, quiet: app_ctx.quiet, verbose: app_ctx.verbose }; if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&mtk_ctx)? { - println!("- MTK BDP file detected!\n"); + info!("- MTK BDP file detected!\n"); formats::mtk_bdp::extract_mtk_bdp(&mtk_ctx, result)?; } else { if app_ctx.has_option("philips_bdp:decrypt") { - println!("- Not an MTK BDP file"); + info!("- Not an MTK BDP file"); } else { - println!("- Not an MTK BDP file (try with decrypt?)"); + info!("- Not an MTK BDP file (try with decrypt?)"); } } } diff --git a/src/formats/pup/mod.rs b/src/formats/pup/mod.rs index bf1d3ba..13dc398 100644 --- a/src/formats/pup/mod.rs +++ b/src/formats/pup/mod.rs @@ -10,6 +10,7 @@ use std::io::{Write, Seek, SeekFrom}; use crate::utils::common; use crate::utils::compression::{decompress_zlib}; use include::*; +use log::info; pub fn is_pup_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -26,7 +27,7 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box = Vec::new(); @@ -42,7 +43,7 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box) -> Result<(), Box = None; - println!("Block size: {}, Block count: {}", block_size, block_count); + info!("Block size: {}, Block count: {}", block_size, block_count); for block_table in &block_tables { if block_table.id() == e_i { my_block_table = Some(block_table.clone()); - println!("Found block table: Offset: {}, Size: {}", block_table.offset, block_table.compressed_size); + info!("Found block table: Offset: {}, Size: {}", block_table.offset, block_table.compressed_size); break } } if my_block_table.is_none() { - println!("Failed to find block table!"); + info!("Failed to find block table!"); continue } @@ -84,13 +85,13 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box) -> Result<(), Box) -> Result<(), Box) -> Result<(), Box Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -31,7 +32,7 @@ pub fn extract_roku(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let mut encrypted_data = Vec::new(); file.read_to_end(&mut encrypted_data)?; - println!("\nDecrypting...\n"); + info!("\nDecrypting...\n"); let tar_data = decrypt_aes128_cbc_pcks7(&encrypted_data, &FILE_KEY, &FILE_IV)?; let tar_reader = Cursor::new(tar_data); let mut tar_archive = Archive::new(tar_reader); @@ -47,20 +48,20 @@ pub fn extract_roku(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< entry.read_to_end(&mut contents)?; let text = String::from_utf8_lossy(&contents[..size - 256]); //dont display signature - println!("Manifest file:\n{}", text); + info!("Manifest file:\n{}", text); } else { let mut contents = Vec::new(); entry.read_to_end(&mut contents)?; //entry cant seek if contents.starts_with(b"\x00\x00\x00\x00\x00\x00\x00\x00imgARMcC") { - println!("\nImage file: {:?}:", path); + info!("\nImage file: {:?}:", path); let size = entry.header().size()? as usize; let mut image_reader = Cursor::new(contents); let mut i = 1; while image_reader.stream_position()? < size as u64 { let image: AImageHeader = image_reader.read_le()?; - println!(" #{} - Type: {}(0x{:x}), Length: {}, encmode: {}", + info!(" #{} - Type: {}(0x{:x}), Length: {}, encmode: {}", i, image.image_type_str(), image.image_type, image.length, image.encmode_str()); let data = @@ -80,20 +81,20 @@ pub fn extract_roku(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; - println!(" - Saved file!\n"); + info!(" - Saved file!\n"); i += 1; } } else { - println!("\nOther/Unknown file: {:?}", path); + info!("\nOther/Unknown file: {:?}", path); let output_path = Path::new(&app_ctx.output_dir).join(&path); fs::create_dir_all(&app_ctx.output_dir)?; let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&contents)?; - println!("- Saved file!"); + info!("- Saved file!"); } } } diff --git a/src/formats/ruf/include.rs b/src/formats/ruf/include.rs index 5b755c7..d92a882 100644 --- a/src/formats/ruf/include.rs +++ b/src/formats/ruf/include.rs @@ -5,7 +5,7 @@ use binrw::BinRead; pub struct RufHeader { _magic_bytes: [u8; 6], _upgrade_type_bytes: [u8; 2], - _unk1: u32, + _unk1: u32, date_time_bytes: [u8; 32], buyer_bytes: [u8; 8], model_bytes: [u8; 32], @@ -33,7 +33,7 @@ impl RufHeader { common::string_from_bytes(&self.region_info_bytes) } pub fn is_dual_ruf(&self) -> bool { - if self.dual_ruf_flag == 0x44 {true} else {false} + self.dual_ruf_flag == 0x44 } } @@ -41,7 +41,7 @@ impl RufHeader { pub struct RufEntry { _metadata: [u8; 32], pub payload_type_bytes: u32, - pub size: u32, + pub size: u32, _unk1: u32, _unk2: [u8; 20], } diff --git a/src/formats/ruf/mod.rs b/src/formats/ruf/mod.rs index 3419e86..a0169fa 100644 --- a/src/formats/ruf/mod.rs +++ b/src/formats/ruf/mod.rs @@ -11,6 +11,7 @@ use crate::utils::common; use crate::keys; use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; use include::*; +use log::info; pub fn is_ruf_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -28,9 +29,9 @@ pub fn extract_ruf(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box = Vec::new(); @@ -64,7 +65,7 @@ fn actually_extract_ruf(mut file: &File, output_folder: &PathBuf, start_offset: vi += 1 } - println!("{}/{}: Type: {}({}), Size: {}", + info!("{}/{}: Type: {}({}), Size: {}", vi, header.payload_count, entry.payload_type_bytes, entry.payload_type(), entry.size); entries.push(entry); @@ -82,7 +83,7 @@ fn actually_extract_ruf(mut file: &File, output_folder: &PathBuf, start_offset: } } if let Some(k) = key { - println!("\nKey: {}", k); + info!("\nKey: {}", k); key_bytes = hex::decode(k)?.as_slice().try_into()?; } else { return Err("This firmware is not supported!".into()); @@ -90,14 +91,14 @@ fn actually_extract_ruf(mut file: &File, output_folder: &PathBuf, start_offset: file.seek(SeekFrom::Start(start_offset + 2048))?; let encrypted_data = common::read_exact(&mut file, header.data_size as usize)?; - println!("Decrypting data..."); + info!("Decrypting data..."); let decrypted_data = decrypt_aes128_cbc_pcks7(&encrypted_data, &key_bytes, &iv_bytes)?; let mut data_reader = Cursor::new(decrypted_data); let mut ei = 1; for entry in entries { - println!("\n({}/{}) - {}({}), Size: {}", + info!("\n({}/{}) - {}({}), Size: {}", ei, header.payload_count, entry.payload_type_bytes, entry.payload_type(), entry.size); let data = common::read_exact(&mut data_reader, entry.size as usize)?; @@ -108,7 +109,7 @@ fn actually_extract_ruf(mut file: &File, output_folder: &PathBuf, start_offset: out_file.write_all(&data)?; - println!("- Saved file!"); + info!("- Saved file!"); ei += 1; } diff --git a/src/formats/rvp/mod.rs b/src/formats/rvp/mod.rs index 492eb5f..4fb0d57 100644 --- a/src/formats/rvp/mod.rs +++ b/src/formats/rvp/mod.rs @@ -9,6 +9,7 @@ use binrw::BinReaderExt; use crate::utils::common; use include::*; +use log::info; pub struct RvpContext { header_type: HeaderType, @@ -35,11 +36,11 @@ pub fn is_rvp_file(app_ctx: &AppContext) -> Result>, Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; if ctx.header_type == HeaderType::RVP { let header: RVPHeader = file.read_be()?; - println!("RVP Info -\nVersion: {}\nYear: {:x}\nForce: {}", header.version_info(), header.year, header.force); + info!("RVP Info -\nVersion: {}\nYear: {:x}\nForce: {}", header.version_info(), header.year, header.force); } else if ctx.header_type == HeaderType::MVP { file.seek(std::io::SeekFrom::Start(36))?; @@ -47,14 +48,14 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box) -> Result<(), Box = Vec::new(); @@ -82,8 +83,8 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box) -> Result<(), Box) -> Result<(), Box = hdr_string.lines().map(|l| l.trim().to_string()).collect(); //BEAUTIFUL - println!("ModelName: {}\nFileName: {}\nModelID: {}\nNewUpdate: {}\nNewMajorVer: {}\nNewMinorVer: {}\nForcedFlag: {}\nStartAddress: {}\nJumpAddress: {}\nMagicAddress: {}\nTotalSize: {}\nTotalSum: {}\nTotalCrc: {}", + info!("ModelName: {}\nFileName: {}\nModelID: {}\nNewUpdate: {}\nNewMajorVer: {}\nNewMinorVer: {}\nForcedFlag: {}\nStartAddress: {}\nJumpAddress: {}\nMagicAddress: {}\nTotalSize: {}\nTotalSum: {}\nTotalCrc: {}", lines[0], lines[1], lines[2], lines[3], lines[4], lines[5], lines[6], lines[7], lines[8], lines[9], lines[10], lines[11], lines[12]); size = lines[10].parse().unwrap(); @@ -116,7 +117,7 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box) -> Result<(), Box) -> Result<(), Box) -> Result<(), Box Result>, Box> { let dir = match app_ctx.dir() {Some(d) => d, None => return Ok(None)}; - if Path::new(&dir).join("image").is_dir() & Path::new(&dir).join("image/info.txt").exists(){ + if Path::new(&dir).join("image").is_dir() && Path::new(&dir).join("image/info.txt").exists(){ Ok(Some(Box::new(()))) } else { Ok(None) @@ -29,7 +30,7 @@ pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box) -> Result<( let path = app_ctx.dir().ok_or("Extractor expected directory")?; let fw_info = fs::read_to_string(Path::new(&path).join("image/info.txt"))?; - println!("Firmware info: {}", fw_info); + info!("Firmware info: {}", fw_info); let image_path = Path::new(&path).join("image"); @@ -43,7 +44,7 @@ pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box) -> Result<( } } if let Some(p) = secret { - println!("Secret: {}", p); + info!("Secret: {}", p); } else { return Err("This firmware is not supported!".into()); } @@ -58,7 +59,7 @@ pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box) -> Result<( let file = File::open(&path)?; let filename = path.file_name().unwrap().to_str().unwrap(); let file_size = file.metadata()?.len(); - println!("\nFile - {}", filename); + info!("\nFile - {}", filename); let data = common::read_file(&file, 0, file_size.try_into().unwrap())?; let salt = &data[8..16]; @@ -103,10 +104,10 @@ pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box) -> Result<( } let end = file_size - 260; - println!("- Decrypting file..."); + info!("- Decrypting file..."); let decrypted_data = decrypt_aes128_cbc_pcks7(&data[16..end.try_into().unwrap()], &key_md5, &iv_md5)?; - println!("-- DeXORing file..."); + info!("-- DeXORing file..."); let xor_key = fw_info.split_whitespace().next().unwrap(); let out_data = decrypt_xor(&decrypted_data, xor_key); @@ -120,7 +121,7 @@ pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box) -> Result<( out_file.write_all(&out_data)?; - println!("--- Saved file!"); + info!("--- Saved file!"); } } } diff --git a/src/formats/sdboot/mod.rs b/src/formats/sdboot/mod.rs index a9dbb9d..b537238 100644 --- a/src/formats/sdboot/mod.rs +++ b/src/formats/sdboot/mod.rs @@ -12,10 +12,11 @@ use crate::utils::common; use crate::formats::sddl_sec::include::*; use crate::utils::compression::decompress_zlib; use include::*; +use log::info; pub fn is_sdboot_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; - let header = common::read_file(&file, 0, 32).expect("Failed to read from file."); + let header = common::read_file(&file, 0, 32)?; let deciph_header = decipher(&header); let chk = deciph_header.iter().all(|&b| { @@ -59,7 +60,7 @@ pub fn extract_sdboot(app_ctx: &AppContext, _ctx: Box) -> Result<(), Bo } let key: KeyEntry = KeyEntry::AES(KEYS[key_id as usize]); - println!("File info -\nKey ID: {}\nFile count: {}", secfile_header.key_id(), secfile_header.num_files()); + info!("File info -\nKey ID: {}\nFile count: {}", secfile_header.key_id(), secfile_header.num_files()); //create file list let mut file_list: Vec = Vec::new(); @@ -84,7 +85,7 @@ pub fn extract_sdboot(app_ctx: &AppContext, _ctx: Box) -> Result<(), Bo let target = s.next().unwrap(); //"nand" let target_filename = s.next().unwrap(); //"nandall.img" - println!("\nSaving {} to {}...", target, target_filename); + info!("\nSaving {} to {}...", target, target_filename); let output_path = Path::new(&app_ctx.output_dir).join(&target_filename); let mut out_file = OpenOptions::new().write(true).create(true).truncate(true).open(&output_path)?; @@ -95,18 +96,18 @@ pub fn extract_sdboot(app_ctx: &AppContext, _ctx: Box) -> Result<(), Bo let info_header: InfoListHeader = infofile_reader.read_le()?; for i in 0..info_header.part_count { let part_entry: InfoListEntry = infofile_reader.read_le()?; - println!("- ({}/{}) Size: {}, Compressed?: {}", i+1, info_header.part_count, part_entry.out_size, part_entry.is_compressed()); + info!("- ({}/{}) Size: {}, Compressed?: {}", i+1, info_header.part_count, part_entry.out_size, part_entry.is_compressed()); let part_file_name = format!("{}{:02x}", target_filename, i); //not sure what happens if it goes over 255 let mut part_data = get_file(&file, &part_file_name, &file_list, &key)?; if part_entry.is_ciphered() { - println!("-- Deciphering..."); + info!("-- Deciphering..."); part_data = decipher(&part_data); } if part_entry.is_compressed() { - println!("-- Decompressing..."); + info!("-- Decompressing..."); part_data = decompress_zlib(&part_data)?; } @@ -114,7 +115,7 @@ pub fn extract_sdboot(app_ctx: &AppContext, _ctx: Box) -> Result<(), Bo processed_image_files.insert(part_file_name); } - println!("--- Saved file!"); + info!("--- Saved file!"); } //extract the rest of the files @@ -123,13 +124,13 @@ pub fn extract_sdboot(app_ctx: &AppContext, _ctx: Box) -> Result<(), Bo continue; } - println!("\nFile: {} - Size: {}", entry.name, entry.size); + info!("\nFile: {} - Size: {}", entry.name, entry.size); let file_data = get_file(&file, &entry.name, &file_list, &key)?; let output_path = Path::new(&app_ctx.output_dir).join(&entry.name); let mut out_file = OpenOptions::new().read(true).write(true).create(true).open(output_path)?; out_file.write_all(&file_data)?; - println!("- Saved file!"); + info!("- Saved file!"); } Ok(()) diff --git a/src/formats/sddl_sec/include.rs b/src/formats/sddl_sec/include.rs index 8f5f61c..921cd5e 100644 --- a/src/formats/sddl_sec/include.rs +++ b/src/formats/sddl_sec/include.rs @@ -50,7 +50,7 @@ pub fn decipher(s: &[u8]) -> Vec { pub fn decrypt_3des(encrypted_data: &[u8], key_entry: &DesKeyEntry) -> Result, Box> { let mut data = encrypted_data.to_vec(); - let decryptor = Decryptor::::new_from_slices(&key_entry.key, &key_entry.iv).unwrap(); + let decryptor = Decryptor::::new_from_slices(&key_entry.key, &key_entry.iv).map_err(|e| format!("Invalid 3DES key: {}", e))?; let out_data = decryptor.decrypt_padded_mut::(&mut data) .map_err(|e| format!("!!Decryption error!!: {:?}", e))?; @@ -142,15 +142,15 @@ pub struct SecHeader { impl SecHeader { pub fn key_id(&self) -> u32 { let string = string_from_bytes(&self.key_id_str_bytes); - string.parse().unwrap() + string.parse::().expect("Invalid key_id in sec header") } pub fn grp_num(&self) -> u32 { let string = string_from_bytes(&self.grp_num_str_bytes); - string.parse().unwrap() + string.parse::().expect("Invalid grp_num in sec header") } pub fn prg_num(&self) -> u32 { let string = string_from_bytes(&self.prg_num_str_bytes); - string.parse().unwrap() + string.parse::().expect("Invalid prg_num in sec header") } } @@ -167,7 +167,7 @@ impl FileHeader { } pub fn size(&self) -> u64 { let string = string_from_bytes(&self.size_str_bytes); - string.parse().unwrap() + string.parse::().expect("Invalid size in file header") } } diff --git a/src/formats/sddl_sec/mod.rs b/src/formats/sddl_sec/mod.rs index 6658d7b..d793d49 100644 --- a/src/formats/sddl_sec/mod.rs +++ b/src/formats/sddl_sec/mod.rs @@ -15,10 +15,11 @@ use crate::utils::aes::{decrypt_aes128_cbc_nopad, decrypt_aes128_cbc_pcks7}; use crate::utils::compression::{decompress_zlib}; use include::*; use util::split_peaks_file; +use log::info; pub fn is_sddl_sec_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; - let header = common::read_file(&file, 0, 32).expect("Failed to read from file."); + let header = common::read_file(&file, 0, 32)?; let deciph_header = decipher(&header); if deciph_header.starts_with(b"\x11\x22\x33\x44") { Ok(Some(Box::new(()))) @@ -43,7 +44,7 @@ fn get_sec_file(mut file: &File, key_entry: &KeyEntry) -> Result<(FileHeader, Ve } else { //extra ciphered data before encrypted data, prefixed by size, like "0021XXXXXX PEAKS.T00/12900002" //this counts into file size but not decrypt size - let extra_size: usize = string_from_bytes(&read_exact(&mut file, 4)?).parse().unwrap(); + let extra_size: usize = string_from_bytes(&read_exact(&mut file, 4)?).parse::().map_err(|e| format!("Parse error: {}", e))?; let _extra_data = read_exact(&mut file, extra_size)?; file_header.size() as usize - (extra_size + 4) @@ -57,10 +58,10 @@ fn get_sec_file(mut file: &File, key_entry: &KeyEntry) -> Result<(FileHeader, Ve //extra info in enc data, like "0021XXXXXX PEAKS.T00/12900002000000571800" //part before size looks to be a duplicate of previous extra data, probably for signing purpose, size used for unpad - let extra_size: usize = string_from_bytes(&read_exact(&mut data_rdr, 4)?).parse().unwrap(); + let extra_size: usize = string_from_bytes(&read_exact(&mut data_rdr, 4)?).parse::().map_err(|e| format!("Parse error: {}", e))?; let _extra_data = read_exact(&mut data_rdr, extra_size + 4)?; - let data_size: usize = string_from_bytes(&read_exact(&mut data_rdr, 12)?).parse().unwrap(); + let data_size: usize = string_from_bytes(&read_exact(&mut data_rdr, 12)?).parse::().map_err(|e| format!("Parse error: {}", e))?; read_exact(&mut data_rdr, data_size)? }; @@ -77,16 +78,16 @@ fn parse_tdi_to_modules(tdi_data: Vec) -> Result, Box = Vec::new(); for _i in 0..tdi_header.num_of_group { let group_head: TdiGroupHead = tdi_reader.read_be()?; - println!("[TDI] Group ID: {}, Target count: {}", group_head.group_id, group_head.num_of_target); + info!("[TDI] Group ID: {}, Target count: {}", group_head.group_id, group_head.num_of_target); for _i in 0..group_head.num_of_target { let tgt_inf: TdiTgtInf = tdi_reader.read_be()?; - println!("[TDI] - {}, Target ID: {}, Segment count: {}, Version: {}", + info!("[TDI] - {}, Target ID: {}, Segment count: {}, Version: {}", tgt_inf.module_name(), tgt_inf.target_id, tgt_inf.num_of_txx, tgt_inf.version_string()); //push unique modules @@ -106,7 +107,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), let mut secfile_hdr_reader = Cursor::new(decipher(&read_exact(&mut file, 32)?)); let secfile_header: SecHeader = secfile_hdr_reader.read_be()?; - println!("File info -\nKey ID: {}\nGroup count: {}\nModule file count: {}", secfile_header.key_id(), secfile_header.grp_num(), secfile_header.prg_num()); + info!("File info -\nKey ID: {}\nGroup count: {}\nModule file count: {}", secfile_header.key_id(), secfile_header.grp_num(), secfile_header.prg_num()); //by knowing that the first file is always SDIT.FDI, find key(and mode) let try_hdr = read_exact(&mut file, 0x20)?; @@ -116,7 +117,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), //for new, key will always be the same if let Ok(dec) = decrypt_aes128_cbc_pcks7(&try_hdr, &NEW_KEY.key, &NEW_KEY.iv) { if dec.starts_with(TDI_FILENAME.as_bytes()) { - println!("- New type detected\n"); + info!("- New type detected\n"); key = Some(KeyEntry::AESPcks7(NEW_KEY)); } } @@ -125,7 +126,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), for key_entry in OLD_KEYS_AES { let dec = decrypt_aes128_cbc_nopad(&try_hdr, &key_entry.key, &key_entry.iv)?; if dec.starts_with(TDI_FILENAME.as_bytes()) { - println!("- Old type detected with AES key={}, iv={}\n", hex::encode(key_entry.key) ,hex::encode(key_entry.iv)); + info!("- Old type detected with AES key={}, iv={}\n", hex::encode(key_entry.key) ,hex::encode(key_entry.iv)); key = Some(KeyEntry::AES(key_entry)); break } @@ -136,7 +137,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), for key_entry in OLD_KEYS_DES { let dec = decrypt_3des(&try_hdr, &key_entry)?; if dec.starts_with(TDI_FILENAME.as_bytes()) { - println!("- Old type detected with DES key={}, iv={}\n", hex::encode(key_entry.key) ,hex::encode(key_entry.iv)); + info!("- Old type detected with DES key={}, iv={}\n", hex::encode(key_entry.key) ,hex::encode(key_entry.iv)); key = Some(KeyEntry::DES(key_entry)); break } @@ -154,7 +155,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), file.seek(SeekFrom::Start(0x20))?; let (tdi_file, tdi_data) = get_sec_file(&file, &key)?; - println!("[TDI] Name: {}, Size: {}", tdi_file.name(), tdi_file.size()); + info!("[TDI] Name: {}, Size: {}", tdi_file.name(), tdi_file.size()); if save_extra { //Save SDIT let mut out_file = OpenOptions::new().write(true).create(true).open(Path::new(&app_ctx.output_dir).join(tdi_file.name()))?; out_file.write_all(&tdi_data)?; @@ -168,7 +169,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), //get info files, each info file belongs to its respecitve group in the TDI for i in 0..secfile_header.grp_num() { let (info_file, info_data) = get_sec_file(&file, &key)?; - println!("\n[INFO] ID: {}, Name: {}, Size: {}", i, info_file.name(), info_file.size()); + info!("\n[INFO] ID: {}, Name: {}, Size: {}", i, info_file.name(), info_file.size()); if !info_file.name().ends_with(INFO_FILE_EXTENSION) { return Err(format!("Info file {} does not have the expected extension {}!", info_file.name(), INFO_FILE_EXTENSION).into()); } @@ -177,12 +178,12 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), out_file.write_all(&info_data)?; } //print info file - println!("{}", String::from_utf8_lossy(&info_data)); + info!("{}", String::from_utf8_lossy(&info_data)); } //parse module data for (i, module) in modules.iter().enumerate(){ - println!("\nModule #{}/{} - {}, Target ID: {}, Segment count: {}, Version: {}", + info!("\nModule #{}/{} - {}, Target ID: {}, Segment count: {}, Version: {}", i+1, &modules.len(), module.module_name(), module.target_id, module.num_of_txx, module.version_string()); let mut final_out_path: Option = None; @@ -192,7 +193,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), if !module_file.name().starts_with(&module.module_name()) { return Err(format!("Module file {} does not start with the module's name: {}!", module_file.name(), module.module_name()).into()); } - println!(" Segment #{}/{} - Name: {}, Size: {}", i+1, module.num_of_txx, module_file.name(), module_file.size()); + info!(" Segment #{}/{} - Name: {}, Size: {}", i+1, module.num_of_txx, module_file.name(), module_file.size()); let mut module_reader = Cursor::new(module_data); let _com_header: ModuleComHeader = module_reader.read_be()?; @@ -203,23 +204,23 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), let module_header: ModuleHeader = module_reader.read_be()?; let mut module_data = read_exact(&mut module_reader, module_header.cmp_size as usize)?; if module_header.is_ciphered() { - println!(" - Deciphering..."); + info!(" - Deciphering..."); module_data = decipher(&module_data); } if module_header.is_compressed() { - println!(" - Decompressing..."); + info!(" - Decompressing..."); module_data = decompress_zlib(&module_data)?; } let mut content_reader = Cursor::new(module_data); let content_header: ContentHeader = content_reader.read_be()?; - println!(" --> 0x{:X} @ 0x{:X}", content_header.size, content_header.dest_offset()); + info!(" --> 0x{:X} @ 0x{:X}", content_header.size, content_header.dest_offset()); let output_path: PathBuf; if content_header.has_subfile() { let sub_filename_bytes = read_exact(&mut content_reader, 0x100)?; let sub_filename = common::string_from_bytes(&sub_filename_bytes); - println!(" --> {}", sub_filename); + info!(" --> {}", sub_filename); let sub_folder_path = Path::new(&app_ctx.output_dir).join(module.module_name()); fs::create_dir_all(&sub_folder_path)?; @@ -238,7 +239,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), } if app_ctx.has_option("sddl_sec:split_peaks") && module.module_name() == "PEAKS" { - println!("\n- Splitting PEAKS"); + info!("\n- Splitting PEAKS"); if let Some(ref path) = final_out_path { split_peaks_file(path, &app_ctx.output_dir, !app_ctx.has_option("sddl_sec:no_decomp_peaks"))?; } diff --git a/src/formats/sddl_sec/util.rs b/src/formats/sddl_sec/util.rs index f9b9a6e..8428e28 100644 --- a/src/formats/sddl_sec/util.rs +++ b/src/formats/sddl_sec/util.rs @@ -3,6 +3,7 @@ use crate::utils::common; use std::{fs::{self, File, OpenOptions}, io::{Cursor, Seek, SeekFrom, Write}, path::{Path, PathBuf}}; use crate::utils::compression::decompress_zlib; +use log::info; pub fn split_peaks_file(path: &PathBuf, out_path: &PathBuf, do_decomp: bool) -> Result<(), Box> { let mut file = File::open(path)?; @@ -11,7 +12,7 @@ pub fn split_peaks_file(path: &PathBuf, out_path: &PathBuf, do_decomp: bool) -> let args_bytes = common::read_file(&mut file, 0, 0x210)?; if !args_bytes.starts_with(b"D50 ") { - println!("- Splitting PEAKS is not supported on this file..."); + info!("- Splitting PEAKS is not supported on this file..."); return Ok(()); } let args = common::string_from_bytes(&args_bytes[16..]); @@ -57,7 +58,7 @@ pub fn split_peaks_file(path: &PathBuf, out_path: &PathBuf, do_decomp: bool) -> } let root = root.ok_or("Failed to get root partition!")?; - println!("Root - {}", root); + info!("Root - {}", root); let root_index = parts.iter().position(|(n, _s, _f)| n == &root).ok_or("Root partition not found in partition list!")?; let mut tsize: u64 = 0; @@ -69,7 +70,7 @@ pub fn split_peaks_file(path: &PathBuf, out_path: &PathBuf, do_decomp: bool) -> break } - println!("- {} - Size: {}{}", part_name, part_size, if let Some(part_flag) = part_flag {format!(", Flag: {}", part_flag)} else {format!("")}); + info!("- {} - Size: {}{}", part_name, part_size, if let Some(part_flag) = part_flag {format!(", Flag: {}", part_flag)} else {format!("")}); tsize += part_size; @@ -79,7 +80,7 @@ pub fn split_peaks_file(path: &PathBuf, out_path: &PathBuf, do_decomp: bool) -> let data = common::read_exact(&mut file, *part_size as usize)?; if do_decomp && let Some(part_flag) = part_flag && *part_flag == "c" { - println!("-- Decompressing ..."); + info!("-- Decompressing ..."); decompress_part_to_file(&data, &output_path)?; continue } @@ -108,7 +109,7 @@ fn decompress_part_to_file(data: &[u8], out_path: &PathBuf) -> Result<(), Box Result<(), Box Result<(), Box Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -28,14 +29,14 @@ pub fn extract_sdimage(app_ctx: &AppContext, _ctx: Box) -> Result<(), B let mut e_i = 0; while file.stream_position()? < file.metadata()?.len() as u64 { let entry: EntryHeader = file.read_le()?; - println!("\n#{} - {} ({}), Size: {}, Version: {}.{}.{}.{}, Model ID: {}, Info: {}{} {}", + info!("\n#{} - {} ({}), Size: {}, Version: {}.{}.{}.{}, Model ID: {}, Info: {}{} {}", e_i+1, entry.target_name(), entry.target_id, entry.size1, entry.version[3], entry.version[2], entry.version[1], entry.version[0], entry.model_id, entry.info(), if entry.is_encrypted() {" [ENCRYPTED]"} else {""}, if entry.is_empty() {"[EMPTY]"} else {""}); if !entry.is_empty() { let info = entry.info(); let filename = info.split("FN=\"").nth(1).and_then(|s| s.split('"').next()).unwrap(); - println!("- Filename: {}", filename); + info!("- Filename: {}", filename); let data = common::read_exact(&mut file, entry.size1 as usize)?; @@ -47,7 +48,7 @@ pub fn extract_sdimage(app_ctx: &AppContext, _ctx: Box) -> Result<(), B let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } e_i += 1; diff --git a/src/formats/slp/mod.rs b/src/formats/slp/mod.rs index a85fc1e..3bad574 100644 --- a/src/formats/slp/mod.rs +++ b/src/formats/slp/mod.rs @@ -9,6 +9,7 @@ use binrw::BinReaderExt; use crate::utils::common; use include::*; +use log::info; pub struct SlpContext { variant: SlpVariant, @@ -39,10 +40,10 @@ pub fn is_slp_file(app_ctx: &AppContext) -> Result>, Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; let meta_header: CommonMetaHeader = file.read_le()?; - println!("File info:\nType: {:?}\nProject name: {}\nFirmware Version: {}\nFirmware Version(USER): {}", + info!("File info:\nType: {:?}\nProject name: {}\nFirmware Version: {}\nFirmware Version(USER): {}", ctx.variant, meta_header.project_name(), meta_header.firmware_version(), meta_header.user_version()); let num_image; @@ -50,21 +51,21 @@ pub fn extract_slp(app_ctx: &AppContext, ctx: Box) -> Result<(), Box) -> Result<(), Box) -> Result<(), Box Result>, Bo pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; //need to decrypt entire file of new aes enc let mut enc_data = Vec::new(); @@ -58,11 +59,11 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), B let dec_data = match ctx.encryption_type { EncryptionType::HexSubst => { - println!("Decrypting with hex substitution..."); + info!("Decrypting with hex substitution..."); hex_substitute(&enc_data) }, EncryptionType::AesOfb((key, iv, key_name)) => { - println!("Decrypting with AES key: {}...", key_name); + info!("Decrypting with AES key: {}...", key_name); ver_up_decrypt_aes128ofb(&key, &iv, &enc_data) } }; @@ -72,7 +73,7 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), B let mut hdr_reader = Cursor::new(header); let hdr: Header = hdr_reader.read_le()?; - println!("File info:\nFirmware: {}\nVersion: {}\nDate: {}\nFile size: {}", + info!("File info:\nFirmware: {}\nVersion: {}\nDate: {}\nFile size: {}", hdr.firmware_name(), hdr.firmware_version(), hdr.date(), hdr.file_size); let mut last_file_path: Option = None; @@ -89,7 +90,7 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), B continue } - println!("\n#{} - Offset: {}, Size: {}", i, entry.offset, entry.size); + info!("\n#{} - Offset: {}, Size: {}", i, entry.offset, entry.size); if i == 0 { first_entry_offset = entry.offset as u64; } @@ -104,7 +105,7 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), B let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; - println!("- Saved file!"); + info!("- Saved file!"); i += 1; } @@ -119,13 +120,14 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), B options: app_ctx.options.clone(), dry_run: app_ctx.dry_run, quiet: app_ctx.quiet, + verbose: app_ctx.verbose, }; if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&ctx)? { - println!("- MTK BDP file detected!\n"); + info!("- MTK BDP file detected!\n"); formats::mtk_bdp::extract_mtk_bdp(&ctx, result)?; } else { - println!("- Not an MTK BDP file."); + info!("- Not an MTK BDP file."); } } diff --git a/src/formats/tsb_bin/mod.rs b/src/formats/tsb_bin/mod.rs index 295fd62..d18f7c9 100644 --- a/src/formats/tsb_bin/mod.rs +++ b/src/formats/tsb_bin/mod.rs @@ -13,6 +13,7 @@ use binrw::BinReaderExt; use crate::utils::common; use include::*; use tsb_des::decrypt; +use log::info; struct TsbBinCtx { key: Option<[u8; 8]> @@ -44,11 +45,11 @@ pub fn is_tsb_bin_file(app_ctx: &AppContext) -> Result>, Box pub fn extract_tsb_bin(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; let mut header = common::read_file(&mut file, 0, 0x400)?; if let Some(key) = ctx.key { //decrypt header - println!("File is encrypted, using key: {}", hex::encode(&key)); + info!("File is encrypted, using key: {}", hex::encode(&key)); header = decrypt(&header, &key); opt_dump_dec_hdr(app_ctx, &header, "header")?; } @@ -56,17 +57,17 @@ pub fn extract_tsb_bin(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo let mut hdr_rdr = Cursor::new(header); let hdr: Header = hdr_rdr.read_be()?; - println!("File info -\nSize: {}\nEntry count: {}\nBuild no.: {}\nEntry address: 0x{:02x}" + info!("File info -\nSize: {}\nEntry count: {}\nBuild no.: {}\nEntry address: 0x{:02x}" ,hdr.length, hdr.entry_count, hdr.build_no(), hdr.entry_addr); for (i, entry) in hdr.entries.iter().enumerate() { - println!("\n({}/{}) - {}, Size: {}, Offset: {}, Load address: 0x{:02x}", + info!("\n({}/{}) - {}, Size: {}, Offset: {}, Load address: 0x{:02x}", i+1, hdr.entry_count, entry.name(), entry.size, entry.offset, entry.load_addr); let mut data; if let Some(key) = ctx.key { let enc_data = common::read_file(&mut file, entry.offset as u64, (entry.size as usize + 7) & !7)?; //read aligned to 8b blocks for decryption - println!("- Decrypting..."); + info!("- Decrypting..."); data = decrypt(&enc_data, &key); data.truncate(entry.size as usize); //discard alignment @@ -75,7 +76,7 @@ pub fn extract_tsb_bin(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo } if entry.is_compressed() { - println!("- Decompressing..."); + info!("- Decompressing..."); data = decompress_zlib(&data)?; } @@ -85,7 +86,7 @@ pub fn extract_tsb_bin(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; - println!("-- Saved file!"); + info!("-- Saved file!"); } Ok(()) diff --git a/src/formats/vestel/mod.rs b/src/formats/vestel/mod.rs index de8505c..2fba9e8 100644 --- a/src/formats/vestel/mod.rs +++ b/src/formats/vestel/mod.rs @@ -8,6 +8,7 @@ use crate::utils::aes::decrypt_aes128_ecb; use crate::keys; use include::*; +use log::info; pub struct VestelCtx { pub is_encrypted: bool, @@ -91,7 +92,7 @@ pub fn extract_vestel( app_ctx: &AppContext, ctx: Box, ) -> Result<(), Box> { - let ctx = ctx.downcast::().expect("Missing context"); + let ctx = ctx.downcast::().map_err(|_| "Invalid context type")?; match ctx.variant { VestelVariant::Mb230 => extract_mb230(app_ctx, &ctx), @@ -116,8 +117,8 @@ fn extract_standard( let key: [u8; 16] = key_vec.try_into().map_err(|_| "Invalid AES key length")?; let (final_data, decrypted_path) = if ctx.is_encrypted { - println!("Encrypted Vestel firmware detected"); - println!("Using key: {} ({})", "VESTEL", key_hex); + info!("Encrypted Vestel firmware detected"); + info!("Using key: {} ({})", "VESTEL", key_hex); let dec = decrypt_aes128_ecb(&key, &data)?; @@ -129,12 +130,12 @@ fn extract_standard( (dec, Some(output_path)) } else { - println!("Detected Vestel firmware (unencrypted)"); + info!("Detected Vestel firmware (unencrypted)"); (data, None) }; let partitions = VESTEL_PARTITIONS; - println!("\nPartition count: {}", partitions.len()); + info!("\nPartition count: {}", partitions.len()); fs::create_dir_all(&app_ctx.output_dir)?; @@ -143,14 +144,14 @@ fn extract_standard( let end = start.saturating_add(*size); if start >= final_data.len() { - println!("Skipping {} (out of range)", name); + info!("Skipping {} (out of range)", name); continue; } let end = end.min(final_data.len()); let segment = &final_data[start..end]; - println!( + info!( "\n{} - Offset: 0x{:X}, Size: 0x{:X}", name, offset, size ); @@ -160,7 +161,7 @@ fn extract_standard( fs::write(&output_path, segment)?; - println!("- Saved {}.bin", name); + info!("- Saved {}.bin", name); } if let Some(path) = decrypted_path { @@ -181,11 +182,11 @@ fn extract_mb230( let file_size = file.metadata()?.len() as usize; let data = common::read_exact(&mut file, file_size)?; - println!("Detected Vestel MB230 firmware (Novatek NT72673)"); - println!("File size: {} bytes ({:.2} MB)", file_size, file_size as f64 / 1024.0 / 1024.0); + info!("Detected Vestel MB230 firmware (Novatek NT72673)"); + info!("File size: {} bytes ({:.2} MB)", file_size, file_size as f64 / 1024.0 / 1024.0); let partitions = MB230_PARTITIONS; - println!("\nPartition count: {}", partitions.len()); + info!("\nPartition count: {}", partitions.len()); fs::create_dir_all(&app_ctx.output_dir)?; @@ -194,14 +195,14 @@ fn extract_mb230( let end = start.saturating_add(*size); if start >= file_size { - println!("Skipping {} (out of range)", name); + info!("Skipping {} (out of range)", name); continue; } let end = end.min(file_size); let segment = &data[start..end]; - println!( + info!( "\n{} - Offset: 0x{:X}, Size: 0x{:X} ({} bytes)", name, offset, size, segment.len() ); @@ -211,7 +212,7 @@ fn extract_mb230( fs::write(&output_path, segment)?; - println!("- Saved {}.bin", name); + info!("- Saved {}.bin", name); } Ok(()) diff --git a/src/main.rs b/src/main.rs index 6355799..2b6a49b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ mod utils; use clap::Parser; use std::path::PathBuf; -use std::io::{self, Seek, SeekFrom}; +use std::io::{self, Seek, SeekFrom, Write}; use std::fs::{self, File}; use crate::formats::{Format, get_registry}; use crate::error::UnixtractError; @@ -43,7 +43,7 @@ struct Args { #[arg(short, long)] quiet: bool, - /// Verbose mode — show detailed progress information + /// Verbose mode — show detailed progress information (use -vv for trace) #[arg(short, long, action = clap::ArgAction::Count)] verbose: u8, } @@ -59,6 +59,7 @@ pub struct AppContext { pub options: Vec, pub dry_run: bool, pub quiet: bool, + pub verbose: u8, } impl AppContext { @@ -81,15 +82,31 @@ impl AppContext { } } +/// Clear the terminal screen +fn clear_terminal() { + // ANSI escape sequence to clear screen and move cursor to top-left + eprint!("\x1B[2J\x1B[H"); +} + fn main() -> Result<(), Box> { let args = Args::parse(); + // Clear the terminal on execution + if !args.quiet { + clear_terminal(); + } + // Initialize logger based on verbosity level + // Default (no flags): show info-level output (normal progress) + // -q: errors only + // -v: info (same as default, explicit) + // -vv: debug level (more details) + // -vvv: trace level (everything) let log_level = if args.quiet { "error" } else { match args.verbose { - 0 => "warn", + 0 => "info", 1 => "info", 2 => "debug", _ => "trace", @@ -98,22 +115,34 @@ fn main() -> Result<(), Box> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(log_level)) .format_timestamp(None) + .format_target(false) + .format_module_path(false) + .format(|buf, record| { + // Clean format: just the message for info/debug/trace + // Add level prefix for warn/error so they stand out + match record.level() { + log::Level::Error => writeln!(buf, "[ERROR] {}", record.args()), + log::Level::Warn => writeln!(buf, "[WARN] {}", record.args()), + log::Level::Info => writeln!(buf, "{}", record.args()), + log::Level::Debug => writeln!(buf, "[DBG] {}", record.args()), + log::Level::Trace => writeln!(buf, "[TRC] {}", record.args()), + } + }) .init(); // Handle --list-formats if args.list_formats { let formats = get_registry(); - println!("Supported formats ({} total):", formats.len()); + eprintln!("Supported formats ({} total):", formats.len()); for (i, fmt) in formats.iter().enumerate() { - println!(" {:2}. {}", i + 1, fmt.name); + eprintln!(" {:2}. {}", i + 1, fmt.name); } return Ok(()); } - log::info!("unixtract Firmware extractor v{}", env!("CARGO_PKG_VERSION")); + log::info!("unixtract v{}", env!("CARGO_PKG_VERSION")); let target_path_str = args.input_target.as_deref().unwrap_or(""); - log::info!("Input target: {}", target_path_str); let target_path = PathBuf::from(target_path_str); let output_path_str = if let Some(ref out) = args.output_directory { @@ -123,7 +152,6 @@ fn main() -> Result<(), Box> { .and_then(|s| s.to_str()) .ok_or_else(|| UnixtractError::Other("Invalid input file name".to_string()))?) }; - log::info!("Output directory: {}", output_path_str); let output_directory_path = PathBuf::from(&output_path_str); if output_directory_path.exists() { @@ -131,7 +159,6 @@ fn main() -> Result<(), Box> { let is_empty = fs::read_dir(&output_directory_path)?.next().is_none(); if !is_empty && !args.quiet { log::warn!("Output folder already exists and is NOT empty! Files may be overwritten."); - eprintln!("Warning: Output folder already exists and is NOT empty! Files may be overwritten!"); eprintln!("Press Enter if you want to continue..."); io::stdin().read_line(&mut String::new())?; } @@ -149,6 +176,7 @@ fn main() -> Result<(), Box> { options: args.options, dry_run: args.dry_run, quiet: args.quiet, + verbose: args.verbose, }; } else if target_path.is_dir() { app_ctx = AppContext { @@ -157,25 +185,20 @@ fn main() -> Result<(), Box> { options: args.options, dry_run: args.dry_run, quiet: args.quiet, + verbose: args.verbose, }; } else { return Err("Invalid input path!".into()); } let formats: Vec = get_registry(); - log::info!("Loaded {} formats", formats.len()); for format in formats { if let Some(ctx) = (format.detector_func)(&app_ctx)? { - log::info!("{} detected!", format.name); - if !app_ctx.quiet { - println!("\n{} detected!", format.name); - } + log::info!("\n{} detected!", format.name); if app_ctx.dry_run { - if !app_ctx.quiet { - println!("Dry run — skipping extraction."); - } + log::info!("Dry run — skipping extraction."); return Ok(()); } @@ -187,15 +210,11 @@ fn main() -> Result<(), Box> { (format.extractor_func)(&app_ctx, ctx)?; // Extractor returned with no error - if !app_ctx.quiet { - println!("\nExtraction finished! Saved extracted files to {}", output_path_str); - } + log::info!("\nExtraction finished! Saved extracted files to {}", output_path_str); return Ok(()); } } - if !app_ctx.quiet { - println!("\nInput format not recognized!"); - } + log::warn!("\nInput format not recognized!"); Err("Unrecognized input format".into()) } diff --git a/src/utils/common.rs b/src/utils/common.rs index 2a00d67..b750fbb 100644 --- a/src/utils/common.rs +++ b/src/utils/common.rs @@ -1,12 +1,12 @@ -use std::fs::{File}; +use std::fs::File; use std::io::{self, Read, Seek, SeekFrom}; pub fn read_file(mut file: &File, offset: u64, size: usize) -> Result, Box> { file.seek(SeekFrom::Start(offset))?; let mut buffer = vec![0u8; size]; - let _bytes_read = file.read(&mut buffer)?; + file.read_exact(&mut buffer)?; - // reset seek (! + // Reset seek position back to the read offset file.seek(SeekFrom::Start(offset))?; Ok(buffer) } @@ -20,4 +20,5 @@ pub fn read_exact(reader: &mut R, size: usize) -> io::Result> { pub fn string_from_bytes(buf: &[u8]) -> String { let end = buf.iter().position(|&b| b == 0).unwrap_or(buf.len()); String::from_utf8_lossy(&buf[..end]).to_string() -} \ No newline at end of file +} + diff --git a/src/utils/compression.rs b/src/utils/compression.rs index 2841920..535a11c 100644 --- a/src/utils/compression.rs +++ b/src/utils/compression.rs @@ -29,7 +29,7 @@ pub fn decompress_gzip_get_filename(compressed_data: &[u8]) -> Result<(Vec, let mut decoder = GzDecoder::new(compressed_data); let mut filename: Option = None; - if let Some(filename_bytes) = decoder.header().unwrap().filename() { + if let Some(filename_bytes) = decoder.header().and_then(|h| h.filename()) { filename = Some(common::string_from_bytes(filename_bytes)); } diff --git a/src/utils/global.rs b/src/utils/global.rs index 7549aed..848026d 100644 --- a/src/utils/global.rs +++ b/src/utils/global.rs @@ -1,6 +1,7 @@ use std::{fs::{self, OpenOptions}, io::Write, path::Path}; use crate::AppContext; +use log::info; pub fn opt_dump_dec_hdr(app_ctx: &AppContext, data: &[u8], name: &str) -> Result<(), Box> { if !app_ctx.has_option("dump_dec_hdrs") { @@ -14,7 +15,7 @@ pub fn opt_dump_dec_hdr(app_ctx: &AppContext, data: &[u8], name: &str) -> Result let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; - println!("[i] Saved {} to {}", name, filename); + info!("[i] Saved {} to {}", name, filename); Ok(()) } \ No newline at end of file diff --git a/src/utils/lzop.rs b/src/utils/lzop.rs index f80d8f8..04482a8 100644 --- a/src/utils/lzop.rs +++ b/src/utils/lzop.rs @@ -5,6 +5,7 @@ use std::io::{Cursor, Write}; use simd_adler32::adler32; use crate::utils::common; +use log::info; #[derive(BinRead)] struct LzopHeader { @@ -48,7 +49,7 @@ pub fn unlzop_to_file(data: &[u8], file_path: PathBuf) -> Result<(), Box segment_header.uncompressed_size { - println!("{:?}", segment_header); + info!("{:?}", segment_header); } if segment_header.uncompressed_size == 0 { break