From 1aaf2f4b0ffe841251cd385432cca0c1a6d0f9be Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Thu, 5 Feb 2026 15:18:26 +0100 Subject: [PATCH 1/9] testing modular format system --- src/formats.rs | 45 +++++++++- src/formats/amlogic.rs | 22 +++-- src/formats/android_ota_payload.rs | 23 +++-- src/formats/bdl.rs | 20 +++-- src/formats/epk.rs | 43 +++++---- src/formats/epk1.rs | 24 +++-- src/formats/epk2.rs | 23 +++-- src/formats/epk2b.rs | 24 +++-- src/formats/epk3.rs | 12 ++- src/formats/funai_upg.rs | 22 +++-- src/formats/invincible_image.rs | 23 +++-- src/formats/msd10.rs | 27 +++--- src/formats/msd11.rs | 23 +++-- src/formats/mstar.rs | 30 +++---- src/formats/mtk_bdp.rs | 22 +++-- src/formats/mtk_pkg.rs | 33 ++++--- src/formats/mtk_pkg_new.rs | 34 ++++--- src/formats/mtk_pkg_old.rs | 34 ++++--- src/formats/novatek.rs | 22 +++-- src/formats/nvt_timg.rs | 24 +++-- src/formats/pana_dvd.rs | 32 ++++--- src/formats/pfl_upg.rs | 24 +++-- src/formats/pup.rs | 27 +++--- src/formats/roku.rs | 27 +++--- src/formats/ruf.rs | 25 ++++-- src/formats/rvp.rs | 32 ++++--- src/formats/sddl_sec.rs | 26 +++--- src/formats/slp.rs | 23 +++-- src/formats/sony_bdp.rs | 31 ++++--- src/main.rs | 139 ++++------------------------- 30 files changed, 521 insertions(+), 395 deletions(-) diff --git a/src/formats.rs b/src/formats.rs index d0d70d1..6370498 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -1,5 +1,14 @@ +use std::any::Any; +use crate::ProgramContext; + +pub struct Format { + pub name: &'static str, + pub detect_func: fn(&ProgramContext) -> Result>, Box>, + pub run_func: fn(&ProgramContext, Option>) -> Result<(), Box>, +} + pub mod mstar; -pub mod samsung_old; +//pub mod samsung_old; not sure what to do with this pub mod nvt_timg; pub mod pfl_upg; pub mod sddl_sec; @@ -31,4 +40,36 @@ pub mod epk3; pub mod mtk_pkg; pub mod mtk_pkg_old; pub mod mtk_pkg_new; -pub mod mtk_bdp; \ No newline at end of file +pub mod mtk_bdp; + +pub fn get_registry() -> Vec { + return vec![ + crate::formats::amlogic::format(), + crate::formats::epk1::format(), + crate::formats::android_ota_payload::format(), + crate::formats::bdl::format(), + crate::formats::epk2::format(), + crate::formats::epk::format(), + crate::formats::epk2b::format(), + crate::formats::funai_upg::format(), + crate::formats::invincible_image::format(), + crate::formats::msd10::format(), + crate::formats::msd11::format(), + crate::formats::mstar::format(), + crate::formats::novatek::format(), + crate::formats::nvt_timg::format(), + crate::formats::pfl_upg::format(), + crate::formats::pup::format(), + crate::formats::roku::format(), + crate::formats::ruf::format(), + crate::formats::rvp::format(), + crate::formats::sddl_sec::format(), + crate::formats::slp::format(), + crate::formats::mtk_pkg_old::format(), + crate::formats::mtk_pkg::format(), + crate::formats::sony_bdp::format(), + crate::formats::mtk_pkg_new::format(), + crate::formats::pana_dvd::format(), + crate::formats::mtk_bdp::format(), + ] +} \ No newline at end of file diff --git a/src/formats/amlogic.rs b/src/formats/amlogic.rs index 0f4180d..08d4793 100644 --- a/src/formats/amlogic.rs +++ b/src/formats/amlogic.rs @@ -1,4 +1,9 @@ -use std::fs::File; +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "amlogic", detect_func: is_amlogic_file, run_func: extract_amlogic } +} + use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; @@ -44,16 +49,17 @@ impl ItemEntry { } } -pub fn is_amlogic_file(file: &File) -> bool { - let header = common::read_file(&file, 8, 4).expect("Failed to read from file."); +pub fn is_amlogic_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 8, 4)?; if header == b"\x56\x19\xB5\x27" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_amlogic(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_amlogic(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; file.seek(SeekFrom::Start(0))?; let header: ImageHeader = file.read_le()?; @@ -85,8 +91,8 @@ pub fn extract_amlogic(mut file: &File, output_folder: &str) -> Result<(), Box Format { + Format { name: "android_ota_payload", detect_func: is_android_ota_payload_file, run_func: extract_android_ota_payload } +} + +use std::fs::{self, OpenOptions}; use std::path::{Path}; use std::io::{Write}; use binrw::{BinRead, BinReaderExt}; @@ -16,16 +22,17 @@ struct Header { metadata_signature_size: u32, } -pub fn is_android_ota_payload_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 4).expect("Failed to read from file."); +pub fn is_android_ota_payload_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"CrAU" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_android_ota_payload(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_android_ota_payload(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let header: Header = file.read_be()?; println!("File info:\nFormat version: {}\nManifest size: {}", header.file_format_version, header.manifest_size); @@ -75,8 +82,8 @@ pub fn extract_android_ota_payload(mut file: &File, output_folder: &str) -> Resu break } - fs::create_dir_all(&output_folder)?; - let output_path = Path::new(&output_folder).join(format!("{}.bin", partition.partition_name)); + fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", partition.partition_name)); let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?; out_file.write_all(&out_data)?; } diff --git a/src/formats/bdl.rs b/src/formats/bdl.rs index bca2206..2b66f41 100644 --- a/src/formats/bdl.rs +++ b/src/formats/bdl.rs @@ -1,4 +1,9 @@ -use std::fs::File; +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "bdl", detect_func: is_bdl_file, run_func: extract_bdl } +} + use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; @@ -80,16 +85,17 @@ impl PkgEntry { } } -pub fn is_bdl_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 4).expect("Failed to read from file."); +pub fn is_bdl_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"ibdl" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_bdl(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_bdl(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let header: BdlHeader = file.read_le()?; println!("File info:\nPackage count: {}\nDate: {}\nManufacturer: {}\nModel: {}\nVersion: {}\nInfo: {}", @@ -116,7 +122,7 @@ pub fn extract_bdl(mut file: &File, output_folder: &str) -> Result<(), Box Format { + Format { name: "epk", detect_func: is_epk_file, run_func: extract_epk } +} use crate::utils::common; use crate::formats; -pub fn is_epk_file(file: &File) -> bool { - let versions = common::read_file(&file, 1712, 36).expect("Failed to read from file."); +pub struct EpkContext { + epk_version: u8, +} - if check_epk_version(&versions).is_some() { - true +pub fn is_epk_file(app_ctx: &ProgramContext) -> Result>, Box> { + let versions = common::read_file(app_ctx.file, 1712, 36)?; + + if let Some(epk_version) = check_epk_version(&versions) { + Ok(Some(Box::new(EpkContext {epk_version}))) } else { - false + Ok(None) } } -fn check_epk_version(versions: &[u8]) -> Option { +fn check_epk_version(versions: &[u8]) -> Option { // _ - 0x00 X - a number . - a dot let epk2_pattern = "____XXXX.XXXX.XXXX__XX.XX.XXX_______"; let epk3_pattern = "____X.X.X___________X.X.X___________"; let epk3_new_pattern = "____XX.X.X__________XX.X.X__________"; if match_with_pattern(&versions, epk2_pattern) { - Some("epk2".to_string()) + Some(2) } else if match_with_pattern(&versions, epk3_pattern) { - Some("epk3".to_string()) + Some(3) } else if match_with_pattern(&versions, epk3_new_pattern) { - Some("epk3".to_string()) + Some(3) }else { None } @@ -42,20 +50,21 @@ fn match_with_pattern(data: &[u8], pattern: &str) -> bool { true } -pub fn extract_epk(file: &File, output_folder: &str) -> Result<(), Box> { - let versions = common::read_file(&file, 1712, 36)?; - let epk_version = check_epk_version(&versions); +pub fn extract_epk(app_ctx: &ProgramContext, ctx: Option>) -> Result<(), Box> { + let ctx = ctx.and_then(|c| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; + + let versions = common::read_file(app_ctx.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); - if epk_version == Some("epk2".to_string()) { + if ctx.epk_version == 2 { println!("EPK2 detected!\n"); - formats::epk2::extract_epk2(file, output_folder)?; - } else if epk_version == Some("epk3".to_string()) { + formats::epk2::extract_epk2(app_ctx, None)?; + } else if ctx.epk_version == 3 { println!("EPK3 detected!\n"); - formats::epk3::extract_epk3(file, output_folder)?; + formats::epk3::extract_epk3(app_ctx, None)?; } Ok(()) diff --git a/src/formats/epk1.rs b/src/formats/epk1.rs index 38ec105..9422ad4 100644 --- a/src/formats/epk1.rs +++ b/src/formats/epk1.rs @@ -1,4 +1,9 @@ -use std::fs::File; +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "epk1", detect_func: is_epk1_file, run_func: extract_epk1 } +} + use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; @@ -35,17 +40,18 @@ struct Pak { size : u32, } -pub fn is_epk1_file(file: &File) -> bool { - let epk2_magic = common::read_file(&file, 12, 4).expect("Failed to read from file."); //for epk2b - let epak_magic = common::read_file(&file, 0, 4).expect("Failed to read from file."); +pub fn is_epk1_file(app_ctx: &ProgramContext) -> Result>, Box> { + let epk2_magic = common::read_file(app_ctx.file, 12, 4)?; //for epk2b + let epak_magic = common::read_file(app_ctx.file, 0, 4)?; if epak_magic == b"epak" && epk2_magic != b"EPK2" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_epk1(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_epk1(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; //check type of epk1 let epk1_type; let init_pak_count_bytes = common::read_file(&file, 8, 4)?; @@ -119,8 +125,8 @@ pub fn extract_epk1(mut file: &File, output_folder: &str) -> Result<(), Box Format { + Format { name: "epk2", detect_func: is_epk2_file, run_func: extract_epk2 } +} + +use std::fs::{self, OpenOptions}; use std::path::{Path}; use std::io::{Write, Seek, SeekFrom, Cursor}; use binrw::{BinRead, BinReaderExt}; @@ -65,16 +71,17 @@ struct Pak { name: String, } -pub fn is_epk2_file(file: &File) -> bool { - let header = common::read_file(&file, 128, 4).expect("Failed to read from file."); +pub fn is_epk2_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 128, 4)?; if header == b"epak" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_epk2(app_ctx: &ProgramContext, _: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?; let stored_header = common::read_exact(&mut file, 1584)?; //max header size @@ -184,8 +191,8 @@ pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box Format { + Format { name: "epk2b", detect_func: is_epk2b_file, run_func: extract_epk2b } +} + use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; @@ -51,17 +56,18 @@ struct Pak { size : u32, } -pub fn is_epk2b_file(file: &File) -> bool { - let epk2_magic = common::read_file(&file, 12, 4).expect("Failed to read from file."); - let epak_magic = common::read_file(&file, 0, 4).expect("Failed to read from file."); +pub fn is_epk2b_file(app_ctx: &ProgramContext) -> Result>, Box> { + let epk2_magic = common::read_file(app_ctx.file, 12, 4)?; + let epak_magic = common::read_file(app_ctx.file, 0, 4)?; if epak_magic == b"epak" && epk2_magic == b"EPK2" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_epk2b(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_epk2b(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let header: EpkHeader = file.read_le()?; println!("EPK info -\nData size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}", @@ -108,8 +114,8 @@ pub fn extract_epk2b(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box> { +pub fn extract_epk3(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; file.seek(SeekFrom::Start(0))?; let stored_header = common::read_exact(&mut file, 1712)?; let header: Vec; @@ -153,8 +157,8 @@ pub fn extract_epk3(mut file: &File, output_folder: &str) -> Result<(), Box Format { + Format { name: "funai_upg", detect_func: is_funai_upg_file, run_func: extract_funai_upg } +} + use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write}; @@ -20,16 +25,17 @@ struct Entry { _unk: u16, } -pub fn is_funai_upg_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 6).expect("Failed to read from file."); +pub fn is_funai_upg_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 6)?; if header == b"UPG\x00\x00\x00" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_funai_upg(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_funai_upg(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let header: Header = file.read_le()?; println!("File info:\nFile size: {}\nEntry count: {}", header.file_size, header.entry_count); @@ -45,9 +51,9 @@ pub fn extract_funai_upg(mut file: &File, output_folder: &str) -> Result<(), Box println!("Descriptor entry info:\n{}", entry_string); } - let output_path = Path::new(&output_folder).join(format!("{}_{}.bin", i + 1, entry.entry_type)); + let output_path = Path::new(app_ctx.output_dir).join(format!("{}_{}.bin", i + 1, entry.entry_type)); - fs::create_dir_all(&output_folder)?; + 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(&data)?; diff --git a/src/formats/invincible_image.rs b/src/formats/invincible_image.rs index 98f39aa..9534962 100644 --- a/src/formats/invincible_image.rs +++ b/src/formats/invincible_image.rs @@ -1,5 +1,11 @@ +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "invincible_image", detect_func: is_invincible_image_file, run_func: extract_invincible_image } +} + use std::path::{Path}; -use std::fs::{self, File, OpenOptions}; +use std::fs::{self, OpenOptions}; use std::io::{Write, Read, Seek, SeekFrom, Cursor}; use binrw::{BinRead, BinReaderExt}; @@ -56,16 +62,17 @@ impl Entry { } } -pub fn is_invincible_image_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 16).expect("Failed to read from file."); +pub fn is_invincible_image_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 16)?; if header == b"INVINCIBLE_IMAGE" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_invincible_image(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_invincible_image(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let header: Header = file.read_le()?; println!("File info:\nFile Version: {}.{}\nVersion(1): {}\nVersion(2): {}\nVersion(3): {}\nVersion(4): {}\nData size: {}\nData start offset: {}\nKeep data size: {}\nSkip data size: {}\n\nPayload Count: {}", @@ -113,9 +120,9 @@ pub fn extract_invincible_image(mut file: &File, output_folder: &str) -> Result< println!("\n({}/{}) - {}, Size: {}", i, header.payload_count, entry.name(), entry.size); let data = common::read_exact(&mut data_reader, entry.size as usize)?; - let output_path = Path::new(&output_folder).join(entry.name() + ".bin"); + let output_path = Path::new(app_ctx.output_dir).join(entry.name() + ".bin"); - fs::create_dir_all(&output_folder)?; + fs::create_dir_all(app_ctx.output_dir)?; let mut out_file = OpenOptions::new() .write(true) .create(true) diff --git a/src/formats/msd10.rs b/src/formats/msd10.rs index 0bd00f0..7c7fefa 100644 --- a/src/formats/msd10.rs +++ b/src/formats/msd10.rs @@ -1,4 +1,10 @@ -use std::fs::{self, File, OpenOptions}; +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "msd10", detect_func: is_msd10_file, run_func: extract_msd10 } +} + +use std::fs::{self, OpenOptions}; use std::path::{Path}; use std::io::{Write, Seek, SeekFrom}; use binrw::{BinRead, BinReaderExt}; @@ -41,16 +47,17 @@ struct Section { size: u32, } -pub fn is_msd10_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 6).expect("Failed to read from file."); +pub fn is_msd10_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 6)?; if header == b"MSDU10" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_msd10(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let header: FileHeader = file.read_le()?; println!("\nNumber of sections: {}", header.section_count); @@ -131,8 +138,8 @@ pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Format { + Format { name: "msd11", detect_func: is_msd11_file, run_func: extract_msd11 } +} + +use std::fs::{self, OpenOptions}; use std::path::{Path}; use std::io::{Write}; use binrw::{BinRead, BinReaderExt}; @@ -42,16 +48,17 @@ struct Section { size: u64, } -pub fn is_msd11_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 6).expect("Failed to read from file."); +pub fn is_msd11_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 6)?; if header == b"MSDU11" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_msd11(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_msd11(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let header: FileHeader = file.read_le()?; println!("\nNumber of sections: {}", header.section_count); @@ -126,8 +133,8 @@ pub fn extract_msd11(mut file: &File, output_folder: &str) -> Result<(), Box Format { + Format { name: "mstar", detect_func: is_mstar_file, run_func: extract_mstar } +} + +use std::fs::{self, OpenOptions}; use std::path::{Path}; use std::io::{Write}; @@ -7,19 +13,14 @@ use crate::utils::compression::{decompress_lzma, decompress_lz4}; use crate::utils::lzop::{unlzop_to_file}; use crate::utils::sparse::{unsparse_to_file}; -//change whether the "userdata" partition is skipped -// this is because the userdata partition is sometimes enourmous sizes like 27gb, and it is empty anyways -// if you want to extract "userdata" you can disable the option -static CONFIG_SKIP_USERDATA: bool = true; - -pub fn is_mstar_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 32768).expect("Failed to read from file."); +pub fn is_mstar_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 32768)?; let header_string = String::from_utf8_lossy(&header); if header_string.contains("filepartload"){ - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } @@ -31,7 +32,8 @@ fn parse_number(s: &str) -> Option { } } -pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_mstar(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let file = app_ctx.file; let mut script = common::read_file(&file, 0, 32768)?; @@ -132,12 +134,10 @@ pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Format { + Format { name: "mtk_bdp", detect_func: is_mtk_bdp_file, run_func: extract_mtk_bdp } +} + use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Seek, SeekFrom, Read, Write}; @@ -72,7 +77,8 @@ fn find_bytes(data: &[u8], pattern: &[u8]) -> Option { data.windows(pattern.len()).position(|window| window == pattern) } -pub fn is_mtk_bdp_file(mut file: &File) -> Result, Box> { +pub fn is_mtk_bdp_file(app_ctx: &ProgramContext) -> Result>, Box> { + let mut file = app_ctx.file; let file_size = file.metadata()?.len(); let mut data = Vec::new(); @@ -83,14 +89,16 @@ pub fn is_mtk_bdp_file(mut file: &File) -> Result, Box Result<(), Box> { - let offset = context.pitit_offset; +pub fn extract_mtk_bdp(app_ctx: &ProgramContext, ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; + let ctx = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; + let offset = ctx.pitit_offset; println!("\nReading PITIT at: {}", offset); file.seek(SeekFrom::Start(offset + 8))?; @@ -173,8 +181,8 @@ pub fn extract_mtk_bdp(mut file: &File, output_folder: &str, context: MtkBdpCont let data = common::read_file(&file, bit_entry.offset as u64, bit_entry.size as usize)?; - let output_path = Path::new(&output_folder).join(format!("{}.bin", name)); - fs::create_dir_all(&output_folder)?; + let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", name)); + fs::create_dir_all(app_ctx.output_dir)?; let mut out_file = OpenOptions::new().read(true).write(true).create(true).open(output_path)?; out_file.seek(SeekFrom::Start(bit_entry.offset_in_target_part as u64))?; out_file.write_all(&data)?; diff --git a/src/formats/mtk_pkg.rs b/src/formats/mtk_pkg.rs index 3a30877..abd35ca 100644 --- a/src/formats/mtk_pkg.rs +++ b/src/formats/mtk_pkg.rs @@ -1,5 +1,11 @@ +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "mtk_pkg", detect_func: is_mtk_pkg_file, run_func: extract_mtk_pkg } +} + use std::path::Path; -use std::fs::{self, File, OpenOptions}; +use std::fs::{self, OpenOptions}; use std::io::{Write, Cursor, Seek, SeekFrom}; use binrw::{BinRead, BinReaderExt}; @@ -75,17 +81,17 @@ static HEADER_KEY: [u8; 16] = [ static HEADER_IV: [u8; 16] = [0x00; 16]; -pub fn is_mtk_pkg_file(file: &File) -> Result, Box> { - let mut encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?; +pub fn is_mtk_pkg_file(app_ctx: &ProgramContext) -> Result>, Box> { + let mut encrypted_header = common::read_file(app_ctx.file, 0, HEADER_SIZE)?; let mut header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?; if &header[4..12] == MTK_HEADER_MAGIC { - Ok(Some(MtkPkgContext { is_philips_variant: false, decrypted_header: header})) + Ok(Some(Box::new(MtkPkgContext { is_philips_variant: false, decrypted_header: header}))) } else { // try for philips which has additional 128 bytes at beginning - encrypted_header = common::read_file(&file, PHILIPS_EXTRA_HEADER_SIZE as u64, HEADER_SIZE)?; + encrypted_header = common::read_file(app_ctx.file, PHILIPS_EXTRA_HEADER_SIZE as u64, HEADER_SIZE)?; header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?; if &header[4..12] == MTK_HEADER_MAGIC { - Ok(Some(MtkPkgContext { is_philips_variant: true, decrypted_header: header })) + Ok(Some(Box::new(MtkPkgContext { is_philips_variant: true, decrypted_header: header }))) } else { Ok(None) @@ -93,16 +99,19 @@ pub fn is_mtk_pkg_file(file: &File) -> Result, Box Result<(), Box> { +pub fn extract_mtk_pkg(app_ctx: &ProgramContext, ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; + let ctx = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; + let file_size = file.metadata()?.len(); - let header = context.decrypted_header; + let header = ctx.decrypted_header; let mut hdr_reader = Cursor::new(header); let hdr: Header = hdr_reader.read_le()?; println!("File info:\nFile size: {}\nVendor magic: {}\nVersion info: {}\nProduct name: {}" , hdr.file_size, hdr.vendor_magic(), hdr.version(), hdr.product_name()); - if context.is_philips_variant { + if ctx.is_philips_variant { file.seek(SeekFrom::Start(HEADER_SIZE as u64 + PHILIPS_EXTRA_HEADER_SIZE as u64))?; } else { file.seek(SeekFrom::Start(HEADER_SIZE as u64))?; @@ -189,13 +198,13 @@ pub fn extract_mtk_pkg(mut file: &File, output_folder: &str, context: MtkPkgCont }; //for compressed part create temp file - let output_path = Path::new(&output_folder).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); - fs::create_dir_all(&output_folder)?; + let output_path = Path::new(app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); + fs::create_dir_all(app_ctx.output_dir)?; let mut out_file = OpenOptions::new().write(true).read(true)/* for lzhs */.create(true).open(&output_path)?; out_file.write_all(&out_data[CRYPTED_HEADER_SIZE + extra_header_len as usize..])?; if part_entry.is_compressed() { - let lzhs_out_path = Path::new(&output_folder).join(part_entry.name() + ".bin"); + let lzhs_out_path = Path::new(app_ctx.output_dir).join(part_entry.name() + ".bin"); match decompress_lzhs_fs_file2file(&out_file, lzhs_out_path) { Ok(()) => { println!("- Decompressed Successfully!"); diff --git a/src/formats/mtk_pkg_new.rs b/src/formats/mtk_pkg_new.rs index 3ba5d5f..0cce9e1 100644 --- a/src/formats/mtk_pkg_new.rs +++ b/src/formats/mtk_pkg_new.rs @@ -1,5 +1,11 @@ +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "mtk_pkg_new", detect_func: is_mtk_pkg_new_file, run_func: extract_mtk_pkg_new } +} + use std::path::Path; -use std::fs::{self, File, OpenOptions}; +use std::fs::{self, OpenOptions}; use std::io::{Write, Cursor, Seek, SeekFrom}; use binrw::{BinRead, BinReaderExt}; @@ -64,34 +70,36 @@ impl PartEntry { static HEADER_SIZE: usize = 0x170; -pub fn is_mtk_pkg_new_file(file: &File) -> Result, Box> { - let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?; +pub fn is_mtk_pkg_new_file(app_ctx: &ProgramContext) -> Result>, Box> { + let encrypted_header = common::read_file(app_ctx.file, 0, HEADER_SIZE)?; for (key_hex, iv_hex, name) in keys::MTK_PKG_CUST { let key_array: [u8; 16] = hex::decode(key_hex)?.as_slice().try_into()?; let iv_array: [u8; 16] = hex::decode(iv_hex)?.as_slice().try_into()?; let try_decrypt = decrypt_aes128_cbc_nopad(&encrypted_header, &key_array, &iv_array)?; if &try_decrypt[4..12] == MTK_HEADER_MAGIC { - return Ok(Some(MtkPkgNewContext { + return Ok(Some(Box::new(MtkPkgNewContext { matching_key_name: name.to_string(), matching_key_key: key_array, matching_key_iv: iv_array, decrypted_header: try_decrypt - })); + }))); } } Ok(None) } -pub fn extract_mtk_pkg_new(mut file: &File, output_folder: &str, context: MtkPkgNewContext) -> Result<(), Box> { +pub fn extract_mtk_pkg_new(app_ctx: &ProgramContext, ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; + let ctx = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; let file_size = file.metadata()?.len(); //the key was founf, and header was decrypted at detection stage so we can reuse - println!("Using key {}", context.matching_key_name); - let key_array = context.matching_key_key; - let iv_array = context.matching_key_iv; - let header = context.decrypted_header; + println!("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; let mut hdr_reader = Cursor::new(header); let hdr: Header = hdr_reader.read_le()?; @@ -147,13 +155,13 @@ pub fn extract_mtk_pkg_new(mut file: &File, output_folder: &str, context: MtkPkg }; //for compressed part create temp file - let output_path = Path::new(&output_folder).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); - fs::create_dir_all(&output_folder)?; + let output_path = Path::new(app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); + fs::create_dir_all(app_ctx.output_dir)?; let mut out_file = OpenOptions::new().write(true).read(true)/* for lzhs */.create(true).open(&output_path)?; out_file.write_all(&out_data[48 + extra_header_len as usize..])?; if part_entry.is_compressed() { - let lzhs_out_path = Path::new(&output_folder).join(part_entry.name() + ".bin"); + let lzhs_out_path = Path::new(app_ctx.output_dir).join(part_entry.name() + ".bin"); match decompress_lzhs_fs_file2file(&out_file, lzhs_out_path) { Ok(()) => { println!("-- Decompressed Successfully!"); diff --git a/src/formats/mtk_pkg_old.rs b/src/formats/mtk_pkg_old.rs index a03231f..9af4b11 100644 --- a/src/formats/mtk_pkg_old.rs +++ b/src/formats/mtk_pkg_old.rs @@ -1,5 +1,11 @@ +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "mtk_pkg_old", detect_func: is_mtk_pkg_old_file, run_func: extract_mtk_pkg_old } +} + use std::path::Path; -use std::fs::{self, File, OpenOptions}; +use std::fs::{self, OpenOptions}; use std::io::{Write, Cursor, Seek}; use binrw::{BinRead, BinReaderExt}; @@ -57,25 +63,27 @@ impl PartEntry { static HEADER_SIZE: usize = 0x98; -pub fn is_mtk_pkg_old_file(mut file: &File) -> bool { - let encrypted_header = common::read_file(&file, 0, HEADER_SIZE).expect("Failed to read from file."); +pub fn is_mtk_pkg_old_file(app_ctx: &ProgramContext) -> Result>, Box> { + let mut file = app_ctx.file; + let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?; let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK)); if &header[4..12] == MTK_HEADER_MAGIC { - true + Ok(Some(Box::new(()))) } else if &header[68..76] == MTK_HEADER_MAGIC { //check for 64 byte additional header used in some Sony and Philips firmwares and skip it - file.seek(std::io::SeekFrom::Start(64)).expect("Failed to seek"); - true + file.seek(std::io::SeekFrom::Start(64))?; + Ok(Some(Box::new(()))) } else if &header[132..140] == MTK_HEADER_MAGIC { //check for 128 byte additional header used in some Philips firmwares and skip it - file.seek(std::io::SeekFrom::Start(128)).expect("Failed to seek"); - true + file.seek(std::io::SeekFrom::Start(128))?; + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_mtk_pkg_old(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_mtk_pkg_old(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let file_size = file.metadata()?.len(); let encrypted_header = common::read_exact(&mut file, HEADER_SIZE)?; let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK)); @@ -118,13 +126,13 @@ pub fn extract_mtk_pkg_old(mut file: &File, output_folder: &str) -> Result<(), B }; //for compressed part create temp file - let output_path = Path::new(&output_folder).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); - fs::create_dir_all(&output_folder)?; + let output_path = Path::new(app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); + fs::create_dir_all(app_ctx.output_dir)?; let mut out_file = OpenOptions::new().write(true).read(true)/* for lzhs */.create(true).open(&output_path)?; out_file.write_all(&out_data[extra_header_len as usize..])?; if part_entry.is_compressed() { - let lzhs_out_path = Path::new(&output_folder).join(part_entry.name() + ".bin"); + let lzhs_out_path = Path::new(app_ctx.output_dir).join(part_entry.name() + ".bin"); match decompress_lzhs_fs_file2file_old(&out_file, lzhs_out_path) { Ok(()) => { println!("- Decompressed Successfully!"); diff --git a/src/formats/novatek.rs b/src/formats/novatek.rs index d99c029..d1c721c 100644 --- a/src/formats/novatek.rs +++ b/src/formats/novatek.rs @@ -1,4 +1,9 @@ -use std::fs::File; +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "novatek", detect_func: is_novatek_file, run_func: extract_novatek } +} + use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write}; @@ -35,16 +40,17 @@ struct PartEntry { #[br(count = 16)] _md5_checksum: Vec, } -pub fn is_novatek_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 4).expect("Failed to read from file."); +pub fn is_novatek_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"NFWB" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_novatek(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_novatek(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let header: Header = file.read_le()?; println!("File info:\nFirmware name: {}\nVersion: {}.{}\nData size: {}\nPart count: {}", @@ -63,9 +69,9 @@ pub fn extract_novatek(mut file: &File, output_folder: &str) -> Result<(), Box Format { + Format { name: "nvt_timg", detect_func: is_nvt_timg_file, run_func: extract_nvt_timg } +} + use std::path::{Path}; use std::io::{Seek, Write}; -use std::fs::{self, File, OpenOptions}; +use std::fs::{self, OpenOptions}; use binrw::{BinRead, BinReaderExt}; use crate::utils::common; @@ -44,16 +49,17 @@ impl PIMG { } } -pub fn is_nvt_timg_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 4).expect("Failed to read from file."); +pub fn is_nvt_timg_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"TIMG" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_nvt_timg(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_nvt_timg(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let file_size = file.metadata()?.len(); let timg: TIMG = file.read_le()?; println!("File info:\nData size: {}", timg.data_size); @@ -72,7 +78,7 @@ pub fn extract_nvt_timg(mut file: &File, output_folder: &str) -> Result<(), Box< println!("\n#{} - {}, Size: {}, Dest: {}, Compression: {}", pimg_i, pimg.name(), pimg.size, pimg.dest_dev(), pimg.comp_type()); let out_data; - let output_path = Path::new(&output_folder).join(pimg.name() + ".bin"); + 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..."); @@ -89,7 +95,7 @@ pub fn extract_nvt_timg(mut file: &File, output_folder: &str) -> Result<(), Box< out_data = data; } - fs::create_dir_all(&output_folder)?; + fs::create_dir_all(app_ctx.output_dir)?; let mut out_file = OpenOptions::new() .write(true) .create(true) diff --git a/src/formats/pana_dvd.rs b/src/formats/pana_dvd.rs index 8985ba4..3f23230 100644 --- a/src/formats/pana_dvd.rs +++ b/src/formats/pana_dvd.rs @@ -1,4 +1,9 @@ -use std::fs::File; +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "pana_dvd", detect_func: is_pana_dvd_file, run_func: extract_pana_dvd } +} + use std::path::{Path, PathBuf}; use std::fs::{self, OpenOptions}; use std::io::{Write, Read, Cursor, Seek, SeekFrom}; @@ -144,38 +149,41 @@ pub fn find_aes_key_pair<'a>(key_array: &'a [(&'a str, &'a str, &'a str)], data: Ok(None) } -pub fn is_pana_dvd_file(file: &File) -> Result, Box> { - let header = common::read_file(&file, 0, 64)?; +pub fn is_pana_dvd_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 64)?; if let Some(matching_key) = find_key(&keys::PANA_DVD_KEYONLY, &header, b"PROG", 0)? { - Ok(Some(PanaDvdContext { + Ok(Some(Box::new(PanaDvdContext { matching_key: matching_key, base_hdr_size: 0, is_aes: false, aes_key: None, aes_iv: None, - })) + }))) } else if header.starts_with(b"PANASONIC\x00\x00\x00") && let Some(matching_key) = find_key(&keys::PANA_DVD_KEYONLY, &header, b"PROG", 48)? { - Ok(Some(PanaDvdContext { + Ok(Some(Box::new(PanaDvdContext { matching_key: matching_key, base_hdr_size: 48, is_aes: false, aes_key: None, aes_iv: None, - })) + }))) } else if let Some((aes_key, aes_iv, matching_key)) = find_aes_key_pair(&keys::PANA_DVD_AESPAIR, &header, b"PANASONIC", 32)? { - Ok(Some(PanaDvdContext { + Ok(Some(Box::new(PanaDvdContext { matching_key: matching_key, base_hdr_size: 48, is_aes: true, aes_key: Some(aes_key), aes_iv: Some(aes_iv), - })) + }))) } else { Ok(None) } } -pub fn extract_pana_dvd(mut file: &File, output_folder: &str, context: PanaDvdContext) -> Result<(), Box> { +pub fn extract_pana_dvd(app_ctx: &ProgramContext, ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; + let context = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; + let mut data = Vec::new(); // we need to load the entire file into memory so we can swap it with AES decrypted data if its AES encrypted file.read_to_end(&mut data)?; let mut file_reader = Cursor::new(data); @@ -209,12 +217,12 @@ pub fn extract_pana_dvd(mut file: &File, output_folder: &str, context: PanaDvdCo if file_entries.len() == 1 { //only one file, standard extraction println!("File contains no extra sub-files...\n"); - extract_file(&mut file_reader, file_entries[0].offset as u64, file_entries[0].base_offset as u64, matching_key, output_folder)?; + extract_file(&mut file_reader, file_entries[0].offset as u64, file_entries[0].base_offset as u64, matching_key, app_ctx.output_dir)?; } else { println!("File contains {} sub-files...", file_entries.len()); for (i, file_entry ) in file_entries.iter().enumerate() { println!("\nExtracting file {}/{} - Offset: {}, base: {}", i + 1, file_entries.len(), file_entry.offset, file_entry.base_offset); - extract_file(&mut file_reader, file_entry.offset as u64, file_entry.base_offset as u64, matching_key, &format!("{}/file_{}", output_folder, i + 1))?; + extract_file(&mut file_reader, file_entry.offset as u64, file_entry.base_offset as u64, matching_key, &format!("{}/file_{}", app_ctx.output_dir, i + 1))?; } } diff --git a/src/formats/pfl_upg.rs b/src/formats/pfl_upg.rs index 2b5b886..ad1f85e 100644 --- a/src/formats/pfl_upg.rs +++ b/src/formats/pfl_upg.rs @@ -1,4 +1,9 @@ -use std::fs::{File}; +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "pfl_upg", detect_func: is_pfl_upg_file, run_func: extract_pfl_upg } +} + use rsa::{RsaPublicKey, BigUint}; use hex::decode; use std::path::Path; @@ -43,12 +48,12 @@ impl FileHeader { } } -pub fn is_pfl_upg_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 8).expect("Failed to read from file."); +pub fn is_pfl_upg_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 8)?; if header == b"2SWU3TXV" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } @@ -84,7 +89,8 @@ fn decrypt_aes256_ecb(key: &[u8], ciphertext: &[u8]) -> Result, Box Result<(), Box> { +pub fn extract_pfl_upg(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let header: Header = file.read_le()?; let signature = common::read_exact(&mut file, 128)?; let _ = common::read_exact(&mut file, 32)?; //unknown @@ -157,7 +163,7 @@ pub fn extract_pfl_upg(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Result<(), Box Format { + Format { name: "pup", detect_func: is_pup_file, run_func: extract_pup } +} + use std::path::{Path}; -use std::fs::{self, File, OpenOptions}; +use std::fs::{self, OpenOptions}; use binrw::{BinRead, BinReaderExt}; use std::io::{Write, Seek, SeekFrom}; @@ -50,16 +56,17 @@ struct BlockEntry { size: u32, } -pub fn is_pup_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 4).expect("Failed to read from file."); +pub fn is_pup_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"\x4F\x15\x3D\x1D" || header == b"\x54\x14\xF5\xEE" { //ps4, ps5 - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_pup(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_pup(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let header: Header = file.read_le()?; println!("File info:\nFile size: {}\nEntry count: {}", @@ -132,9 +139,9 @@ pub fn extract_pup(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Format { + Format { name: "roku", detect_func: is_roku_file, run_func: extract_roku } +} + +use std::fs::{self, OpenOptions}; use std::path::Path; use std::io::{Write, Seek, Read, Cursor}; use tar::Archive; @@ -55,18 +61,19 @@ impl ImageHeader { } } -pub fn is_roku_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 32).expect("Failed to read from file."); - let try_decrypt_header = decrypt_aes128_cbc_nopad(&header, &FILE_KEY, &FILE_IV).expect("Decryption error!"); +pub fn is_roku_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 32)?; + let try_decrypt_header = decrypt_aes128_cbc_nopad(&header, &FILE_KEY, &FILE_IV)?; if try_decrypt_header.starts_with(b"manifest\x00\x00\x00\x00\x00\x00\x00\x00") { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_roku(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_roku(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let mut encrypted_data = Vec::new(); file.read_to_end(&mut encrypted_data)?; @@ -109,7 +116,7 @@ pub fn extract_roku(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Format { + Format { name: "ruf", detect_func: is_ruf_file, run_func: extract_ruf } +} + use std::path::{Path}; use std::fs::{self, File, OpenOptions}; use binrw::{BinRead, BinReaderExt}; @@ -69,24 +75,25 @@ impl RufEntry { } } -pub fn is_ruf_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 3).expect("Failed to read from file."); - if header == b"RUF" { - true +pub fn is_ruf_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 6)?; + if header == b"RUF\x00\x00\x00" { + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_ruf(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_ruf(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let header: RufHeader = file.read_be()?; if header.is_dual_ruf() { println!("\nDual RUF detected! Extracting 1st RUF...\n"); - actually_extract_ruf(&file, &format!("{}/RUF_1", output_folder), 0)?; + actually_extract_ruf(file, &format!("{}/RUF_1", app_ctx.output_dir), 0)?; println!("\nExtracting 2nd RUF...\n"); - actually_extract_ruf(&file, &format!("{}/RUF_2", output_folder), 41943088)?; + actually_extract_ruf(file, &format!("{}/RUF_2", app_ctx.output_dir), 41943088)?; } else { - actually_extract_ruf(&file, &output_folder, 0)?; + actually_extract_ruf(file, app_ctx.output_dir, 0)?; } println!("\nExtraction finished!"); diff --git a/src/formats/rvp.rs b/src/formats/rvp.rs index 9400fb7..f91abe0 100644 --- a/src/formats/rvp.rs +++ b/src/formats/rvp.rs @@ -1,4 +1,9 @@ -use std::fs::File; +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "rvp", detect_func: is_rvp_file, run_func: extract_rvp } +} + use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Read, Cursor, Seek}; @@ -13,26 +18,29 @@ fn decrypt_xor(data: &[u8]) -> Vec { .collect() } -pub fn is_rvp_file(mut file: &File) -> bool { +pub fn is_rvp_file(app_ctx: &ProgramContext) -> Result>, Box> { + let mut file = app_ctx.file; //MVP - let header = common::read_file(&file, 0, 4).expect("Failed to read from file."); + let header = common::read_file(file, 0, 4)?; if header == b"UPDT" { - file.seek(std::io::SeekFrom::Start(36)).expect("Failed to seek"); //skip rest of header - return true; + file.seek(std::io::SeekFrom::Start(36))?; //skip rest of header // NOT GOOD PRACTICE SHOULD BE REMOVED + return Ok(Some(Box::new(()))) } //RVP - let bytes = common::read_file(&file, 16, 18).expect("Failed to read from file."); + let bytes = common::read_file(file, 16, 18)?; for (_i, &b) in bytes.iter().enumerate().step_by(2) { if b != 0xA3 { - return false; + return Ok(None); } } - file.seek(std::io::SeekFrom::Start(64)).expect("Failed to seek"); //skip rest of header - true + + file.seek(std::io::SeekFrom::Start(64))?; //skip rest of header // NOT GOOD PRACTICE SHOULD BE REMOVED + Ok(Some(Box::new(()))) } -pub fn extract_rvp(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_rvp(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let mut obf_data = Vec::new(); //we sadly cannot deXOR on the fly because of its 32 byte pattern file.read_to_end(&mut obf_data)?; println!("DeXORing data.."); @@ -114,9 +122,9 @@ pub fn extract_rvp(mut file: &File, output_folder: &str) -> Result<(), Box Format { + Format { name: "sddl_sec", detect_func: is_sddl_sec_file, run_func: extract_sddl_sec } +} use std::path::{Path, PathBuf}; -use std::fs::{self, File, OpenOptions}; +use std::fs::{self, OpenOptions}; use std::io::{Cursor, Seek, SeekFrom, Write}; use binrw::{BinRead, BinReaderExt}; @@ -98,13 +103,13 @@ static DEC_IV: [u8; 16] = [ 0xCD, 0x88, 0x38, 0xC4, 0xB9, 0x0C, 0x76, 0x66, ]; -pub fn is_sddl_sec_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 32).expect("Failed to read from file."); +pub fn is_sddl_sec_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 32)?; let deciph_header = decipher(&header); if deciph_header.starts_with(b"\x11\x22\x33\x44") { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } @@ -150,7 +155,8 @@ fn decipher(s: &[u8]) -> Vec { out } -pub fn extract_sddl_sec(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_sddl_sec(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let mut hdr_reader = Cursor::new(decipher(&common::read_exact(&mut file, 32)?)); let hdr: SddlSecHeader = hdr_reader.read_be()?; @@ -168,10 +174,10 @@ pub fn extract_sddl_sec(mut file: &File, output_folder: &str) -> Result<(), Box< let data = common::read_exact(&mut file, entry_header.size() as usize)?; let dec_data = decrypt_aes128_cbc_pcks7(&data, &DEC_KEY, &DEC_IV)?; - fs::create_dir_all(&output_folder)?; + fs::create_dir_all(app_ctx.output_dir)?; //detect the file type based on the counts of each file if i == 0 { //SDIT.FDI file - let output_path = Path::new(&output_folder).join(entry_header.name()); + let output_path = Path::new(app_ctx.output_dir).join(entry_header.name()); let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&dec_data)?; println!("-- Saved file!"); @@ -210,11 +216,11 @@ pub fn extract_sddl_sec(mut file: &File, output_folder: &str) -> Result<(), Box< let file_name = common::string_from_bytes(&file_name_bytes); println!("--- File name: {}", file_name); - let out_folder_path = Path::new(&output_folder).join(source_name); + let out_folder_path = Path::new(app_ctx.output_dir).join(source_name); fs::create_dir_all(&out_folder_path)?; output_path = Path::new(&out_folder_path).join(file_name); } else { - output_path = Path::new(&output_folder).join(format!("{}.bin", source_name)); + output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", source_name)); } let data = common::read_exact(&mut content_reader, content_header.size as usize)?; diff --git a/src/formats/slp.rs b/src/formats/slp.rs index 9308f10..de1aff5 100644 --- a/src/formats/slp.rs +++ b/src/formats/slp.rs @@ -1,5 +1,11 @@ +use std::any::Any; +use crate::{ProgramContext, formats::Format}; +pub fn format() -> Format { + Format { name: "slp", detect_func: is_slp_file, run_func: extract_slp } +} + use std::path::{Path}; -use std::fs::{self, File, OpenOptions}; +use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; use binrw::{BinRead, BinReaderExt}; @@ -46,16 +52,17 @@ struct EntryNew { #[br(count = 12)] _unk2: Vec, } -pub fn is_slp_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 4).expect("Failed to read from file."); +pub fn is_slp_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"SLP\x00" { - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_slp(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_slp(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let header: Header = file.read_le()?; println!("File info:\nModel: {}\nVersion: {}\nFirmware: {}\nNew type: {}\n", @@ -92,9 +99,9 @@ pub fn extract_slp(mut file: &File, output_folder: &str) -> Result<(), Box Format { + Format { name: "sony_bdp", detect_func: is_sony_bdp_file, run_func: extract_sony_bdp } +} + use std::fs::File; use std::path::{Path, PathBuf}; use std::fs::{self, OpenOptions}; @@ -57,16 +63,17 @@ struct Entry { size: u32, } -pub fn is_sony_bdp_file(file: &File) -> bool { - let header = common::read_file(&file, 0, 4).expect("Failed to read from file."); +pub fn is_sony_bdp_file(app_ctx: &ProgramContext) -> Result>, Box> { + let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"\x01\x73\xEC\xC9" || header == b"\x01\x73\xEC\x1F" || header == b"\xEC\x7D\xB0\xB0" { //MSB1x, MSB0x, BDPPxx - true + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } -pub fn extract_sony_bdp(mut file: &File, output_folder: &str) -> Result<(), Box> { +pub fn extract_sony_bdp(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { + let mut file = app_ctx.file; let obf_header = common::read_exact(&mut file, 300)?; let header = hex_substitute(&obf_header); let mut hdr_reader = Cursor::new(header); @@ -96,10 +103,10 @@ pub fn extract_sony_bdp(mut file: &File, output_folder: &str) -> Result<(), Box< let obf_data = common::read_file(&file, entry.offset as u64, entry.size as usize)?; let data = hex_substitute(&obf_data); - let output_path = Path::new(&output_folder).join(format!("{}.bin", i)); + let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", i)); last_file_path = Some(output_path.clone()); - fs::create_dir_all(&output_folder)?; + 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(&data)?; @@ -110,11 +117,15 @@ pub fn extract_sony_bdp(mut file: &File, output_folder: &str) -> Result<(), Box< //The last file is often a Mtk BDP file so we can extract that here. if last_file_path.is_some() { println!("\nChecking if it's also MTK BDP..."); + let last_file = File::open(last_file_path.unwrap())?; - if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&last_file)? { + let mtk_extraction_path = format!("{}/{}", app_ctx.output_dir, i - 1); + let ctx: ProgramContext = ProgramContext { file: &last_file, output_dir: &mtk_extraction_path }; + + if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&ctx)? { println!("- MTK BDP file detected!\n"); - let mtk_extraction_path = format!("{}/{}", &output_folder, i - 1); - formats::mtk_bdp::extract_mtk_bdp(&last_file, &mtk_extraction_path, result)?; + + formats::mtk_bdp::extract_mtk_bdp(&ctx, Some(result))?; } else { println!("- Not an MTK BDP file."); } diff --git a/src/main.rs b/src/main.rs index 5bf66f2..b7cb468 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use clap::Parser; use std::path::{PathBuf}; use std::io::{self}; use std::fs::{self, File}; +use crate::formats::{Format, get_registry}; #[derive(Parser, Debug)] struct Args { @@ -13,6 +14,11 @@ struct Args { output_folder: Option, } +pub struct ProgramContext<'a> { + pub file: &'a std::fs::File, + pub output_dir: &'a str, +} + fn main() -> Result<(), Box> { println!("unixtract Firmware extractor"); let args = Args::parse(); @@ -39,130 +45,19 @@ fn main() -> Result<(), Box> { } } } - - if path.is_dir() { - if formats::samsung_old::is_samsung_old_dir(&path) { - println!("Samsung old firmware dir detected!\n"); - formats::samsung_old::extract_samsung_old(&path, &output_path)? - } else { - println!("Input format not recognized!"); - } - } else { - let file = File::open(path)?; - if formats::sddl_sec::is_sddl_sec_file(&file) { - println!("SDDL.SEC file detected!"); - formats::sddl_sec::extract_sddl_sec(&file, &output_path)?; - } - else if formats::invincible_image::is_invincible_image_file(&file) { - println!("INVINCIBLE_IMAGE file detected!"); - formats::invincible_image::extract_invincible_image(&file, &output_path)?; - } - else if formats::msd10::is_msd10_file(&file) { - println!("MSD10 file detected!"); - formats::msd10::extract_msd10(&file, &output_path)?; - } - else if formats::msd11::is_msd11_file(&file) { - println!("MSD11 file detected!"); - formats::msd11::extract_msd11(&file, &output_path)?; - } - else if formats::nvt_timg::is_nvt_timg_file(&file) { - println!("Novatek TIMG file detected!"); - formats::nvt_timg::extract_nvt_timg(&file, &output_path)?; - } - else if formats::bdl::is_bdl_file(&file) { - println!("BDL file detected!"); - formats::bdl::extract_bdl(&file, &output_path)?; - } - else if formats::android_ota_payload::is_android_ota_payload_file(&file) { - println!("Android OTA payload file detected!"); - formats::android_ota_payload::extract_android_ota_payload(&file, &output_path)?; - } - else if formats::novatek::is_novatek_file(&file) { - println!("Novatek file detected!"); - formats::novatek::extract_novatek(&file, &output_path)?; - } - else if formats::slp::is_slp_file(&file) { - println!("SLP file detected!"); - formats::slp::extract_slp(&file, &output_path)?; - } - else if formats::epk1::is_epk1_file(&file) { - println!("EPK1 file detected!"); - formats::epk1::extract_epk1(&file, &output_path)?; - } - else if formats::epk2b::is_epk2b_file(&file) { - println!("EPK2B file detected!"); - formats::epk2b::extract_epk2b(&file, &output_path)?; - } - //epk with encrypted header - it can be epk2 or epk3 so we need to check - else if formats::epk::is_epk_file(&file) { - println!("EPK file detected!"); - formats::epk::extract_epk(&file, &output_path)?; - } - //epk2 with unencrypted header - else if formats::epk2::is_epk2_file(&file) { - println!("EPK2 file detected!"); - formats::epk2::extract_epk2(&file, &output_path)?; - } - else if formats::ruf::is_ruf_file(&file) { - println!("RUF file detected!"); - formats::ruf::extract_ruf(&file, &output_path)?; - } - else if formats::funai_upg::is_funai_upg_file(&file) { - println!("Funai UPG file detected!"); - formats::funai_upg::extract_funai_upg(&file, &output_path)?; - } - else if formats::pfl_upg::is_pfl_upg_file(&file) { - println!("PFL UPG file detected!"); - formats::pfl_upg::extract_pfl_upg(&file, &output_path)?; - } - else if formats::amlogic::is_amlogic_file(&file) { - println!("Amlogic image file detected!"); - formats::amlogic::extract_amlogic(&file, &output_path)?; - } - else if let Some(result) = formats::pana_dvd::is_pana_dvd_file(&file)? { - println!("PANA_DVD file detected!"); - formats::pana_dvd::extract_pana_dvd(&file, &output_path, result)?; - } - else if formats::pup::is_pup_file(&file) { - println!("PUP file detected!"); - formats::pup::extract_pup(&file, &output_path)?; - } - else if formats::sony_bdp::is_sony_bdp_file(&file) { - println!("Sony BDP file detected!"); - formats::sony_bdp::extract_sony_bdp(&file, &output_path)?; - } - else if formats::rvp::is_rvp_file(&file) { - println!("RVP/MVP file detected!"); - formats::rvp::extract_rvp(&file, &output_path)?; - } - else if formats::mstar::is_mstar_file(&file) { - println!("Mstar upgrade file detected!"); - formats::mstar::extract_mstar(&file, &output_path)?; - } - else if formats::roku::is_roku_file(&file) { - println!("Roku file detected!"); - formats::roku::extract_roku(&file, &output_path)?; - } - else if let Some(result) = formats::mtk_pkg::is_mtk_pkg_file(&file)? { - println!("MTK PKG file detected!"); - formats::mtk_pkg::extract_mtk_pkg(&file, &output_path, result)?; - } - else if formats::mtk_pkg_old::is_mtk_pkg_old_file(&file) { - println!("MTK PKG (Old) file detected!"); - formats::mtk_pkg_old::extract_mtk_pkg_old(&file, &output_path)?; - } - else if let Some(result) = formats::mtk_pkg_new::is_mtk_pkg_new_file(&file)? { - println!("MTK PKG (New) file detected!"); - formats::mtk_pkg_new::extract_mtk_pkg_new(&file, &output_path, result)?; - } - else if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&file)? { - println!("MTK BDP file detected!"); - formats::mtk_bdp::extract_mtk_bdp(&file, &output_path, result)?; - } - else { - println!("Input format not recognized!"); + + let file = File::open(path)?; + let program_context: ProgramContext = ProgramContext { file: &file, output_dir: &output_path }; + let formats: Vec = get_registry(); + + for format in formats { + if let Some(ctx) = (format.detect_func)(&program_context)? { + println!("{} detected!", format.name); + (format.run_func)(&program_context, Some(ctx))?; + return Ok(()); } } + println!("\nInput format not recognized!"); Ok(()) } From 59f24f58022e576cfdebb80738393ecde835ecd3 Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Thu, 5 Feb 2026 18:53:35 +0100 Subject: [PATCH 2/9] newreg: mostly var name changes --- src/formats.rs | 45 +++++++++++++++--------------- src/formats/amlogic.rs | 8 +++--- src/formats/android_ota_payload.rs | 8 +++--- src/formats/bdl.rs | 8 +++--- src/formats/epk.rs | 8 +++--- src/formats/epk1.rs | 8 +++--- src/formats/epk2.rs | 8 +++--- src/formats/epk2b.rs | 8 +++--- src/formats/epk3.rs | 11 ++++++-- src/formats/funai_upg.rs | 8 +++--- src/formats/invincible_image.rs | 8 +++--- src/formats/msd10.rs | 8 +++--- src/formats/msd11.rs | 8 +++--- src/formats/mstar.rs | 8 +++--- src/formats/mtk_bdp.rs | 8 +++--- src/formats/mtk_pkg.rs | 8 +++--- src/formats/mtk_pkg_new.rs | 8 +++--- src/formats/mtk_pkg_old.rs | 8 +++--- src/formats/novatek.rs | 8 +++--- src/formats/nvt_timg.rs | 8 +++--- src/formats/pana_dvd.rs | 8 +++--- src/formats/pfl_upg.rs | 8 +++--- src/formats/pup.rs | 8 +++--- src/formats/roku.rs | 8 +++--- src/formats/ruf.rs | 8 +++--- src/formats/rvp.rs | 8 +++--- src/formats/sddl_sec.rs | 8 +++--- src/formats/slp.rs | 8 +++--- src/formats/sony_bdp.rs | 10 +++---- src/keys.rs | 20 ++++++------- src/main.rs | 8 +++--- 31 files changed, 155 insertions(+), 147 deletions(-) diff --git a/src/formats.rs b/src/formats.rs index 6370498..1228759 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -1,10 +1,10 @@ use std::any::Any; -use crate::ProgramContext; +use crate::AppContext; pub struct Format { pub name: &'static str, - pub detect_func: fn(&ProgramContext) -> Result>, Box>, - pub run_func: fn(&ProgramContext, Option>) -> Result<(), Box>, + pub detector_func: fn(&AppContext) -> Result>, Box>, + pub extractor_func: fn(&AppContext, Option>) -> Result<(), Box>, } pub mod mstar; @@ -44,32 +44,33 @@ pub mod mtk_bdp; pub fn get_registry() -> Vec { return vec![ - crate::formats::amlogic::format(), - crate::formats::epk1::format(), - crate::formats::android_ota_payload::format(), - crate::formats::bdl::format(), - crate::formats::epk2::format(), - crate::formats::epk::format(), - crate::formats::epk2b::format(), - crate::formats::funai_upg::format(), - crate::formats::invincible_image::format(), - crate::formats::msd10::format(), - crate::formats::msd11::format(), crate::formats::mstar::format(), - crate::formats::novatek::format(), crate::formats::nvt_timg::format(), crate::formats::pfl_upg::format(), - crate::formats::pup::format(), - crate::formats::roku::format(), - crate::formats::ruf::format(), - crate::formats::rvp::format(), crate::formats::sddl_sec::format(), + crate::formats::novatek::format(), + crate::formats::ruf::format(), + crate::formats::invincible_image::format(), crate::formats::slp::format(), - crate::formats::mtk_pkg_old::format(), - crate::formats::mtk_pkg::format(), + crate::formats::roku::format(), crate::formats::sony_bdp::format(), - crate::formats::mtk_pkg_new::format(), + crate::formats::rvp::format(), + crate::formats::funai_upg::format(), crate::formats::pana_dvd::format(), + crate::formats::android_ota_payload::format(), + crate::formats::bdl::format(), + crate::formats::amlogic::format(), + crate::formats::pup::format(), + crate::formats::msd10::format(), + crate::formats::msd11::format(), + crate::formats::epk::format(), + crate::formats::epk1::format(), + crate::formats::epk2::format(), + crate::formats::epk2b::format(), + crate::formats::epk3::format(), + crate::formats::mtk_pkg::format(), + crate::formats::mtk_pkg_old::format(), + crate::formats::mtk_pkg_new::format(), crate::formats::mtk_bdp::format(), ] } \ No newline at end of file diff --git a/src/formats/amlogic.rs b/src/formats/amlogic.rs index 08d4793..cd9b6c0 100644 --- a/src/formats/amlogic.rs +++ b/src/formats/amlogic.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "amlogic", detect_func: is_amlogic_file, run_func: extract_amlogic } + Format { name: "amlogic", detector_func: is_amlogic_file, extractor_func: extract_amlogic } } use std::path::{Path}; @@ -49,7 +49,7 @@ impl ItemEntry { } } -pub fn is_amlogic_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_amlogic_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 8, 4)?; if header == b"\x56\x19\xB5\x27" { Ok(Some(Box::new(()))) @@ -58,7 +58,7 @@ pub fn is_amlogic_file(app_ctx: &ProgramContext) -> Result>, } } -pub fn extract_amlogic(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; file.seek(SeekFrom::Start(0))?; let header: ImageHeader = file.read_le()?; diff --git a/src/formats/android_ota_payload.rs b/src/formats/android_ota_payload.rs index 0153dd8..3e670cf 100644 --- a/src/formats/android_ota_payload.rs +++ b/src/formats/android_ota_payload.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "android_ota_payload", detect_func: is_android_ota_payload_file, run_func: extract_android_ota_payload } + Format { name: "android_ota_payload", detector_func: is_android_ota_payload_file, extractor_func: extract_android_ota_payload } } use std::fs::{self, OpenOptions}; @@ -22,7 +22,7 @@ struct Header { metadata_signature_size: u32, } -pub fn is_android_ota_payload_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_android_ota_payload_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"CrAU" { Ok(Some(Box::new(()))) @@ -31,7 +31,7 @@ pub fn is_android_ota_payload_file(app_ctx: &ProgramContext) -> Result>) -> Result<(), Box> { +pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let header: Header = file.read_be()?; println!("File info:\nFormat version: {}\nManifest size: {}", header.file_format_version, header.manifest_size); diff --git a/src/formats/bdl.rs b/src/formats/bdl.rs index 2b66f41..7964ce7 100644 --- a/src/formats/bdl.rs +++ b/src/formats/bdl.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "bdl", detect_func: is_bdl_file, run_func: extract_bdl } + Format { name: "bdl", detector_func: is_bdl_file, extractor_func: extract_bdl } } use std::path::{Path}; @@ -85,7 +85,7 @@ impl PkgEntry { } } -pub fn is_bdl_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_bdl_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"ibdl" { Ok(Some(Box::new(()))) @@ -94,7 +94,7 @@ pub fn is_bdl_file(app_ctx: &ProgramContext) -> Result>, Box } } -pub fn extract_bdl(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_bdl(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let header: BdlHeader = file.read_le()?; diff --git a/src/formats/epk.rs b/src/formats/epk.rs index eb5eacf..5ca3dfd 100644 --- a/src/formats/epk.rs +++ b/src/formats/epk.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "epk", detect_func: is_epk_file, run_func: extract_epk } + Format { name: "epk", detector_func: is_epk_file, extractor_func: extract_epk } } use crate::utils::common; @@ -11,7 +11,7 @@ pub struct EpkContext { epk_version: u8, } -pub fn is_epk_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_epk_file(app_ctx: &AppContext) -> Result>, Box> { let versions = common::read_file(app_ctx.file, 1712, 36)?; if let Some(epk_version) = check_epk_version(&versions) { @@ -50,7 +50,7 @@ fn match_with_pattern(data: &[u8], pattern: &str) -> bool { true } -pub fn extract_epk(app_ctx: &ProgramContext, ctx: Option>) -> Result<(), Box> { +pub fn extract_epk(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { let ctx = ctx.and_then(|c| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; let versions = common::read_file(app_ctx.file, 1712, 36)?; diff --git a/src/formats/epk1.rs b/src/formats/epk1.rs index 9422ad4..74f2240 100644 --- a/src/formats/epk1.rs +++ b/src/formats/epk1.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "epk1", detect_func: is_epk1_file, run_func: extract_epk1 } + Format { name: "epk1", detector_func: is_epk1_file, extractor_func: extract_epk1 } } use std::path::{Path}; @@ -40,7 +40,7 @@ struct Pak { size : u32, } -pub fn is_epk1_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_epk1_file(app_ctx: &AppContext) -> Result>, Box> { let epk2_magic = common::read_file(app_ctx.file, 12, 4)?; //for epk2b let epak_magic = common::read_file(app_ctx.file, 0, 4)?; if epak_magic == b"epak" && epk2_magic != b"EPK2" { @@ -50,7 +50,7 @@ pub fn is_epk1_file(app_ctx: &ProgramContext) -> Result>, Bo } } -pub fn extract_epk1(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_epk1(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; //check type of epk1 let epk1_type; diff --git a/src/formats/epk2.rs b/src/formats/epk2.rs index 8194474..7a05c27 100644 --- a/src/formats/epk2.rs +++ b/src/formats/epk2.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "epk2", detect_func: is_epk2_file, run_func: extract_epk2 } + Format { name: "epk2", detector_func: is_epk2_file, extractor_func: extract_epk2 } } use std::fs::{self, OpenOptions}; @@ -71,7 +71,7 @@ struct Pak { name: String, } -pub fn is_epk2_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_epk2_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 128, 4)?; if header == b"epak" { Ok(Some(Box::new(()))) @@ -80,7 +80,7 @@ pub fn is_epk2_file(app_ctx: &ProgramContext) -> Result>, Bo } } -pub fn extract_epk2(app_ctx: &ProgramContext, _: Option>) -> Result<(), Box> { +pub fn extract_epk2(app_ctx: &AppContext, _: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?; diff --git a/src/formats/epk2b.rs b/src/formats/epk2b.rs index 59ee06b..e972e82 100644 --- a/src/formats/epk2b.rs +++ b/src/formats/epk2b.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "epk2b", detect_func: is_epk2b_file, run_func: extract_epk2b } + Format { name: "epk2b", detector_func: is_epk2b_file, extractor_func: extract_epk2b } } use std::path::{Path}; @@ -56,7 +56,7 @@ struct Pak { size : u32, } -pub fn is_epk2b_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_epk2b_file(app_ctx: &AppContext) -> Result>, Box> { let epk2_magic = common::read_file(app_ctx.file, 12, 4)?; let epak_magic = common::read_file(app_ctx.file, 0, 4)?; if epak_magic == b"epak" && epk2_magic == b"EPK2" { @@ -66,7 +66,7 @@ pub fn is_epk2b_file(app_ctx: &ProgramContext) -> Result>, B } } -pub fn extract_epk2b(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let header: EpkHeader = file.read_le()?; diff --git a/src/formats/epk3.rs b/src/formats/epk3.rs index 58488a6..4b0e489 100644 --- a/src/formats/epk3.rs +++ b/src/formats/epk3.rs @@ -1,5 +1,8 @@ use std::any::Any; -use crate::{ProgramContext}; +use crate::{AppContext, formats::Format}; +pub fn format() -> Format { + Format { name: "epk3", detector_func: is_epk3_file, extractor_func: extract_epk3 } +} use std::fs::{self, OpenOptions}; use std::path::{Path}; @@ -73,7 +76,11 @@ impl PkgInfoEntry { } } -pub fn extract_epk3(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn is_epk3_file(_app_ctx: &AppContext) -> Result>, Box> { + Ok(None) +} + +pub fn extract_epk3(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; file.seek(SeekFrom::Start(0))?; let stored_header = common::read_exact(&mut file, 1712)?; diff --git a/src/formats/funai_upg.rs b/src/formats/funai_upg.rs index d9a5dac..57d73a2 100644 --- a/src/formats/funai_upg.rs +++ b/src/formats/funai_upg.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "funai_upg", detect_func: is_funai_upg_file, run_func: extract_funai_upg } + Format { name: "funai_upg", detector_func: is_funai_upg_file, extractor_func: extract_funai_upg } } use std::path::Path; @@ -25,7 +25,7 @@ struct Entry { _unk: u16, } -pub fn is_funai_upg_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 6)?; if header == b"UPG\x00\x00\x00" { Ok(Some(Box::new(()))) @@ -34,7 +34,7 @@ pub fn is_funai_upg_file(app_ctx: &ProgramContext) -> Result } } -pub fn extract_funai_upg(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let header: Header = file.read_le()?; println!("File info:\nFile size: {}\nEntry count: {}", header.file_size, header.entry_count); diff --git a/src/formats/invincible_image.rs b/src/formats/invincible_image.rs index 9534962..5e8caea 100644 --- a/src/formats/invincible_image.rs +++ b/src/formats/invincible_image.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "invincible_image", detect_func: is_invincible_image_file, run_func: extract_invincible_image } + Format { name: "invincible_image", detector_func: is_invincible_image_file, extractor_func: extract_invincible_image } } use std::path::{Path}; @@ -62,7 +62,7 @@ impl Entry { } } -pub fn is_invincible_image_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 16)?; if header == b"INVINCIBLE_IMAGE" { Ok(Some(Box::new(()))) @@ -71,7 +71,7 @@ pub fn is_invincible_image_file(app_ctx: &ProgramContext) -> Result>) -> Result<(), Box> { +pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let header: Header = file.read_le()?; diff --git a/src/formats/msd10.rs b/src/formats/msd10.rs index 7c7fefa..59e522c 100644 --- a/src/formats/msd10.rs +++ b/src/formats/msd10.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "msd10", detect_func: is_msd10_file, run_func: extract_msd10 } + Format { name: "msd10", detector_func: is_msd10_file, extractor_func: extract_msd10 } } use std::fs::{self, OpenOptions}; @@ -47,7 +47,7 @@ struct Section { size: u32, } -pub fn is_msd10_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_msd10_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 6)?; if header == b"MSDU10" { Ok(Some(Box::new(()))) @@ -56,7 +56,7 @@ pub fn is_msd10_file(app_ctx: &ProgramContext) -> Result>, B } } -pub fn extract_msd10(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_msd10(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let header: FileHeader = file.read_le()?; println!("\nNumber of sections: {}", header.section_count); diff --git a/src/formats/msd11.rs b/src/formats/msd11.rs index 38b058e..9a1aa6d 100644 --- a/src/formats/msd11.rs +++ b/src/formats/msd11.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "msd11", detect_func: is_msd11_file, run_func: extract_msd11 } + Format { name: "msd11", detector_func: is_msd11_file, extractor_func: extract_msd11 } } use std::fs::{self, OpenOptions}; @@ -48,7 +48,7 @@ struct Section { size: u64, } -pub fn is_msd11_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_msd11_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 6)?; if header == b"MSDU11" { Ok(Some(Box::new(()))) @@ -57,7 +57,7 @@ pub fn is_msd11_file(app_ctx: &ProgramContext) -> Result>, B } } -pub fn extract_msd11(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_msd11(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let header: FileHeader = file.read_le()?; println!("\nNumber of sections: {}", header.section_count); diff --git a/src/formats/mstar.rs b/src/formats/mstar.rs index e02852d..1579979 100644 --- a/src/formats/mstar.rs +++ b/src/formats/mstar.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "mstar", detect_func: is_mstar_file, run_func: extract_mstar } + Format { name: "mstar", detector_func: is_mstar_file, extractor_func: extract_mstar } } use std::fs::{self, OpenOptions}; @@ -13,7 +13,7 @@ use crate::utils::compression::{decompress_lzma, decompress_lz4}; use crate::utils::lzop::{unlzop_to_file}; use crate::utils::sparse::{unsparse_to_file}; -pub fn is_mstar_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_mstar_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 32768)?; let header_string = String::from_utf8_lossy(&header); @@ -32,7 +32,7 @@ fn parse_number(s: &str) -> Option { } } -pub fn extract_mstar(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_mstar(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let file = app_ctx.file; let mut script = common::read_file(&file, 0, 32768)?; diff --git a/src/formats/mtk_bdp.rs b/src/formats/mtk_bdp.rs index 6e782c7..e410dd2 100644 --- a/src/formats/mtk_bdp.rs +++ b/src/formats/mtk_bdp.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "mtk_bdp", detect_func: is_mtk_bdp_file, run_func: extract_mtk_bdp } + Format { name: "mtk_bdp", detector_func: is_mtk_bdp_file, extractor_func: extract_mtk_bdp } } use std::path::{Path}; @@ -77,7 +77,7 @@ fn find_bytes(data: &[u8], pattern: &[u8]) -> Option { data.windows(pattern.len()).position(|window| window == pattern) } -pub fn is_mtk_bdp_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result>, Box> { let mut file = app_ctx.file; let file_size = file.metadata()?.len(); let mut data = Vec::new(); @@ -95,7 +95,7 @@ pub fn is_mtk_bdp_file(app_ctx: &ProgramContext) -> Result>, } } -pub fn extract_mtk_bdp(app_ctx: &ProgramContext, ctx: Option>) -> Result<(), Box> { +pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let ctx = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; let offset = ctx.pitit_offset; diff --git a/src/formats/mtk_pkg.rs b/src/formats/mtk_pkg.rs index abd35ca..262330f 100644 --- a/src/formats/mtk_pkg.rs +++ b/src/formats/mtk_pkg.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "mtk_pkg", detect_func: is_mtk_pkg_file, run_func: extract_mtk_pkg } + Format { name: "mtk_pkg", detector_func: is_mtk_pkg_file, extractor_func: extract_mtk_pkg } } use std::path::Path; @@ -81,7 +81,7 @@ static HEADER_KEY: [u8; 16] = [ static HEADER_IV: [u8; 16] = [0x00; 16]; -pub fn is_mtk_pkg_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result>, Box> { let mut encrypted_header = common::read_file(app_ctx.file, 0, HEADER_SIZE)?; let mut header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?; if &header[4..12] == MTK_HEADER_MAGIC { @@ -99,7 +99,7 @@ pub fn is_mtk_pkg_file(app_ctx: &ProgramContext) -> Result>, } } -pub fn extract_mtk_pkg(app_ctx: &ProgramContext, ctx: Option>) -> Result<(), Box> { +pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let ctx = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; diff --git a/src/formats/mtk_pkg_new.rs b/src/formats/mtk_pkg_new.rs index 0cce9e1..72b3db5 100644 --- a/src/formats/mtk_pkg_new.rs +++ b/src/formats/mtk_pkg_new.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "mtk_pkg_new", detect_func: is_mtk_pkg_new_file, run_func: extract_mtk_pkg_new } + Format { name: "mtk_pkg_new", detector_func: is_mtk_pkg_new_file, extractor_func: extract_mtk_pkg_new } } use std::path::Path; @@ -70,7 +70,7 @@ impl PartEntry { static HEADER_SIZE: usize = 0x170; -pub fn is_mtk_pkg_new_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result>, Box> { let encrypted_header = common::read_file(app_ctx.file, 0, HEADER_SIZE)?; for (key_hex, iv_hex, name) in keys::MTK_PKG_CUST { let key_array: [u8; 16] = hex::decode(key_hex)?.as_slice().try_into()?; @@ -90,7 +90,7 @@ pub fn is_mtk_pkg_new_file(app_ctx: &ProgramContext) -> Result>) -> Result<(), Box> { +pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let ctx = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; let file_size = file.metadata()?.len(); diff --git a/src/formats/mtk_pkg_old.rs b/src/formats/mtk_pkg_old.rs index 9af4b11..ae4faa7 100644 --- a/src/formats/mtk_pkg_old.rs +++ b/src/formats/mtk_pkg_old.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "mtk_pkg_old", detect_func: is_mtk_pkg_old_file, run_func: extract_mtk_pkg_old } + Format { name: "mtk_pkg_old", detector_func: is_mtk_pkg_old_file, extractor_func: extract_mtk_pkg_old } } use std::path::Path; @@ -63,7 +63,7 @@ impl PartEntry { static HEADER_SIZE: usize = 0x98; -pub fn is_mtk_pkg_old_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result>, Box> { let mut file = app_ctx.file; let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?; let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK)); @@ -82,7 +82,7 @@ pub fn is_mtk_pkg_old_file(app_ctx: &ProgramContext) -> Result>) -> Result<(), Box> { +pub fn extract_mtk_pkg_old(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let file_size = file.metadata()?.len(); let encrypted_header = common::read_exact(&mut file, HEADER_SIZE)?; diff --git a/src/formats/novatek.rs b/src/formats/novatek.rs index d1c721c..e9194db 100644 --- a/src/formats/novatek.rs +++ b/src/formats/novatek.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "novatek", detect_func: is_novatek_file, run_func: extract_novatek } + Format { name: "novatek", detector_func: is_novatek_file, extractor_func: extract_novatek } } use std::path::Path; @@ -40,7 +40,7 @@ struct PartEntry { #[br(count = 16)] _md5_checksum: Vec, } -pub fn is_novatek_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_novatek_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"NFWB" { Ok(Some(Box::new(()))) @@ -49,7 +49,7 @@ pub fn is_novatek_file(app_ctx: &ProgramContext) -> Result>, } } -pub fn extract_novatek(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_novatek(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let header: Header = file.read_le()?; diff --git a/src/formats/nvt_timg.rs b/src/formats/nvt_timg.rs index 1eafb5c..2ceb071 100644 --- a/src/formats/nvt_timg.rs +++ b/src/formats/nvt_timg.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "nvt_timg", detect_func: is_nvt_timg_file, run_func: extract_nvt_timg } + Format { name: "nvt_timg", detector_func: is_nvt_timg_file, extractor_func: extract_nvt_timg } } use std::path::{Path}; @@ -49,7 +49,7 @@ impl PIMG { } } -pub fn is_nvt_timg_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"TIMG" { Ok(Some(Box::new(()))) @@ -58,7 +58,7 @@ pub fn is_nvt_timg_file(app_ctx: &ProgramContext) -> Result> } } -pub fn extract_nvt_timg(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let file_size = file.metadata()?.len(); let timg: TIMG = file.read_le()?; diff --git a/src/formats/pana_dvd.rs b/src/formats/pana_dvd.rs index 3f23230..ef73e80 100644 --- a/src/formats/pana_dvd.rs +++ b/src/formats/pana_dvd.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "pana_dvd", detect_func: is_pana_dvd_file, run_func: extract_pana_dvd } + Format { name: "pana_dvd", detector_func: is_pana_dvd_file, extractor_func: extract_pana_dvd } } use std::path::{Path, PathBuf}; @@ -149,7 +149,7 @@ pub fn find_aes_key_pair<'a>(key_array: &'a [(&'a str, &'a str, &'a str)], data: Ok(None) } -pub fn is_pana_dvd_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_pana_dvd_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 64)?; if let Some(matching_key) = find_key(&keys::PANA_DVD_KEYONLY, &header, b"PROG", 0)? { Ok(Some(Box::new(PanaDvdContext { @@ -180,7 +180,7 @@ pub fn is_pana_dvd_file(app_ctx: &ProgramContext) -> Result> } } -pub fn extract_pana_dvd(app_ctx: &ProgramContext, ctx: Option>) -> Result<(), Box> { +pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let context = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; diff --git a/src/formats/pfl_upg.rs b/src/formats/pfl_upg.rs index ad1f85e..def64c2 100644 --- a/src/formats/pfl_upg.rs +++ b/src/formats/pfl_upg.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "pfl_upg", detect_func: is_pfl_upg_file, run_func: extract_pfl_upg } + Format { name: "pfl_upg", detector_func: is_pfl_upg_file, extractor_func: extract_pfl_upg } } use rsa::{RsaPublicKey, BigUint}; @@ -48,7 +48,7 @@ impl FileHeader { } } -pub fn is_pfl_upg_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 8)?; if header == b"2SWU3TXV" { Ok(Some(Box::new(()))) @@ -89,7 +89,7 @@ fn decrypt_aes256_ecb(key: &[u8], ciphertext: &[u8]) -> Result, Box>) -> Result<(), Box> { +pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let header: Header = file.read_le()?; let signature = common::read_exact(&mut file, 128)?; diff --git a/src/formats/pup.rs b/src/formats/pup.rs index 9dd86f1..cdd25e3 100644 --- a/src/formats/pup.rs +++ b/src/formats/pup.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "pup", detect_func: is_pup_file, run_func: extract_pup } + Format { name: "pup", detector_func: is_pup_file, extractor_func: extract_pup } } use std::path::{Path}; @@ -56,7 +56,7 @@ struct BlockEntry { size: u32, } -pub fn is_pup_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_pup_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"\x4F\x15\x3D\x1D" || header == b"\x54\x14\xF5\xEE" { //ps4, ps5 Ok(Some(Box::new(()))) @@ -65,7 +65,7 @@ pub fn is_pup_file(app_ctx: &ProgramContext) -> Result>, Box } } -pub fn extract_pup(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_pup(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let header: Header = file.read_le()?; diff --git a/src/formats/roku.rs b/src/formats/roku.rs index ede6644..2b60ed0 100644 --- a/src/formats/roku.rs +++ b/src/formats/roku.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "roku", detect_func: is_roku_file, run_func: extract_roku } + Format { name: "roku", detector_func: is_roku_file, extractor_func: extract_roku } } use std::fs::{self, OpenOptions}; @@ -61,7 +61,7 @@ impl ImageHeader { } } -pub fn is_roku_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_roku_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 32)?; let try_decrypt_header = decrypt_aes128_cbc_nopad(&header, &FILE_KEY, &FILE_IV)?; @@ -72,7 +72,7 @@ pub fn is_roku_file(app_ctx: &ProgramContext) -> Result>, Bo } } -pub fn extract_roku(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_roku(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let mut encrypted_data = Vec::new(); file.read_to_end(&mut encrypted_data)?; diff --git a/src/formats/ruf.rs b/src/formats/ruf.rs index cb235e5..b76a4b0 100644 --- a/src/formats/ruf.rs +++ b/src/formats/ruf.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "ruf", detect_func: is_ruf_file, run_func: extract_ruf } + Format { name: "ruf", detector_func: is_ruf_file, extractor_func: extract_ruf } } use std::path::{Path}; @@ -75,7 +75,7 @@ impl RufEntry { } } -pub fn is_ruf_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_ruf_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 6)?; if header == b"RUF\x00\x00\x00" { Ok(Some(Box::new(()))) @@ -84,7 +84,7 @@ pub fn is_ruf_file(app_ctx: &ProgramContext) -> Result>, Box } } -pub fn extract_ruf(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_ruf(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let header: RufHeader = file.read_be()?; if header.is_dual_ruf() { diff --git a/src/formats/rvp.rs b/src/formats/rvp.rs index f91abe0..e05e63c 100644 --- a/src/formats/rvp.rs +++ b/src/formats/rvp.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "rvp", detect_func: is_rvp_file, run_func: extract_rvp } + Format { name: "rvp", detector_func: is_rvp_file, extractor_func: extract_rvp } } use std::path::Path; @@ -18,7 +18,7 @@ fn decrypt_xor(data: &[u8]) -> Vec { .collect() } -pub fn is_rvp_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_rvp_file(app_ctx: &AppContext) -> Result>, Box> { let mut file = app_ctx.file; //MVP let header = common::read_file(file, 0, 4)?; @@ -39,7 +39,7 @@ pub fn is_rvp_file(app_ctx: &ProgramContext) -> Result>, Box Ok(Some(Box::new(()))) } -pub fn extract_rvp(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_rvp(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let mut obf_data = Vec::new(); //we sadly cannot deXOR on the fly because of its 32 byte pattern file.read_to_end(&mut obf_data)?; diff --git a/src/formats/sddl_sec.rs b/src/formats/sddl_sec.rs index 180c58b..4a8719f 100644 --- a/src/formats/sddl_sec.rs +++ b/src/formats/sddl_sec.rs @@ -1,8 +1,8 @@ //sddl_dec 5.0 use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "sddl_sec", detect_func: is_sddl_sec_file, run_func: extract_sddl_sec } + Format { name: "sddl_sec", detector_func: is_sddl_sec_file, extractor_func: extract_sddl_sec } } use std::path::{Path, PathBuf}; @@ -103,7 +103,7 @@ static DEC_IV: [u8; 16] = [ 0xCD, 0x88, 0x38, 0xC4, 0xB9, 0x0C, 0x76, 0x66, ]; -pub fn is_sddl_sec_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_sddl_sec_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 32)?; let deciph_header = decipher(&header); if deciph_header.starts_with(b"\x11\x22\x33\x44") { @@ -155,7 +155,7 @@ fn decipher(s: &[u8]) -> Vec { out } -pub fn extract_sddl_sec(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let mut hdr_reader = Cursor::new(decipher(&common::read_exact(&mut file, 32)?)); let hdr: SddlSecHeader = hdr_reader.read_be()?; diff --git a/src/formats/slp.rs b/src/formats/slp.rs index de1aff5..f5940ae 100644 --- a/src/formats/slp.rs +++ b/src/formats/slp.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "slp", detect_func: is_slp_file, run_func: extract_slp } + Format { name: "slp", detector_func: is_slp_file, extractor_func: extract_slp } } use std::path::{Path}; @@ -52,7 +52,7 @@ struct EntryNew { #[br(count = 12)] _unk2: Vec, } -pub fn is_slp_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_slp_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"SLP\x00" { Ok(Some(Box::new(()))) @@ -61,7 +61,7 @@ pub fn is_slp_file(app_ctx: &ProgramContext) -> Result>, Box } } -pub fn extract_slp(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_slp(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let header: Header = file.read_le()?; diff --git a/src/formats/sony_bdp.rs b/src/formats/sony_bdp.rs index 22fcbde..61ce0aa 100644 --- a/src/formats/sony_bdp.rs +++ b/src/formats/sony_bdp.rs @@ -1,7 +1,7 @@ use std::any::Any; -use crate::{ProgramContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { - Format { name: "sony_bdp", detect_func: is_sony_bdp_file, run_func: extract_sony_bdp } + Format { name: "sony_bdp", detector_func: is_sony_bdp_file, extractor_func: extract_sony_bdp } } use std::fs::File; @@ -63,7 +63,7 @@ struct Entry { size: u32, } -pub fn is_sony_bdp_file(app_ctx: &ProgramContext) -> Result>, Box> { +pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result>, Box> { let header = common::read_file(app_ctx.file, 0, 4)?; if header == b"\x01\x73\xEC\xC9" || header == b"\x01\x73\xEC\x1F" || header == b"\xEC\x7D\xB0\xB0" { //MSB1x, MSB0x, BDPPxx Ok(Some(Box::new(()))) @@ -72,7 +72,7 @@ pub fn is_sony_bdp_file(app_ctx: &ProgramContext) -> Result> } } -pub fn extract_sony_bdp(app_ctx: &ProgramContext, _ctx: Option>) -> Result<(), Box> { +pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { let mut file = app_ctx.file; let obf_header = common::read_exact(&mut file, 300)?; let header = hex_substitute(&obf_header); @@ -120,7 +120,7 @@ pub fn extract_sony_bdp(app_ctx: &ProgramContext, _ctx: Option>) -> let last_file = File::open(last_file_path.unwrap())?; let mtk_extraction_path = format!("{}/{}", app_ctx.output_dir, i - 1); - let ctx: ProgramContext = ProgramContext { file: &last_file, output_dir: &mtk_extraction_path }; + let ctx: AppContext = AppContext { file: &last_file, output_dir: &mtk_extraction_path }; if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&ctx)? { println!("- MTK BDP file detected!\n"); diff --git a/src/keys.rs b/src/keys.rs index af7ab8a..5b5e38f 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -2,16 +2,16 @@ //samsung old keys //github.com/george-hopkins/samygo-patcher -pub static SAMSUNG: &[(&str, &str)] = &[ - ("T-GA", "SHWJUH:85a045ae-2296-484c-b457-ede832fcfbe1-646390a3-105e-40aa-85f6-da3086c70111"), - ("T-MST5", "SHWJUH:eceb2c14-db11-425e-9ebf-5f9607f0eb4b-3c38193e-751e-4719-8884-9e76322c0cec"), - ("T-MST10P","b4c136-fbc93576-b3e8-4035-bf4e-ba4cb4ada1ac-f0d81cc4-8301-4832-bd60-f331295743ba"), - ("T-VAL", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00001abc2010"), - ("T-TDT", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00002abc2010"), - ("T-MSX", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00004abc2010"), - ("T-CH", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-611c4f8d4a71"), - ("T-ECP", "3EF6067262CF0C678598BFF22169D1F1EA57C284"), -]; +//pub static SAMSUNG: &[(&str, &str)] = &[ +// ("T-GA", "SHWJUH:85a045ae-2296-484c-b457-ede832fcfbe1-646390a3-105e-40aa-85f6-da3086c70111"), +// ("T-MST5", "SHWJUH:eceb2c14-db11-425e-9ebf-5f9607f0eb4b-3c38193e-751e-4719-8884-9e76322c0cec"), +// ("T-MST10P","b4c136-fbc93576-b3e8-4035-bf4e-ba4cb4ada1ac-f0d81cc4-8301-4832-bd60-f331295743ba"), +// ("T-VAL", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00001abc2010"), +// ("T-TDT", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00002abc2010"), +// ("T-MSX", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00004abc2010"), +// ("T-CH", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-611c4f8d4a71"), +// ("T-ECP", "3EF6067262CF0C678598BFF22169D1F1EA57C284"), +//]; //MSD10 keys //fw prefix, type, key diff --git a/src/main.rs b/src/main.rs index b7cb468..a0a6771 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ struct Args { output_folder: Option, } -pub struct ProgramContext<'a> { +pub struct AppContext<'a> { pub file: &'a std::fs::File, pub output_dir: &'a str, } @@ -47,13 +47,13 @@ fn main() -> Result<(), Box> { } let file = File::open(path)?; - let program_context: ProgramContext = ProgramContext { file: &file, output_dir: &output_path }; + let app_ctx: AppContext = AppContext { file: &file, output_dir: &output_path }; let formats: Vec = get_registry(); for format in formats { - if let Some(ctx) = (format.detect_func)(&program_context)? { + if let Some(ctx) = (format.detector_func)(&app_ctx)? { println!("{} detected!", format.name); - (format.run_func)(&program_context, Some(ctx))?; + (format.extractor_func)(&app_ctx, Some(ctx))?; return Ok(()); } } From 43128ca117859a77a02781460b2b9c8179b7dae8 Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Thu, 5 Feb 2026 19:38:50 +0100 Subject: [PATCH 3/9] cleanup main --- src/main.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main.rs b/src/main.rs index a0a6771..6da30b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use crate::formats::{Format, get_registry}; #[derive(Parser, Debug)] struct Args { input_target: String, - output_folder: Option, + output_directory: Option, } pub struct AppContext<'a> { @@ -23,21 +23,21 @@ fn main() -> Result<(), Box> { println!("unixtract Firmware extractor"); let args = Args::parse(); - let target_path = args.input_target; - println!("Input target: {}", target_path); - let path = PathBuf::from(target_path); - - let output_path = if args.output_folder.is_some() { - args.output_folder.unwrap() + let target_path_str = args.input_target; + println!("Input target: {}", target_path_str); + let target_path = PathBuf::from(&target_path_str); + + let output_path_str = if args.output_directory.is_some() { + args.output_directory.unwrap() } else { - format!("_{}", path.file_name().and_then(|s| s.to_str()).unwrap()) + format!("_{}", target_path.file_name().and_then(|s| s.to_str()).unwrap()) }; - println!("Output folder: {}\n", output_path); + println!("Output directory: {}\n", output_path_str); + let output_directory_path = PathBuf::from(&output_path_str); - let output_folder_path = PathBuf::from(&output_path); - if output_folder_path.exists() { - if output_folder_path.is_dir() { - let is_empty = fs::read_dir(&output_folder_path)?.next().is_none(); + if output_directory_path.exists() { + if output_directory_path.is_dir() { + let is_empty = fs::read_dir(&output_directory_path)?.next().is_none(); if !is_empty { println!("Warning: Output folder already exists and is NOT empty! Files may be overwritten!"); println!("Press Enter if you want to continue..."); @@ -46,10 +46,10 @@ fn main() -> Result<(), Box> { } } - let file = File::open(path)?; - let app_ctx: AppContext = AppContext { file: &file, output_dir: &output_path }; - let formats: Vec = get_registry(); + let file = File::open(target_path)?; + let app_ctx: AppContext = AppContext { file: &file, output_dir: &output_path_str }; + let formats: Vec = get_registry(); for format in formats { if let Some(ctx) = (format.detector_func)(&app_ctx)? { println!("{} detected!", format.name); From 9b059c67ab8d9d65f458b6bc2496f8f193e50ea4 Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Fri, 6 Feb 2026 15:08:38 +0100 Subject: [PATCH 4/9] bring back samsung_old - by adding InputTarget system --- src/formats.rs | 3 ++- src/formats/amlogic.rs | 13 ++++++----- src/formats/android_ota_payload.rs | 13 ++++++----- src/formats/bdl.rs | 11 ++++++---- src/formats/epk.rs | 8 ++++--- src/formats/epk1.rs | 14 +++++++----- src/formats/epk2.rs | 14 +++++++----- src/formats/epk2b.rs | 16 ++++++++------ src/formats/epk3.rs | 8 +++---- src/formats/funai_upg.rs | 12 +++++----- src/formats/invincible_image.rs | 20 ++++++++--------- src/formats/msd10.rs | 17 +++++++++------ src/formats/msd11.rs | 13 ++++++----- src/formats/mstar.rs | 13 ++++++----- src/formats/mtk_bdp.rs | 11 +++++----- src/formats/mtk_pkg.rs | 16 ++++++++------ src/formats/mtk_pkg_new.rs | 15 ++++++++----- src/formats/mtk_pkg_old.rs | 14 +++++++----- src/formats/novatek.rs | 20 ++++++++--------- src/formats/nvt_timg.rs | 19 ++++++++-------- src/formats/pana_dvd.rs | 13 ++++++----- src/formats/pfl_upg.rs | 20 ++++++++--------- src/formats/pup.rs | 30 ++++++++++--------------- src/formats/roku.rs | 15 ++++++++----- src/formats/ruf.rs | 24 ++++++++++---------- src/formats/rvp.rs | 15 +++++++------ src/formats/samsung_old.rs | 25 ++++++++++++++------- src/formats/sddl_sec.rs | 17 +++++++++------ src/formats/slp.rs | 14 +++++++----- src/formats/sony_bdp.rs | 17 +++++++++------ src/keys.rs | 20 ++++++++--------- src/main.rs | 35 ++++++++++++++++++++++++------ 32 files changed, 291 insertions(+), 224 deletions(-) diff --git a/src/formats.rs b/src/formats.rs index 1228759..38b9035 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -8,7 +8,7 @@ pub struct Format { } pub mod mstar; -//pub mod samsung_old; not sure what to do with this +pub mod samsung_old; pub mod nvt_timg; pub mod pfl_upg; pub mod sddl_sec; @@ -45,6 +45,7 @@ pub mod mtk_bdp; pub fn get_registry() -> Vec { return vec![ crate::formats::mstar::format(), + crate::formats::samsung_old::format(), crate::formats::nvt_timg::format(), crate::formats::pfl_upg::format(), crate::formats::sddl_sec::format(), diff --git a/src/formats/amlogic.rs b/src/formats/amlogic.rs index cd9b6c0..2e8521c 100644 --- a/src/formats/amlogic.rs +++ b/src/formats/amlogic.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "amlogic", detector_func: is_amlogic_file, extractor_func: extract_amlogic } } @@ -50,7 +50,9 @@ impl ItemEntry { } pub fn is_amlogic_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 8, 4)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 8, 4)?; if header == b"\x56\x19\xB5\x27" { Ok(Some(Box::new(()))) } else { @@ -59,7 +61,8 @@ pub fn is_amlogic_file(app_ctx: &AppContext) -> Result>, Box } pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + file.seek(SeekFrom::Start(0))?; let header: ImageHeader = file.read_le()?; @@ -91,8 +94,8 @@ pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Option>) -> Resu let data = common::read_file(&file, item.offset_in_image, item.item_size as usize)?; let extension = if item.item_type() == "PARTITION" {"img"} else {&item.item_type()}; - let output_path = Path::new(app_ctx.output_dir).join(format!("{}.{}", item.name(), extension)); - fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.{}", item.name(), extension)); + fs::create_dir_all(&app_ctx.output_dir)?; if item.is_sparse() { println!("- Unsparsing..."); diff --git a/src/formats/android_ota_payload.rs b/src/formats/android_ota_payload.rs index 3e670cf..be173fd 100644 --- a/src/formats/android_ota_payload.rs +++ b/src/formats/android_ota_payload.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "android_ota_payload", detector_func: is_android_ota_payload_file, extractor_func: extract_android_ota_payload } } @@ -23,7 +23,9 @@ struct Header { } pub fn is_android_ota_payload_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 4)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 4)?; if header == b"CrAU" { Ok(Some(Box::new(()))) } else { @@ -32,7 +34,8 @@ pub fn is_android_ota_payload_file(app_ctx: &AppContext) -> Result>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let header: Header = file.read_be()?; println!("File info:\nFormat version: {}\nManifest size: {}", header.file_format_version, header.manifest_size); @@ -82,8 +85,8 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Option Format { Format { name: "bdl", detector_func: is_bdl_file, extractor_func: extract_bdl } } @@ -86,7 +86,9 @@ impl PkgEntry { } pub fn is_bdl_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 4)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 4)?; if header == b"ibdl" { Ok(Some(Box::new(()))) } else { @@ -95,7 +97,8 @@ pub fn is_bdl_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let header: BdlHeader = file.read_le()?; println!("File info:\nPackage count: {}\nDate: {}\nManufacturer: {}\nModel: {}\nVersion: {}\nInfo: {}", @@ -122,7 +125,7 @@ pub fn extract_bdl(app_ctx: &AppContext, _ctx: Option>) -> Result<( pkg_entries.push(pkg_entry); } - let pkg_folder = Path::new(app_ctx.output_dir).join(pkg_header.name()); + let pkg_folder = Path::new(&app_ctx.output_dir).join(pkg_header.name()); fs::create_dir_all(&pkg_folder)?; for (i, pkg_entry) in pkg_entries.iter().enumerate() { diff --git a/src/formats/epk.rs b/src/formats/epk.rs index 5ca3dfd..994cd8b 100644 --- a/src/formats/epk.rs +++ b/src/formats/epk.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "epk", detector_func: is_epk_file, extractor_func: extract_epk } } @@ -12,8 +12,9 @@ pub struct EpkContext { } pub fn is_epk_file(app_ctx: &AppContext) -> Result>, Box> { - let versions = common::read_file(app_ctx.file, 1712, 36)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let versions = common::read_file(&file, 1712, 36)?; if let Some(epk_version) = check_epk_version(&versions) { Ok(Some(Box::new(EpkContext {epk_version}))) } else { @@ -51,9 +52,10 @@ fn match_with_pattern(data: &[u8], pattern: &str) -> bool { } pub fn extract_epk(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; let ctx = ctx.and_then(|c| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; - let versions = common::read_file(app_ctx.file, 1712, 36)?; + 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]); diff --git a/src/formats/epk1.rs b/src/formats/epk1.rs index 74f2240..afc477a 100644 --- a/src/formats/epk1.rs +++ b/src/formats/epk1.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "epk1", detector_func: is_epk1_file, extractor_func: extract_epk1 } } @@ -41,8 +41,10 @@ struct Pak { } pub fn is_epk1_file(app_ctx: &AppContext) -> Result>, Box> { - let epk2_magic = common::read_file(app_ctx.file, 12, 4)?; //for epk2b - let epak_magic = common::read_file(app_ctx.file, 0, 4)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let epk2_magic = common::read_file(&file, 12, 4)?; //for epk2b + let epak_magic = common::read_file(&file, 0, 4)?; if epak_magic == b"epak" && epk2_magic != b"EPK2" { Ok(Some(Box::new(()))) } else { @@ -51,7 +53,7 @@ pub fn is_epk1_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; //check type of epk1 let epk1_type; let init_pak_count_bytes = common::read_file(&file, 8, 4)?; @@ -125,8 +127,8 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Option>) -> Result< let data = common::read_exact(&mut file, pak_header.image_size as usize)?; - let output_path = Path::new(app_ctx.output_dir).join(pak_header.pak_name() + ".bin"); - fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(pak_header.pak_name() + ".bin"); + 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(&data)?; diff --git a/src/formats/epk2.rs b/src/formats/epk2.rs index 7a05c27..a3ed2c2 100644 --- a/src/formats/epk2.rs +++ b/src/formats/epk2.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "epk2", detector_func: is_epk2_file, extractor_func: extract_epk2 } } @@ -72,7 +72,9 @@ struct Pak { } pub fn is_epk2_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 128, 4)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 128, 4)?; if header == b"epak" { Ok(Some(Box::new(()))) } else { @@ -81,9 +83,9 @@ pub fn is_epk2_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; - let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?; let stored_header = common::read_exact(&mut file, 1584)?; //max header size let header; @@ -191,8 +193,8 @@ pub fn extract_epk2(app_ctx: &AppContext, _: Option>) -> Result<(), 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)?; - let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", pak.name)); - fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", pak.name)); + 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)?; diff --git a/src/formats/epk2b.rs b/src/formats/epk2b.rs index e972e82..110517c 100644 --- a/src/formats/epk2b.rs +++ b/src/formats/epk2b.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "epk2b", detector_func: is_epk2b_file, extractor_func: extract_epk2b } } @@ -57,8 +57,10 @@ struct Pak { } pub fn is_epk2b_file(app_ctx: &AppContext) -> Result>, Box> { - let epk2_magic = common::read_file(app_ctx.file, 12, 4)?; - let epak_magic = common::read_file(app_ctx.file, 0, 4)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let epk2_magic = common::read_file(&file, 12, 4)?; + let epak_magic = common::read_file(&file, 0, 4)?; if epak_magic == b"epak" && epk2_magic == b"EPK2" { Ok(Some(Box::new(()))) } else { @@ -67,9 +69,9 @@ pub fn is_epk2b_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; - let header: EpkHeader = file.read_le()?; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let header: EpkHeader = file.read_le()?; println!("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]); @@ -114,8 +116,8 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Option>) -> Result pak_header.segment_size }; - let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", pak_header.pak_name())); - fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", pak_header.pak_name())); + 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[..segment_limit as usize])?; diff --git a/src/formats/epk3.rs b/src/formats/epk3.rs index 4b0e489..b48fdd1 100644 --- a/src/formats/epk3.rs +++ b/src/formats/epk3.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "epk3", detector_func: is_epk3_file, extractor_func: extract_epk3 } } @@ -81,7 +81,7 @@ pub fn is_epk3_file(_app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; file.seek(SeekFrom::Start(0))?; let stored_header = common::read_exact(&mut file, 1712)?; let header: Vec; @@ -164,8 +164,8 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Option>) -> Result< let encrypted_data = common::read_exact(&mut file, entry.segment_size as usize + extra_segment_size)?; let out_data = decrypt_aes_ecb_auto(matching_key_bytes, &encrypted_data)?; - let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", entry.package_name())); - fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", entry.package_name())); + 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[extra_segment_size..])?; diff --git a/src/formats/funai_upg.rs b/src/formats/funai_upg.rs index 57d73a2..e398843 100644 --- a/src/formats/funai_upg.rs +++ b/src/formats/funai_upg.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "funai_upg", detector_func: is_funai_upg_file, extractor_func: extract_funai_upg } } @@ -26,7 +26,8 @@ struct Entry { } pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 6)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let header = common::read_file(&file, 0, 6)?; if header == b"UPG\x00\x00\x00" { Ok(Some(Box::new(()))) } else { @@ -35,7 +36,8 @@ pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result>, B } pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let header: Header = file.read_le()?; println!("File info:\nFile size: {}\nEntry count: {}", header.file_size, header.entry_count); @@ -51,9 +53,9 @@ pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Option>) -> Re println!("Descriptor entry info:\n{}", entry_string); } - let output_path = Path::new(app_ctx.output_dir).join(format!("{}_{}.bin", i + 1, entry.entry_type)); + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}_{}.bin", i + 1, entry.entry_type)); - fs::create_dir_all(app_ctx.output_dir)?; + 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(&data)?; diff --git a/src/formats/invincible_image.rs b/src/formats/invincible_image.rs index 5e8caea..5d21dcd 100644 --- a/src/formats/invincible_image.rs +++ b/src/formats/invincible_image.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "invincible_image", detector_func: is_invincible_image_file, extractor_func: extract_invincible_image } } @@ -63,7 +63,9 @@ impl Entry { } pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 16)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 16)?; if header == b"INVINCIBLE_IMAGE" { Ok(Some(Box::new(()))) } else { @@ -72,9 +74,9 @@ pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result>) -> Result<(), Box> { - let mut file = app_ctx.file; - let header: Header = file.read_le()?; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let header: Header = file.read_le()?; println!("File info:\nFile Version: {}.{}\nVersion(1): {}\nVersion(2): {}\nVersion(3): {}\nVersion(4): {}\nData size: {}\nData start offset: {}\nKeep data size: {}\nSkip data size: {}\n\nPayload Count: {}", header.file_version[0], header.file_version[1], header.ver1(), header.ver2(), header.ver3(), header.ver4(), header.data_size, header.data_start_offset, header.keep_size, header.skip_size, header.payload_count); @@ -120,14 +122,10 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Option> println!("\n({}/{}) - {}, Size: {}", i, header.payload_count, entry.name(), entry.size); let data = common::read_exact(&mut data_reader, entry.size as usize)?; - let output_path = Path::new(app_ctx.output_dir).join(entry.name() + ".bin"); + let output_path = Path::new(&app_ctx.output_dir).join(entry.name() + ".bin"); - fs::create_dir_all(app_ctx.output_dir)?; - let mut out_file = OpenOptions::new() - .write(true) - .create(true) - .open(output_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(&data)?; println!("- Saved file!"); diff --git a/src/formats/msd10.rs b/src/formats/msd10.rs index 59e522c..7bc2e96 100644 --- a/src/formats/msd10.rs +++ b/src/formats/msd10.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "msd10", detector_func: is_msd10_file, extractor_func: extract_msd10 } } @@ -48,7 +48,9 @@ struct Section { } pub fn is_msd10_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 6)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 6)?; if header == b"MSDU10" { Ok(Some(Box::new(()))) } else { @@ -57,7 +59,8 @@ pub fn is_msd10_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let header: FileHeader = file.read_le()?; println!("\nNumber of sections: {}", header.section_count); @@ -138,8 +141,8 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Option>) -> Result out_data = stored_data; } - let output_path = Path::new(app_ctx.output_dir).join(item.name.clone()); - fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(item.name.clone()); + 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(&out_data)?; @@ -183,8 +186,8 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Option>) -> Result out_data = stored_data; } - let output_path = Path::new(app_ctx.output_dir).join(item.name.clone()); - fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(item.name.clone()); + 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(&out_data)?; diff --git a/src/formats/msd11.rs b/src/formats/msd11.rs index 9a1aa6d..1159d86 100644 --- a/src/formats/msd11.rs +++ b/src/formats/msd11.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "msd11", detector_func: is_msd11_file, extractor_func: extract_msd11 } } @@ -49,7 +49,9 @@ struct Section { } pub fn is_msd11_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 6)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 6)?; if header == b"MSDU11" { Ok(Some(Box::new(()))) } else { @@ -58,7 +60,8 @@ pub fn is_msd11_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let header: FileHeader = file.read_le()?; println!("\nNumber of sections: {}", header.section_count); @@ -133,8 +136,8 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Option>) -> Result out_data = stored_data; } - let output_path = Path::new(app_ctx.output_dir).join(item.name.clone()); - fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(item.name.clone()); + 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(&out_data)?; diff --git a/src/formats/mstar.rs b/src/formats/mstar.rs index 1579979..09b6fd6 100644 --- a/src/formats/mstar.rs +++ b/src/formats/mstar.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "mstar", detector_func: is_mstar_file, extractor_func: extract_mstar } } @@ -14,9 +14,10 @@ use crate::utils::lzop::{unlzop_to_file}; use crate::utils::sparse::{unsparse_to_file}; pub fn is_mstar_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 32768)?; - let header_string = String::from_utf8_lossy(&header); + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let header = common::read_file(&file, 0, 32768)?; + let header_string = String::from_utf8_lossy(&header); if header_string.contains("filepartload"){ Ok(Some(Box::new(()))) } else { @@ -33,7 +34,7 @@ fn parse_number(s: &str) -> Option { } pub fn extract_mstar(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let file = app_ctx.file; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; let mut script = common::read_file(&file, 0, 32768)?; @@ -137,7 +138,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Option>) -> Result } else { let data = common::read_file(&file, offset, size.try_into().unwrap())?; let out_data; - let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", partname)); + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", partname)); if compression == "lzma" { println!("- Decompressing LZMA..."); @@ -166,7 +167,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Option>) -> Result out_data = data; } - fs::create_dir_all(app_ctx.output_dir)?; + 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!"); diff --git a/src/formats/mtk_bdp.rs b/src/formats/mtk_bdp.rs index e410dd2..6b66f1c 100644 --- a/src/formats/mtk_bdp.rs +++ b/src/formats/mtk_bdp.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "mtk_bdp", detector_func: is_mtk_bdp_file, extractor_func: extract_mtk_bdp } } @@ -78,7 +78,7 @@ fn find_bytes(data: &[u8], pattern: &[u8]) -> Option { } pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result>, Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; let file_size = file.metadata()?.len(); let mut data = Vec::new(); @@ -96,8 +96,9 @@ pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result>, Box } pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; let ctx = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; + let offset = ctx.pitit_offset; println!("\nReading PITIT at: {}", offset); @@ -181,8 +182,8 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Option>) -> Resul let data = common::read_file(&file, bit_entry.offset as u64, bit_entry.size as usize)?; - let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", name)); - fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", name)); + fs::create_dir_all(&app_ctx.output_dir)?; let mut out_file = OpenOptions::new().read(true).write(true).create(true).open(output_path)?; out_file.seek(SeekFrom::Start(bit_entry.offset_in_target_part as u64))?; out_file.write_all(&data)?; diff --git a/src/formats/mtk_pkg.rs b/src/formats/mtk_pkg.rs index 262330f..087934d 100644 --- a/src/formats/mtk_pkg.rs +++ b/src/formats/mtk_pkg.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "mtk_pkg", detector_func: is_mtk_pkg_file, extractor_func: extract_mtk_pkg } } @@ -82,13 +82,15 @@ static HEADER_KEY: [u8; 16] = [ static HEADER_IV: [u8; 16] = [0x00; 16]; pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result>, Box> { - let mut encrypted_header = common::read_file(app_ctx.file, 0, HEADER_SIZE)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let mut encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?; let mut header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?; if &header[4..12] == MTK_HEADER_MAGIC { Ok(Some(Box::new(MtkPkgContext { is_philips_variant: false, decrypted_header: header}))) } else { // try for philips which has additional 128 bytes at beginning - encrypted_header = common::read_file(app_ctx.file, PHILIPS_EXTRA_HEADER_SIZE as u64, HEADER_SIZE)?; + encrypted_header = common::read_file(&file, PHILIPS_EXTRA_HEADER_SIZE as u64, HEADER_SIZE)?; header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?; if &header[4..12] == MTK_HEADER_MAGIC { Ok(Some(Box::new(MtkPkgContext { is_philips_variant: true, decrypted_header: header }))) @@ -100,7 +102,7 @@ pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result>, Box } pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; let ctx = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; let file_size = file.metadata()?.len(); @@ -198,13 +200,13 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Option>) -> Resul }; //for compressed part create temp file - let output_path = Path::new(app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); - fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); + fs::create_dir_all(&app_ctx.output_dir)?; let mut out_file = OpenOptions::new().write(true).read(true)/* for lzhs */.create(true).open(&output_path)?; out_file.write_all(&out_data[CRYPTED_HEADER_SIZE + extra_header_len as usize..])?; if part_entry.is_compressed() { - let lzhs_out_path = Path::new(app_ctx.output_dir).join(part_entry.name() + ".bin"); + let lzhs_out_path = Path::new(&app_ctx.output_dir).join(part_entry.name() + ".bin"); match decompress_lzhs_fs_file2file(&out_file, lzhs_out_path) { Ok(()) => { println!("- Decompressed Successfully!"); diff --git a/src/formats/mtk_pkg_new.rs b/src/formats/mtk_pkg_new.rs index 72b3db5..2fdb74b 100644 --- a/src/formats/mtk_pkg_new.rs +++ b/src/formats/mtk_pkg_new.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "mtk_pkg_new", detector_func: is_mtk_pkg_new_file, extractor_func: extract_mtk_pkg_new } } @@ -71,7 +71,9 @@ impl PartEntry { static HEADER_SIZE: usize = 0x170; pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result>, Box> { - let encrypted_header = common::read_file(app_ctx.file, 0, HEADER_SIZE)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?; for (key_hex, iv_hex, name) in keys::MTK_PKG_CUST { let key_array: [u8; 16] = hex::decode(key_hex)?.as_slice().try_into()?; let iv_array: [u8; 16] = hex::decode(iv_hex)?.as_slice().try_into()?; @@ -91,8 +93,9 @@ pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result>, } pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; let ctx = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; + let file_size = file.metadata()?.len(); //the key was founf, and header was decrypted at detection stage so we can reuse @@ -155,13 +158,13 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Option>) -> R }; //for compressed part create temp file - let output_path = Path::new(app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); - fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); + fs::create_dir_all(&app_ctx.output_dir)?; let mut out_file = OpenOptions::new().write(true).read(true)/* for lzhs */.create(true).open(&output_path)?; out_file.write_all(&out_data[48 + extra_header_len as usize..])?; if part_entry.is_compressed() { - let lzhs_out_path = Path::new(app_ctx.output_dir).join(part_entry.name() + ".bin"); + let lzhs_out_path = Path::new(&app_ctx.output_dir).join(part_entry.name() + ".bin"); match decompress_lzhs_fs_file2file(&out_file, lzhs_out_path) { Ok(()) => { println!("-- Decompressed Successfully!"); diff --git a/src/formats/mtk_pkg_old.rs b/src/formats/mtk_pkg_old.rs index ae4faa7..f70c32b 100644 --- a/src/formats/mtk_pkg_old.rs +++ b/src/formats/mtk_pkg_old.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "mtk_pkg_old", detector_func: is_mtk_pkg_old_file, extractor_func: extract_mtk_pkg_old } } @@ -64,7 +64,8 @@ impl PartEntry { static HEADER_SIZE: usize = 0x98; pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result>, Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?; let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK)); if &header[4..12] == MTK_HEADER_MAGIC { @@ -83,7 +84,8 @@ pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result>, } pub fn extract_mtk_pkg_old(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let file_size = file.metadata()?.len(); let encrypted_header = common::read_exact(&mut file, HEADER_SIZE)?; let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK)); @@ -126,13 +128,13 @@ pub fn extract_mtk_pkg_old(app_ctx: &AppContext, _ctx: Option>) -> }; //for compressed part create temp file - let output_path = Path::new(app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); - fs::create_dir_all(app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); + fs::create_dir_all(&app_ctx.output_dir)?; let mut out_file = OpenOptions::new().write(true).read(true)/* for lzhs */.create(true).open(&output_path)?; out_file.write_all(&out_data[extra_header_len as usize..])?; if part_entry.is_compressed() { - let lzhs_out_path = Path::new(app_ctx.output_dir).join(part_entry.name() + ".bin"); + let lzhs_out_path = Path::new(&app_ctx.output_dir).join(part_entry.name() + ".bin"); match decompress_lzhs_fs_file2file_old(&out_file, lzhs_out_path) { Ok(()) => { println!("- Decompressed Successfully!"); diff --git a/src/formats/novatek.rs b/src/formats/novatek.rs index e9194db..b8a22d3 100644 --- a/src/formats/novatek.rs +++ b/src/formats/novatek.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "novatek", detector_func: is_novatek_file, extractor_func: extract_novatek } } @@ -41,7 +41,9 @@ struct PartEntry { } pub fn is_novatek_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 4)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 4)?; if header == b"NFWB" { Ok(Some(Box::new(()))) } else { @@ -50,9 +52,9 @@ pub fn is_novatek_file(app_ctx: &AppContext) -> Result>, Box } pub fn extract_novatek(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = app_ctx.file; - let header: Header = file.read_le()?; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let header: Header = file.read_le()?; println!("File info:\nFirmware name: {}\nVersion: {}.{}\nData size: {}\nPart count: {}", header.firmware_name(), header.version_major, header.version_minor, header.data_size, header.part_count); @@ -69,14 +71,10 @@ pub fn extract_novatek(app_ctx: &AppContext, _ctx: Option>) -> Resu let data = common::read_file(&file, entry.offset as u64, entry.size as usize)?; - let output_path = Path::new(app_ctx.output_dir).join(format!("{}_{}.bin", e_i, entry.id)); + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}_{}.bin", e_i, entry.id)); - fs::create_dir_all(app_ctx.output_dir)?; - let mut out_file = OpenOptions::new() - .write(true) - .create(true) - .open(output_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(&data)?; println!("- Saved file!"); diff --git a/src/formats/nvt_timg.rs b/src/formats/nvt_timg.rs index 2ceb071..afcb85c 100644 --- a/src/formats/nvt_timg.rs +++ b/src/formats/nvt_timg.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "nvt_timg", detector_func: is_nvt_timg_file, extractor_func: extract_nvt_timg } } @@ -50,7 +50,9 @@ impl PIMG { } pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 4)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 4)?; if header == b"TIMG" { Ok(Some(Box::new(()))) } else { @@ -59,7 +61,8 @@ pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result>, Bo } pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let file_size = file.metadata()?.len(); let timg: TIMG = file.read_le()?; println!("File info:\nData size: {}", timg.data_size); @@ -78,7 +81,7 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Option>) -> Res println!("\n#{} - {}, Size: {}, Dest: {}, Compression: {}", pimg_i, pimg.name(), pimg.size, pimg.dest_dev(), pimg.comp_type()); let out_data; - let output_path = Path::new(app_ctx.output_dir).join(pimg.name() + ".bin"); + 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..."); @@ -95,12 +98,8 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Option>) -> Res out_data = data; } - fs::create_dir_all(app_ctx.output_dir)?; - let mut out_file = OpenOptions::new() - .write(true) - .create(true) - .open(output_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(&out_data)?; println!("-- Saved file!"); diff --git a/src/formats/pana_dvd.rs b/src/formats/pana_dvd.rs index ef73e80..76769d2 100644 --- a/src/formats/pana_dvd.rs +++ b/src/formats/pana_dvd.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "pana_dvd", detector_func: is_pana_dvd_file, extractor_func: extract_pana_dvd } } @@ -150,7 +150,8 @@ pub fn find_aes_key_pair<'a>(key_array: &'a [(&'a str, &'a str, &'a str)], data: } pub fn is_pana_dvd_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 64)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let header = common::read_file(&file, 0, 64)?; if let Some(matching_key) = find_key(&keys::PANA_DVD_KEYONLY, &header, b"PROG", 0)? { Ok(Some(Box::new(PanaDvdContext { matching_key: matching_key, @@ -181,7 +182,7 @@ pub fn is_pana_dvd_file(app_ctx: &AppContext) -> Result>, Bo } pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; let context = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; let mut data = Vec::new(); // we need to load the entire file into memory so we can swap it with AES decrypted data if its AES encrypted @@ -217,12 +218,12 @@ pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Option>) -> Resu if file_entries.len() == 1 { //only one file, standard extraction println!("File contains no extra sub-files...\n"); - extract_file(&mut file_reader, file_entries[0].offset as u64, file_entries[0].base_offset as u64, matching_key, app_ctx.output_dir)?; + extract_file(&mut file_reader, file_entries[0].offset as u64, file_entries[0].base_offset as u64, matching_key, &app_ctx.output_dir)?; } else { println!("File contains {} sub-files...", file_entries.len()); for (i, file_entry ) in file_entries.iter().enumerate() { println!("\nExtracting file {}/{} - Offset: {}, base: {}", i + 1, file_entries.len(), file_entry.offset, file_entry.base_offset); - extract_file(&mut file_reader, file_entry.offset as u64, file_entry.base_offset as u64, matching_key, &format!("{}/file_{}", app_ctx.output_dir, i + 1))?; + extract_file(&mut file_reader, file_entry.offset as u64, file_entry.base_offset as u64, matching_key, &app_ctx.output_dir.join(format!("file_{}", i + 1)))?; } } @@ -231,7 +232,7 @@ pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Option>) -> Resu Ok(()) } -fn extract_file(file_reader: &mut Cursor>, offset: u64, base_offset: u64, key: [u8; 8], output_folder: &str) -> Result<(), Box> { +fn extract_file(file_reader: &mut Cursor>, offset: u64, base_offset: u64, key: [u8; 8], output_folder: &PathBuf) -> Result<(), Box> { file_reader.seek(SeekFrom::Start(offset + base_offset))?; let enc_header = common::read_exact(file_reader, MAX_HEADER_SIZE)?; diff --git a/src/formats/pfl_upg.rs b/src/formats/pfl_upg.rs index def64c2..d2dd61a 100644 --- a/src/formats/pfl_upg.rs +++ b/src/formats/pfl_upg.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "pfl_upg", detector_func: is_pfl_upg_file, extractor_func: extract_pfl_upg } } @@ -49,7 +49,9 @@ impl FileHeader { } pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 8)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 8)?; if header == b"2SWU3TXV" { Ok(Some(Box::new(()))) } else { @@ -90,7 +92,8 @@ fn decrypt_aes256_ecb(key: &[u8], ciphertext: &[u8]) -> Result, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let header: Header = file.read_le()?; let signature = common::read_exact(&mut file, 128)?; let _ = common::read_exact(&mut file, 32)?; //unknown @@ -163,7 +166,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Option>) -> Resu //its a folder not a file if (file_header.attributes[3] & (1 << 1)) != 0 { println!("\nFolder - {}", file_header.file_name()); - let output_path = Path::new(app_ctx.output_dir).join(file_header.file_name().trim_start_matches('/')); + let output_path = Path::new(&app_ctx.output_dir).join(file_header.file_name().trim_start_matches('/')); fs::create_dir_all(output_path)?; continue } @@ -181,7 +184,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Option>) -> Resu println!("\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('/')); + let output_path = Path::new(&app_ctx.output_dir).join(file_name.trim_start_matches('/')); let output_path_parent = output_path.parent().expect("Failed to get parent of the output path!"); //prevent collisions @@ -194,11 +197,8 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Option>) -> Resu fs::create_dir_all(parent)?; } - fs::create_dir_all(app_ctx.output_dir)?; - let mut out_file = OpenOptions::new() - .write(true) - .create(true) - .open(output_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(&data[..file_header.real_size as usize])?; diff --git a/src/formats/pup.rs b/src/formats/pup.rs index cdd25e3..b390ce4 100644 --- a/src/formats/pup.rs +++ b/src/formats/pup.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "pup", detector_func: is_pup_file, extractor_func: extract_pup } } @@ -57,7 +57,9 @@ struct BlockEntry { } pub fn is_pup_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 4)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 4)?; if header == b"\x4F\x15\x3D\x1D" || header == b"\x54\x14\xF5\xEE" { //ps4, ps5 Ok(Some(Box::new(()))) } else { @@ -66,9 +68,9 @@ pub fn is_pup_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; - let header: Header = file.read_le()?; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let header: Header = file.read_le()?; println!("File info:\nFile size: {}\nEntry count: {}", header.file_size, header.entry_count); @@ -139,14 +141,10 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Option>) -> Result<( out_data = data; } - let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", entry.id())); - - fs::create_dir_all(app_ctx.output_dir)?; - let mut out_file = OpenOptions::new() - .append(true) - .create(true) - .open(output_path)?; + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", entry.id())); + 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!"); @@ -165,14 +163,10 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Option>) -> Result<( out_data = data; } - let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", entry.id())); - - fs::create_dir_all(app_ctx.output_dir)?; - let mut out_file = OpenOptions::new() - .write(true) - .create(true) - .open(output_path)?; + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", entry.id())); + 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(&out_data)?; println!("-- Saved file!"); diff --git a/src/formats/roku.rs b/src/formats/roku.rs index 2b60ed0..44bc99a 100644 --- a/src/formats/roku.rs +++ b/src/formats/roku.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "roku", detector_func: is_roku_file, extractor_func: extract_roku } } @@ -62,7 +62,9 @@ impl ImageHeader { } pub fn is_roku_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 32)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 32)?; let try_decrypt_header = decrypt_aes128_cbc_nopad(&header, &FILE_KEY, &FILE_IV)?; if try_decrypt_header.starts_with(b"manifest\x00\x00\x00\x00\x00\x00\x00\x00") { @@ -73,7 +75,8 @@ pub fn is_roku_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let mut encrypted_data = Vec::new(); file.read_to_end(&mut encrypted_data)?; @@ -116,7 +119,7 @@ pub fn extract_roku(app_ctx: &AppContext, _ctx: Option>) -> Result< common::read_exact(&mut image_reader, image.size1 as usize - image.data_start_offset as usize)? }; - let folder_path = Path::new(app_ctx.output_dir).join(&path); + let folder_path = Path::new(&app_ctx.output_dir).join(&path); let output_path = Path::new(&folder_path).join(format!("{}_{}.bin", i, image.type_string())); fs::create_dir_all(&folder_path)?; @@ -130,9 +133,9 @@ pub fn extract_roku(app_ctx: &AppContext, _ctx: Option>) -> Result< } else { println!("\nOther/Unknown file: {:?}", path); - let output_path = Path::new(app_ctx.output_dir).join(&path); + let output_path = Path::new(&app_ctx.output_dir).join(&path); - fs::create_dir_all(app_ctx.output_dir)?; + 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)?; diff --git a/src/formats/ruf.rs b/src/formats/ruf.rs index b76a4b0..ef1a67c 100644 --- a/src/formats/ruf.rs +++ b/src/formats/ruf.rs @@ -1,10 +1,10 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "ruf", detector_func: is_ruf_file, extractor_func: extract_ruf } } -use std::path::{Path}; +use std::path::{Path, PathBuf}; use std::fs::{self, File, OpenOptions}; use binrw::{BinRead, BinReaderExt}; use std::io::{Write, Seek, SeekFrom, Cursor}; @@ -76,7 +76,9 @@ impl RufEntry { } pub fn is_ruf_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 6)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 6)?; if header == b"RUF\x00\x00\x00" { Ok(Some(Box::new(()))) } else { @@ -85,22 +87,23 @@ pub fn is_ruf_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let header: RufHeader = file.read_be()?; if header.is_dual_ruf() { println!("\nDual RUF detected! Extracting 1st RUF...\n"); - actually_extract_ruf(file, &format!("{}/RUF_1", app_ctx.output_dir), 0)?; + actually_extract_ruf(file, &app_ctx.output_dir.join("RUF_1"), 0)?; println!("\nExtracting 2nd RUF...\n"); - actually_extract_ruf(file, &format!("{}/RUF_2", app_ctx.output_dir), 41943088)?; + actually_extract_ruf(file, &app_ctx.output_dir.join("RUF_2"), 41943088)?; } else { - actually_extract_ruf(file, app_ctx.output_dir, 0)?; + actually_extract_ruf(file, &app_ctx.output_dir, 0)?; } println!("\nExtraction finished!"); Ok(()) } -fn actually_extract_ruf(mut file: &File, output_folder: &str, start_offset: u64) -> Result<(), Box> { +fn actually_extract_ruf(mut file: &File, output_folder: &PathBuf, start_offset: u64) -> Result<(), Box> { file.seek(SeekFrom::Start(start_offset))?; let header: RufHeader = file.read_be()?; @@ -166,10 +169,7 @@ fn actually_extract_ruf(mut file: &File, output_folder: &str, start_offset: u64) let output_path = Path::new(&output_folder).join(format!("{}_{}.bin", entry.payload_type_bytes, entry.payload_type())); fs::create_dir_all(&output_folder)?; - let mut out_file = OpenOptions::new() - .write(true) - .create(true) - .open(output_path)?; + let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; diff --git a/src/formats/rvp.rs b/src/formats/rvp.rs index e05e63c..83cd619 100644 --- a/src/formats/rvp.rs +++ b/src/formats/rvp.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "rvp", detector_func: is_rvp_file, extractor_func: extract_rvp } } @@ -19,16 +19,16 @@ fn decrypt_xor(data: &[u8]) -> Vec { } pub fn is_rvp_file(app_ctx: &AppContext) -> Result>, Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; //MVP - let header = common::read_file(file, 0, 4)?; + let header = common::read_file(&file, 0, 4)?; if header == b"UPDT" { file.seek(std::io::SeekFrom::Start(36))?; //skip rest of header // NOT GOOD PRACTICE SHOULD BE REMOVED return Ok(Some(Box::new(()))) } //RVP - let bytes = common::read_file(file, 16, 18)?; + let bytes = common::read_file(&file, 16, 18)?; for (_i, &b) in bytes.iter().enumerate().step_by(2) { if b != 0xA3 { return Ok(None); @@ -40,7 +40,8 @@ pub fn is_rvp_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let mut obf_data = Vec::new(); //we sadly cannot deXOR on the fly because of its 32 byte pattern file.read_to_end(&mut obf_data)?; println!("DeXORing data.."); @@ -122,9 +123,9 @@ pub fn extract_rvp(app_ctx: &AppContext, _ctx: Option>) -> Result<( println!("Size: {}", size); let data = common::read_exact(&mut data_reader, size as usize)?; - let output_path = Path::new(app_ctx.output_dir).join(if name=="" {format!("{}.bin", i)} else {format!("{}_{}.bin", i, name)}); + let output_path = Path::new(&app_ctx.output_dir).join(if name=="" {format!("{}.bin", i)} else {format!("{}_{}.bin", i, name)}); - fs::create_dir_all(app_ctx.output_dir)?; + 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(&data)?; diff --git a/src/formats/samsung_old.rs b/src/formats/samsung_old.rs index 9d130eb..5dad9e4 100644 --- a/src/formats/samsung_old.rs +++ b/src/formats/samsung_old.rs @@ -1,5 +1,11 @@ +use std::any::Any; +use crate::{InputTarget, AppContext, formats::Format}; +pub fn format() -> Format { + Format { name: "samsung_old", detector_func: is_samsung_old_dir, extractor_func: extract_samsung_old } +} + use std::fs; -use std::path::{Path, PathBuf}; +use std::path::{Path}; use std::fs::{File, OpenOptions}; use std::io::{Write}; use hex::decode; @@ -11,11 +17,13 @@ use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; use md5; -pub fn is_samsung_old_dir(path: &PathBuf) -> bool { - if Path::new(&path).join("image").is_dir() & Path::new(&path).join("image/info.txt").exists(){ - true +pub fn is_samsung_old_dir(app_ctx: &AppContext) -> Result>, Box> { + let dir = match &app_ctx.input {InputTarget::Directory(p) => p, InputTarget::File(_) => return Ok(None)}; + + if Path::new(&dir).join("image").is_dir() & Path::new(&dir).join("image/info.txt").exists(){ + Ok(Some(Box::new(()))) } else { - false + Ok(None) } } @@ -27,7 +35,8 @@ fn decrypt_xor(data: &[u8], key: &str) -> Vec { .collect() } -pub fn extract_samsung_old(path: &PathBuf, output_folder: &str) -> Result<(), Box> { +pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { + let path = match &app_ctx.input {InputTarget::Directory(p) => p, InputTarget::File(_) => return Err("Extractor expected directory, not file".into())}; let fw_info = fs::read_to_string(Path::new(&path).join("image/info.txt"))?; println!("Firmware info: {}", fw_info); @@ -114,9 +123,9 @@ pub fn extract_samsung_old(path: &PathBuf, output_folder: &str) -> Result<(), Bo let xor_key = fw_info.split_whitespace().next().unwrap(); let out_data = decrypt_xor(&decrypted_data, xor_key); - let output_path = Path::new(&output_folder).join(filename.rsplit_once('.').map(|(left, _)| left).unwrap()); + let output_path = Path::new(&app_ctx.output_dir).join(filename.rsplit_once('.').map(|(left, _)| left).unwrap()); - fs::create_dir_all(&output_folder)?; + fs::create_dir_all(&app_ctx.output_dir)?; let mut out_file = OpenOptions::new() .write(true) .create(true) diff --git a/src/formats/sddl_sec.rs b/src/formats/sddl_sec.rs index 4a8719f..73b59f1 100644 --- a/src/formats/sddl_sec.rs +++ b/src/formats/sddl_sec.rs @@ -1,6 +1,6 @@ //sddl_dec 5.0 use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "sddl_sec", detector_func: is_sddl_sec_file, extractor_func: extract_sddl_sec } } @@ -104,7 +104,9 @@ static DEC_IV: [u8; 16] = [ ]; pub fn is_sddl_sec_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 32)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + 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(()))) @@ -156,7 +158,8 @@ fn decipher(s: &[u8]) -> Vec { } pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let mut hdr_reader = Cursor::new(decipher(&common::read_exact(&mut file, 32)?)); let hdr: SddlSecHeader = hdr_reader.read_be()?; @@ -174,10 +177,10 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Option>) -> Res let data = common::read_exact(&mut file, entry_header.size() as usize)?; let dec_data = decrypt_aes128_cbc_pcks7(&data, &DEC_KEY, &DEC_IV)?; - fs::create_dir_all(app_ctx.output_dir)?; + fs::create_dir_all(&app_ctx.output_dir)?; //detect the file type based on the counts of each file if i == 0 { //SDIT.FDI file - let output_path = Path::new(app_ctx.output_dir).join(entry_header.name()); + let output_path = Path::new(&app_ctx.output_dir).join(entry_header.name()); let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&dec_data)?; println!("-- Saved file!"); @@ -216,11 +219,11 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Option>) -> Res let file_name = common::string_from_bytes(&file_name_bytes); println!("--- File name: {}", file_name); - let out_folder_path = Path::new(app_ctx.output_dir).join(source_name); + let out_folder_path = Path::new(&app_ctx.output_dir).join(source_name); fs::create_dir_all(&out_folder_path)?; output_path = Path::new(&out_folder_path).join(file_name); } else { - output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", source_name)); + output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", source_name)); } let data = common::read_exact(&mut content_reader, content_header.size as usize)?; diff --git a/src/formats/slp.rs b/src/formats/slp.rs index f5940ae..c0ebba4 100644 --- a/src/formats/slp.rs +++ b/src/formats/slp.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "slp", detector_func: is_slp_file, extractor_func: extract_slp } } @@ -53,7 +53,9 @@ struct EntryNew { } pub fn is_slp_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 4)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 4)?; if header == b"SLP\x00" { Ok(Some(Box::new(()))) } else { @@ -62,9 +64,9 @@ pub fn is_slp_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = app_ctx.file; - let header: Header = file.read_le()?; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let header: Header = file.read_le()?; println!("File info:\nModel: {}\nVersion: {}\nFirmware: {}\nNew type: {}\n", header.model(), header.version(), header.firmware(), header.is_new_type()); @@ -99,9 +101,9 @@ pub fn extract_slp(app_ctx: &AppContext, _ctx: Option>) -> Result<( file.seek(SeekFrom::Start(entry.offset.into()))?; let data = common::read_exact(&mut file, entry.size as usize)?; - let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", i)); + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", i)); - fs::create_dir_all(app_ctx.output_dir)?; + fs::create_dir_all(&app_ctx.output_dir)?; let mut out_file = OpenOptions::new() .write(true) .create(true) diff --git a/src/formats/sony_bdp.rs b/src/formats/sony_bdp.rs index 61ce0aa..21a27c9 100644 --- a/src/formats/sony_bdp.rs +++ b/src/formats/sony_bdp.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; +use crate::{InputTarget, AppContext, formats::Format}; pub fn format() -> Format { Format { name: "sony_bdp", detector_func: is_sony_bdp_file, extractor_func: extract_sony_bdp } } @@ -64,7 +64,9 @@ struct Entry { } pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result>, Box> { - let header = common::read_file(app_ctx.file, 0, 4)?; + let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + + let header = common::read_file(&file, 0, 4)?; if header == b"\x01\x73\xEC\xC9" || header == b"\x01\x73\xEC\x1F" || header == b"\xEC\x7D\xB0\xB0" { //MSB1x, MSB0x, BDPPxx Ok(Some(Box::new(()))) } else { @@ -73,7 +75,8 @@ pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result>, Bo } pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = app_ctx.file; + let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; + let obf_header = common::read_exact(&mut file, 300)?; let header = hex_substitute(&obf_header); let mut hdr_reader = Cursor::new(header); @@ -103,10 +106,10 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Option>) -> Res let obf_data = common::read_file(&file, entry.offset as u64, entry.size as usize)?; let data = hex_substitute(&obf_data); - let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", i)); + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", i)); last_file_path = Some(output_path.clone()); - fs::create_dir_all(app_ctx.output_dir)?; + 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(&data)?; @@ -119,8 +122,8 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Option>) -> Res println!("\nChecking if it's also MTK BDP..."); let last_file = File::open(last_file_path.unwrap())?; - let mtk_extraction_path = format!("{}/{}", app_ctx.output_dir, i - 1); - let ctx: AppContext = AppContext { file: &last_file, output_dir: &mtk_extraction_path }; + let mtk_extraction_path = app_ctx.output_dir.join(format!("{}", i + 1)); + let ctx: AppContext = AppContext { input: InputTarget::File(last_file), output_dir: mtk_extraction_path }; if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&ctx)? { println!("- MTK BDP file detected!\n"); diff --git a/src/keys.rs b/src/keys.rs index 5b5e38f..af7ab8a 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -2,16 +2,16 @@ //samsung old keys //github.com/george-hopkins/samygo-patcher -//pub static SAMSUNG: &[(&str, &str)] = &[ -// ("T-GA", "SHWJUH:85a045ae-2296-484c-b457-ede832fcfbe1-646390a3-105e-40aa-85f6-da3086c70111"), -// ("T-MST5", "SHWJUH:eceb2c14-db11-425e-9ebf-5f9607f0eb4b-3c38193e-751e-4719-8884-9e76322c0cec"), -// ("T-MST10P","b4c136-fbc93576-b3e8-4035-bf4e-ba4cb4ada1ac-f0d81cc4-8301-4832-bd60-f331295743ba"), -// ("T-VAL", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00001abc2010"), -// ("T-TDT", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00002abc2010"), -// ("T-MSX", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00004abc2010"), -// ("T-CH", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-611c4f8d4a71"), -// ("T-ECP", "3EF6067262CF0C678598BFF22169D1F1EA57C284"), -//]; +pub static SAMSUNG: &[(&str, &str)] = &[ + ("T-GA", "SHWJUH:85a045ae-2296-484c-b457-ede832fcfbe1-646390a3-105e-40aa-85f6-da3086c70111"), + ("T-MST5", "SHWJUH:eceb2c14-db11-425e-9ebf-5f9607f0eb4b-3c38193e-751e-4719-8884-9e76322c0cec"), + ("T-MST10P","b4c136-fbc93576-b3e8-4035-bf4e-ba4cb4ada1ac-f0d81cc4-8301-4832-bd60-f331295743ba"), + ("T-VAL", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00001abc2010"), + ("T-TDT", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00002abc2010"), + ("T-MSX", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00004abc2010"), + ("T-CH", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-611c4f8d4a71"), + ("T-ECP", "3EF6067262CF0C678598BFF22169D1F1EA57C284"), +]; //MSD10 keys //fw prefix, type, key diff --git a/src/main.rs b/src/main.rs index 6da30b2..1ad9d39 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,9 +14,14 @@ struct Args { output_directory: Option, } -pub struct AppContext<'a> { - pub file: &'a std::fs::File, - pub output_dir: &'a str, +pub enum InputTarget { + File(File), + Directory(PathBuf), +} + +pub struct AppContext { + pub input: InputTarget, + pub output_dir: PathBuf, } fn main() -> Result<(), Box> { @@ -32,24 +37,40 @@ fn main() -> Result<(), Box> { } else { format!("_{}", target_path.file_name().and_then(|s| s.to_str()).unwrap()) }; - println!("Output directory: {}\n", output_path_str); + println!("Output directory: {}", output_path_str); let output_directory_path = PathBuf::from(&output_path_str); if output_directory_path.exists() { if output_directory_path.is_dir() { let is_empty = fs::read_dir(&output_directory_path)?.next().is_none(); if !is_empty { - println!("Warning: Output folder already exists and is NOT empty! Files may be overwritten!"); + println!("\nWarning: Output folder already exists and is NOT empty! Files may be overwritten!"); println!("Press Enter if you want to continue..."); io::stdin().read_line(&mut String::new())?; } } } - let file = File::open(target_path)?; - let app_ctx: AppContext = AppContext { file: &file, output_dir: &output_path_str }; + let app_ctx; + + if target_path.is_file() { + let file = File::open(&target_path)?; + app_ctx = AppContext { + input: InputTarget::File(file), + output_dir: output_directory_path, + }; + } else if target_path.is_dir() { + app_ctx = AppContext { + input: InputTarget::Directory(target_path), + output_dir: output_directory_path, + }; + } else { + return Err("Invalid input path!".into()); + } let formats: Vec = get_registry(); + println!("Loaded {} formats!\n", formats.len()); + for format in formats { if let Some(ctx) = (format.detector_func)(&app_ctx)? { println!("{} detected!", format.name); From 438d106c96b539ccb79f3623f96b30574bfee25b Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Fri, 6 Feb 2026 16:27:13 +0100 Subject: [PATCH 5/9] newreg: simplify getting file/dir and context --- src/formats.rs | 2 +- src/formats/amlogic.rs | 8 ++++---- src/formats/android_ota_payload.rs | 8 ++++---- src/formats/bdl.rs | 8 ++++---- src/formats/epk.rs | 14 +++++++------- src/formats/epk1.rs | 8 ++++---- src/formats/epk2.rs | 8 ++++---- src/formats/epk2b.rs | 8 ++++---- src/formats/epk3.rs | 6 +++--- src/formats/funai_upg.rs | 8 ++++---- src/formats/invincible_image.rs | 8 ++++---- src/formats/msd10.rs | 8 ++++---- src/formats/msd11.rs | 8 ++++---- src/formats/mstar.rs | 8 ++++---- src/formats/mtk_bdp.rs | 10 +++++----- src/formats/mtk_pkg.rs | 10 +++++----- src/formats/mtk_pkg_new.rs | 10 +++++----- src/formats/mtk_pkg_old.rs | 10 +++++----- src/formats/novatek.rs | 8 ++++---- src/formats/nvt_timg.rs | 8 ++++---- src/formats/pana_dvd.rs | 10 +++++----- src/formats/pfl_upg.rs | 8 ++++---- src/formats/pup.rs | 8 ++++---- src/formats/roku.rs | 8 ++++---- src/formats/ruf.rs | 8 ++++---- src/formats/rvp.rs | 8 ++++---- src/formats/samsung_old.rs | 8 ++++---- src/formats/sddl_sec.rs | 8 ++++---- src/formats/slp.rs | 8 ++++---- src/formats/sony_bdp.rs | 8 ++++---- src/main.rs | 21 ++++++++++++++++++--- 31 files changed, 142 insertions(+), 127 deletions(-) diff --git a/src/formats.rs b/src/formats.rs index 38b9035..47f8a89 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -4,7 +4,7 @@ use crate::AppContext; pub struct Format { pub name: &'static str, pub detector_func: fn(&AppContext) -> Result>, Box>, - pub extractor_func: fn(&AppContext, Option>) -> Result<(), Box>, + pub extractor_func: fn(&AppContext, Box) -> Result<(), Box>, } pub mod mstar; diff --git a/src/formats/amlogic.rs b/src/formats/amlogic.rs index 2e8521c..74e55f1 100644 --- a/src/formats/amlogic.rs +++ b/src/formats/amlogic.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "amlogic", detector_func: is_amlogic_file, extractor_func: extract_amlogic } } @@ -50,7 +50,7 @@ impl ItemEntry { } pub fn is_amlogic_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 8, 4)?; if header == b"\x56\x19\xB5\x27" { @@ -60,8 +60,8 @@ pub fn is_amlogic_file(app_ctx: &AppContext) -> Result>, Box } } -pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; file.seek(SeekFrom::Start(0))?; let header: ImageHeader = file.read_le()?; diff --git a/src/formats/android_ota_payload.rs b/src/formats/android_ota_payload.rs index be173fd..0fa13fd 100644 --- a/src/formats/android_ota_payload.rs +++ b/src/formats/android_ota_payload.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "android_ota_payload", detector_func: is_android_ota_payload_file, extractor_func: extract_android_ota_payload } } @@ -23,7 +23,7 @@ struct Header { } pub fn is_android_ota_payload_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 4)?; if header == b"CrAU" { @@ -33,8 +33,8 @@ pub fn is_android_ota_payload_file(app_ctx: &AppContext) -> Result>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box) -> Result<(), 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); diff --git a/src/formats/bdl.rs b/src/formats/bdl.rs index 29bf0f0..bd7952f 100644 --- a/src/formats/bdl.rs +++ b/src/formats/bdl.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "bdl", detector_func: is_bdl_file, extractor_func: extract_bdl } } @@ -86,7 +86,7 @@ impl PkgEntry { } pub fn is_bdl_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 4)?; if header == b"ibdl" { @@ -96,8 +96,8 @@ pub fn is_bdl_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_bdl(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: BdlHeader = file.read_le()?; diff --git a/src/formats/epk.rs b/src/formats/epk.rs index 994cd8b..9307d0b 100644 --- a/src/formats/epk.rs +++ b/src/formats/epk.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "epk", detector_func: is_epk_file, extractor_func: extract_epk } } @@ -12,7 +12,7 @@ pub struct EpkContext { } pub fn is_epk_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let versions = common::read_file(&file, 1712, 36)?; if let Some(epk_version) = check_epk_version(&versions) { @@ -51,9 +51,9 @@ fn match_with_pattern(data: &[u8], pattern: &str) -> bool { true } -pub fn extract_epk(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; - let ctx = ctx.and_then(|c| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; +pub fn extract_epk(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { + let file = app_ctx.file().ok_or("Extractor expected file")?; + let ctx = ctx.downcast::().expect("Missing context"); let versions = common::read_file(&file, 1712, 36)?; @@ -63,10 +63,10 @@ pub fn extract_epk(app_ctx: &AppContext, ctx: Option>) -> Result<() if ctx.epk_version == 2 { println!("EPK2 detected!\n"); - formats::epk2::extract_epk2(app_ctx, None)?; + formats::epk2::extract_epk2(app_ctx, Box::new(()))?; } else if ctx.epk_version == 3 { println!("EPK3 detected!\n"); - formats::epk3::extract_epk3(app_ctx, None)?; + formats::epk3::extract_epk3(app_ctx, Box::new(()))?; } Ok(()) diff --git a/src/formats/epk1.rs b/src/formats/epk1.rs index afc477a..12a177b 100644 --- a/src/formats/epk1.rs +++ b/src/formats/epk1.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "epk1", detector_func: is_epk1_file, extractor_func: extract_epk1 } } @@ -41,7 +41,7 @@ struct Pak { } pub fn is_epk1_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let epk2_magic = common::read_file(&file, 12, 4)?; //for epk2b let epak_magic = common::read_file(&file, 0, 4)?; @@ -52,8 +52,8 @@ pub fn is_epk1_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; //check type of epk1 let epk1_type; let init_pak_count_bytes = common::read_file(&file, 8, 4)?; diff --git a/src/formats/epk2.rs b/src/formats/epk2.rs index a3ed2c2..79dbb1b 100644 --- a/src/formats/epk2.rs +++ b/src/formats/epk2.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "epk2", detector_func: is_epk2_file, extractor_func: extract_epk2 } } @@ -72,7 +72,7 @@ struct Pak { } pub fn is_epk2_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 128, 4)?; if header == b"epak" { @@ -82,8 +82,8 @@ pub fn is_epk2_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?; let stored_header = common::read_exact(&mut file, 1584)?; //max header size diff --git a/src/formats/epk2b.rs b/src/formats/epk2b.rs index 110517c..ef39257 100644 --- a/src/formats/epk2b.rs +++ b/src/formats/epk2b.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "epk2b", detector_func: is_epk2b_file, extractor_func: extract_epk2b } } @@ -57,7 +57,7 @@ struct Pak { } pub fn is_epk2b_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let epk2_magic = common::read_file(&file, 12, 4)?; let epak_magic = common::read_file(&file, 0, 4)?; @@ -68,8 +68,8 @@ pub fn is_epk2b_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +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?}", diff --git a/src/formats/epk3.rs b/src/formats/epk3.rs index b48fdd1..ed935fb 100644 --- a/src/formats/epk3.rs +++ b/src/formats/epk3.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "epk3", detector_func: is_epk3_file, extractor_func: extract_epk3 } } @@ -80,8 +80,8 @@ pub fn is_epk3_file(_app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; file.seek(SeekFrom::Start(0))?; let stored_header = common::read_exact(&mut file, 1712)?; let header: Vec; diff --git a/src/formats/funai_upg.rs b/src/formats/funai_upg.rs index e398843..31c86c3 100644 --- a/src/formats/funai_upg.rs +++ b/src/formats/funai_upg.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "funai_upg", detector_func: is_funai_upg_file, extractor_func: extract_funai_upg } } @@ -26,7 +26,7 @@ struct Entry { } pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 6)?; if header == b"UPG\x00\x00\x00" { Ok(Some(Box::new(()))) @@ -35,8 +35,8 @@ pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result>, B } } -pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: Header = file.read_le()?; println!("File info:\nFile size: {}\nEntry count: {}", header.file_size, header.entry_count); diff --git a/src/formats/invincible_image.rs b/src/formats/invincible_image.rs index 5d21dcd..33a0247 100644 --- a/src/formats/invincible_image.rs +++ b/src/formats/invincible_image.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "invincible_image", detector_func: is_invincible_image_file, extractor_func: extract_invincible_image } } @@ -63,7 +63,7 @@ impl Entry { } pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 16)?; if header == b"INVINCIBLE_IMAGE" { @@ -73,8 +73,8 @@ pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: Header = file.read_le()?; println!("File info:\nFile Version: {}.{}\nVersion(1): {}\nVersion(2): {}\nVersion(3): {}\nVersion(4): {}\nData size: {}\nData start offset: {}\nKeep data size: {}\nSkip data size: {}\n\nPayload Count: {}", diff --git a/src/formats/msd10.rs b/src/formats/msd10.rs index 7bc2e96..49acb85 100644 --- a/src/formats/msd10.rs +++ b/src/formats/msd10.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "msd10", detector_func: is_msd10_file, extractor_func: extract_msd10 } } @@ -48,7 +48,7 @@ struct Section { } pub fn is_msd10_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 6)?; if header == b"MSDU10" { @@ -58,8 +58,8 @@ pub fn is_msd10_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +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); diff --git a/src/formats/msd11.rs b/src/formats/msd11.rs index 1159d86..1c5bd1d 100644 --- a/src/formats/msd11.rs +++ b/src/formats/msd11.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "msd11", detector_func: is_msd11_file, extractor_func: extract_msd11 } } @@ -49,7 +49,7 @@ struct Section { } pub fn is_msd11_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 6)?; if header == b"MSDU11" { @@ -59,8 +59,8 @@ pub fn is_msd11_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +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); diff --git a/src/formats/mstar.rs b/src/formats/mstar.rs index 09b6fd6..81b68cb 100644 --- a/src/formats/mstar.rs +++ b/src/formats/mstar.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "mstar", detector_func: is_mstar_file, extractor_func: extract_mstar } } @@ -14,7 +14,7 @@ use crate::utils::lzop::{unlzop_to_file}; use crate::utils::sparse::{unsparse_to_file}; pub fn is_mstar_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 32768)?; let header_string = String::from_utf8_lossy(&header); @@ -33,8 +33,8 @@ fn parse_number(s: &str) -> Option { } } -pub fn extract_mstar(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let file = app_ctx.file().ok_or("Extractor expected file")?; let mut script = common::read_file(&file, 0, 32768)?; diff --git a/src/formats/mtk_bdp.rs b/src/formats/mtk_bdp.rs index 6b66f1c..ba01d1b 100644 --- a/src/formats/mtk_bdp.rs +++ b/src/formats/mtk_bdp.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "mtk_bdp", detector_func: is_mtk_bdp_file, extractor_func: extract_mtk_bdp } } @@ -78,7 +78,7 @@ fn find_bytes(data: &[u8], pattern: &[u8]) -> Option { } pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result>, Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file_size = file.metadata()?.len(); let mut data = Vec::new(); @@ -95,9 +95,9 @@ pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result>, Box } } -pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; - let ctx = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; +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 offset = ctx.pitit_offset; println!("\nReading PITIT at: {}", offset); diff --git a/src/formats/mtk_pkg.rs b/src/formats/mtk_pkg.rs index 087934d..9b8bac5 100644 --- a/src/formats/mtk_pkg.rs +++ b/src/formats/mtk_pkg.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "mtk_pkg", detector_func: is_mtk_pkg_file, extractor_func: extract_mtk_pkg } } @@ -82,7 +82,7 @@ static HEADER_KEY: [u8; 16] = [ static HEADER_IV: [u8; 16] = [0x00; 16]; pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let mut encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?; let mut header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?; @@ -101,9 +101,9 @@ pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result>, Box } } -pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; - let ctx = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; +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 file_size = file.metadata()?.len(); let header = ctx.decrypted_header; diff --git a/src/formats/mtk_pkg_new.rs b/src/formats/mtk_pkg_new.rs index 2fdb74b..fd0e664 100644 --- a/src/formats/mtk_pkg_new.rs +++ b/src/formats/mtk_pkg_new.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "mtk_pkg_new", detector_func: is_mtk_pkg_new_file, extractor_func: extract_mtk_pkg_new } } @@ -71,7 +71,7 @@ impl PartEntry { static HEADER_SIZE: usize = 0x170; pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?; for (key_hex, iv_hex, name) in keys::MTK_PKG_CUST { @@ -92,9 +92,9 @@ pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result>, Ok(None) } -pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; - let ctx = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; +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 file_size = file.metadata()?.len(); diff --git a/src/formats/mtk_pkg_old.rs b/src/formats/mtk_pkg_old.rs index f70c32b..3ffe9e1 100644 --- a/src/formats/mtk_pkg_old.rs +++ b/src/formats/mtk_pkg_old.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "mtk_pkg_old", detector_func: is_mtk_pkg_old_file, extractor_func: extract_mtk_pkg_old } } @@ -64,8 +64,8 @@ impl PartEntry { static HEADER_SIZE: usize = 0x98; pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result>, Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; - + let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; + let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?; let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK)); if &header[4..12] == MTK_HEADER_MAGIC { @@ -83,8 +83,8 @@ pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result>, } } -pub fn extract_mtk_pkg_old(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +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 file_size = file.metadata()?.len(); let encrypted_header = common::read_exact(&mut file, HEADER_SIZE)?; diff --git a/src/formats/novatek.rs b/src/formats/novatek.rs index b8a22d3..45ead49 100644 --- a/src/formats/novatek.rs +++ b/src/formats/novatek.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "novatek", detector_func: is_novatek_file, extractor_func: extract_novatek } } @@ -41,7 +41,7 @@ struct PartEntry { } pub fn is_novatek_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 4)?; if header == b"NFWB" { @@ -51,8 +51,8 @@ pub fn is_novatek_file(app_ctx: &AppContext) -> Result>, Box } } -pub fn extract_novatek(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_novatek(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + 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: {}", diff --git a/src/formats/nvt_timg.rs b/src/formats/nvt_timg.rs index afcb85c..040a89e 100644 --- a/src/formats/nvt_timg.rs +++ b/src/formats/nvt_timg.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "nvt_timg", detector_func: is_nvt_timg_file, extractor_func: extract_nvt_timg } } @@ -50,7 +50,7 @@ impl PIMG { } pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 4)?; if header == b"TIMG" { @@ -60,8 +60,8 @@ pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result>, Bo } } -pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let file_size = file.metadata()?.len(); let timg: TIMG = file.read_le()?; diff --git a/src/formats/pana_dvd.rs b/src/formats/pana_dvd.rs index 76769d2..5eda4e8 100644 --- a/src/formats/pana_dvd.rs +++ b/src/formats/pana_dvd.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "pana_dvd", detector_func: is_pana_dvd_file, extractor_func: extract_pana_dvd } } @@ -150,7 +150,7 @@ pub fn find_aes_key_pair<'a>(key_array: &'a [(&'a str, &'a str, &'a str)], data: } pub fn is_pana_dvd_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 64)?; if let Some(matching_key) = find_key(&keys::PANA_DVD_KEYONLY, &header, b"PROG", 0)? { Ok(Some(Box::new(PanaDvdContext { @@ -181,9 +181,9 @@ pub fn is_pana_dvd_file(app_ctx: &AppContext) -> Result>, Bo } } -pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Option>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; - let context = ctx.and_then(|c: Box| c.downcast::().ok()).ok_or("Context is invalid or missing!")?; +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 mut data = Vec::new(); // we need to load the entire file into memory so we can swap it with AES decrypted data if its AES encrypted file.read_to_end(&mut data)?; diff --git a/src/formats/pfl_upg.rs b/src/formats/pfl_upg.rs index d2dd61a..886566f 100644 --- a/src/formats/pfl_upg.rs +++ b/src/formats/pfl_upg.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "pfl_upg", detector_func: is_pfl_upg_file, extractor_func: extract_pfl_upg } } @@ -49,7 +49,7 @@ impl FileHeader { } pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 8)?; if header == b"2SWU3TXV" { @@ -91,8 +91,8 @@ fn decrypt_aes256_ecb(key: &[u8], ciphertext: &[u8]) -> Result, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: Header = file.read_le()?; let signature = common::read_exact(&mut file, 128)?; diff --git a/src/formats/pup.rs b/src/formats/pup.rs index b390ce4..2ad20a4 100644 --- a/src/formats/pup.rs +++ b/src/formats/pup.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "pup", detector_func: is_pup_file, extractor_func: extract_pup } } @@ -57,7 +57,7 @@ struct BlockEntry { } pub fn is_pup_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 4)?; if header == b"\x4F\x15\x3D\x1D" || header == b"\x54\x14\xF5\xEE" { //ps4, ps5 @@ -67,8 +67,8 @@ pub fn is_pup_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_pup(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: Header = file.read_le()?; println!("File info:\nFile size: {}\nEntry count: {}", diff --git a/src/formats/roku.rs b/src/formats/roku.rs index 44bc99a..6d0b78f 100644 --- a/src/formats/roku.rs +++ b/src/formats/roku.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "roku", detector_func: is_roku_file, extractor_func: extract_roku } } @@ -62,7 +62,7 @@ impl ImageHeader { } pub fn is_roku_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 32)?; let try_decrypt_header = decrypt_aes128_cbc_nopad(&header, &FILE_KEY, &FILE_IV)?; @@ -74,8 +74,8 @@ pub fn is_roku_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_roku(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let mut encrypted_data = Vec::new(); file.read_to_end(&mut encrypted_data)?; diff --git a/src/formats/ruf.rs b/src/formats/ruf.rs index ef1a67c..ea3ab7a 100644 --- a/src/formats/ruf.rs +++ b/src/formats/ruf.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "ruf", detector_func: is_ruf_file, extractor_func: extract_ruf } } @@ -76,7 +76,7 @@ impl RufEntry { } pub fn is_ruf_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 6)?; if header == b"RUF\x00\x00\x00" { @@ -86,8 +86,8 @@ pub fn is_ruf_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_ruf(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: RufHeader = file.read_be()?; if header.is_dual_ruf() { diff --git a/src/formats/rvp.rs b/src/formats/rvp.rs index 83cd619..9c2dfdc 100644 --- a/src/formats/rvp.rs +++ b/src/formats/rvp.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "rvp", detector_func: is_rvp_file, extractor_func: extract_rvp } } @@ -19,7 +19,7 @@ fn decrypt_xor(data: &[u8]) -> Vec { } pub fn is_rvp_file(app_ctx: &AppContext) -> Result>, Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; //MVP let header = common::read_file(&file, 0, 4)?; if header == b"UPDT" { @@ -39,8 +39,8 @@ pub fn is_rvp_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_rvp(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let mut obf_data = Vec::new(); //we sadly cannot deXOR on the fly because of its 32 byte pattern file.read_to_end(&mut obf_data)?; diff --git a/src/formats/samsung_old.rs b/src/formats/samsung_old.rs index 5dad9e4..2680c83 100644 --- a/src/formats/samsung_old.rs +++ b/src/formats/samsung_old.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "samsung_old", detector_func: is_samsung_old_dir, extractor_func: extract_samsung_old } } @@ -18,7 +18,7 @@ use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; use md5; pub fn is_samsung_old_dir(app_ctx: &AppContext) -> Result>, Box> { - let dir = match &app_ctx.input {InputTarget::Directory(p) => p, InputTarget::File(_) => return Ok(None)}; + 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(){ Ok(Some(Box::new(()))) @@ -35,8 +35,8 @@ fn decrypt_xor(data: &[u8], key: &str) -> Vec { .collect() } -pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let path = match &app_ctx.input {InputTarget::Directory(p) => p, InputTarget::File(_) => return Err("Extractor expected directory, not file".into())}; +pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + 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); diff --git a/src/formats/sddl_sec.rs b/src/formats/sddl_sec.rs index 73b59f1..0734d5f 100644 --- a/src/formats/sddl_sec.rs +++ b/src/formats/sddl_sec.rs @@ -1,6 +1,6 @@ //sddl_dec 5.0 use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "sddl_sec", detector_func: is_sddl_sec_file, extractor_func: extract_sddl_sec } } @@ -104,7 +104,7 @@ static DEC_IV: [u8; 16] = [ ]; pub fn is_sddl_sec_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 32)?; let deciph_header = decipher(&header); @@ -157,8 +157,8 @@ fn decipher(s: &[u8]) -> Vec { out } -pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let mut hdr_reader = Cursor::new(decipher(&common::read_exact(&mut file, 32)?)); let hdr: SddlSecHeader = hdr_reader.read_be()?; diff --git a/src/formats/slp.rs b/src/formats/slp.rs index c0ebba4..0ec6a54 100644 --- a/src/formats/slp.rs +++ b/src/formats/slp.rs @@ -1,5 +1,5 @@ use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; +use crate::{AppContext, formats::Format}; pub fn format() -> Format { Format { name: "slp", detector_func: is_slp_file, extractor_func: extract_slp } } @@ -53,7 +53,7 @@ struct EntryNew { } pub fn is_slp_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 4)?; if header == b"SLP\x00" { @@ -63,8 +63,8 @@ pub fn is_slp_file(app_ctx: &AppContext) -> Result>, Box>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_slp(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let header: Header = file.read_le()?; println!("File info:\nModel: {}\nVersion: {}\nFirmware: {}\nNew type: {}\n", diff --git a/src/formats/sony_bdp.rs b/src/formats/sony_bdp.rs index 21a27c9..9543374 100644 --- a/src/formats/sony_bdp.rs +++ b/src/formats/sony_bdp.rs @@ -64,7 +64,7 @@ struct Entry { } pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 4)?; if header == b"\x01\x73\xEC\xC9" || header == b"\x01\x73\xEC\x1F" || header == b"\xEC\x7D\xB0\xB0" { //MSB1x, MSB0x, BDPPxx @@ -74,8 +74,8 @@ pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result>, Bo } } -pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Option>) -> Result<(), Box> { - let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())}; +pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let obf_header = common::read_exact(&mut file, 300)?; let header = hex_substitute(&obf_header); @@ -128,7 +128,7 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Option>) -> Res if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&ctx)? { println!("- MTK BDP file detected!\n"); - formats::mtk_bdp::extract_mtk_bdp(&ctx, Some(result))?; + formats::mtk_bdp::extract_mtk_bdp(&ctx, result)?; } else { println!("- Not an MTK BDP file."); } diff --git a/src/main.rs b/src/main.rs index 1ad9d39..e602d3b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,21 @@ pub struct AppContext { pub input: InputTarget, pub output_dir: PathBuf, } +impl AppContext { + pub fn file(&self) -> Option<&File> { + match &self.input { + InputTarget::File(f) => Some(f), + _ => None, + } + } + + pub fn dir(&self) -> Option<&PathBuf> { + match &self.input { + InputTarget::Directory(p) => Some(p), + _ => None, + } + } +} fn main() -> Result<(), Box> { println!("unixtract Firmware extractor"); @@ -69,12 +84,12 @@ fn main() -> Result<(), Box> { } let formats: Vec = get_registry(); - println!("Loaded {} formats!\n", formats.len()); + println!("Loaded {} formats!", formats.len()); for format in formats { if let Some(ctx) = (format.detector_func)(&app_ctx)? { - println!("{} detected!", format.name); - (format.extractor_func)(&app_ctx, Some(ctx))?; + println!("\n{} detected!", format.name); + (format.extractor_func)(&app_ctx, ctx)?; return Ok(()); } } From aa2249f0247b93785bb22138dede3c735ef202d1 Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:28:59 +0100 Subject: [PATCH 6/9] RESTRUCTURING 1 - change formats to have their own folders with mod + include files + anything they want - change format list to be defined in formats.rs instead of in each format - move utils to their respective formats - some minor code changes in some formats --- src/formats.rs | 176 +++++++++++--- src/formats/amlogic/include.rs | 39 ++++ src/formats/{amlogic.rs => amlogic/mod.rs} | 50 +--- .../android_ota_update_metadata.rs | 0 src/formats/android_ota_payload/include.rs | 9 + .../mod.rs} | 24 +- src/formats/bdl/include.rs | 76 ++++++ src/formats/{bdl.rs => bdl/mod.rs} | 85 +------ src/formats/{epk.rs => epk/mod.rs} | 5 +- src/formats/epk1/include.rs | 31 +++ src/formats/{epk1.rs => epk1/mod.rs} | 40 +--- src/formats/epk2/include.rs | 59 +++++ src/formats/{epk2.rs => epk2/mod.rs} | 71 +----- src/formats/epk2b/include.rs | 47 ++++ src/formats/{epk2b.rs => epk2b/mod.rs} | 54 +---- src/formats/epk3/include.rs | 64 +++++ src/formats/{epk3.rs => epk3/mod.rs} | 74 +----- src/formats/funai_upg/include.rs | 15 ++ .../{funai_upg.rs => funai_upg/mod.rs} | 25 +- src/formats/invincible_image/include.rs | 56 +++++ .../mod.rs} | 65 +----- src/formats/{msd.rs => msd/mod.rs} | 5 + .../msd}/msd_ouith_parser_old.rs | 0 .../msd}/msd_ouith_parser_tizen_1_8.rs | 0 .../msd}/msd_ouith_parser_tizen_1_9.rs | 0 src/formats/msd10/include.rs | 28 +++ src/formats/{msd10.rs => msd10/mod.rs} | 51 +--- src/formats/msd11/include.rs | 30 +++ src/formats/{msd11.rs => msd11/mod.rs} | 53 +---- src/formats/mstar/include.rs | 7 + src/formats/{mstar.rs => mstar/mod.rs} | 19 +- src/formats/mtk_bdp/include.rs | 65 ++++++ src/formats/{mtk_bdp.rs => mtk_bdp/mod.rs} | 73 +----- .../mtk_pkg}/huffman_tables.rs | 0 src/formats/mtk_pkg/include.rs | 63 +++++ src/{utils => formats/mtk_pkg}/lzhs.rs | 6 +- src/formats/{mtk_pkg.rs => mtk_pkg/mod.rs} | 75 +----- src/formats/mtk_pkg_new/include.rs | 1 + .../{mtk_pkg_new.rs => mtk_pkg_new/mod.rs} | 61 +---- src/formats/mtk_pkg_old/include.rs | 33 +++ .../{mtk_pkg_old.rs => mtk_pkg_old/mod.rs} | 65 +----- .../mtk_pkg_old}/mtk_crypto.rs | 0 src/formats/novatek/include.rs | 30 +++ src/formats/{novatek.rs => novatek/mod.rs} | 40 +--- src/formats/nvt_timg/include.rs | 38 +++ src/formats/{nvt_timg.rs => nvt_timg/mod.rs} | 49 +--- src/formats/pana_dvd/include.rs | 132 +++++++++++ src/{utils => formats/pana_dvd}/lzss.rs | 0 src/formats/{pana_dvd.rs => pana_dvd/mod.rs} | 142 +---------- .../pana_dvd}/pana_dvd_crypto.rs | 0 src/formats/pfl_upg/include.rs | 67 ++++++ src/formats/{pfl_upg.rs => pfl_upg/mod.rs} | 75 +----- src/formats/pup/include.rs | 45 ++++ src/formats/{pup.rs => pup/mod.rs} | 53 +---- src/formats/roku/include.rs | 49 ++++ src/formats/{roku.rs => roku/mod.rs} | 57 +---- src/formats/ruf/include.rs | 64 +++++ src/formats/{ruf.rs => ruf/mod.rs} | 71 +----- src/formats/rvp/include.rs | 7 + src/formats/{rvp.rs => rvp/mod.rs} | 16 +- src/formats/samsung_old/include.rs | 7 + .../{samsung_old.rs => samsung_old/mod.rs} | 18 +- src/formats/sddl_sec/include.rs | 212 +++++++++++++++++ src/formats/{sddl_sec.rs => sddl_sec/mod.rs} | 220 +----------------- src/formats/slp/include.rs | 43 ++++ src/formats/{slp.rs => slp/mod.rs} | 50 +--- src/formats/sony_bdp/include.rs | 52 +++++ src/formats/{sony_bdp.rs => sony_bdp/mod.rs} | 59 +---- src/utils.rs | 11 +- 69 files changed, 1678 insertions(+), 1629 deletions(-) create mode 100644 src/formats/amlogic/include.rs rename src/formats/{amlogic.rs => amlogic/mod.rs} (69%) rename src/{utils => formats/android_ota_payload}/android_ota_update_metadata.rs (100%) create mode 100644 src/formats/android_ota_payload/include.rs rename src/formats/{android_ota_payload.rs => android_ota_payload/mod.rs} (86%) create mode 100644 src/formats/bdl/include.rs rename src/formats/{bdl.rs => bdl/mod.rs} (55%) rename src/formats/{epk.rs => epk/mod.rs} (96%) create mode 100644 src/formats/epk1/include.rs rename src/formats/{epk1.rs => epk1/mod.rs} (84%) create mode 100644 src/formats/epk2/include.rs rename src/formats/{epk2.rs => epk2/mod.rs} (79%) create mode 100644 src/formats/epk2b/include.rs rename src/formats/{epk2b.rs => epk2b/mod.rs} (71%) create mode 100644 src/formats/epk3/include.rs rename src/formats/{epk3.rs => epk3/mod.rs} (72%) create mode 100644 src/formats/funai_upg/include.rs rename src/formats/{funai_upg.rs => funai_upg/mod.rs} (79%) create mode 100644 src/formats/invincible_image/include.rs rename src/formats/{invincible_image.rs => invincible_image/mod.rs} (60%) rename src/formats/{msd.rs => msd/mod.rs} (94%) rename src/{utils => formats/msd}/msd_ouith_parser_old.rs (100%) rename src/{utils => formats/msd}/msd_ouith_parser_tizen_1_8.rs (100%) rename src/{utils => formats/msd}/msd_ouith_parser_tizen_1_9.rs (100%) create mode 100644 src/formats/msd10/include.rs rename src/formats/{msd10.rs => msd10/mod.rs} (85%) create mode 100644 src/formats/msd11/include.rs rename src/formats/{msd11.rs => msd11/mod.rs} (76%) create mode 100644 src/formats/mstar/include.rs rename src/formats/{mstar.rs => mstar/mod.rs} (94%) create mode 100644 src/formats/mtk_bdp/include.rs rename src/formats/{mtk_bdp.rs => mtk_bdp/mod.rs} (75%) rename src/{utils => formats/mtk_pkg}/huffman_tables.rs (100%) create mode 100644 src/formats/mtk_pkg/include.rs rename src/{utils => formats/mtk_pkg}/lzhs.rs (98%) rename src/formats/{mtk_pkg.rs => mtk_pkg/mod.rs} (78%) create mode 100644 src/formats/mtk_pkg_new/include.rs rename src/formats/{mtk_pkg_new.rs => mtk_pkg_new/mod.rs} (77%) create mode 100644 src/formats/mtk_pkg_old/include.rs rename src/formats/{mtk_pkg_old.rs => mtk_pkg_old/mod.rs} (71%) rename src/{utils => formats/mtk_pkg_old}/mtk_crypto.rs (100%) create mode 100644 src/formats/novatek/include.rs rename src/formats/{novatek.rs => novatek/mod.rs} (64%) create mode 100644 src/formats/nvt_timg/include.rs rename src/formats/{nvt_timg.rs => nvt_timg/mod.rs} (65%) create mode 100644 src/formats/pana_dvd/include.rs rename src/{utils => formats/pana_dvd}/lzss.rs (100%) rename src/formats/{pana_dvd.rs => pana_dvd/mod.rs} (73%) rename src/{utils => formats/pana_dvd}/pana_dvd_crypto.rs (100%) create mode 100644 src/formats/pfl_upg/include.rs rename src/formats/{pfl_upg.rs => pfl_upg/mod.rs} (73%) create mode 100644 src/formats/pup/include.rs rename src/formats/{pup.rs => pup/mod.rs} (84%) create mode 100644 src/formats/roku/include.rs rename src/formats/{roku.rs => roku/mod.rs} (72%) create mode 100644 src/formats/ruf/include.rs rename src/formats/{ruf.rs => ruf/mod.rs} (66%) create mode 100644 src/formats/rvp/include.rs rename src/formats/{rvp.rs => rvp/mod.rs} (91%) create mode 100644 src/formats/samsung_old/include.rs rename src/formats/{samsung_old.rs => samsung_old/mod.rs} (92%) create mode 100644 src/formats/sddl_sec/include.rs rename src/formats/{sddl_sec.rs => sddl_sec/mod.rs} (52%) create mode 100644 src/formats/slp/include.rs rename src/formats/{slp.rs => slp/mod.rs} (67%) create mode 100644 src/formats/sony_bdp/include.rs rename src/formats/{sony_bdp.rs => sony_bdp/mod.rs} (50%) diff --git a/src/formats.rs b/src/formats.rs index 47f8a89..5d88d44 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -42,36 +42,154 @@ pub mod mtk_pkg_old; pub mod mtk_pkg_new; pub mod mtk_bdp; +//define all formats here pub fn get_registry() -> Vec { return vec![ - crate::formats::mstar::format(), - crate::formats::samsung_old::format(), - crate::formats::nvt_timg::format(), - crate::formats::pfl_upg::format(), - crate::formats::sddl_sec::format(), - crate::formats::novatek::format(), - crate::formats::ruf::format(), - crate::formats::invincible_image::format(), - crate::formats::slp::format(), - crate::formats::roku::format(), - crate::formats::sony_bdp::format(), - crate::formats::rvp::format(), - crate::formats::funai_upg::format(), - crate::formats::pana_dvd::format(), - crate::formats::android_ota_payload::format(), - crate::formats::bdl::format(), - crate::formats::amlogic::format(), - crate::formats::pup::format(), - crate::formats::msd10::format(), - crate::formats::msd11::format(), - crate::formats::epk::format(), - crate::formats::epk1::format(), - crate::formats::epk2::format(), - crate::formats::epk2b::format(), - crate::formats::epk3::format(), - crate::formats::mtk_pkg::format(), - crate::formats::mtk_pkg_old::format(), - crate::formats::mtk_pkg_new::format(), - crate::formats::mtk_bdp::format(), + Format { + name: "mstar", + detector_func: crate::formats::mstar::is_mstar_file, + extractor_func: crate::formats::mstar::extract_mstar, + }, + Format { + name: "samsung_old", + detector_func: crate::formats::samsung_old::is_samsung_old_dir, + extractor_func: crate::formats::samsung_old::extract_samsung_old, + }, + Format { + name: "nvt_timg", + detector_func: crate::formats::nvt_timg::is_nvt_timg_file, + extractor_func: crate::formats::nvt_timg::extract_nvt_timg, + }, + Format { + name: "pfl_upg", + detector_func: crate::formats::pfl_upg::is_pfl_upg_file, + extractor_func: crate::formats::pfl_upg::extract_pfl_upg, + }, + Format { + name: "sddl_sec", + detector_func: crate::formats::sddl_sec::is_sddl_sec_file, + extractor_func: crate::formats::sddl_sec::extract_sddl_sec, + }, + Format { + name: "novatek", + detector_func: crate::formats::novatek::is_novatek_file, + extractor_func: crate::formats::novatek::extract_novatek, + }, + Format { + name: "ruf", + detector_func: crate::formats::ruf::is_ruf_file, + extractor_func: crate::formats::ruf::extract_ruf, + }, + Format { + name: "invincible_image", + detector_func: crate::formats::invincible_image::is_invincible_image_file, + extractor_func: crate::formats::invincible_image::extract_invincible_image, + }, + Format { + name: "slp", + detector_func: crate::formats::slp::is_slp_file, + extractor_func: crate::formats::slp::extract_slp, + }, + Format { + name: "roku", + detector_func: crate::formats::roku::is_roku_file, + extractor_func: crate::formats::roku::extract_roku, + }, + Format { + name: "sony_bdp", + detector_func: crate::formats::sony_bdp::is_sony_bdp_file, + extractor_func: crate::formats::sony_bdp::extract_sony_bdp, + }, + Format { + name: "rvp", + detector_func: crate::formats::rvp::is_rvp_file, + extractor_func: crate::formats::rvp::extract_rvp, + }, + Format { + name: "funai_upg", + detector_func: crate::formats::funai_upg::is_funai_upg_file, + extractor_func: crate::formats::funai_upg::extract_funai_upg, + }, + Format { + name: "pana_dvd", + detector_func: crate::formats::pana_dvd::is_pana_dvd_file, + extractor_func: crate::formats::pana_dvd::extract_pana_dvd, + }, + Format { + name: "android_ota_payload", + detector_func: crate::formats::android_ota_payload::is_android_ota_payload_file, + extractor_func: crate::formats::android_ota_payload::extract_android_ota_payload, + }, + Format { + name: "bdl", + detector_func: crate::formats::bdl::is_bdl_file, + extractor_func: crate::formats::bdl::extract_bdl, + }, + Format { + name: "amlogic", + detector_func: crate::formats::amlogic::is_amlogic_file, + extractor_func: crate::formats::amlogic::extract_amlogic, + }, + Format { + name: "pup", + detector_func: crate::formats::pup::is_pup_file, + extractor_func: crate::formats::pup::extract_pup, + }, + Format { + name: "msd10", + detector_func: crate::formats::msd10::is_msd10_file, + extractor_func: crate::formats::msd10::extract_msd10, + }, + Format { + name: "msd11", + detector_func: crate::formats::msd11::is_msd11_file, + extractor_func: crate::formats::msd11::extract_msd11, + }, + Format { + name: "epk", + detector_func: crate::formats::epk::is_epk_file, + extractor_func: crate::formats::epk::extract_epk, + }, + Format { + name: "epk1", + detector_func: crate::formats::epk1::is_epk1_file, + extractor_func: crate::formats::epk1::extract_epk1, + }, + Format { + name: "epk2", + detector_func: crate::formats::epk2::is_epk2_file, + extractor_func: crate::formats::epk2::extract_epk2, + }, + Format { + name: "epk2b", + detector_func: crate::formats::epk2b::is_epk2b_file, + extractor_func: crate::formats::epk2b::extract_epk2b, + }, + Format { + name: "epk3", + detector_func: crate::formats::epk3::is_epk3_file, + extractor_func: crate::formats::epk3::extract_epk3, + }, + Format { + name: "mtk_pkg", + detector_func: crate::formats::mtk_pkg::is_mtk_pkg_file, + extractor_func: crate::formats::mtk_pkg::extract_mtk_pkg, + }, + Format { + name: "mtk_pkg_old", + detector_func: crate::formats::mtk_pkg_old::is_mtk_pkg_old_file, + extractor_func: crate::formats::mtk_pkg_old::extract_mtk_pkg_old, + }, + Format { + name: "mtk_pkg_new", + detector_func: crate::formats::mtk_pkg_new::is_mtk_pkg_new_file, + extractor_func: crate::formats::mtk_pkg_new::extract_mtk_pkg_new, + }, + Format { + name: "mtk_bdp", + detector_func: crate::formats::mtk_bdp::is_mtk_bdp_file, + extractor_func: crate::formats::mtk_bdp::extract_mtk_bdp, + }, + ] } \ No newline at end of file diff --git a/src/formats/amlogic/include.rs b/src/formats/amlogic/include.rs new file mode 100644 index 0000000..428115e --- /dev/null +++ b/src/formats/amlogic/include.rs @@ -0,0 +1,39 @@ +use crate::utils::common; +use binrw::{BinRead}; + +#[derive(BinRead)] +pub struct ImageHeader { + _crc32: u32, + pub version: u32, + _magic_bytes: [u8; 4], //56 19 B5 27 + pub image_size: u64, + pub item_align_size: u32, + pub item_count: u32, + _reserved: [u8; 36], +} + +#[derive(BinRead)] +pub struct ItemEntry { + _item_id: u32, + pub file_type: u32, + _current_offset_in_item: u64, + pub offset_in_image: u64, + pub item_size: u64, + item_type_bytes: [u8; 256], + name_bytes: [u8; 256], + _verify: u32, + _is_backup_item: u16, + _backup_item_id: u16, + _reserved: [u8; 24], +} +impl ItemEntry { + pub fn item_type(&self) -> String { + common::string_from_bytes(&self.item_type_bytes) + } + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } + pub fn is_sparse(&self) -> bool { + self.file_type == 254 + } +} \ No newline at end of file diff --git a/src/formats/amlogic.rs b/src/formats/amlogic/mod.rs similarity index 69% rename from src/formats/amlogic.rs rename to src/formats/amlogic/mod.rs index 74e55f1..53b1706 100644 --- a/src/formats/amlogic.rs +++ b/src/formats/amlogic/mod.rs @@ -1,53 +1,15 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "amlogic", detector_func: is_amlogic_file, extractor_func: extract_amlogic } -} +use crate::AppContext; -use std::path::{Path}; +use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; -use crate::utils::sparse::{unsparse_to_file}; - -#[derive(BinRead)] -struct ImageHeader { - _crc32: u32, - version: u32, - #[br(count = 4)] _magic_bytes: Vec, //56 19 B5 27 - image_size: u64, - item_align_size: u32, - item_count: u32, - #[br(count = 36)] _reserved: Vec, -} - -#[derive(BinRead)] -struct ItemEntry { - _item_id: u32, - file_type: u32, - _current_offset_in_item: u64, - offset_in_image: u64, - item_size: u64, - #[br(count = 256)] item_type_bytes: Vec, - #[br(count = 256)] name_bytes: Vec, - _verify: u32, - _is_backup_item: u16, - _backup_item_id: u16, - #[br(count = 24)] _reserved: Vec, -} -impl ItemEntry { - fn item_type(&self) -> String { - common::string_from_bytes(&self.item_type_bytes) - } - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } - fn is_sparse(&self) -> bool { - self.file_type == 254 - } -} +use crate::utils::sparse::unsparse_to_file; +use include::*; pub fn is_amlogic_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/utils/android_ota_update_metadata.rs b/src/formats/android_ota_payload/android_ota_update_metadata.rs similarity index 100% rename from src/utils/android_ota_update_metadata.rs rename to src/formats/android_ota_payload/android_ota_update_metadata.rs diff --git a/src/formats/android_ota_payload/include.rs b/src/formats/android_ota_payload/include.rs new file mode 100644 index 0000000..e508bbd --- /dev/null +++ b/src/formats/android_ota_payload/include.rs @@ -0,0 +1,9 @@ +use binrw::{BinRead}; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 4], //CrAU + pub file_format_version: u64, + pub manifest_size: u64, + pub metadata_signature_size: u32, +} \ No newline at end of file diff --git a/src/formats/android_ota_payload.rs b/src/formats/android_ota_payload/mod.rs similarity index 86% rename from src/formats/android_ota_payload.rs rename to src/formats/android_ota_payload/mod.rs index 0fa13fd..adcc919 100644 --- a/src/formats/android_ota_payload.rs +++ b/src/formats/android_ota_payload/mod.rs @@ -1,26 +1,18 @@ +mod include; +mod android_ota_update_metadata; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "android_ota_payload", detector_func: is_android_ota_payload_file, extractor_func: extract_android_ota_payload } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; -use std::path::{Path}; -use std::io::{Write}; -use binrw::{BinRead, BinReaderExt}; +use std::path::Path; +use std::io::Write; +use binrw::BinReaderExt; use prost::Message; use crate::utils::common; -use crate::utils::android_ota_update_metadata::{DeltaArchiveManifest, install_operation}; +use android_ota_update_metadata::{DeltaArchiveManifest, install_operation}; use crate::utils::compression::{decompress_bzip, decompress_xz}; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] _magic_bytes: Vec, //CrAU - file_format_version: u64, - manifest_size: u64, - metadata_signature_size: u32, -} +use include::*; pub fn is_android_ota_payload_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/bdl/include.rs b/src/formats/bdl/include.rs new file mode 100644 index 0000000..3f94337 --- /dev/null +++ b/src/formats/bdl/include.rs @@ -0,0 +1,76 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct BdlHeader { + _magic_bytes: [u8; 4], //ibdl + _file_version: [u8; 8], + _unk1: u32, + pub pkg_count: u32, + _unk2: [u8; 12], + date_bytes: [u8; 256], + manufacturer_bytes: [u8; 256], + model_bytes: [u8; 256], + _unk3: [u8; 9], + version_bytes: [u8; 256], + info_bytes: [u8; 1280], +} +impl BdlHeader { + pub fn date(&self) -> String { + common::string_from_bytes(&self.date_bytes) + } + pub fn manufacturer(&self) -> String { + common::string_from_bytes(&self.manufacturer_bytes) + } + pub fn model(&self) -> String { + common::string_from_bytes(&self.model_bytes) + } + pub fn version(&self) -> String { + common::string_from_bytes(&self.version_bytes) + } + pub fn info(&self) -> String { + common::string_from_bytes(&self.info_bytes) + } +} + +#[derive(BinRead)] +pub struct PkgListEntry { + pub offset: u64, + pub size: u64, +} + +#[derive(BinRead)] +pub struct PkgHeader { + _magic_bytes: [u8; 4], //ipkg + _unk1: [u8; 12], + pub entry_count: u32, + _unk2: [u8; 12], + version_bytes: [u8; 256], + manufacturer_bytes: [u8; 256], + name_bytes: [u8; 256], + _unk3: [u8; 285], +} +impl PkgHeader { + pub fn version(&self) -> String { + common::string_from_bytes(&self.version_bytes) + } + pub fn manufacturer(&self) -> String { + common::string_from_bytes(&self.manufacturer_bytes) + } + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} + +#[derive(BinRead)] +pub struct PkgEntry { + name_bytes: [u8; 256], + pub offset: u64, + pub size: u64, + _crc32: u32, +} +impl PkgEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} \ No newline at end of file diff --git a/src/formats/bdl.rs b/src/formats/bdl/mod.rs similarity index 55% rename from src/formats/bdl.rs rename to src/formats/bdl/mod.rs index bd7952f..4445cae 100644 --- a/src/formats/bdl.rs +++ b/src/formats/bdl/mod.rs @@ -1,89 +1,14 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "bdl", detector_func: is_bdl_file, extractor_func: extract_bdl } -} +use crate::AppContext; -use std::path::{Path}; +use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; - -#[derive(BinRead)] -struct BdlHeader { - #[br(count = 4)] _magic_bytes: Vec, //ibdl - #[br(count = 8)] _file_version: Vec, - _unk1: u32, - pkg_count: u32, - #[br(count = 12)] _unk2: Vec, - #[br(count = 256)] date_bytes: Vec, - #[br(count = 256)] manufacturer_bytes: Vec, - #[br(count = 256)] model_bytes: Vec, - #[br(count = 9)] _unk3: Vec, - #[br(count = 256)] version_bytes: Vec, - #[br(count = 1280)] info_bytes: Vec, -} -impl BdlHeader { - fn date(&self) -> String { - common::string_from_bytes(&self.date_bytes) - } - fn manufacturer(&self) -> String { - common::string_from_bytes(&self.manufacturer_bytes) - } - fn model(&self) -> String { - common::string_from_bytes(&self.model_bytes) - } - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn info(&self) -> String { - common::string_from_bytes(&self.info_bytes) - } -} - -#[derive(BinRead)] -struct PkgListEntry { - offset: u64, - size: u64, -} - -#[derive(BinRead)] -struct PkgHeader { - #[br(count = 4)] _magic_bytes: Vec, //ipkg - #[br(count = 12)] _unk1: Vec, - entry_count: u32, - #[br(count = 12)] _unk2: Vec, - #[br(count = 256)] version_bytes: Vec, - #[br(count = 256)] manufacturer_bytes: Vec, - #[br(count = 256)] name_bytes: Vec, - #[br(count = 285)] _unk3: Vec, -} -impl PkgHeader { - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn manufacturer(&self) -> String { - common::string_from_bytes(&self.manufacturer_bytes) - } - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} - -#[derive(BinRead)] -struct PkgEntry { - #[br(count = 256)] name_bytes: Vec, - offset: u64, - size: u64, - _crc32: u32, -} -impl PkgEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} +use include::*; pub fn is_bdl_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/epk.rs b/src/formats/epk/mod.rs similarity index 96% rename from src/formats/epk.rs rename to src/formats/epk/mod.rs index 9307d0b..d23fbec 100644 --- a/src/formats/epk.rs +++ b/src/formats/epk/mod.rs @@ -1,8 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "epk", detector_func: is_epk_file, extractor_func: extract_epk } -} +use crate::AppContext; use crate::utils::common; use crate::formats; diff --git a/src/formats/epk1/include.rs b/src/formats/epk1/include.rs new file mode 100644 index 0000000..43cad85 --- /dev/null +++ b/src/formats/epk1/include.rs @@ -0,0 +1,31 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct CommonHeader { + _magic_bytes: [u8; 4], + pub file_size: u32, + pub pak_count: u32, +} + +#[derive(BinRead)] +pub struct PakHeader { + pak_name_bytes: [u8; 4], + pub image_size: u32, + platform_id_bytes: [u8; 64], + _reserved: [u8; 56], +} +impl PakHeader { + pub fn pak_name(&self) -> String { + common::string_from_bytes(&self.pak_name_bytes) + } + pub fn platform_id(&self) -> String { + common::string_from_bytes(&self.platform_id_bytes) + } +} + +#[derive(BinRead)] +pub struct Pak { + pub offset : u32, + pub size : u32, +} \ No newline at end of file diff --git a/src/formats/epk1.rs b/src/formats/epk1/mod.rs similarity index 84% rename from src/formats/epk1.rs rename to src/formats/epk1/mod.rs index 12a177b..110671c 100644 --- a/src/formats/epk1.rs +++ b/src/formats/epk1/mod.rs @@ -1,44 +1,14 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "epk1", detector_func: is_epk1_file, extractor_func: extract_epk1 } -} +use crate::AppContext; -use std::path::{Path}; +use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; - -#[derive(BinRead)] -struct CommonHeader { - #[br(count = 4)] _magic_bytes: Vec, - file_size: u32, - pak_count: u32, -} - -#[derive(BinRead)] -struct PakHeader { - #[br(count = 4)] pak_name_bytes: Vec, - image_size: u32, - #[br(count = 64)] platform_id_bytes: Vec, - #[br(count = 56)] _reserved: Vec, -} -impl PakHeader { - fn pak_name(&self) -> String { - common::string_from_bytes(&self.pak_name_bytes) - } - fn platform_id(&self) -> String { - common::string_from_bytes(&self.platform_id_bytes) - } -} - -#[derive(BinRead)] -struct Pak { - offset : u32, - size : u32, -} +use include::*; pub fn is_epk1_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/epk2/include.rs b/src/formats/epk2/include.rs new file mode 100644 index 0000000..47a1bfb --- /dev/null +++ b/src/formats/epk2/include.rs @@ -0,0 +1,59 @@ +use crate::utils::common; +use binrw::BinRead; + +pub static SIGNATURE_SIZE: u32 = 128; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 4], //epak + pub file_size: u32, + pub pak_count: u32, + _epk2_magic: [u8; 4], //EPK2 + pub version: [u8; 4], + ota_id_bytes: [u8; 32], +} +impl Header { + pub fn ota_id(&self) -> String { + common::string_from_bytes(&self.ota_id_bytes) + } +} + +#[derive(BinRead)] +pub struct PakEntry { + pub offset: u32, + pub size: u32, + name_bytes: [u8; 4], + _version: [u8; 4], + pub segment_size: u32, +} +impl PakEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} + +#[derive(BinRead)] +pub struct PakHeader { + _pak_name_bytes: [u8; 4], + pub image_size: u32, + platform_id_bytes: [u8; 64], + _sw_version: u32, + _sw_date: u32, + _build_type: u32, + pub segment_count: u32, + pub segment_size: u32, + pub segment_index: u32, + _pak_magic_bytes: [u8; 4], //MPAK + _reserved: [u8; 24], + _segment_crc32: u32, +} +impl PakHeader { + pub fn platform_id(&self) -> String { + common::string_from_bytes(&self.platform_id_bytes) + } +} +pub struct Pak { + pub offset: u32, + pub _size: u32, + pub name: String, +} \ No newline at end of file diff --git a/src/formats/epk2.rs b/src/formats/epk2/mod.rs similarity index 79% rename from src/formats/epk2.rs rename to src/formats/epk2/mod.rs index 843ffc5..dd0e8d0 100644 --- a/src/formats/epk2.rs +++ b/src/formats/epk2/mod.rs @@ -1,75 +1,16 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "epk2", detector_func: is_epk2_file, extractor_func: extract_epk2 } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; -use std::path::{Path}; +use std::path::Path; use std::io::{Write, Seek, SeekFrom, Cursor}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::keys; use crate::formats::epk::{decrypt_aes_ecb_auto, find_key}; - -static SIGNATURE_SIZE: u32 = 128; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] _magic_bytes: Vec, //epak - file_size: u32, - pak_count: u32, - #[br(count = 4)] _epk2_magic: Vec, //EPK2 - #[br(count = 4)] version: Vec, - #[br(count = 32)] ota_id_bytes: Vec, -} -impl Header { - fn ota_id(&self) -> String { - common::string_from_bytes(&self.ota_id_bytes) - } -} - -#[derive(BinRead)] -struct PakEntry { - offset: u32, - size: u32, - #[br(count = 4)] name_bytes: Vec, - #[br(count = 4)] _version: Vec, - segment_size: u32, -} -impl PakEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} - -#[derive(BinRead)] -struct PakHeader { - #[br(count = 4)] _pak_name_bytes: Vec, - image_size: u32, - #[br(count = 64)] platform_id_bytes: Vec, - _sw_version: u32, - _sw_date: u32, - _build_type: u32, - segment_count: u32, - segment_size: u32, - segment_index: u32, - #[br(count = 4)] _pak_magic_bytes: Vec, //MPAK - #[br(count = 24)] _reserved: Vec, - _segment_crc32: u32, -} -impl PakHeader { - fn platform_id(&self) -> String { - common::string_from_bytes(&self.platform_id_bytes) - } -} - -struct Pak { - offset: u32, - _size: u32, - name: String, -} +use include::*; pub fn is_epk2_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -86,8 +27,6 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let mut file = app_ctx.file().ok_or("Extractor expected file")?; file.seek(SeekFrom::Start(0))?; - let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?; - let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?; let stored_header = common::read_exact(&mut file, 1584)?; //max header size let header; diff --git a/src/formats/epk2b/include.rs b/src/formats/epk2b/include.rs new file mode 100644 index 0000000..71954c0 --- /dev/null +++ b/src/formats/epk2b/include.rs @@ -0,0 +1,47 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct EpkHeader { + _epk_magic: [u8; 4], //epak + pub file_size: u32, + pub pak_count: u32, + _epk2_magic: [u8; 4], //EPK2 + pub version: [u8; 4], + ota_id_bytes: [u8; 32], +} +impl EpkHeader { + pub fn ota_id(&self) -> String { + common::string_from_bytes(&self.ota_id_bytes) + } +} + +#[derive(BinRead)] +pub struct PakHeader { + pak_name_bytes: [u8; 4], + pub image_size: u32, + platform_id_bytes: [u8; 64], + _sw_version: u32, + _sw_date: u32, + _build_type: u32, + pub segment_count: u32, + pub segment_size: u32, + pub segment_index: u32, + _pak_magic_bytes: [u8; 4], //MPAK + _reserved: [u8; 24], + _segment_crc32: u32, +} +impl PakHeader { + pub fn pak_name(&self) -> String { + common::string_from_bytes(&self.pak_name_bytes) + } + pub fn platform_id(&self) -> String { + common::string_from_bytes(&self.platform_id_bytes) + } +} + +#[derive(BinRead)] +pub struct Pak { + pub offset : u32, + pub size : u32, +} \ No newline at end of file diff --git a/src/formats/epk2b.rs b/src/formats/epk2b/mod.rs similarity index 71% rename from src/formats/epk2b.rs rename to src/formats/epk2b/mod.rs index ef39257..37ff92b 100644 --- a/src/formats/epk2b.rs +++ b/src/formats/epk2b/mod.rs @@ -1,60 +1,14 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "epk2b", detector_func: is_epk2b_file, extractor_func: extract_epk2b } -} +use crate::AppContext; use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; - -#[derive(BinRead)] -struct EpkHeader { - #[br(count = 4)] _epk_magic: Vec, //epak - file_size: u32, - pak_count: u32, - #[br(count = 4)] _epk2_magic: Vec, //EPK2 - #[br(count = 4)] version: Vec, - #[br(count = 32)] ota_id_bytes: Vec, -} -impl EpkHeader { - fn ota_id(&self) -> String { - common::string_from_bytes(&self.ota_id_bytes) - } -} - -#[derive(BinRead)] -struct PakHeader { - #[br(count = 4)] pak_name_bytes: Vec, - image_size: u32, - #[br(count = 64)] platform_id_bytes: Vec, - _sw_version: u32, - _sw_date: u32, - _build_type: u32, - segment_count: u32, - segment_size: u32, - segment_index: u32, - #[br(count = 4)] _pak_magic_bytes: Vec, //MPAK - #[br(count = 24)] _reserved: Vec, - _segment_crc32: u32, -} -impl PakHeader { - fn pak_name(&self) -> String { - common::string_from_bytes(&self.pak_name_bytes) - } - fn platform_id(&self) -> String { - common::string_from_bytes(&self.platform_id_bytes) - } -} - -#[derive(BinRead)] -struct Pak { - offset : u32, - size : u32, -} +use include::*; pub fn is_epk2b_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/epk3/include.rs b/src/formats/epk3/include.rs new file mode 100644 index 0000000..c0972d0 --- /dev/null +++ b/src/formats/epk3/include.rs @@ -0,0 +1,64 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 4], //EPK3 + pub version: [u8; 4], + ota_id_bytes: [u8; 32], + pub package_info_size: u32, + _bchunked: u32, +} +impl Header { + pub fn ota_id(&self) -> String { + common::string_from_bytes(&self.ota_id_bytes) + } +} + +#[derive(BinRead)] +pub struct HeaderNewEx { + _pak_info_magic: [u8; 4], + encrypt_type_bytes: [u8; 6], + update_type_bytes: [u8; 6], + pub update_platform_version: f32, + pub compatible_minimum_version: f32, + pub need_to_check_compatible_version: i32, +} +impl HeaderNewEx { + pub fn encrypt_type(&self) -> String { + common::string_from_bytes(&self.encrypt_type_bytes) + } + pub fn update_type(&self) -> String { + common::string_from_bytes(&self.update_type_bytes) + } +} + +#[derive(BinRead)] +pub struct PkgInfoHeader { + pub package_info_list_size: u32, + pub package_info_count: u32, +} + +#[derive(BinRead)] +pub struct PkgInfoEntry { + _package_type: u32, + _package_info_size: u32, + package_name_bytes: [u8; 128], + _package_version_bytes: [u8; 96], + _package_architecture_bytes: [u8; 32], + _checksum: [u8; 32], + pub package_size: u32, + _dipk: u32, + //segment info + _is_segmented: u32, + pub segment_index: u32, + pub segment_count: u32, + pub segment_size: u32, + // + _unk: u32, +} +impl PkgInfoEntry { + pub fn package_name(&self) -> String { + common::string_from_bytes(&self.package_name_bytes) + } +} diff --git a/src/formats/epk3.rs b/src/formats/epk3/mod.rs similarity index 72% rename from src/formats/epk3.rs rename to src/formats/epk3/mod.rs index ed935fb..4858c7a 100644 --- a/src/formats/epk3.rs +++ b/src/formats/epk3/mod.rs @@ -1,80 +1,16 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "epk3", detector_func: is_epk3_file, extractor_func: extract_epk3 } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; -use std::path::{Path}; +use std::path::Path; use std::io::{Write, Seek, SeekFrom, Cursor}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::keys; use crate::formats::epk::{decrypt_aes_ecb_auto, find_key}; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] _magic_bytes: Vec, //EPK3 - #[br(count = 4)] version: Vec, - #[br(count = 32)] ota_id_bytes: Vec, - package_info_size: u32, - _bchunked: u32, -} -impl Header { - fn ota_id(&self) -> String { - common::string_from_bytes(&self.ota_id_bytes) - } -} - -#[derive(BinRead)] -struct HeaderNewEx { - #[br(count = 4)] _pak_info_magic: Vec, - #[br(count = 6)] encrypt_type_bytes: Vec, - #[br(count = 6)] update_type_bytes: Vec, - update_platform_version: f32, - compatible_minimum_version: f32, - need_to_check_compatible_version: i32, -} -impl HeaderNewEx { - fn encrypt_type(&self) -> String { - common::string_from_bytes(&self.encrypt_type_bytes) - } - fn update_type(&self) -> String { - common::string_from_bytes(&self.update_type_bytes) - } -} - - -#[derive(BinRead)] -struct PkgInfoHeader { - package_info_list_size: u32, - package_info_count: u32, -} - -#[derive(BinRead)] -struct PkgInfoEntry { - _package_type: u32, - _package_info_size: u32, - #[br(count = 128)] package_name_bytes: Vec, - #[br(count = 96)] _package_version_bytes: Vec, - #[br(count = 32)] _package_architecture_bytes: Vec, - #[br(count = 32)] _checksum: Vec, - package_size: u32, - _dipk: u32, - //segment info - _is_segmented: u32, - segment_index: u32, - segment_count: u32, - segment_size: u32, - // - _unk: u32, -} -impl PkgInfoEntry { - fn package_name(&self) -> String { - common::string_from_bytes(&self.package_name_bytes) - } -} +use include::*; pub fn is_epk3_file(_app_ctx: &AppContext) -> Result>, Box> { Ok(None) diff --git a/src/formats/funai_upg/include.rs b/src/formats/funai_upg/include.rs new file mode 100644 index 0000000..08331ae --- /dev/null +++ b/src/formats/funai_upg/include.rs @@ -0,0 +1,15 @@ +use binrw::BinRead; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 6], + pub entry_count: u16, + pub file_size: u32, +} + +#[derive(BinRead)] +pub struct Entry { + pub entry_type: u16, + pub entry_size: u32, + _unk: u16, +} \ No newline at end of file diff --git a/src/formats/funai_upg.rs b/src/formats/funai_upg/mod.rs similarity index 79% rename from src/formats/funai_upg.rs rename to src/formats/funai_upg/mod.rs index 31c86c3..b721f2a 100644 --- a/src/formats/funai_upg.rs +++ b/src/formats/funai_upg/mod.rs @@ -1,29 +1,14 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "funai_upg", detector_func: is_funai_upg_file, extractor_func: extract_funai_upg } -} +use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; -use std::io::{Write}; -use binrw::{BinRead, BinReaderExt}; +use std::io::Write; +use binrw::BinReaderExt; use crate::utils::common; - -#[derive(BinRead)] -struct Header { - #[br(count = 6)] _magic_bytes: Vec, - entry_count: u16, - file_size: u32, -} - -#[derive(BinRead)] -struct Entry { - entry_type: u16, - entry_size: u32, - _unk: u16, -} +use include::*; pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/invincible_image/include.rs b/src/formats/invincible_image/include.rs new file mode 100644 index 0000000..5faa69d --- /dev/null +++ b/src/formats/invincible_image/include.rs @@ -0,0 +1,56 @@ +use crate::utils::common; +use binrw::BinRead; + +//v3 key + iv +pub static V3_KEY: [u8; 16] = [0x32, 0xe5, 0x26, 0x1e, 0x22, 0x67, 0x5e, 0x93, 0x20, 0xcf, 0x35, 0x91, 0x7c, 0x63, 0x7a, 0x36]; +pub static V3_IV: [u8; 16] = [0xe3, 0x9f, 0x36, 0x39, 0x56, 0x9a, 0x6b, 0x8d, 0x3f, 0x2e, 0xc9, 0x44, 0xd9, 0xbc, 0xec, 0x43]; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 16], + pub file_version: [u8; 4], + _unk1: u32, + ver1_bytes: [u8; 16], + ver2_bytes: [u8; 16], + _unk2: u16, + _type: u8, + pub keep_size: u32, + _unk3: u8, + pub data_start_offset: u32, + pub data_size: u32, + _data_size_2: u32, + pub skip_size: u32, + _unk4: u16, + _encryption_method: u8, // 0x01 - AES128, 0x02 - AES256 + _hash_type: u8, // 0x01 - MD5, 0x02 - SHA1 + ver3_bytes: [u8; 16], + ver4_bytes: [u8; 16], + _unk6: [u8; 11], + pub payload_count: u8, +} +impl Header { + pub fn ver1(&self) -> String { + common::string_from_bytes(&self.ver1_bytes).replace('\n', "") + } + pub fn ver2(&self) -> String { + common::string_from_bytes(&self.ver2_bytes).replace('\n', "") + } + pub fn ver3(&self) -> String { + common::string_from_bytes(&self.ver3_bytes).replace('\n', "") + } + pub fn ver4(&self) -> String { + common::string_from_bytes(&self.ver4_bytes).replace('\n', "") + } +} + +#[derive(BinRead)] +pub struct Entry { + name_bytes: [u8; 16], + pub start_offset: u32, + pub size: u32, +} +impl Entry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} \ No newline at end of file diff --git a/src/formats/invincible_image.rs b/src/formats/invincible_image/mod.rs similarity index 60% rename from src/formats/invincible_image.rs rename to src/formats/invincible_image/mod.rs index 33a0247..fae3853 100644 --- a/src/formats/invincible_image.rs +++ b/src/formats/invincible_image/mod.rs @@ -1,66 +1,15 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "invincible_image", detector_func: is_invincible_image_file, extractor_func: extract_invincible_image } -} +use crate::AppContext; use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Write, Read, Seek, SeekFrom, Cursor}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::utils::common; - -#[derive(BinRead)] -struct Header { - #[br(count = 16)] _magic_bytes: Vec, - #[br(count = 4)] file_version: Vec, - _unk1: u32, - #[br(count = 16)] ver1_bytes: Vec, - #[br(count = 16)] ver2_bytes: Vec, - _unk2: u16, - _type: u8, - keep_size: u32, - _unk3: u8, - data_start_offset: u32, - data_size: u32, - _data_size_2: u32, - skip_size: u32, - _unk4: u16, - _encryption_method: u8, // 0x01 - AES128, 0x02 - AES256 - _hash_type: u8, // 0x01 - MD5, 0x02 - SHA1 - #[br(count = 16)] ver3_bytes: Vec, - #[br(count = 16)] ver4_bytes: Vec, - #[br(count = 11)] _unk6: Vec, - payload_count: u8, -} -impl Header { - fn ver1(&self) -> String { - common::string_from_bytes(&self.ver1_bytes).replace('\n', "") - } - fn ver2(&self) -> String { - common::string_from_bytes(&self.ver2_bytes).replace('\n', "") - } - fn ver3(&self) -> String { - common::string_from_bytes(&self.ver3_bytes).replace('\n', "") - } - fn ver4(&self) -> String { - common::string_from_bytes(&self.ver4_bytes).replace('\n', "") - } -} - -#[derive(BinRead)] -struct Entry { - #[br(count = 16)] name_bytes: Vec, - start_offset: u32, - size: u32, -} -impl Entry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} +use include::*; pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -108,12 +57,8 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Box) -> Res file.seek(SeekFrom::Current(header.skip_size.into()))?; } - //v3 key + iv - let key: [u8; 16] = [0x32, 0xe5, 0x26, 0x1e, 0x22, 0x67, 0x5e, 0x93, 0x20, 0xcf, 0x35, 0x91, 0x7c, 0x63, 0x7a, 0x36]; - let iv: [u8; 16] = [0xe3, 0x9f, 0x36, 0x39, 0x56, 0x9a, 0x6b, 0x8d, 0x3f, 0x2e, 0xc9, 0x44, 0xd9, 0xbc, 0xec, 0x43]; - println!("\nDecrypting data..."); - let decrypted_data = decrypt_aes128_cbc_nopad(&encrypted_data, &key, &iv)?; + let decrypted_data = decrypt_aes128_cbc_nopad(&encrypted_data, &V3_KEY, &V3_IV)?; let mut data_reader = Cursor::new(decrypted_data); diff --git a/src/formats/msd.rs b/src/formats/msd/mod.rs similarity index 94% rename from src/formats/msd.rs rename to src/formats/msd/mod.rs index 54196db..230265b 100644 --- a/src/formats/msd.rs +++ b/src/formats/msd/mod.rs @@ -1,3 +1,8 @@ +//MSD OUITH parsers +pub mod msd_ouith_parser_old; +pub mod msd_ouith_parser_tizen_1_8; +pub mod msd_ouith_parser_tizen_1_9; + // COMMON MSD FUNCTIONS use aes::Aes128; use cbc::{Decryptor, cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit}}; diff --git a/src/utils/msd_ouith_parser_old.rs b/src/formats/msd/msd_ouith_parser_old.rs similarity index 100% rename from src/utils/msd_ouith_parser_old.rs rename to src/formats/msd/msd_ouith_parser_old.rs diff --git a/src/utils/msd_ouith_parser_tizen_1_8.rs b/src/formats/msd/msd_ouith_parser_tizen_1_8.rs similarity index 100% rename from src/utils/msd_ouith_parser_tizen_1_8.rs rename to src/formats/msd/msd_ouith_parser_tizen_1_8.rs diff --git a/src/utils/msd_ouith_parser_tizen_1_9.rs b/src/formats/msd/msd_ouith_parser_tizen_1_9.rs similarity index 100% rename from src/utils/msd_ouith_parser_tizen_1_9.rs rename to src/formats/msd/msd_ouith_parser_tizen_1_9.rs diff --git a/src/formats/msd10/include.rs b/src/formats/msd10/include.rs new file mode 100644 index 0000000..4642fea --- /dev/null +++ b/src/formats/msd10/include.rs @@ -0,0 +1,28 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct FileHeader { + _magic_bytes: [u8; 6], + pub section_count: u32 +} + +#[derive(BinRead)] +pub struct SectionEntry { + pub index: u32, + pub offset: u32, + pub size: u32, +} + +#[derive(BinRead)] +pub struct HeaderEntry { + pub offset: u32, + pub size: u32, + _name_length: u8, + #[br(count = _name_length)] name_bytes: Vec, +} +impl HeaderEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} \ No newline at end of file diff --git a/src/formats/msd10.rs b/src/formats/msd10/mod.rs similarity index 85% rename from src/formats/msd10.rs rename to src/formats/msd10/mod.rs index 49acb85..7b99408 100644 --- a/src/formats/msd10.rs +++ b/src/formats/msd10/mod.rs @@ -1,51 +1,18 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "msd10", detector_func: is_msd10_file, extractor_func: extract_msd10 } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; -use std::path::{Path}; +use std::path::Path; use std::io::{Write, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::keys; use crate::formats::msd::{decrypt_aes_salted_old, decrypt_aes_salted_tizen, decrypt_aes_tizen}; -use crate::utils::msd_ouith_parser_old::{parse_ouith_blob}; -use crate::utils::msd_ouith_parser_tizen_1_8::{parse_blob_1_8}; - -#[derive(BinRead)] -struct FileHeader { - #[br(count = 6)] _magic_bytes: Vec, - section_count: u32 -} - -#[derive(BinRead)] -struct SectionEntry { - index: u32, - offset: u32, - size: u32, -} - -#[derive(BinRead)] -struct HeaderEntry { - offset: u32, - size: u32, - _name_length: u8, - #[br(count = _name_length)] name_bytes: Vec, -} -impl HeaderEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} - -struct Section { - index: u32, - offset: u32, - size: u32, -} +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::*; pub fn is_msd10_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -64,11 +31,11 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let header: FileHeader = file.read_le()?; println!("\nNumber of sections: {}", header.section_count); - let mut sections: Vec
= Vec::new(); + 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); - sections.push(Section {index: section.index, offset: section.offset, size: section.size}); + sections.push(section); } let _zero_padding = common::read_exact(&mut file, 4)?; diff --git a/src/formats/msd11/include.rs b/src/formats/msd11/include.rs new file mode 100644 index 0000000..75dc4d8 --- /dev/null +++ b/src/formats/msd11/include.rs @@ -0,0 +1,30 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct FileHeader { + _magic_bytes: [u8; 6], + _header_checksum: u32, //CRC32 of data with the size of "_header_size" + _header_size: u64, + pub section_count: u32, +} + +#[derive(BinRead)] +pub struct SectionEntry { + pub index: u32, + pub offset: u64, + pub size: u64, +} + +#[derive(BinRead)] +pub struct HeaderEntry { + pub offset: u64, + pub size: u32, + _name_length: u8, + #[br(count = _name_length)] name_bytes: Vec, +} +impl HeaderEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} \ No newline at end of file diff --git a/src/formats/msd11.rs b/src/formats/msd11/mod.rs similarity index 76% rename from src/formats/msd11.rs rename to src/formats/msd11/mod.rs index 1c5bd1d..6b87cb3 100644 --- a/src/formats/msd11.rs +++ b/src/formats/msd11/mod.rs @@ -1,52 +1,17 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "msd11", detector_func: is_msd11_file, extractor_func: extract_msd11 } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; -use std::path::{Path}; -use std::io::{Write}; -use binrw::{BinRead, BinReaderExt}; +use std::path::Path; +use std::io::Write; +use binrw::BinReaderExt; use crate::utils::common; use crate::keys; use crate::formats::msd::{decrypt_aes_salted_tizen, decrypt_aes_tizen}; -use crate::utils::msd_ouith_parser_tizen_1_9::{parse_blob_1_9}; - -#[derive(BinRead)] -struct FileHeader { - #[br(count = 6)] _magic_bytes: Vec, - _header_checksum: u32, //CRC32 of data with the size of "_header_size" - _header_size: u64, - section_count: u32, -} - -#[derive(BinRead)] -struct SectionEntry { - index: u32, - offset: u64, - size: u64, -} - -#[derive(BinRead)] -struct HeaderEntry { - offset: u64, - size: u32, - _name_length: u8, - #[br(count = _name_length)] name_bytes: Vec, -} -impl HeaderEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} - -struct Section { - index: u32, - offset: u64, - size: u64, -} +use crate::formats::msd::msd_ouith_parser_tizen_1_9::{parse_blob_1_9}; +use include::*; pub fn is_msd11_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -65,11 +30,11 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let header: FileHeader = file.read_le()?; println!("\nNumber of sections: {}", header.section_count); - let mut sections: Vec
= Vec::new(); + 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); - sections.push(Section {index: section.index, offset: section.offset, size: section.size}); + sections.push(section); } let header_count: u32 = file.read_le()?; diff --git a/src/formats/mstar/include.rs b/src/formats/mstar/include.rs new file mode 100644 index 0000000..f51b1b4 --- /dev/null +++ b/src/formats/mstar/include.rs @@ -0,0 +1,7 @@ +pub fn parse_number(s: &str) -> Option { + if let Some(hex_str) = s.strip_prefix("0x") { + u64::from_str_radix(hex_str, 16).ok() + } else { + u64::from_str_radix(s, 16).ok() + } +} \ No newline at end of file diff --git a/src/formats/mstar.rs b/src/formats/mstar/mod.rs similarity index 94% rename from src/formats/mstar.rs rename to src/formats/mstar/mod.rs index 81b68cb..906dce0 100644 --- a/src/formats/mstar.rs +++ b/src/formats/mstar/mod.rs @@ -1,17 +1,16 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "mstar", detector_func: is_mstar_file, extractor_func: extract_mstar } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; -use std::path::{Path}; -use std::io::{Write}; +use std::path::Path; +use std::io::Write; use crate::utils::common; use crate::utils::compression::{decompress_lzma, decompress_lz4}; use crate::utils::lzop::{unlzop_to_file}; use crate::utils::sparse::{unsparse_to_file}; +use include::*; pub fn is_mstar_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -25,14 +24,6 @@ pub fn is_mstar_file(app_ctx: &AppContext) -> Result>, Box Option { - if let Some(hex_str) = s.strip_prefix("0x") { - u64::from_str_radix(hex_str, 16).ok() - } else { - u64::from_str_radix(s, 16).ok() - } -} - pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { let file = app_ctx.file().ok_or("Extractor expected file")?; diff --git a/src/formats/mtk_bdp/include.rs b/src/formats/mtk_bdp/include.rs new file mode 100644 index 0000000..6a2686c --- /dev/null +++ b/src/formats/mtk_bdp/include.rs @@ -0,0 +1,65 @@ +use crate::utils::common; +use binrw::BinRead; + +pub fn find_bytes(data: &[u8], pattern: &[u8]) -> Option { + data.windows(pattern.len()).position(|window| window == pattern) +} + +pub static PITIT_MAGIC: [u8; 8] = [0x69, 0x54, 0x49, 0x50, 0x69, 0x54, 0x49, 0x50]; +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 pit_offset: u32, + pub pit_size: u32, + _table_id: u32, +} + +#[derive(BinRead)] +pub struct PITITBITEntry { + pub bit_offset: u32, + pub bit_size: u32, + _private_data_1: u32, + _private_data_2: u32, +} + +#[derive(BinRead)] +pub struct PITHeader { + pub pit_magic: [u8; 8], + _version: u32, + pub first_entry_offset: u32, //"header len" + pub entry_size: u32, //"item lenght" + pub entry_count: u32, //"item num" +} + +pub static PIT_MAGIC: [u8; 8] = [0xDC, 0xEA, 0x30, 0x85, 0xDC, 0xEA, 0x30, 0x85]; + +#[derive(BinRead)] +pub struct PITEntry { + name_bytes: [u8; 16], + pub partition_id: u32, + _part_info: u32, + pub offset_on_nand: u32, + pub size_on_nand: u32, + _enc_size: u32, + _no_enc_size: u32, + _reserve: [u8; 24], +} +impl PITEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} + +#[derive(BinRead)] +pub struct BITEntry { + pub partition_id: u32, + pub offset: u32, + pub size: u32, + pub offset_in_target_part: u32, + _bin_info: u32, //"Bin info" +} + +pub static BIT_MAGIC: [u8; 20] = [0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85]; +pub static BIT_END_MARKER: u32 = 0x85_30_EF_EF; //EF EF 30 85 - end marker of BIT \ No newline at end of file diff --git a/src/formats/mtk_bdp.rs b/src/formats/mtk_bdp/mod.rs similarity index 75% rename from src/formats/mtk_bdp.rs rename to src/formats/mtk_bdp/mod.rs index ba01d1b..5431255 100644 --- a/src/formats/mtk_bdp.rs +++ b/src/formats/mtk_bdp/mod.rs @@ -1,82 +1,19 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "mtk_bdp", detector_func: is_mtk_bdp_file, extractor_func: extract_mtk_bdp } -} +use crate::AppContext; use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Seek, SeekFrom, Read, Write}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; +use include::*; + pub struct MtkBdpContext { pitit_offset: u64, } -static PITIT_MAGIC: [u8; 8] = [0x69, 0x54, 0x49, 0x50, 0x69, 0x54, 0x49, 0x50]; -static PITIT_END_MARKER: u32 = 0x69_54_49_50; //PITi - end marker of PITIT - -#[derive(BinRead)] -struct PITITPITEntry { - nand_size: u32, - pit_offset: u32, - pit_size: u32, - _table_id: u32, -} - -#[derive(BinRead)] -struct PITITBITEntry { - bit_offset: u32, - bit_size: u32, - _private_data_1: u32, - _private_data_2: u32, -} - -#[derive(BinRead)] -struct PITHeader { - #[br(count = 8)] pit_magic: Vec, - _version: u32, - first_entry_offset: u32, //"header len" - entry_size: u32, //"item lenght" - entry_count: u32, //"item num" -} - -static PIT_MAGIC: [u8; 8] = [0xDC, 0xEA, 0x30, 0x85, 0xDC, 0xEA, 0x30, 0x85]; - -#[derive(BinRead)] -struct PITEntry { - #[br(count = 16)] name_bytes: Vec, - partition_id: u32, - _part_info: u32, - offset_on_nand: u32, - size_on_nand: u32, - _enc_size: u32, - _no_enc_size: u32, - #[br(count = 24)] _reserve: Vec, -} -impl PITEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} - -#[derive(BinRead)] -struct BITEntry { - partition_id: u32, - offset: u32, - size: u32, - offset_in_target_part: u32, - _bin_info: u32, //"Bin info" -} - -static BIT_MAGIC: [u8; 20] = [0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85]; -static BIT_END_MARKER: u32 = 0x85_30_EF_EF; //EF EF 30 85 - end marker of BIT - -fn find_bytes(data: &[u8], pattern: &[u8]) -> Option { - data.windows(pattern.len()).position(|window| window == pattern) -} - pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result>, Box> { let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file_size = file.metadata()?.len(); diff --git a/src/utils/huffman_tables.rs b/src/formats/mtk_pkg/huffman_tables.rs similarity index 100% rename from src/utils/huffman_tables.rs rename to src/formats/mtk_pkg/huffman_tables.rs diff --git a/src/formats/mtk_pkg/include.rs b/src/formats/mtk_pkg/include.rs new file mode 100644 index 0000000..7b01dfd --- /dev/null +++ b/src/formats/mtk_pkg/include.rs @@ -0,0 +1,63 @@ +use crate::utils::common; +use binrw::BinRead; + +pub static HEADER_KEY: [u8; 16] = [ + 0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94, + 0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94, +]; + +pub static HEADER_IV: [u8; 16] = [0x00; 16]; + +pub static MTK_HEADER_MAGIC: &[u8; 8] = b"#DH@FiRm"; +pub static MTK_RESERVED_MAGIC: &[u8; 16] = b"reserved mtk inc"; +pub static MTK_META_MAGIC: &[u8; 4] = b"iMtK"; +pub static MTK_META_PAD_MAGIC: &[u8; 4] = b"iPAd"; +pub static CRYPTED_HEADER_SIZE: usize = 0x30; + +pub static HEADER_SIZE: usize = 0x90; + +pub static PHILIPS_EXTRA_HEADER_SIZE: usize = 0x80; +pub static _PHILIPS_FOOTER_SIGNATURE_SIZE: usize = 0x100; + +#[derive(BinRead)] +pub struct Header { + pub vendor_magic_bytes: [u8; 4], + _mtk_magic: [u8; 8], //#DH@FiRm + version_bytes: [u8; 60], + pub file_size: u32, + _flags: u32, + product_name_bytes: [u8; 32], +} +impl Header { + pub fn vendor_magic(&self) -> String { + common::string_from_bytes(&self.vendor_magic_bytes) + } + pub fn version(&self) -> String { + common::string_from_bytes(&self.version_bytes) + } + pub fn product_name(&self) -> String { + common::string_from_bytes(&self.product_name_bytes) + } + +} + +#[derive(BinRead)] +pub struct PartEntry { + name_bytes: [u8; 4], + pub flags: u32, + pub size: u32, +} +impl PartEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } + pub fn is_valid(&self) -> bool { + self.name().is_ascii() + } + pub fn is_encrypted(&self) -> bool { + (self.flags & 1 << 0) != 0 + } + pub fn is_compressed(&self) -> bool { //lzhs fs + (self.flags & 1 << 8) != 0 + } +} \ No newline at end of file diff --git a/src/utils/lzhs.rs b/src/formats/mtk_pkg/lzhs.rs similarity index 98% rename from src/utils/lzhs.rs rename to src/formats/mtk_pkg/lzhs.rs index db82286..634218e 100644 --- a/src/utils/lzhs.rs +++ b/src/formats/mtk_pkg/lzhs.rs @@ -3,7 +3,7 @@ use std::io::{Write, Cursor, Seek, SeekFrom, Read}; use binrw::{BinRead, BinReaderExt}; use std::path::{PathBuf}; -use crate::utils::huffman_tables::{CHARLEN, POS}; +use super::huffman_tables::{CHARLEN, POS}; use crate::utils::compression::{decompress_lz4}; #[derive(BinRead)] @@ -11,7 +11,7 @@ struct LzhsHeader { uncompressed_size: u32, compressed_size: u32, checksum_or_seg_idx: u16, //as checksum in normal lzhs header, as index in lzhs_fs header - #[br(count = 6)] padding: Vec, + padding: [u8; 6], } #[derive(BinRead)] @@ -35,7 +35,7 @@ pub fn decompress_lzhs_fs_file2file(mut file: &File, output_file: PathBuf) -> Re //lz4 type uses a 4 byte checksum instead of 1(2) byte , so the padding is not 0 anymore //maybe this method will be changed - let is_lz4 = if lzhs_header.padding != b"\x00\x00\x00\x00\x00\x00" {true} else {false}; + let is_lz4 = if &lzhs_header.padding != b"\x00\x00\x00\x00\x00\x00" {true} else {false}; println!("[{}] Segment {} - Compressed size: {}, Decompressed size: {}, Expected Checksum: 0x{:02x?}", if is_lz4 {"LZ4"} else {"LZHS"}, segment_header.checksum_or_seg_idx, lzhs_header.compressed_size, lzhs_header.uncompressed_size, lzhs_header.checksum_or_seg_idx); diff --git a/src/formats/mtk_pkg.rs b/src/formats/mtk_pkg/mod.rs similarity index 78% rename from src/formats/mtk_pkg.rs rename to src/formats/mtk_pkg/mod.rs index 9b8bac5..aa1f523 100644 --- a/src/formats/mtk_pkg.rs +++ b/src/formats/mtk_pkg/mod.rs @@ -1,86 +1,25 @@ +pub mod include; +pub mod lzhs; +mod huffman_tables; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "mtk_pkg", detector_func: is_mtk_pkg_file, extractor_func: extract_mtk_pkg } -} +use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Cursor, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; -use crate::utils::lzhs::{decompress_lzhs_fs_file2file}; use crate::keys; +use lzhs::{decompress_lzhs_fs_file2file}; +use include::*; pub struct MtkPkgContext { is_philips_variant: bool, decrypted_header: Vec, } -#[derive(BinRead)] -struct Header { - #[br(count = 4)] vendor_magic_bytes: Vec, - #[br(count = 8)] _mtk_magic: Vec, //#DH@FiRm - #[br(count = 60)] version_bytes: Vec, - file_size: u32, - _flags: u32, - #[br(count = 32)] product_name_bytes: Vec, - #[br(count = 32)] _digest: Vec, -} -impl Header { - fn vendor_magic(&self) -> String { - common::string_from_bytes(&self.vendor_magic_bytes) - } - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn product_name(&self) -> String { - common::string_from_bytes(&self.product_name_bytes) - } - -} - -#[derive(BinRead)] -struct PartEntry { - #[br(count = 4)] name_bytes: Vec, - flags: u32, - size: u32, -} -impl PartEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } - fn is_valid(&self) -> bool { - self.name().is_ascii() - } - fn is_encrypted(&self) -> bool { - (self.flags & 1 << 0) != 0 - } - fn is_compressed(&self) -> bool { //lzhs fs - (self.flags & 1 << 8) != 0 - } -} - -pub static MTK_HEADER_MAGIC: &[u8; 8] = b"#DH@FiRm"; -pub static MTK_RESERVED_MAGIC: &[u8; 16] = b"reserved mtk inc"; -pub static MTK_META_MAGIC: &[u8; 4] = b"iMtK"; -pub static MTK_META_PAD_MAGIC: &[u8; 4] = b"iPAd"; -pub static CRYPTED_HEADER_SIZE: usize = 0x30; - -static HEADER_SIZE: usize = 0x90; - -static PHILIPS_EXTRA_HEADER_SIZE: usize = 0x80; -static _PHILIPS_FOOTER_SIGNATURE_SIZE: usize = 0x100; - -static HEADER_KEY: [u8; 16] = [ - 0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94, - 0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94, -]; - -static HEADER_IV: [u8; 16] = [0x00; 16]; - pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/mtk_pkg_new/include.rs b/src/formats/mtk_pkg_new/include.rs new file mode 100644 index 0000000..7a53f4f --- /dev/null +++ b/src/formats/mtk_pkg_new/include.rs @@ -0,0 +1 @@ +pub static HEADER_SIZE: usize = 0x170; \ No newline at end of file diff --git a/src/formats/mtk_pkg_new.rs b/src/formats/mtk_pkg_new/mod.rs similarity index 77% rename from src/formats/mtk_pkg_new.rs rename to src/formats/mtk_pkg_new/mod.rs index fd0e664..dd0bfc1 100644 --- a/src/formats/mtk_pkg_new.rs +++ b/src/formats/mtk_pkg_new/mod.rs @@ -1,20 +1,18 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "mtk_pkg_new", detector_func: is_mtk_pkg_new_file, extractor_func: extract_mtk_pkg_new } -} +use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Cursor, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; -use crate::utils::lzhs::{decompress_lzhs_fs_file2file}; use crate::keys; - -use crate::formats::mtk_pkg::{MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC}; +use crate::formats::mtk_pkg::lzhs::{decompress_lzhs_fs_file2file}; +use crate::formats::mtk_pkg::include::{Header, PartEntry, MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC}; +use include::*; pub struct MtkPkgNewContext { matching_key_name: String, @@ -23,53 +21,6 @@ pub struct MtkPkgNewContext { decrypted_header: Vec, } -#[derive(BinRead)] -struct Header { - #[br(count = 4)] vendor_magic_bytes: Vec, - #[br(count = 8)] _mtk_magic: Vec, //#DH@FiRm - #[br(count = 56)] version_bytes: Vec, - _unk: u32, - file_size: u32, - _flags: u32, - #[br(count = 32)] product_name_bytes: Vec, - #[br(count = 256)] _digest: Vec, -} -impl Header { - fn vendor_magic(&self) -> String { - common::string_from_bytes(&self.vendor_magic_bytes) - } - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn product_name(&self) -> String { - common::string_from_bytes(&self.product_name_bytes) - } - -} - -#[derive(BinRead)] -struct PartEntry { - #[br(count = 4)] name_bytes: Vec, - flags: u32, - size: u32, -} -impl PartEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } - fn is_valid(&self) -> bool { - self.name().is_ascii() - } - fn is_encrypted(&self) -> bool { - (self.flags & 1 << 0) != 0 - } - fn is_compressed(&self) -> bool { //lzhs fs - (self.flags & 1 << 8) != 0 - } -} - -static HEADER_SIZE: usize = 0x170; - pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/mtk_pkg_old/include.rs b/src/formats/mtk_pkg_old/include.rs new file mode 100644 index 0000000..11e36e8 --- /dev/null +++ b/src/formats/mtk_pkg_old/include.rs @@ -0,0 +1,33 @@ +use crate::utils::common; +use binrw::BinRead; + +pub static KEY: u32 = 0x94102909; //09 29 10 94 +// first 4 bytes of header and content are additionally XORed, they have different masks although only differ by half a byte +pub static HEADER_XOR_MASK: u32 = 0x04BE7C75; //75 7C BE 04 +pub static CONTENT_XOR_MASK: u32 = 0x04BE7C72; //72 7C BE 04 + +pub static HEADER_SIZE: usize = 0x98; + +#[derive(BinRead)] +pub struct Header { + pub vendor_magic_bytes: [u8; 4], + _mtk_magic: [u8; 8], + version_bytes: [u8; 68], + pub file_size: u32, + _flags: u32, + product_name_bytes: [u8; 32], + _digest: [u8; 32], +} +impl Header { + pub fn vendor_magic(&self) -> String { + common::string_from_bytes(&self.vendor_magic_bytes) + } + pub fn version(&self) -> String { + common::string_from_bytes(&self.version_bytes) + } + pub fn product_name(&self) -> String { + common::string_from_bytes(&self.product_name_bytes) + } + +} + diff --git a/src/formats/mtk_pkg_old.rs b/src/formats/mtk_pkg_old/mod.rs similarity index 71% rename from src/formats/mtk_pkg_old.rs rename to src/formats/mtk_pkg_old/mod.rs index 3ffe9e1..6719736 100644 --- a/src/formats/mtk_pkg_old.rs +++ b/src/formats/mtk_pkg_old/mod.rs @@ -1,67 +1,18 @@ +mod include; +mod mtk_crypto; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "mtk_pkg_old", detector_func: is_mtk_pkg_old_file, extractor_func: extract_mtk_pkg_old } -} +use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Cursor, Seek}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; -use crate::utils::mtk_crypto::{decrypt}; -use crate::utils::lzhs::{decompress_lzhs_fs_file2file_old}; - -static KEY: u32 = 0x94102909; //09 29 10 94 -// first 4 bytes of header and content are additionally XORed, they have different masks although only differ by half a byte -static HEADER_XOR_MASK: u32 = 0x04BE7C75; //75 7C BE 04 -static CONTENT_XOR_MASK: u32 = 0x04BE7C72; //72 7C BE 04 - -use crate::formats::mtk_pkg::{MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC}; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] vendor_magic_bytes: Vec, - #[br(count = 8)] _mtk_magic: Vec, //#DH@FiRm - #[br(count = 68)] version_bytes: Vec, - file_size: u32, - _flags: u32, - #[br(count = 32)] product_name_bytes: Vec, - #[br(count = 32)] _digest: Vec, -} -impl Header { - fn vendor_magic(&self) -> String { - common::string_from_bytes(&self.vendor_magic_bytes) - } - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn product_name(&self) -> String { - common::string_from_bytes(&self.product_name_bytes) - } - -} - -#[derive(BinRead)] -struct PartEntry { - #[br(count = 4)] name_bytes: Vec, - flags: u32, - size: u32, -} -impl PartEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } - fn is_encrypted(&self) -> bool { - (self.flags & 1 << 0) == 1 << 0 - } - fn is_compressed(&self) -> bool { //lzhs fs - (self.flags & 1 << 8) != 0 - } -} - -static HEADER_SIZE: usize = 0x98; +use crate::formats::mtk_pkg::lzhs::{decompress_lzhs_fs_file2file_old}; +use crate::formats::mtk_pkg::include::{PartEntry, MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC}; +use mtk_crypto::{decrypt}; +use include::*; pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result>, Box> { let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/utils/mtk_crypto.rs b/src/formats/mtk_pkg_old/mtk_crypto.rs similarity index 100% rename from src/utils/mtk_crypto.rs rename to src/formats/mtk_pkg_old/mtk_crypto.rs diff --git a/src/formats/novatek/include.rs b/src/formats/novatek/include.rs new file mode 100644 index 0000000..f4eda9a --- /dev/null +++ b/src/formats/novatek/include.rs @@ -0,0 +1,30 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 4], + pub version_major: u32, + pub version_minor: u32, + _unused: u32, + firmware_name_bytes: [u8; 16], + pub data_size: u32, + _md5_checksum: [u8; 16], //data checksum + pub part_count: u32, + _data_start_offset: u32, + _signature: [u8; 128], + _header_checksum: u32, //CRC32, calculated with the field set to 0 +} +impl Header { + pub fn firmware_name(&self) -> String { + common::string_from_bytes(&self.firmware_name_bytes) + } +} + +#[derive(BinRead)] +pub struct PartEntry { + pub id: u32, + pub size: u32, + pub offset: u32, + _md5_checksum: [u8; 16], +} \ No newline at end of file diff --git a/src/formats/novatek.rs b/src/formats/novatek/mod.rs similarity index 64% rename from src/formats/novatek.rs rename to src/formats/novatek/mod.rs index 45ead49..3ef7c6f 100644 --- a/src/formats/novatek.rs +++ b/src/formats/novatek/mod.rs @@ -1,44 +1,14 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "novatek", detector_func: is_novatek_file, extractor_func: extract_novatek } -} +use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; -use std::io::{Write}; - -use binrw::{BinRead, BinReaderExt}; +use std::io::Write; +use binrw::BinReaderExt; use crate::utils::common; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] _magic_bytes: Vec, - version_major: u32, - version_minor: u32, - _unused: u32, - #[br(count = 16)] firmware_name_bytes: Vec, - data_size: u32, - #[br(count = 16)] _md5_checksum: Vec, //data checksum - part_count: u32, - _data_start_offset: u32, - #[br(count = 128)] _signature: Vec, - _header_checksum: u32, //CRC32, calculated with the field set to 0 -} -impl Header { - fn firmware_name(&self) -> String { - common::string_from_bytes(&self.firmware_name_bytes) - } -} - -#[derive(BinRead)] -struct PartEntry { - id: u32, - size: u32, - offset: u32, - #[br(count = 16)] _md5_checksum: Vec, -} +use include::*; pub fn is_novatek_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/nvt_timg/include.rs b/src/formats/nvt_timg/include.rs new file mode 100644 index 0000000..1b91aa1 --- /dev/null +++ b/src/formats/nvt_timg/include.rs @@ -0,0 +1,38 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(Debug, BinRead)] +pub struct TIMG { + _magic_bytes: [u8; 4], //TIMG + _unused1: u32, + pub data_size: u32, + _unused2: u32, + _md5_checksum: [u8; 16], + _signature: [u8; 256], +} + +#[derive(Debug, BinRead)] +pub struct PIMG { + pub magic_bytes: [u8; 4], //PIMG + _unused1: u32, + pub size: u32, + _unused2: u32, + _md5_checksum: [u8; 16], + name_bytes: [u8; 16], + dest_dev_bytes: [u8; 64], + comp_type_bytes: [u8; 16], + _unknown1: u32, + _comment: [u8; 1024], + _unknown2: u32, +} +impl PIMG { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } + pub fn dest_dev(&self) -> String { + common::string_from_bytes(&self.dest_dev_bytes) + } + pub fn comp_type(&self) -> String { + common::string_from_bytes(&self.comp_type_bytes) + } +} \ No newline at end of file diff --git a/src/formats/nvt_timg.rs b/src/formats/nvt_timg/mod.rs similarity index 65% rename from src/formats/nvt_timg.rs rename to src/formats/nvt_timg/mod.rs index 040a89e..774486c 100644 --- a/src/formats/nvt_timg.rs +++ b/src/formats/nvt_timg/mod.rs @@ -1,53 +1,16 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "nvt_timg", detector_func: is_nvt_timg_file, extractor_func: extract_nvt_timg } -} +use crate::AppContext; -use std::path::{Path}; +use std::path::Path; use std::io::{Seek, Write}; use std::fs::{self, OpenOptions}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::utils::compression::{decompress_gzip}; use crate::utils::sparse::{unsparse_to_file}; - -#[derive(Debug, BinRead)] -struct TIMG { - #[br(count = 4)] _magic_bytes: Vec, //TIMG - _unused1: u32, - data_size: u32, - _unused2: u32, - #[br(count = 16)] _md5_checksum: Vec, - #[br(count = 256)] _signature: Vec, -} - -#[derive(Debug, BinRead)] -struct PIMG { - #[br(count = 4)] magic_bytes: Vec, //PIMG - _unused1: u32, - size: u32, - _unused2: u32, - #[br(count = 16)] _md5_checksum: Vec, - #[br(count = 16)] name_bytes: Vec, - #[br(count = 64)] dest_dev_bytes: Vec, - #[br(count = 16)] comp_type_bytes: Vec, - _unknown1: u32, - #[br(count = 1024)] _comment: Vec, - _unknown2: u32, -} -impl PIMG { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } - fn dest_dev(&self) -> String { - common::string_from_bytes(&self.dest_dev_bytes) - } - fn comp_type(&self) -> String { - common::string_from_bytes(&self.comp_type_bytes) - } -} +use include::*; pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -71,7 +34,7 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Box) -> Result<(), while file.stream_position()? < file_size as u64 { pimg_i += 1; let pimg: PIMG = file.read_le()?; - if pimg.magic_bytes != b"PIMG" { + if &pimg.magic_bytes != b"PIMG" { println!("Invalid PIMG magic!"); return Ok(()); } diff --git a/src/formats/pana_dvd/include.rs b/src/formats/pana_dvd/include.rs new file mode 100644 index 0000000..080ccb0 --- /dev/null +++ b/src/formats/pana_dvd/include.rs @@ -0,0 +1,132 @@ +use crate::utils::common; +use binrw::BinRead; +use super::pana_dvd_crypto::{decrypt_data}; +use crate::utils::aes::{decrypt_aes128_cbc_nopad}; + +pub fn find_key<'a>(key_array: &'a [&'a str], data: &[u8], expected_magic: &[u8], magic_offset: usize) -> Result, Box> { + for key_hex in key_array { + let key_bytes = hex::decode(key_hex)?; + let key_array: [u8; 8] = key_bytes.as_slice().try_into()?; + let decrypted = decrypt_data(data, &key_array); + + if decrypted[magic_offset..].starts_with(expected_magic) { + return Ok(Some(key_array)); + } + } + Ok(None) +} + +pub fn find_aes_key_pair<'a>(key_array: &'a [(&'a str, &'a str, &'a str)], data: &[u8], expected_magic: &[u8], magic_offset: usize) -> Result, Box> { + for (aes_key_hex, aes_iv_hex, cust_key_hex) in key_array { + let aes_key: [u8; 16] = hex::decode(aes_key_hex)?.as_slice().try_into()?; + let aes_iv: [u8; 16] = hex::decode(aes_iv_hex)?.as_slice().try_into()?; + let aes_decrypted = decrypt_aes128_cbc_nopad(data, &aes_key, &aes_iv)?; + + let key_bytes = hex::decode(cust_key_hex)?; + let key_array: [u8; 8] = key_bytes.as_slice().try_into()?; + let decrypted = decrypt_data(&aes_decrypted, &key_array); + + if decrypted[magic_offset..].starts_with(expected_magic) { + return Ok(Some((aes_key, aes_iv, key_array))); + } + } + Ok(None) +} + +pub static MAX_HEADER_SIZE: usize = 0x2000; + +#[derive(BinRead)] +pub struct AesHeaderFileEntry { + pub offset: u32, + pub size: u32, +} + +pub struct FileEntry { + pub offset: u32, + pub base_offset: u32, +} + +//checksums are mostly Adler32, but some very old files use Checksum32 instead. + +#[derive(BinRead)] +pub struct ModuleEntry { + #[br(count = 4)] pub name_bytes: Vec, + version_bytes: [u8; 4], + _unk: u32, + pub offset: u32, + platform_bytes: [u8; 8], + _unk1: u16, + id_bytes: [u8; 6], + pub size: u32, + pub data_checksum: u32, //checksum of the entrys' DATA + _unk2: u32, + _entry_checksum: u32, //checksum of THIS header entry (all previous 44 bytes) +} +impl ModuleEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } + pub fn version(&self) -> String { + common::string_from_bytes(&self.version_bytes) + } + pub fn platform(&self) -> String { + common::string_from_bytes(&self.platform_bytes) + } + pub fn id(&self) -> String { + common::string_from_bytes(&self.id_bytes) + } + pub fn is_valid(&self) -> bool { + self.name().is_ascii() && self.platform().is_ascii() + } +} + +#[derive(BinRead)] +pub struct MainListHeader { + _checksum: u32, //checksum of the MAIN LIST + _unk: u32, //seems to be always 1? + pub list_size: u32, + pub decompressed_part_size: u32, + _unk2: u32, +} +impl MainListHeader { + pub fn entry_count(&self) -> u32 { + (&self.list_size - 20) / 8 + } +} + +#[derive(BinRead)] +pub struct MainListEntry { + pub size: u32, + pub checksum: u32, //checksum of this MAIN entrys' data +} + +pub const COMPRESSED_FILE_MAGIC: &[u8; 8] = b"EXTRHEAD"; + +#[derive(BinRead)] +pub struct CompressedFileHeader { + _header_string: [u8; 14], //EXTRHEADDRV \x01\x00 + pub compression_type_byte: u16, + pub decompressed_size: u32, + _destination_address: u32, + pub compressed_size: u32, + _unk: u32, + _footer_offset: u32, + _base_address: u32, + _checksum: u32, //unknown type of checksum + _checksum_flag: u8, + _unused: [u8; 19], +} +impl CompressedFileHeader { + pub fn compression_type(&self) -> &str { + if self.compression_type_byte == 0 { + return "Uncompressed" + } else if self.compression_type_byte == 1 { + return "GZIP" + } else if self.compression_type_byte == 2 { + return "LZSS" + } else { + return "Unknown" + } + } +} + diff --git a/src/utils/lzss.rs b/src/formats/pana_dvd/lzss.rs similarity index 100% rename from src/utils/lzss.rs rename to src/formats/pana_dvd/lzss.rs diff --git a/src/formats/pana_dvd.rs b/src/formats/pana_dvd/mod.rs similarity index 73% rename from src/formats/pana_dvd.rs rename to src/formats/pana_dvd/mod.rs index 41919f6..ae907f6 100644 --- a/src/formats/pana_dvd.rs +++ b/src/formats/pana_dvd/mod.rs @@ -1,20 +1,21 @@ +mod include; +mod pana_dvd_crypto; +mod lzss; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "pana_dvd", detector_func: is_pana_dvd_file, extractor_func: extract_pana_dvd } -} +use crate::AppContext; use std::path::{Path, PathBuf}; use std::fs::{self, OpenOptions}; use std::io::{Write, Read, Cursor, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::keys; use crate::utils::common; -use crate::utils::pana_dvd_crypto::{decrypt_data}; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::utils::compression::{decompress_gzip}; -use crate::utils::lzss::{decompress_lzss}; +use pana_dvd_crypto::{decrypt_data}; +use lzss::{decompress_lzss}; +use include::*; pub struct PanaDvdContext { matching_key: [u8; 8], @@ -24,133 +25,6 @@ pub struct PanaDvdContext { aes_iv: Option<[u8; 16]> } -#[derive(BinRead)] -struct AesHeaderFileEntry { - offset: u32, - size: u32, -} - -struct FileEntry { - offset: u32, - base_offset: u32, -} - -//checksums are mostly Adler32, but some very old files use Checksum32 instead. - -#[derive(BinRead)] -struct ModuleEntry { - #[br(count = 4)] name_bytes: Vec, - #[br(count = 4)] version_bytes: Vec, - _unk: u32, - offset: u32, - #[br(count = 8)] platform_bytes: Vec, - _unk1: u16, - #[br(count = 6)] id_bytes: Vec, - size: u32, - data_checksum: u32, //checksum of the entrys' DATA - _unk2: u32, - _entry_checksum: u32, //checksum of THIS header entry (all previous 44 bytes) -} -impl ModuleEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn platform(&self) -> String { - common::string_from_bytes(&self.platform_bytes) - } - fn id(&self) -> String { - common::string_from_bytes(&self.id_bytes) - } - fn is_valid(&self) -> bool { - self.name().is_ascii() && self.platform().is_ascii() - } -} - -#[derive(BinRead)] -struct MainListHeader { - _checksum: u32, //checksum of the MAIN LIST - _unk: u32, //seems to be always 1? - list_size: u32, - decompressed_part_size: u32, - _unk2: u32, -} -impl MainListHeader { - fn entry_count(&self) -> u32 { - (&self.list_size - 20) / 8 - } -} - -#[derive(BinRead)] -struct MainListEntry { - size: u32, - checksum: u32, //checksum of this MAIN entrys' data -} - -const COMPRESSED_FILE_MAGIC: &[u8; 8] = b"EXTRHEAD"; - -#[derive(BinRead)] -struct CompressedFileHeader { - #[br(count = 14)] _header_string: Vec, //EXTRHEADDRV \x01\x00 - compression_type_byte: u16, - decompressed_size: u32, - _destination_address: u32, - compressed_size: u32, - _unk: u32, - _footer_offset: u32, - _base_address: u32, - _checksum: u32, //unknown type of checksum - _checksum_flag: u8, - #[br(count = 19)] _unused: Vec, -} -impl CompressedFileHeader { - fn compression_type(&self) -> &str { - if self.compression_type_byte == 0 { - return "Uncompressed" - } else if self.compression_type_byte == 1 { - return "GZIP" - } else if self.compression_type_byte == 2 { - return "LZSS" - } else { - return "Unknown" - } - } -} - -static MAX_HEADER_SIZE: usize = 0x2000; - -pub fn find_key<'a>(key_array: &'a [&'a str], data: &[u8], expected_magic: &[u8], magic_offset: usize) -> Result, Box> { - for key_hex in key_array { - let key_bytes = hex::decode(key_hex)?; - let key_array: [u8; 8] = key_bytes.as_slice().try_into()?; - let decrypted = decrypt_data(data, &key_array); - - if decrypted[magic_offset..].starts_with(expected_magic) { - return Ok(Some(key_array)); - } - } - Ok(None) -} - -pub fn find_aes_key_pair<'a>(key_array: &'a [(&'a str, &'a str, &'a str)], data: &[u8], expected_magic: &[u8], magic_offset: usize) -> Result, Box> { - for (aes_key_hex, aes_iv_hex, cust_key_hex) in key_array { - let aes_key: [u8; 16] = hex::decode(aes_key_hex)?.as_slice().try_into()?; - let aes_iv: [u8; 16] = hex::decode(aes_iv_hex)?.as_slice().try_into()?; - let aes_decrypted = decrypt_aes128_cbc_nopad(data, &aes_key, &aes_iv)?; - - let key_bytes = hex::decode(cust_key_hex)?; - let key_array: [u8; 8] = key_bytes.as_slice().try_into()?; - let decrypted = decrypt_data(&aes_decrypted, &key_array); - - if decrypted[magic_offset..].starts_with(expected_magic) { - return Ok(Some((aes_key, aes_iv, key_array))); - } - } - Ok(None) -} - pub fn is_pana_dvd_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, 64)?; diff --git a/src/utils/pana_dvd_crypto.rs b/src/formats/pana_dvd/pana_dvd_crypto.rs similarity index 100% rename from src/utils/pana_dvd_crypto.rs rename to src/formats/pana_dvd/pana_dvd_crypto.rs diff --git a/src/formats/pfl_upg/include.rs b/src/formats/pfl_upg/include.rs new file mode 100644 index 0000000..233e540 --- /dev/null +++ b/src/formats/pfl_upg/include.rs @@ -0,0 +1,67 @@ +use crate::utils::common; +use binrw::BinRead; +use aes::Aes256; +use ecb::{Decryptor, cipher::{BlockDecryptMut, KeyInit, generic_array::GenericArray}}; + +pub static AUTO_FWS: &[(&str, &str)] = &[ + ("Q5551", "q5551"), + ("Q5553", "q5551"), + ("Q554E", "q5551"), + ("Q554M", "q5551"), + ("QF1EU", "qf1eu"), + ("QF2EU", "qf1eu"), + ("Q591E", "q591e"), + ("Q522E", "q522e"), + ("Q582E", "q522e"), + ("Q5481", "q5481"), + ("Q5431", "q5431"), + ("Q5492", "q5492"), + ("S5551", "q5551"), //Sharp +]; + +type Aes256EcbDec = Decryptor; + +pub fn decrypt_aes256_ecb(key: &[u8], ciphertext: &[u8]) -> Result, Box> { + let key_array: [u8; 32] = key.try_into()?; + + let mut decryptor = Aes256EcbDec::new(&key_array.into()); + let mut buffer = ciphertext.to_vec(); + + for chunk in buffer.chunks_exact_mut(16) { + let block: &mut [u8; 16] = chunk.try_into()?; + decryptor.decrypt_block_mut(GenericArray::from_mut_slice(block)); + } + + Ok(buffer) +} + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 8], + pub header_size: u32, + pub data_size: u32, + _crc32: u32, + pub mask: u32, + _data_size_decompressed: u32, + _padding2: u32, + description_bytes: [u8; 512], +} +impl Header { + pub fn description(&self) -> String { + common::string_from_bytes(&self.description_bytes) + } +} + +#[derive(BinRead)] +pub struct FileHeader { + file_name_bytes: [u8; 60], + pub real_size: u32, + pub stored_size: u32, + pub header_size: u32, + pub attributes: [u8; 4], +} +impl FileHeader { + pub fn file_name(&self) -> String { + common::string_from_bytes(&self.file_name_bytes) + } +} \ No newline at end of file diff --git a/src/formats/pfl_upg.rs b/src/formats/pfl_upg/mod.rs similarity index 73% rename from src/formats/pfl_upg.rs rename to src/formats/pfl_upg/mod.rs index 886566f..2e1b118 100644 --- a/src/formats/pfl_upg.rs +++ b/src/formats/pfl_upg/mod.rs @@ -1,52 +1,17 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "pfl_upg", detector_func: is_pfl_upg_file, extractor_func: extract_pfl_upg } -} +use crate::AppContext; use rsa::{RsaPublicKey, BigUint}; use hex::decode; use std::path::Path; use std::io::{Read, Cursor, Write}; use std::fs::{self, OpenOptions}; - -use aes::Aes256; -use ecb::{Decryptor, cipher::{BlockDecryptMut, KeyInit, generic_array::GenericArray}}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::keys; - -#[derive(BinRead)] -struct Header { - #[br(count = 8)] _magic_bytes: Vec, - header_size: u32, - data_size: u32, - #[br(count = 4)] _crc32: Vec, - mask: u32, - _data_size_decompressed: u32, - _padding2: u32, - #[br(count = 512)] description_bytes: Vec, -} -impl Header { - fn description(&self) -> String { - common::string_from_bytes(&self.description_bytes) - } -} - -#[derive(BinRead)] -struct FileHeader { - #[br(count = 60)] file_name_bytes: Vec, - real_size: u32, - stored_size: u32, - header_size: u32, - #[br(count = 4)] attributes: Vec, -} -impl FileHeader { - fn file_name(&self) -> String { - common::string_from_bytes(&self.file_name_bytes) - } -} +use include::*; pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -59,38 +24,6 @@ pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result>, Box } } -static AUTO_FWS: &[(&str, &str)] = &[ - ("Q5551", "q5551"), - ("Q5553", "q5551"), - ("Q554E", "q5551"), - ("Q554M", "q5551"), - ("QF1EU", "qf1eu"), - ("QF2EU", "qf1eu"), - ("Q591E", "q591e"), - ("Q522E", "q522e"), - ("Q582E", "q522e"), - ("Q5481", "q5481"), - ("Q5431", "q5431"), - ("Q5492", "q5492"), - ("S5551", "q5551"), //Sharp -]; - -type Aes256EcbDec = Decryptor; - -fn decrypt_aes256_ecb(key: &[u8], ciphertext: &[u8]) -> Result, Box> { - let key_array: [u8; 32] = key.try_into()?; - - let mut decryptor = Aes256EcbDec::new(&key_array.into()); - let mut buffer = ciphertext.to_vec(); - - for chunk in buffer.chunks_exact_mut(16) { - let block: &mut [u8; 16] = chunk.try_into()?; - decryptor.decrypt_block_mut(GenericArray::from_mut_slice(block)); - } - - Ok(buffer) -} - pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; diff --git a/src/formats/pup/include.rs b/src/formats/pup/include.rs new file mode 100644 index 0000000..b479d1c --- /dev/null +++ b/src/formats/pup/include.rs @@ -0,0 +1,45 @@ +use binrw::BinRead; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 4], + _unk1: u32, + _unk2: u16, + _flags: u8, + _unk3: u8, + _header_size: u16, + _hash_size: u16, + pub file_size: u64, + pub entry_count: u16, + _hash_count: u16, + _unk4: u32, +} + +#[derive(BinRead, Clone)] +pub struct Entry { + pub flags: u32, + _unk1: u32, + pub offset: u64, + pub compressed_size: u64, + pub uncompressed_size: u64, +} +impl Entry { + pub fn id(&self) -> u32 { + self.flags >> 20 + } + pub fn is_compressed(&self) -> bool { + (self.flags & 8) != 0 + } + pub fn is_blocked(&self) -> bool { + (self.flags & 0x800) != 0 + } + pub fn is_block_table(&self) -> bool { + (self.flags & 1) != 0 + } +} + +#[derive(BinRead, Clone)] +pub struct BlockEntry { + pub offset: u32, + pub size: u32, +} \ No newline at end of file diff --git a/src/formats/pup.rs b/src/formats/pup/mod.rs similarity index 84% rename from src/formats/pup.rs rename to src/formats/pup/mod.rs index 2ad20a4..f8eb905 100644 --- a/src/formats/pup.rs +++ b/src/formats/pup/mod.rs @@ -1,60 +1,15 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "pup", detector_func: is_pup_file, extractor_func: extract_pup } -} +use crate::AppContext; use std::path::{Path}; use std::fs::{self, OpenOptions}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use std::io::{Write, Seek, SeekFrom}; use crate::utils::common; use crate::utils::compression::{decompress_zlib}; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] _magic_bytes: Vec, - _unk1: u32, - _unk2: u16, - _flags: u8, - _unk3: u8, - _header_size: u16, - _hash_size: u16, - file_size: u64, - entry_count: u16, - _hash_count: u16, - _unk4: u32, -} - -#[derive(BinRead, Clone)] -struct Entry { - flags: u32, - _unk1: u32, - offset: u64, - compressed_size: u64, - uncompressed_size: u64, -} -impl Entry { - fn id(&self) -> u32 { - self.flags >> 20 - } - fn is_compressed(&self) -> bool { - (self.flags & 8) != 0 - } - fn is_blocked(&self) -> bool { - (self.flags & 0x800) != 0 - } - fn is_block_table(&self) -> bool { - (self.flags & 1) != 0 - } -} - -#[derive(BinRead, Clone)] -struct BlockEntry { - offset: u32, - size: u32, -} +use include::*; pub fn is_pup_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/roku/include.rs b/src/formats/roku/include.rs new file mode 100644 index 0000000..b2d0bc2 --- /dev/null +++ b/src/formats/roku/include.rs @@ -0,0 +1,49 @@ +use binrw::BinRead; + +pub static FILE_KEY: [u8; 16] = [ + 0x2A, 0x54, 0xA5, 0x30, 0xE0, 0x09, 0xA3, 0xDC, + 0x03, 0xFB, 0xC3, 0x5E, 0x23, 0xA2, 0xC1, 0x0D, +]; + +pub static FILE_IV: [u8; 16] = [0x00; 16]; + +#[derive(BinRead)] +pub struct ImageHeader { + _empty: [u8; 8], + _magic_bytes: [u8; 8], //imgARMcC + _target_bytes: [u8; 4], + _platform_id: [u8; 4], + pub image_type: u32, + pub size1: u32, + _size2: u32, + pub data_start_offset: u32, + _data_link_address: u32, + _data_entry_point_offset: u32, + pub flags: u32, + _timestamp: u32, + _build_host: [u8; 4], + _unk: [u8; 4], + _rest_of_header: [u8; 192], +} +impl ImageHeader { + pub fn is_encrypted(&self) -> bool { + self.flags == 0x80 + } + pub fn type_string(&self) -> &str { + if self.image_type == 0xa { + return "initfs" + } else if self.image_type == 0x18 { + return "uImage" + } else if self.image_type == 0x3 { + return "loader" + } else if self.image_type == 0xd { + return "app_cramfs" + } else if self.image_type == 0x6 { + return "Customization Package" + } else if self.image_type == 0xe { + return "firmware_blob" + } else { + return "unknown" + } + } +} \ No newline at end of file diff --git a/src/formats/roku.rs b/src/formats/roku/mod.rs similarity index 72% rename from src/formats/roku.rs rename to src/formats/roku/mod.rs index 6d0b78f..1b11099 100644 --- a/src/formats/roku.rs +++ b/src/formats/roku/mod.rs @@ -1,65 +1,16 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "roku", detector_func: is_roku_file, extractor_func: extract_roku } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; use std::path::Path; use std::io::{Write, Seek, Read, Cursor}; use tar::Archive; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::utils::aes::{decrypt_aes128_cbc_nopad, decrypt_aes128_cbc_pcks7}; - -static FILE_KEY: [u8; 16] = [ - 0x2A, 0x54, 0xA5, 0x30, 0xE0, 0x09, 0xA3, 0xDC, - 0x03, 0xFB, 0xC3, 0x5E, 0x23, 0xA2, 0xC1, 0x0D, -]; - -static FILE_IV: [u8; 16] = [0x00; 16]; - -#[derive(BinRead)] -struct ImageHeader { - #[br(count = 8)] _empty: Vec, - #[br(count = 8)] _magic_bytes: Vec, //imgARMcC - #[br(count = 4)] _target_bytes: Vec, - #[br(count = 4)] _platform_id: Vec, - image_type: u32, - size1: u32, - _size2: u32, - data_start_offset: u32, - _data_link_address: u32, - _data_entry_point_offset: u32, - flags: u32, - _timestamp: u32, - #[br(count = 4)] _build_host: Vec, - #[br(count = 4)] _unk: Vec, - #[br(count = 192)] _rest_of_header: Vec, -} -impl ImageHeader { - fn is_encrypted(&self) -> bool { - self.flags == 0x80 - } - fn type_string(&self) -> &str { - if self.image_type == 0xa { - return "initfs" - } else if self.image_type == 0x18 { - return "uImage" - } else if self.image_type == 0x3 { - return "loader" - } else if self.image_type == 0xd { - return "app_cramfs" - } else if self.image_type == 0x6 { - return "Customization Package" - } else if self.image_type == 0xe { - return "firmware_blob" - } else { - return "unknown" - } - } -} +use include::*; pub fn is_roku_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/ruf/include.rs b/src/formats/ruf/include.rs new file mode 100644 index 0000000..5b755c7 --- /dev/null +++ b/src/formats/ruf/include.rs @@ -0,0 +1,64 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct RufHeader { + _magic_bytes: [u8; 6], + _upgrade_type_bytes: [u8; 2], + _unk1: u32, + date_time_bytes: [u8; 32], + buyer_bytes: [u8; 8], + model_bytes: [u8; 32], + region_info_bytes: [u8; 32], + pub version_bytes: [u8; 4], + pub data_size: u32, + _unk2: [u8; 20], + pub dual_ruf_flag: u32, + _unk3: [u8; 44], + pub payload_count: u16, + _payload_entry_size: u16, + pub payloads_start_offset: u32, +} +impl RufHeader { + pub fn date_time(&self) -> String { + common::string_from_bytes(&self.date_time_bytes) + } + pub fn buyer(&self) -> String { + common::string_from_bytes(&self.buyer_bytes) + } + pub fn model(&self) -> String { + common::string_from_bytes(&self.model_bytes) + } + pub fn region_info(&self) -> String { + common::string_from_bytes(&self.region_info_bytes) + } + pub fn is_dual_ruf(&self) -> bool { + if self.dual_ruf_flag == 0x44 {true} else {false} + } +} + +#[derive(BinRead)] +pub struct RufEntry { + _metadata: [u8; 32], + pub payload_type_bytes: u32, + pub size: u32, + _unk1: u32, + _unk2: [u8; 20], +} +impl RufEntry { + pub fn payload_type(&self) -> &str { + if self.payload_type_bytes == 1 { + return "squashfs" + } else if self.payload_type_bytes == 2 { + return "cfe" + } else if self.payload_type_bytes == 3 { + return "vmlinuz" + } else if self.payload_type_bytes == 4 { + return "loader" + } else if self.payload_type_bytes == 5 { + return "splash" + } else { + return "unknown" + } + } +} \ No newline at end of file diff --git a/src/formats/ruf.rs b/src/formats/ruf/mod.rs similarity index 66% rename from src/formats/ruf.rs rename to src/formats/ruf/mod.rs index ea3ab7a..a8b39f8 100644 --- a/src/formats/ruf.rs +++ b/src/formats/ruf/mod.rs @@ -1,79 +1,16 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "ruf", detector_func: is_ruf_file, extractor_func: extract_ruf } -} +use crate::AppContext; use std::path::{Path, PathBuf}; use std::fs::{self, File, OpenOptions}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use std::io::{Write, Seek, SeekFrom, Cursor}; use crate::utils::common; use crate::keys; use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; - -#[derive(BinRead)] -struct RufHeader { - #[br(count = 6)] _magic_bytes: Vec, - #[br(count = 2)] _upgrade_type_bytes: Vec, - _unk1: u32, - #[br(count = 32)] date_time_bytes: Vec, - #[br(count = 8)] buyer_bytes: Vec, - #[br(count = 32)] model_bytes: Vec, - #[br(count = 32)] region_info_bytes: Vec, - #[br(count = 4)] version_bytes: Vec, - data_size: u32, - #[br(count = 20)] _unk2: Vec, - dual_ruf_flag: u32, - #[br(count = 44)] _unk3: Vec, - payload_count: u16, - _payload_entry_size: u16, - payloads_start_offset: u32, -} -impl RufHeader { - fn date_time(&self) -> String { - common::string_from_bytes(&self.date_time_bytes) - } - fn buyer(&self) -> String { - common::string_from_bytes(&self.buyer_bytes) - } - fn model(&self) -> String { - common::string_from_bytes(&self.model_bytes) - } - fn region_info(&self) -> String { - common::string_from_bytes(&self.region_info_bytes) - } - fn is_dual_ruf(&self) -> bool { - if self.dual_ruf_flag == 0x44 {true} else {false} - } -} - -#[derive(BinRead)] -struct RufEntry { - #[br(count = 32)] _metadata: Vec, - payload_type_bytes: u32, - size: u32, - _unk1: u32, - #[br(count = 20)] _unk2: Vec, -} -impl RufEntry { - fn payload_type(&self) -> &str { - if self.payload_type_bytes == 1 { - return "squashfs" - } else if self.payload_type_bytes == 2 { - return "cfe" - } else if self.payload_type_bytes == 3 { - return "vmlinuz" - } else if self.payload_type_bytes == 4 { - return "loader" - } else if self.payload_type_bytes == 5 { - return "splash" - } else { - return "unknown" - } - } -} +use include::*; pub fn is_ruf_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/rvp/include.rs b/src/formats/rvp/include.rs new file mode 100644 index 0000000..0429955 --- /dev/null +++ b/src/formats/rvp/include.rs @@ -0,0 +1,7 @@ +pub fn decrypt_xor(data: &[u8]) -> Vec { + let key_bytes = b"\xCC\xF0\xC8\xC4\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA"; + data.iter() + .enumerate() + .map(|(i, &byte)| byte ^ key_bytes[i % key_bytes.len()]) + .collect() +} \ No newline at end of file diff --git a/src/formats/rvp.rs b/src/formats/rvp/mod.rs similarity index 91% rename from src/formats/rvp.rs rename to src/formats/rvp/mod.rs index 9c2dfdc..1779ba1 100644 --- a/src/formats/rvp.rs +++ b/src/formats/rvp/mod.rs @@ -1,22 +1,13 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "rvp", detector_func: is_rvp_file, extractor_func: extract_rvp } -} +use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Read, Cursor, Seek}; use crate::utils::common; - -fn decrypt_xor(data: &[u8]) -> Vec { - let key_bytes = b"\xCC\xF0\xC8\xC4\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA"; - data.iter() - .enumerate() - .map(|(i, &byte)| byte ^ key_bytes[i % key_bytes.len()]) - .collect() -} +use include::*; pub fn is_rvp_file(app_ctx: &AppContext) -> Result>, Box> { let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -118,7 +109,6 @@ pub fn extract_rvp(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box Vec { + let key_bytes = key.as_bytes(); + data.iter() + .enumerate() + .map(|(i, &byte)| byte ^ key_bytes[i % key_bytes.len()]) + .collect() +} \ No newline at end of file diff --git a/src/formats/samsung_old.rs b/src/formats/samsung_old/mod.rs similarity index 92% rename from src/formats/samsung_old.rs rename to src/formats/samsung_old/mod.rs index 2680c83..b147b31 100644 --- a/src/formats/samsung_old.rs +++ b/src/formats/samsung_old/mod.rs @@ -1,8 +1,6 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "samsung_old", detector_func: is_samsung_old_dir, extractor_func: extract_samsung_old } -} +use crate::AppContext; use std::fs; use std::path::{Path}; @@ -10,12 +8,12 @@ use std::fs::{File, OpenOptions}; use std::io::{Write}; use hex::decode; use sha1::{Digest, Sha1}; +use md5; use crate::utils::common; use crate::keys; use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; - -use md5; +use include::decrypt_xor; pub fn is_samsung_old_dir(app_ctx: &AppContext) -> Result>, Box> { let dir = match app_ctx.dir() {Some(d) => d, None => return Ok(None)}; @@ -27,14 +25,6 @@ pub fn is_samsung_old_dir(app_ctx: &AppContext) -> Result>, } } -fn decrypt_xor(data: &[u8], key: &str) -> Vec { - let key_bytes = key.as_bytes(); - data.iter() - .enumerate() - .map(|(i, &byte)| byte ^ key_bytes[i % key_bytes.len()]) - .collect() -} - pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { let path = app_ctx.dir().ok_or("Extractor expected directory")?; diff --git a/src/formats/sddl_sec/include.rs b/src/formats/sddl_sec/include.rs new file mode 100644 index 0000000..6c29ade --- /dev/null +++ b/src/formats/sddl_sec/include.rs @@ -0,0 +1,212 @@ +//sddl_dec 6.0 https://github.com/theubusu/sddl_dec +use crate::utils::common; +use binrw::BinRead; + +pub fn decipher(s: &[u8]) -> Vec { + let len_ = s.len(); + let mut v3: u32 = 904; + let mut out = s.to_vec(); + let mut cnt = 0; + + if len_ > 0 { + let true_len = if len_ >= 0x80 { + 128 + } else { + len_ + }; + + if true_len > 0 { + let mut i = 0; + let mut j: u8 = 0; + + while i < s.len() { + let iter_ = s[i]; + i += 1; + j = j.wrapping_add(1); + + let v11 = (iter_ as u32).wrapping_add(38400) & 0xffffffff; + let v12 = iter_ ^ ((v3 & 0xff00) >> 8) as u8; + v3 = v3.wrapping_add(v11).wrapping_add(163) & 0xffffffff; + + if j == 0 { + v3 = 904; + } + + if cnt < out.len() { + out[cnt] = v12; + cnt += 1; + } + } + } + } + + out +} + +// -- STRUCTURES -- +// -- SECFILE -- + +pub static DOWNLOAD_ID: [u8; 4] = [0x11, 0x22, 0x33, 0x44]; + +#[derive(Debug, BinRead)] +pub struct SecHeader { + pub _download_id: [u8; 4], //always 0x11, 0x22, 0x33, 0x44 - magic? + key_id_str_bytes: [u8; 4], //"key_id", purpose unknown + grp_num_str_bytes: [u8; 4], //"grp_num", the count of groups, also represents the count of info files because each group has a respective info file + prg_num_str_bytes: [u8; 4], //"prg_num", the count of module (.FXX) files + _unused_or_reserved: [u8; 16], //not used, is zeros +} +impl SecHeader { + pub fn key_id(&self) -> u32 { + let string = common::string_from_bytes(&self.key_id_str_bytes); + string.parse().unwrap() + } + pub fn grp_num(&self) -> u32 { + let string = common::string_from_bytes(&self.grp_num_str_bytes); + string.parse().unwrap() + } + pub fn prg_num(&self) -> u32 { + let string = common::string_from_bytes(&self.prg_num_str_bytes); + string.parse().unwrap() + } +} + +pub static INFO_FILE_EXTENSION: &str = ".TXT"; + +#[derive(Debug, BinRead)] +pub struct FileHeader { + name_str_bytes: [u8; 12], + size_str_bytes: [u8; 12], +} +impl FileHeader { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_str_bytes) + } + pub fn size(&self) -> u64 { + let string = common::string_from_bytes(&self.size_str_bytes); + string.parse().unwrap() + } +} + +// -- MODULE -- +#[derive(Debug, BinRead)] +pub struct ModuleComHeader { //"com_header" + pub download_id: [u8; 4], //always 0x11, 0x22, 0x33, 0x44 - magic? + _outer_maker_id: u8, + _outer_model_id: u8, + _inner_maker_id: u8, + _reserve1: u8, + _reserve2: u32, + _reserve3: u32, + _start_version: [u8; 4], //the first version that can upgrade to the new version + _end_version: [u8; 4], //the last version that can upgrade to the new version + _new_version: [u8; 4], //the new version, as in the version of the data in this module + _reserve4: u16, + _module_num: u16, //the logic seems to indicate that there can be multiple entries in one module, but i have never seen this go above 1. +} + +#[derive(Debug, BinRead)] +pub struct ModuleHeader { //"header", appears after com_header + _module_id: u16, + module_atr: u8, + _target_id: u8, + pub cmp_size: u32, + _org_size: u32, + _crc_value: u32, +} +impl ModuleHeader { + pub fn is_ciphered(&self) -> bool { + (self.module_atr & 0x02) != 0 + } + pub fn is_compressed(&self) -> bool { + (self.module_atr & 0x01) != 0 + } +} + +#[derive(Debug, BinRead)] +pub struct ContentHeader { + _magic1: u8, //always 0x01? + _dest_offset: u32, + _source_offset: u32, + pub size: u32, + _magic2: u8, //always 0x21? +} +impl ContentHeader { + //these hacks are needed because for some reason older files have the first nibble of the offset set to D/C + //no idea why, but masking them off makes it works properly + pub fn dest_offset(&self) -> u32 { + if ((self._dest_offset >> 28) & 0xF) == 0xD { + self._dest_offset & 0x0FFFFFFF + } else { + self._dest_offset + } + } + pub fn source_offset(&self) -> u32 { + if ((self._source_offset >> 28) & 0xF) == 0xC { + self._source_offset & 0x0FFFFFFF + } else { + self._source_offset + } + } + pub fn has_subfile(&self) -> bool { + self.source_offset() == 0x10E + } +} + +// -- TDI -- +// Called SDIT.FDI in the secfile + +pub static TDI_FILENAME: &str = "SDIT.FDI"; +pub static SUPPORTED_TDI_VERSION: u16 = 2; + +#[derive(Debug, BinRead)] +pub struct TdiHead { + pub download_id: [u8; 4], //always 0x11, 0x22, 0x33, 0x44 - magic? + pub num_of_group: u8, + _reserve1: u8, + pub format_version: u16, //checks for "2" here +} + +#[derive(Debug, BinRead)] +pub struct TdiGroupHead { + pub group_id: u8, + pub num_of_target: u8, //logic checks that this is not more than 5 + _reserved: u16, +} + +#[derive(Debug, BinRead)] +pub struct TdiTgtInf { + _outer_maker_id: u8, + _outer_model_id: u8, + _inner_maker_id: u8, + _reserve3: u8, + _inner_model_id: [u8; 4], + _ext_model_id: [u8; 4], + pub _start_version: [u8; 4], //the first version that can upgrade to the new version + pub _end_version: [u8; 4], //the last version that can upgrade to the new version + pub new_version: [u8; 4], //the new version, as in the version of the data in this module + pub target_id: u8, + _num_of_compatible_target: u8, + pub num_of_txx: u16, //"TXX" refers to the ".FXX" segment files of each module. I assume F is an encrypted version of T, the same happens with SDIT; "TDI" -> "FDI" + _unknown: [u8; 8], + module_name_bytes: [u8; 8], +} +impl TdiTgtInf { + pub fn module_name(&self) -> String { + common::string_from_bytes(&self.module_name_bytes) + } + pub fn version_string(&self) -> String { + format!("{}.{}{}{}", self.new_version[0], self.new_version[1], self.new_version[2], self.new_version[3]) + } +} + +// -- dec key -- +pub static DEC_KEY: [u8; 16] = [ + 0x26, 0xE0, 0x96, 0xD3, 0xEF, 0x8A, 0x8F, 0xBB, + 0xAA, 0x5E, 0x51, 0x6F, 0x77, 0x26, 0xC2, 0x2C, +]; + +pub static DEC_IV: [u8; 16] = [ + 0x3E, 0x4A, 0xE2, 0x3A, 0x69, 0xDB, 0x81, 0x54, + 0xCD, 0x88, 0x38, 0xC4, 0xB9, 0x0C, 0x76, 0x66, +]; \ No newline at end of file diff --git a/src/formats/sddl_sec.rs b/src/formats/sddl_sec/mod.rs similarity index 52% rename from src/formats/sddl_sec.rs rename to src/formats/sddl_sec/mod.rs index 0820193..823ba48 100644 --- a/src/formats/sddl_sec.rs +++ b/src/formats/sddl_sec/mod.rs @@ -1,227 +1,17 @@ -//sddl_dec 6.0 +//sddl_dec 6.0 https://github.com/theubusu/sddl_dec +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "sddl_sec", detector_func: is_sddl_sec_file, extractor_func: extract_sddl_sec } -} +use crate::AppContext; use std::path::{Path, PathBuf}; use std::fs::{self, File, OpenOptions}; use std::io::{Cursor, Seek, SeekFrom, Write}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; use crate::utils::compression::{decompress_zlib}; - -pub fn decipher(s: &[u8]) -> Vec { - let len_ = s.len(); - let mut v3: u32 = 904; - let mut out = s.to_vec(); - let mut cnt = 0; - - if len_ > 0 { - let true_len = if len_ >= 0x80 { - 128 - } else { - len_ - }; - - if true_len > 0 { - let mut i = 0; - let mut j: u8 = 0; - - while i < s.len() { - let iter_ = s[i]; - i += 1; - j = j.wrapping_add(1); - - let v11 = (iter_ as u32).wrapping_add(38400) & 0xffffffff; - let v12 = iter_ ^ ((v3 & 0xff00) >> 8) as u8; - v3 = v3.wrapping_add(v11).wrapping_add(163) & 0xffffffff; - - if j == 0 { - v3 = 904; - } - - if cnt < out.len() { - out[cnt] = v12; - cnt += 1; - } - } - } - } - - out -} - -// -- STRUCTURES -- -// -- SECFILE -- - -pub static DOWNLOAD_ID: [u8; 4] = [0x11, 0x22, 0x33, 0x44]; - -#[derive(Debug, BinRead)] -pub struct SecHeader { - pub _download_id: [u8; 4], //always 0x11, 0x22, 0x33, 0x44 - magic? - key_id_str_bytes: [u8; 4], //"key_id", purpose unknown - grp_num_str_bytes: [u8; 4], //"grp_num", the count of groups, also represents the count of info files because each group has a respective info file - prg_num_str_bytes: [u8; 4], //"prg_num", the count of module (.FXX) files - _unused_or_reserved: [u8; 16], //not used, is zeros -} -impl SecHeader { - pub fn key_id(&self) -> u32 { - let string = common::string_from_bytes(&self.key_id_str_bytes); - string.parse().unwrap() - } - pub fn grp_num(&self) -> u32 { - let string = common::string_from_bytes(&self.grp_num_str_bytes); - string.parse().unwrap() - } - pub fn prg_num(&self) -> u32 { - let string = common::string_from_bytes(&self.prg_num_str_bytes); - string.parse().unwrap() - } -} - -pub static INFO_FILE_EXTENSION: &str = ".TXT"; - -#[derive(Debug, BinRead)] -pub struct FileHeader { - name_str_bytes: [u8; 12], - size_str_bytes: [u8; 12], -} -impl FileHeader { - pub fn name(&self) -> String { - common::string_from_bytes(&self.name_str_bytes) - } - pub fn size(&self) -> u64 { - let string = common::string_from_bytes(&self.size_str_bytes); - string.parse().unwrap() - } -} - -// -- MODULE -- -#[derive(Debug, BinRead)] -pub struct ModuleComHeader { //"com_header" - pub download_id: [u8; 4], //always 0x11, 0x22, 0x33, 0x44 - magic? - _outer_maker_id: u8, - _outer_model_id: u8, - _inner_maker_id: u8, - _reserve1: u8, - _reserve2: u32, - _reserve3: u32, - _start_version: [u8; 4], //the first version that can upgrade to the new version - _end_version: [u8; 4], //the last version that can upgrade to the new version - _new_version: [u8; 4], //the new version, as in the version of the data in this module - _reserve4: u16, - _module_num: u16, //the logic seems to indicate that there can be multiple entries in one module, but i have never seen this go above 1. -} - -#[derive(Debug, BinRead)] -pub struct ModuleHeader { //"header", appears after com_header - _module_id: u16, - module_atr: u8, - _target_id: u8, - pub cmp_size: u32, - _org_size: u32, - _crc_value: u32, -} -impl ModuleHeader { - pub fn is_ciphered(&self) -> bool { - (self.module_atr & 0x02) != 0 - } - pub fn is_compressed(&self) -> bool { - (self.module_atr & 0x01) != 0 - } -} - -#[derive(Debug, BinRead)] -pub struct ContentHeader { - _magic1: u8, //always 0x01? - _dest_offset: u32, - _source_offset: u32, - pub size: u32, - _magic2: u8, //always 0x21? -} -impl ContentHeader { - //these hacks are needed because for some reason older files have the first nibble of the offset set to D/C - //no idea why, but masking them off makes it works properly - pub fn dest_offset(&self) -> u32 { - if ((self._dest_offset >> 28) & 0xF) == 0xD { - self._dest_offset & 0x0FFFFFFF - } else { - self._dest_offset - } - } - pub fn source_offset(&self) -> u32 { - if ((self._source_offset >> 28) & 0xF) == 0xC { - self._source_offset & 0x0FFFFFFF - } else { - self._source_offset - } - } - pub fn has_subfile(&self) -> bool { - self.source_offset() == 0x10E - } -} - -// -- TDI -- -// Called SDIT.FDI in the secfile - -pub static TDI_FILENAME: &str = "SDIT.FDI"; -pub static SUPPORTED_TDI_VERSION: u16 = 2; - -#[derive(Debug, BinRead)] -pub struct TdiHead { - pub download_id: [u8; 4], //always 0x11, 0x22, 0x33, 0x44 - magic? - pub num_of_group: u8, - _reserve1: u8, - pub format_version: u16, //checks for "2" here -} - -#[derive(Debug, BinRead)] -pub struct TdiGroupHead { - pub group_id: u8, - pub num_of_target: u8, //logic checks that this is not more than 5 - _reserved: u16, -} - -#[derive(Debug, BinRead)] -pub struct TdiTgtInf { - _outer_maker_id: u8, - _outer_model_id: u8, - _inner_maker_id: u8, - _reserve3: u8, - _inner_model_id: [u8; 4], - _ext_model_id: [u8; 4], - pub _start_version: [u8; 4], //the first version that can upgrade to the new version - pub _end_version: [u8; 4], //the last version that can upgrade to the new version - pub new_version: [u8; 4], //the new version, as in the version of the data in this module - pub target_id: u8, - _num_of_compatible_target: u8, - pub num_of_txx: u16, //"TXX" refers to the ".FXX" segment files of each module. I assume F is an encrypted version of T, the same happens with SDIT; "TDI" -> "FDI" - _unknown: [u8; 8], - module_name_bytes: [u8; 8], -} -impl TdiTgtInf { - pub fn module_name(&self) -> String { - common::string_from_bytes(&self.module_name_bytes) - } - pub fn version_string(&self) -> String { - format!("{}.{}{}{}", self.new_version[0], self.new_version[1], self.new_version[2], self.new_version[3]) - } -} - -// -- dec key -- -static DEC_KEY: [u8; 16] = [ - 0x26, 0xE0, 0x96, 0xD3, 0xEF, 0x8A, 0x8F, 0xBB, - 0xAA, 0x5E, 0x51, 0x6F, 0x77, 0x26, 0xC2, 0x2C, -]; - -static DEC_IV: [u8; 16] = [ - 0x3E, 0x4A, 0xE2, 0x3A, 0x69, 0xDB, 0x81, 0x54, - 0xCD, 0x88, 0x38, 0xC4, 0xB9, 0x0C, 0x76, 0x66, -]; +use include::*; pub fn is_sddl_sec_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/slp/include.rs b/src/formats/slp/include.rs new file mode 100644 index 0000000..cc31b6d --- /dev/null +++ b/src/formats/slp/include.rs @@ -0,0 +1,43 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 4], + version_bytes: [u8; 8], + model_bytes: [u8; 16], + firmware_bytes: [u8; 16], + _unk: u32, + check: [u8; 8], + _unk2: [u8; 8], +} +impl Header { + pub fn version(&self) -> String { + common::string_from_bytes(&self.version_bytes) + } + pub fn model(&self) -> String { + common::string_from_bytes(&self.model_bytes) + } + pub fn firmware(&self) -> String { + common::string_from_bytes(&self.firmware_bytes) + } + pub fn is_new_type(&self) -> bool { + &self.check == b"\x01VER_PR1" + } +} + +#[derive(BinRead)] +pub struct EntryOld { + pub size: u32, + pub _unk: u32, + pub offset: u32, + pub _unk2: u32, +} + +#[derive(BinRead)] +pub struct EntryNew { + pub size: u32, + _unk: u32, + pub offset: u32, + _unk2: [u8; 12], +} \ No newline at end of file diff --git a/src/formats/slp.rs b/src/formats/slp/mod.rs similarity index 67% rename from src/formats/slp.rs rename to src/formats/slp/mod.rs index 0ec6a54..20833b8 100644 --- a/src/formats/slp.rs +++ b/src/formats/slp/mod.rs @@ -1,56 +1,14 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "slp", detector_func: is_slp_file, extractor_func: extract_slp } -} +use crate::AppContext; use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] _magic_bytes: Vec, - #[br(count = 8)] version_bytes: Vec, - #[br(count = 16)] model_bytes: Vec, - #[br(count = 16)] firmware_bytes: Vec, - _unk: u32, - #[br(count = 8)] check: Vec, - #[br(count = 8)] _unk2: Vec, -} -impl Header { - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn model(&self) -> String { - common::string_from_bytes(&self.model_bytes) - } - fn firmware(&self) -> String { - common::string_from_bytes(&self.firmware_bytes) - } - fn is_new_type(&self) -> bool { - self.check == b"\x01VER_PR1" - } -} - -#[derive(BinRead)] -struct EntryOld { - size: u32, - _unk: u32, - offset: u32, - _unk2: u32, -} - -#[derive(BinRead)] -struct EntryNew { - size: u32, - _unk: u32, - offset: u32, - #[br(count = 12)] _unk2: Vec, -} +use include::*; pub fn is_slp_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/sony_bdp/include.rs b/src/formats/sony_bdp/include.rs new file mode 100644 index 0000000..1859f1d --- /dev/null +++ b/src/formats/sony_bdp/include.rs @@ -0,0 +1,52 @@ +use crate::utils::common; +use binrw::BinRead; + +//thx sony +static HEX_SUBSTITUTION: [u8; 256] = [ + 0xE8, 0x4D, 0x63, 0xF4, 0xF8, 0xA9, 0x21, 0x9C, 0xC7, 0x82, 0xCD, 0xE3, 0xC1, 0xCE, 0xC0, 0xFA, 0xE7, 0xD6, 0x96, 0x46, 0x12, 0x03, 0x14, 0x33, 0xED, 0x10, 0xEC, 0x69, 0x16, 0xE0, 0x28, 0x30, + 0x77, 0x0E, 0x3D, 0xEF, 0x36, 0x4C, 0x18, 0xEB, 0x41, 0x89, 0x64, 0x8A, 0x70, 0x0C, 0x23, 0xA3, 0x79, 0x6D, 0x75, 0x7E, 0x1A, 0x2D, 0x01, 0x91, 0x88, 0xCB, 0xFC, 0x8B, 0xFD, 0x94, 0x0A, 0x39, + 0xBC, 0x98, 0x87, 0xBB, 0xC2, 0x9B, 0x81, 0x1C, 0x4B, 0xA6, 0x58, 0x59, 0x45, 0x57, 0x8C, 0x7B, 0x5A, 0x56, 0x08, 0x73, 0x65, 0xEE, 0x2A, 0x25, 0xB0, 0x5B, 0x55, 0xB2, 0xB8, 0x1E, 0xEA, 0xC5, + 0x6A, 0x40, 0x86, 0x5C, 0x3C, 0x54, 0xBF, 0xF6, 0xA8, 0xF2, 0x06, 0x4A, 0xFE, 0xC4, 0x32, 0x8D, 0xF0, 0x5D, 0x35, 0x53, 0xD7, 0xBD, 0xBA, 0x20, 0x2F, 0xCA, 0xE1, 0xCC, 0xA4, 0x44, 0x85, 0xDE, + 0xDB, 0x5E, 0x27, 0x52, 0x6E, 0x38, 0x04, 0x66, 0xD0, 0x92, 0xD3, 0xC6, 0x7D, 0x71, 0xDA, 0x2C, 0x9A, 0x49, 0x8E, 0x80, 0x13, 0x5F, 0x11, 0x51, 0x15, 0x22, 0xCF, 0xAC, 0x0F, 0xD5, 0xFF, 0x3F, + 0x17, 0xAD, 0xD8, 0xD9, 0xAB, 0x02, 0x6B, 0x0D, 0xDF, 0xF1, 0x84, 0x3B, 0x78, 0x19, 0x76, 0x60, 0x50, 0xDC, 0xC3, 0xB5, 0x43, 0x0B, 0x95, 0x97, 0x67, 0xD4, 0x29, 0xF7, 0xE4, 0x1B, 0xAE, 0x48, + 0xB1, 0x8F, 0x24, 0x7A, 0x74, 0xFB, 0x34, 0x09, 0x00, 0x31, 0x9F, 0x61, 0x4F, 0xB6, 0xA0, 0xA7, 0xB4, 0x9E, 0x1D, 0xAA, 0xF9, 0xBE, 0x37, 0x2E, 0xB9, 0x6F, 0xA5, 0x83, 0xA1, 0x93, 0x07, 0xE2, + 0x7F, 0x3E, 0xF3, 0x99, 0x62, 0x4E, 0xE9, 0xC8, 0x6C, 0x68, 0x1F, 0x47, 0x42, 0x26, 0x9D, 0xE5, 0x7C, 0x72, 0x3A, 0x2B, 0xE6, 0xF5, 0xD2, 0x90, 0x05, 0xD1, 0xDD, 0xC9, 0xAF, 0xB7, 0xA2, 0xB3, +]; + +pub fn hex_substitute(data: &[u8]) -> Vec { + data.iter().map(|&b| HEX_SUBSTITUTION[b as usize]).collect() +} + +#[derive(BinRead)] +pub struct Header { + firmware_name_bytes: [u8; 8], + _unk_version_bytes: [u8; 8], + firmware_version_bytes: [u8; 16], + date_bytes: [u8; 16], + _unk2_version_bytes: [u8; 8], + _unk3_version_bytes: [u8; 8], + _unk4_version_bytes: [u8; 16], + _unk: u32, + _checksum: u64, + pub file_size: u32, + _unk5_version_bytes: [u8; 16], + _unk6_version_bytes: [u8; 16], + _unk2: [u8; 32], +} +impl Header { + pub fn firmware_name(&self) -> String { + common::string_from_bytes(&self.firmware_name_bytes) + } + pub fn firmware_version(&self) -> String { + common::string_from_bytes(&self.firmware_version_bytes) + } + pub fn date(&self) -> String { + common::string_from_bytes(&self.date_bytes) + } +} + +#[derive(BinRead)] +pub struct Entry { + pub offset: u32, + pub size: u32, +} \ No newline at end of file diff --git a/src/formats/sony_bdp.rs b/src/formats/sony_bdp/mod.rs similarity index 50% rename from src/formats/sony_bdp.rs rename to src/formats/sony_bdp/mod.rs index 9543374..1ee0a9d 100644 --- a/src/formats/sony_bdp.rs +++ b/src/formats/sony_bdp/mod.rs @@ -1,67 +1,16 @@ +mod include; use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "sony_bdp", detector_func: is_sony_bdp_file, extractor_func: extract_sony_bdp } -} +use crate::{InputTarget, AppContext}; use std::fs::File; use std::path::{Path, PathBuf}; use std::fs::{self, OpenOptions}; use std::io::{Cursor, Write}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::formats; - -//thx sony -static HEX_SUBSTITUTION: [u8; 256] = [ - 0xE8, 0x4D, 0x63, 0xF4, 0xF8, 0xA9, 0x21, 0x9C, 0xC7, 0x82, 0xCD, 0xE3, 0xC1, 0xCE, 0xC0, 0xFA, 0xE7, 0xD6, 0x96, 0x46, 0x12, 0x03, 0x14, 0x33, 0xED, 0x10, 0xEC, 0x69, 0x16, 0xE0, 0x28, 0x30, - 0x77, 0x0E, 0x3D, 0xEF, 0x36, 0x4C, 0x18, 0xEB, 0x41, 0x89, 0x64, 0x8A, 0x70, 0x0C, 0x23, 0xA3, 0x79, 0x6D, 0x75, 0x7E, 0x1A, 0x2D, 0x01, 0x91, 0x88, 0xCB, 0xFC, 0x8B, 0xFD, 0x94, 0x0A, 0x39, - 0xBC, 0x98, 0x87, 0xBB, 0xC2, 0x9B, 0x81, 0x1C, 0x4B, 0xA6, 0x58, 0x59, 0x45, 0x57, 0x8C, 0x7B, 0x5A, 0x56, 0x08, 0x73, 0x65, 0xEE, 0x2A, 0x25, 0xB0, 0x5B, 0x55, 0xB2, 0xB8, 0x1E, 0xEA, 0xC5, - 0x6A, 0x40, 0x86, 0x5C, 0x3C, 0x54, 0xBF, 0xF6, 0xA8, 0xF2, 0x06, 0x4A, 0xFE, 0xC4, 0x32, 0x8D, 0xF0, 0x5D, 0x35, 0x53, 0xD7, 0xBD, 0xBA, 0x20, 0x2F, 0xCA, 0xE1, 0xCC, 0xA4, 0x44, 0x85, 0xDE, - 0xDB, 0x5E, 0x27, 0x52, 0x6E, 0x38, 0x04, 0x66, 0xD0, 0x92, 0xD3, 0xC6, 0x7D, 0x71, 0xDA, 0x2C, 0x9A, 0x49, 0x8E, 0x80, 0x13, 0x5F, 0x11, 0x51, 0x15, 0x22, 0xCF, 0xAC, 0x0F, 0xD5, 0xFF, 0x3F, - 0x17, 0xAD, 0xD8, 0xD9, 0xAB, 0x02, 0x6B, 0x0D, 0xDF, 0xF1, 0x84, 0x3B, 0x78, 0x19, 0x76, 0x60, 0x50, 0xDC, 0xC3, 0xB5, 0x43, 0x0B, 0x95, 0x97, 0x67, 0xD4, 0x29, 0xF7, 0xE4, 0x1B, 0xAE, 0x48, - 0xB1, 0x8F, 0x24, 0x7A, 0x74, 0xFB, 0x34, 0x09, 0x00, 0x31, 0x9F, 0x61, 0x4F, 0xB6, 0xA0, 0xA7, 0xB4, 0x9E, 0x1D, 0xAA, 0xF9, 0xBE, 0x37, 0x2E, 0xB9, 0x6F, 0xA5, 0x83, 0xA1, 0x93, 0x07, 0xE2, - 0x7F, 0x3E, 0xF3, 0x99, 0x62, 0x4E, 0xE9, 0xC8, 0x6C, 0x68, 0x1F, 0x47, 0x42, 0x26, 0x9D, 0xE5, 0x7C, 0x72, 0x3A, 0x2B, 0xE6, 0xF5, 0xD2, 0x90, 0x05, 0xD1, 0xDD, 0xC9, 0xAF, 0xB7, 0xA2, 0xB3, -]; - -fn hex_substitute(data: &[u8]) -> Vec { - data.iter().map(|&b| HEX_SUBSTITUTION[b as usize]).collect() -} - -#[derive(BinRead)] -struct Header { - #[br(count = 8)] firmware_name_bytes: Vec, - #[br(count = 8)] _unk_version_bytes: Vec, - #[br(count = 16)] firmware_version_bytes: Vec, - #[br(count = 16)] date_bytes: Vec, - #[br(count = 8)] _unk2_version_bytes: Vec, - #[br(count = 8)] _unk3_version_bytes: Vec, - #[br(count = 16)] _unk4_version_bytes: Vec, - _unk: u32, - _checksum: u64, - file_size: u32, - #[br(count = 16)] _unk5_version_bytes: Vec, - #[br(count = 16)] _unk6_version_bytes: Vec, - #[br(count = 32)] _unk2: Vec, -} -impl Header { - fn firmware_name(&self) -> String { - common::string_from_bytes(&self.firmware_name_bytes) - } - fn firmware_version(&self) -> String { - common::string_from_bytes(&self.firmware_version_bytes) - } - fn date(&self) -> String { - common::string_from_bytes(&self.date_bytes) - } -} - -#[derive(BinRead)] -struct Entry { - offset: u32, - size: u32, -} +use include::*; pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/utils.rs b/src/utils.rs index b155bb2..5eec0cd 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,14 +1,5 @@ pub mod common; pub mod aes; -pub mod pana_dvd_crypto; -pub mod lzss; pub mod lzop; pub mod sparse; -pub mod compression; -pub mod android_ota_update_metadata; -pub mod mtk_crypto; -pub mod lzhs; -pub mod huffman_tables; -pub mod msd_ouith_parser_old; -pub mod msd_ouith_parser_tizen_1_8; -pub mod msd_ouith_parser_tizen_1_9; \ No newline at end of file +pub mod compression; \ No newline at end of file From 10dd854685127d0402afcf730f1e931105b13492 Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Tue, 17 Feb 2026 18:34:46 +0100 Subject: [PATCH 7/9] unify error and success handling in extractors --- src/formats/amlogic/mod.rs | 4 +--- src/formats/android_ota_payload/mod.rs | 5 +---- src/formats/bdl/mod.rs | 2 -- src/formats/epk1/mod.rs | 19 ++++++++++++------- src/formats/epk2/mod.rs | 12 +++++------- src/formats/epk2b/mod.rs | 10 ++++++---- src/formats/epk3/mod.rs | 4 +--- src/formats/funai_upg/mod.rs | 4 +--- src/formats/invincible_image/mod.rs | 5 +---- src/formats/msd/mod.rs | 8 ++++++-- src/formats/msd10/mod.rs | 13 +++++++------ src/formats/msd11/mod.rs | 9 ++++----- src/formats/mstar/mod.rs | 5 +---- src/formats/mtk_bdp/mod.rs | 10 +++------- src/formats/mtk_pkg/mod.rs | 2 -- src/formats/mtk_pkg_new/mod.rs | 2 -- src/formats/mtk_pkg_old/mod.rs | 2 -- src/formats/nvt_timg/mod.rs | 7 ++----- src/formats/pana_dvd/mod.rs | 2 -- src/formats/pfl_upg/mod.rs | 5 +---- src/formats/pup/mod.rs | 1 - src/formats/roku/mod.rs | 1 - src/formats/ruf/mod.rs | 4 +--- src/formats/rvp/mod.rs | 2 -- src/formats/samsung_old/mod.rs | 5 +---- src/formats/sddl_sec/mod.rs | 2 -- src/formats/slp/mod.rs | 8 +------- src/formats/sony_bdp/mod.rs | 2 -- src/main.rs | 3 +++ 29 files changed, 58 insertions(+), 100 deletions(-) diff --git a/src/formats/amlogic/mod.rs b/src/formats/amlogic/mod.rs index 53b1706..b68190b 100644 --- a/src/formats/amlogic/mod.rs +++ b/src/formats/amlogic/mod.rs @@ -32,8 +32,7 @@ pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Box) -> Result<(), B header.image_size, header.item_align_size, header.item_count, header.version); if header.version != 2 { - println!("\nSorry, this format version is not yet supported!"); - return Ok(()); + return Err("Unsupported format version! (Only 2 is supported right now)".into()); } let mut items: Vec = Vec::new(); @@ -74,6 +73,5 @@ pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Box) -> Result<(), B } } - println!("\nExtraction finished!"); Ok(()) } \ No newline at end of file diff --git a/src/formats/android_ota_payload/mod.rs b/src/formats/android_ota_payload/mod.rs index adcc919..dd721e7 100644 --- a/src/formats/android_ota_payload/mod.rs +++ b/src/formats/android_ota_payload/mod.rs @@ -32,8 +32,7 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box) -> println!("File info:\nFormat version: {}\nManifest size: {}", header.file_format_version, header.manifest_size); if header.file_format_version != 2 { - println!("\nSorry, this version of the file is not supported!"); - return Ok(()) + return Err("Unsupported format version! (Only 2 is supported right now)".into()); } let base_offset = 24 /* size of header */ + header.manifest_size + header.metadata_signature_size as u64; @@ -85,7 +84,5 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box) -> println!("\n-- Saved!"); } - println!("\nExtraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/bdl/mod.rs b/src/formats/bdl/mod.rs index 4445cae..4839106 100644 --- a/src/formats/bdl/mod.rs +++ b/src/formats/bdl/mod.rs @@ -70,7 +70,5 @@ pub fn extract_bdl(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box) -> Result<(), Box< println!("\nLittle endian EPK1 detected."); epk1_type = "le"; } else { - println!("\nUnknown EPK1 variant!"); - return Ok(()); + return Err("Unknown EPK1 variant!".into()); } file.seek(SeekFrom::Start(0))?; @@ -54,7 +53,10 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< } paks.push(Pak { offset: pak.offset, size: pak.size }); } - assert!(header.pak_count as usize == paks.len(), "Paks count in header({}) does not match the amount of non empty pak entries({})!", header.pak_count, paks.len()); + + if header.pak_count as usize != paks.len() { + return Err(format!("Paks count in header({}) does not match the amount of non empty pak entries({})!", header.pak_count, paks.len()).into()); + } let version = common::read_exact(&mut file, 4)?; @@ -68,7 +70,9 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let header_size_bytes = common::read_file(&file, 12, 4)?; //offset of first entry, can be treated as header size let header_size = u32::from_le_bytes(header_size_bytes.try_into().unwrap()); let max_pak_count = (header_size - 48) / 8; //header size minus common header + ota id (48) divide by size of pak entry (8). - assert!(max_pak_count < 128, "Unreasonable calculated pak count {}!!", max_pak_count); + if max_pak_count > 128 { + return Err(format!("Unreasonable calculated pak count {}!!", max_pak_count).into()); + } for _i in 0..max_pak_count { let pak: Pak = file.read_le()?; @@ -77,7 +81,10 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< } paks.push(Pak { offset: pak.offset, size: pak.size }); } - assert!(header.pak_count as usize == paks.len(), "Paks count in header({}) does not match the amount of non empty pak entries({})!", header.pak_count, paks.len()); + + if header.pak_count as usize != paks.len() { + return Err(format!("Paks count in header({}) does not match the amount of non empty pak entries({})!", header.pak_count, paks.len()).into()); + } let version = common::read_exact(&mut file, 4)?; @@ -104,8 +111,6 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< println!("- Saved file!"); } - - println!("\nExtraction finished!"); Ok(()) } \ No newline at end of file diff --git a/src/formats/epk2/mod.rs b/src/formats/epk2/mod.rs index dd0e8d0..83ff3e7 100644 --- a/src/formats/epk2/mod.rs +++ b/src/formats/epk2/mod.rs @@ -47,8 +47,7 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< matching_key = Some(key_bytes); header = decrypt_aes_ecb_auto(matching_key.as_ref().unwrap(), &stored_header)?; } else { - println!("No valid key found!"); - return Ok(()); + return Err("No valid key found!".into()); } } //parse header @@ -86,8 +85,7 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< println!("Found correct key: {}", key_name); matching_key = Some(key_bytes); } else { - println!("No valid key found!"); - return Ok(()); + return Err("No valid key found!".into()); } } let matching_key_bytes = matching_key.as_ref().unwrap(); @@ -109,7 +107,9 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< pak_header = pak_header_reader.read_le()?; } - assert!(i == pak_header.segment_index, "Unexpected segment index in pak header!, expected: {}, got: {}", i , pak_header.segment_index); + if i != pak_header.segment_index { + return Err(format!("Unexpected segment index in pak header!, expected: {}, got: {}", i , pak_header.segment_index).into()); + } let actual_segment_size = // check if this is the last segment and not the last PAK @@ -144,7 +144,5 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< } } - println!("\nExtraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/epk2b/mod.rs b/src/formats/epk2b/mod.rs index 37ff92b..53996cd 100644 --- a/src/formats/epk2b/mod.rs +++ b/src/formats/epk2b/mod.rs @@ -39,7 +39,9 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box paks.push(Pak { offset: pak.offset, size: pak.size }); } - assert!(header.pak_count as usize == paks.len(), "Paks count in header does not match the amount of non empty pak entries!"); + if header.pak_count as usize != paks.len() { + return Err(format!("Paks count in header({}) does not match the amount of non empty pak entries({})!", header.pak_count, paks.len()).into()); + } for (i, pak) in paks.iter().enumerate() { file.seek(SeekFrom::Start(pak.offset as u64))?; @@ -56,7 +58,9 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box pak_header = file.read_le()?; } - assert!(i == pak_header.segment_index, "Unexpected segment index in pak header!, expected: {}, got: {}", i , pak_header.segment_index); + if i != pak_header.segment_index { + 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); let out_data = common::read_exact(&mut file, pak_header.segment_size as usize)?; @@ -79,7 +83,5 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box } } - println!("\nExtraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/epk3/mod.rs b/src/formats/epk3/mod.rs index 4858c7a..112260f 100644 --- a/src/formats/epk3/mod.rs +++ b/src/formats/epk3/mod.rs @@ -43,8 +43,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< new_type = true; } else { - println!("No valid key found!"); - return Ok(()); + return Err("No valid key found!".into()); } let signature_size = if new_type {256} else {128}; @@ -110,6 +109,5 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< pak_i += 1; } - println!("\nExtraction finished!"); Ok(()) } \ No newline at end of file diff --git a/src/formats/funai_upg/mod.rs b/src/formats/funai_upg/mod.rs index b721f2a..198f563 100644 --- a/src/formats/funai_upg/mod.rs +++ b/src/formats/funai_upg/mod.rs @@ -46,8 +46,6 @@ pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), println!("- Saved file!"); } - - println!("\nExtraction finished!"); - + Ok(()) } \ No newline at end of file diff --git a/src/formats/invincible_image/mod.rs b/src/formats/invincible_image/mod.rs index fae3853..8b49fb7 100644 --- a/src/formats/invincible_image/mod.rs +++ b/src/formats/invincible_image/mod.rs @@ -39,8 +39,7 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Box) -> Res } if header.file_version[0] != 3 { - println!("\nSorry, this version of the file is not supported!"); - return Ok(()) + return Err("Unsupported format version! (Only 3 is supported right now)".into()); } let mut encrypted_data = Vec::new(); @@ -77,7 +76,5 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Box) -> Res i += 1; } - println!("\nExtraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/msd/mod.rs b/src/formats/msd/mod.rs index 230265b..bed6f21 100644 --- a/src/formats/msd/mod.rs +++ b/src/formats/msd/mod.rs @@ -12,7 +12,9 @@ type Aes128CbcDec = Decryptor; pub fn decrypt_aes_salted_old(encrypted_data: &[u8], passphrase_bytes: &Vec) -> Result, Box> { let mut data = encrypted_data.to_vec(); - assert!(data[0..8].to_vec() == b"Salted__", "invalid encrypted data!"); + if data[0..8].to_vec() != b"Salted__" { + return Err("Invalid encrypted data!".into()); + } let salt = &data[8..16]; //key = md5 of (passphrase + salt) @@ -38,7 +40,9 @@ pub fn decrypt_aes_salted_old(encrypted_data: &[u8], passphrase_bytes: &Vec) pub fn decrypt_aes_salted_tizen(encrypted_data: &[u8], passphrase_bytes: &Vec) -> Result, Box> { let mut data = encrypted_data.to_vec(); - assert!(data[0..8].to_vec() == b"Salted__", "invalid encrypted data!"); + if data[0..8].to_vec() != b"Salted__" { + return Err("Invalid encrypted data!".into()); + } let salt = &data[8..16]; //iv = md5 of salt diff --git a/src/formats/msd10/mod.rs b/src/formats/msd10/mod.rs index 7b99408..8f1fae3 100644 --- a/src/formats/msd10/mod.rs +++ b/src/formats/msd10/mod.rs @@ -70,8 +70,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box passphrase_bytes = hex::decode(p)?; println!("Firmware type: {}", firmware_type); } else { - println!("Sorry, this firmware is not supported!"); - std::process::exit(1); + return Err("This firmware is not supported!".into()); } let toc_offset = headers[0].offset; @@ -95,7 +94,9 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box println!("\n({}/{}) - {}, Size: {}", i + 1, items.len(), item.name, size); - assert!(sections[i as usize].index == item.item_id, "Item ID in TOC does not match ID from header!"); + if sections[i as usize].index != item.item_id { + return Err("Item ID in TOC does not match ID from header!".into()); + } let stored_data = common::read_file(&file, offset as u64, size as usize)?; @@ -132,7 +133,9 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box println!("\n({}/{}) - {}, Type: {}, Size: {}", item.item_id, items.len(), item.name, type_str, item.all_size); - assert!(sections[i as usize].index == item.item_id, "Item ID in TOC does not match ID from header!"); + if sections[i as usize].index != item.item_id { + return Err("Item ID in TOC does not match ID from header!".into()); + } if item.item_type == 0x11 { //Skip CMAC DATA println!("- Skipping CMAC Data..."); @@ -162,7 +165,5 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box } } - println!("\nExtraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/msd11/mod.rs b/src/formats/msd11/mod.rs index 6b87cb3..09bbb69 100644 --- a/src/formats/msd11/mod.rs +++ b/src/formats/msd11/mod.rs @@ -65,8 +65,7 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box println!("Passphrase: {}", p); passphrase_bytes = hex::decode(p)?; } else { - println!("Sorry, this firmware is not supported!"); - std::process::exit(1); + return Err("This firmware is not supported!".into()); } let toc_offset = headers[0].offset + 8; @@ -88,7 +87,9 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box println!("\n({}/{}) - {}, Size: {}", i + 1, items.len(), item.name, size); - assert!(sections[i as usize].index == item.item_id, "Item ID in TOC does not match ID from header!"); + if sections[i as usize].index != item.item_id { + return Err("Item ID in TOC does not match ID from header!".into()); + } let stored_data = common::read_file(&file, offset as u64, size as usize)?; @@ -109,7 +110,5 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box println!("-- Saved file!"); } - println!("\nExtraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/mstar/mod.rs b/src/formats/mstar/mod.rs index 906dce0..28cb790 100644 --- a/src/formats/mstar/mod.rs +++ b/src/formats/mstar/mod.rs @@ -47,7 +47,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box script_string = String::from_utf8_lossy(&script); if script_string == "" { - println!("Failed to get script."); + return Err("Failed to get script".into()); } } @@ -168,8 +168,5 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box i += 1; } - println!(); - println!("Extraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/mtk_bdp/mod.rs b/src/formats/mtk_bdp/mod.rs index 5431255..22c9bbc 100644 --- a/src/formats/mtk_bdp/mod.rs +++ b/src/formats/mtk_bdp/mod.rs @@ -72,8 +72,7 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo let mut pit_entries: Vec = Vec::new(); let pit_header: PITHeader = file.read_le()?; if pit_header.pit_magic != PIT_MAGIC { - println!("Invalid PIT Magic!"); - return Ok(()); + 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); file.seek(SeekFrom::Start(pit_offset + pit_header.first_entry_offset as u64))?; @@ -90,8 +89,7 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo let mut bit_entries: Vec = Vec::new(); let bit_magic = common::read_exact(&mut file, 20)?; if bit_magic != BIT_MAGIC { - println!("Invalid BIT Magic!"); - return Ok(()); + return Err("Invalid BIT magic!".into()); } let mut bit_i = 0; @@ -127,8 +125,6 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo println!("-- Saved file!"); } - - println!("\nExtraction finished!"); - + Ok(()) } \ No newline at end of file diff --git a/src/formats/mtk_pkg/mod.rs b/src/formats/mtk_pkg/mod.rs index aa1f523..f728ed6 100644 --- a/src/formats/mtk_pkg/mod.rs +++ b/src/formats/mtk_pkg/mod.rs @@ -162,7 +162,5 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo println!("-- Saved file!"); } - println!("\nExtraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/mtk_pkg_new/mod.rs b/src/formats/mtk_pkg_new/mod.rs index dd0bfc1..1203a8e 100644 --- a/src/formats/mtk_pkg_new/mod.rs +++ b/src/formats/mtk_pkg_new/mod.rs @@ -132,7 +132,5 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box) -> Result<() println!("-- Saved file!"); } - println!("\nExtraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/mtk_pkg_old/mod.rs b/src/formats/mtk_pkg_old/mod.rs index 6719736..0d91749 100644 --- a/src/formats/mtk_pkg_old/mod.rs +++ b/src/formats/mtk_pkg_old/mod.rs @@ -102,7 +102,5 @@ pub fn extract_mtk_pkg_old(app_ctx: &AppContext, _ctx: Box) -> Result<( println!("-- Saved file!"); } - println!("\nExtraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/nvt_timg/mod.rs b/src/formats/nvt_timg/mod.rs index 774486c..581e4fd 100644 --- a/src/formats/nvt_timg/mod.rs +++ b/src/formats/nvt_timg/mod.rs @@ -35,8 +35,7 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Box) -> Result<(), pimg_i += 1; let pimg: PIMG = file.read_le()?; if &pimg.magic_bytes != b"PIMG" { - println!("Invalid PIMG magic!"); - return Ok(()); + return Err("Invalid PIMG magic!".into()); } let data = common::read_exact(&mut file, pimg.size as usize)?; @@ -57,7 +56,7 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Box) -> Result<(), println!("-- Saved file!"); continue } else { - println!("- Warning: unsupported compression type!"); + println!("- Warning: unsupported compression type, saving stored data!"); out_data = data; } @@ -68,7 +67,5 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Box) -> Result<(), println!("-- Saved file!"); } - println!("\nExtraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/pana_dvd/mod.rs b/src/formats/pana_dvd/mod.rs index ae907f6..541e92d 100644 --- a/src/formats/pana_dvd/mod.rs +++ b/src/formats/pana_dvd/mod.rs @@ -105,8 +105,6 @@ pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Box) -> Result<(), B } } - println!("\nExtraction finished!"); - Ok(()) } diff --git a/src/formats/pfl_upg/mod.rs b/src/formats/pfl_upg/mod.rs index 2e1b118..df189ea 100644 --- a/src/formats/pfl_upg/mod.rs +++ b/src/formats/pfl_upg/mod.rs @@ -52,8 +52,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), B } } if key.is_none() { - println!("Sorry, this firmware is not supported!"); - std::process::exit(1); + return Err("This firmware is not supported!".into()); } //get key @@ -142,8 +141,6 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), B println!("- Saved file!"); } - - println!("\nExtraction finished!"); Ok(()) } \ No newline at end of file diff --git a/src/formats/pup/mod.rs b/src/formats/pup/mod.rs index f8eb905..bf1d3ba 100644 --- a/src/formats/pup/mod.rs +++ b/src/formats/pup/mod.rs @@ -133,6 +133,5 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box) -> Result<(), Box< } } - println!("\nExtraction finished!"); Ok(()) } \ No newline at end of file diff --git a/src/formats/ruf/mod.rs b/src/formats/ruf/mod.rs index a8b39f8..3419e86 100644 --- a/src/formats/ruf/mod.rs +++ b/src/formats/ruf/mod.rs @@ -36,7 +36,6 @@ pub fn extract_ruf(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box) -> Result<(), Box) -> Result<( if let Some(p) = secret { println!("Secret: {}", p); } else { - println!("Sorry, this firmware is not supported!"); - std::process::exit(1); + return Err("This firmware is not supported!".into()); } for entry in fs::read_dir(image_path)? { @@ -129,7 +128,5 @@ pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box) -> Result<( } } - println!("\nExtraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/sddl_sec/mod.rs b/src/formats/sddl_sec/mod.rs index 823ba48..96db2cc 100644 --- a/src/formats/sddl_sec/mod.rs +++ b/src/formats/sddl_sec/mod.rs @@ -155,7 +155,5 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), } } - println!("\nExtraction finished!"); - Ok(()) } \ No newline at end of file diff --git a/src/formats/slp/mod.rs b/src/formats/slp/mod.rs index 20833b8..6b3813b 100644 --- a/src/formats/slp/mod.rs +++ b/src/formats/slp/mod.rs @@ -62,11 +62,7 @@ pub fn extract_slp(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box) -> Result<(), Box) -> Result<(), println!("- Not an MTK BDP file."); } } - - println!("\nExtraction finished!"); Ok(()) } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e602d3b..bba8397 100644 --- a/src/main.rs +++ b/src/main.rs @@ -90,6 +90,9 @@ fn main() -> Result<(), Box> { if let Some(ctx) = (format.detector_func)(&app_ctx)? { println!("\n{} detected!", format.name); (format.extractor_func)(&app_ctx, ctx)?; + + //extractor returned with no error + println!("\nExtraction finished! Saved extracted files to {}", output_path_str); return Ok(()); } } From c72966cd7e4cea30bd9fa812bd78f19ff79dfb42 Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Tue, 17 Feb 2026 18:57:14 +0100 Subject: [PATCH 8/9] always seek file to start before starting extractor --- src/formats/amlogic/mod.rs | 4 +--- src/formats/epk/mod.rs | 5 ++++- src/formats/epk2/mod.rs | 1 - src/formats/epk2b/mod.rs | 2 +- src/formats/epk3/mod.rs | 4 ++-- src/formats/rvp/mod.rs | 17 +++++++++++------ src/formats/sddl_sec/mod.rs | 1 - src/main.rs | 8 +++++++- 8 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/formats/amlogic/mod.rs b/src/formats/amlogic/mod.rs index b68190b..a704c70 100644 --- a/src/formats/amlogic/mod.rs +++ b/src/formats/amlogic/mod.rs @@ -4,7 +4,7 @@ use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; -use std::io::{Write, Seek, SeekFrom}; +use std::io::Write; use binrw::BinReaderExt; use crate::utils::common; @@ -25,9 +25,7 @@ pub fn is_amlogic_file(app_ctx: &AppContext) -> Result>, Box pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - file.seek(SeekFrom::Start(0))?; let header: ImageHeader = file.read_le()?; - println!("File info -\nImage size: {}\nItem align size: {}\nItem count: {}\nFormat version: {}", header.image_size, header.item_align_size, header.item_count, header.version); diff --git a/src/formats/epk/mod.rs b/src/formats/epk/mod.rs index d23fbec..5643887 100644 --- a/src/formats/epk/mod.rs +++ b/src/formats/epk/mod.rs @@ -1,4 +1,5 @@ use std::any::Any; +use std::io::Seek; use crate::AppContext; use crate::utils::common; @@ -49,7 +50,7 @@ fn match_with_pattern(data: &[u8], pattern: &str) -> bool { } pub fn extract_epk(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { - let file = app_ctx.file().ok_or("Extractor expected file")?; + let mut file = app_ctx.file().ok_or("Extractor expected file")?; let ctx = ctx.downcast::().expect("Missing context"); let versions = common::read_file(&file, 1712, 36)?; @@ -58,6 +59,8 @@ pub fn extract_epk(app_ctx: &AppContext, ctx: Box) -> Result<(), Box Result>, Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - file.seek(SeekFrom::Start(0))?; let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?; let stored_header = common::read_exact(&mut file, 1584)?; //max header size diff --git a/src/formats/epk2b/mod.rs b/src/formats/epk2b/mod.rs index 53996cd..6d5735d 100644 --- a/src/formats/epk2b/mod.rs +++ b/src/formats/epk2b/mod.rs @@ -13,8 +13,8 @@ use include::*; pub fn is_epk2b_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; - let epk2_magic = common::read_file(&file, 12, 4)?; let epak_magic = common::read_file(&file, 0, 4)?; + let epk2_magic = common::read_file(&file, 12, 4)?; if epak_magic == b"epak" && epk2_magic == b"EPK2" { Ok(Some(Box::new(()))) } else { diff --git a/src/formats/epk3/mod.rs b/src/formats/epk3/mod.rs index 112260f..804b252 100644 --- a/src/formats/epk3/mod.rs +++ b/src/formats/epk3/mod.rs @@ -4,7 +4,7 @@ use crate::AppContext; use std::fs::{self, OpenOptions}; use std::path::Path; -use std::io::{Write, Seek, SeekFrom, Cursor}; +use std::io::{Write, Cursor}; use binrw::BinReaderExt; use crate::utils::common; @@ -18,7 +18,7 @@ pub fn is_epk3_file(_app_ctx: &AppContext) -> Result>, Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - file.seek(SeekFrom::Start(0))?; + let stored_header = common::read_exact(&mut file, 1712)?; let header: Vec; let _header_signature; diff --git a/src/formats/rvp/mod.rs b/src/formats/rvp/mod.rs index acebb10..1f896fc 100644 --- a/src/formats/rvp/mod.rs +++ b/src/formats/rvp/mod.rs @@ -9,13 +9,16 @@ use std::io::{Write, Read, Cursor, Seek}; use crate::utils::common; use include::*; +pub struct RvpContext { + header_offset: u64, +} + pub fn is_rvp_file(app_ctx: &AppContext) -> Result>, Box> { - let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; //MVP let header = common::read_file(&file, 0, 4)?; if header == b"UPDT" { - file.seek(std::io::SeekFrom::Start(36))?; //skip rest of header // NOT GOOD PRACTICE SHOULD BE REMOVED - return Ok(Some(Box::new(()))) + return Ok(Some(Box::new(RvpContext {header_offset: 36}))) } //RVP @@ -26,12 +29,14 @@ pub fn is_rvp_file(app_ctx: &AppContext) -> Result>, Box) -> Result<(), Box> { +pub fn extract_rvp(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"); + + file.seek(std::io::SeekFrom::Start(ctx.header_offset))?; let mut obf_data = Vec::new(); //we sadly cannot deXOR on the fly because of its 32 byte pattern file.read_to_end(&mut obf_data)?; diff --git a/src/formats/sddl_sec/mod.rs b/src/formats/sddl_sec/mod.rs index 96db2cc..5211446 100644 --- a/src/formats/sddl_sec/mod.rs +++ b/src/formats/sddl_sec/mod.rs @@ -66,7 +66,6 @@ fn parse_tdi_to_modules(tdi_data: Vec) -> Result, Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; - file.seek(SeekFrom::Start(0))?; let mut secfile_hdr_reader = Cursor::new(decipher(&common::read_exact(&mut file, 32)?)); let secfile_header: SecHeader = secfile_hdr_reader.read_be()?; diff --git a/src/main.rs b/src/main.rs index bba8397..7d23419 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ mod utils; use clap::Parser; use std::path::{PathBuf}; -use std::io::{self}; +use std::io::{self, Seek, SeekFrom}; use std::fs::{self, File}; use crate::formats::{Format, get_registry}; @@ -89,6 +89,12 @@ fn main() -> Result<(), Box> { for format in formats { if let Some(ctx) = (format.detector_func)(&app_ctx)? { println!("\n{} detected!", format.name); + + //reset seek of the file if present + if let Some(mut file) = app_ctx.file() { + file.seek(SeekFrom::Start(0))?; + } + (format.extractor_func)(&app_ctx, ctx)?; //extractor returned with no error From 4c648bf5a5f8c94b41d38efb73484a0831be745d Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:28:43 +0100 Subject: [PATCH 9/9] add format specific options (example on sddl_sec and msd10) --- src/formats/msd10/mod.rs | 16 +++++++++++----- src/formats/sddl_sec/mod.rs | 18 ++++++++++-------- src/formats/sony_bdp/mod.rs | 4 +++- src/main.rs | 7 +++++++ 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/formats/msd10/mod.rs b/src/formats/msd10/mod.rs index 8f1fae3..eb79515 100644 --- a/src/formats/msd10/mod.rs +++ b/src/formats/msd10/mod.rs @@ -27,6 +27,7 @@ pub fn is_msd10_file(app_ctx: &AppContext) -> Result>, Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; + let save_cmac = app_ctx.options.iter().any(|e| e == "msd10:save_cmac"); let header: FileHeader = file.read_le()?; println!("\nNumber of sections: {}", header.section_count); @@ -109,7 +110,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box out_data = stored_data; } - let output_path = Path::new(&app_ctx.output_dir).join(item.name.clone()); + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}", item.name)); 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(&out_data)?; @@ -137,9 +138,14 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box return Err("Item ID in TOC does not match ID from header!".into()); } - if item.item_type == 0x11 { //Skip CMAC DATA - println!("- Skipping CMAC Data..."); - continue + let mut out_filename = format!("{}", item.name); + if item.item_type == 0x11 { //CMAC DATA + if 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..."); + continue + } } file.seek(SeekFrom::Start(offset as u64))?; @@ -156,7 +162,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box out_data = stored_data; } - let output_path = Path::new(&app_ctx.output_dir).join(item.name.clone()); + let output_path = Path::new(&app_ctx.output_dir).join(out_filename); 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(&out_data)?; diff --git a/src/formats/sddl_sec/mod.rs b/src/formats/sddl_sec/mod.rs index 5211446..76009b1 100644 --- a/src/formats/sddl_sec/mod.rs +++ b/src/formats/sddl_sec/mod.rs @@ -66,6 +66,8 @@ fn parse_tdi_to_modules(tdi_data: Vec) -> Result, Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; + let save_extra = app_ctx.options.iter().any(|e| e == "sddl_sec:save_extra"); + let mut secfile_hdr_reader = Cursor::new(decipher(&common::read_exact(&mut file, 32)?)); let secfile_header: SecHeader = secfile_hdr_reader.read_be()?; @@ -74,10 +76,10 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), let (tdi_file, tdi_data) = get_sec_file(&file)?; println!("[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(&output_folder).join(tdi_file.name()))?; - // out_file.write_all(&tdi_data)?; - //} + 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)?; + } if tdi_file.name() != TDI_FILENAME { return Err(format!("Invalid TDI filename {}!, expected: {}", tdi_file.name(), TDI_FILENAME).into()); } @@ -91,10 +93,10 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), 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()); } - //if save_extra { //Save info file - // let mut out_file = OpenOptions::new().write(true).create(true).open(Path::new(&output_folder).join(info_file.name()))?; - // out_file.write_all(&info_data)?; - //} + if save_extra { //Save info file + let mut out_file = OpenOptions::new().write(true).create(true).open(Path::new(&app_ctx.output_dir).join(info_file.name()))?; + out_file.write_all(&info_data)?; + } //print info file println!("{}", String::from_utf8_lossy(&info_data)); } diff --git a/src/formats/sony_bdp/mod.rs b/src/formats/sony_bdp/mod.rs index 2468bbb..646c206 100644 --- a/src/formats/sony_bdp/mod.rs +++ b/src/formats/sony_bdp/mod.rs @@ -72,7 +72,9 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Box) -> Result<(), let last_file = File::open(last_file_path.unwrap())?; let mtk_extraction_path = app_ctx.output_dir.join(format!("{}", i + 1)); - let ctx: AppContext = AppContext { input: InputTarget::File(last_file), output_dir: mtk_extraction_path }; + + //this is getting stupid... + let ctx: AppContext = AppContext { input: InputTarget::File(last_file), output_dir: mtk_extraction_path, options: app_ctx.options.clone() }; if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&ctx)? { println!("- MTK BDP file detected!\n"); diff --git a/src/main.rs b/src/main.rs index 7d23419..6247135 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,10 @@ use crate::formats::{Format, get_registry}; struct Args { input_target: String, output_directory: Option, + + ///format specific options + #[arg(short, long)] + options: Vec, } pub enum InputTarget { @@ -22,6 +26,7 @@ pub enum InputTarget { pub struct AppContext { pub input: InputTarget, pub output_dir: PathBuf, + pub options: Vec, } impl AppContext { pub fn file(&self) -> Option<&File> { @@ -73,11 +78,13 @@ fn main() -> Result<(), Box> { app_ctx = AppContext { input: InputTarget::File(file), output_dir: output_directory_path, + options: args.options, }; } else if target_path.is_dir() { app_ctx = AppContext { input: InputTarget::Directory(target_path), output_dir: output_directory_path, + options: args.options, }; } else { return Err("Invalid input path!".into());