diff --git a/README.md b/README.md index 5f34781..7620acb 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,10 @@ Options: **Used in:** Funai & Funai-made Philips Blu-Ray player/HTS (USA market) **Notes:** N/A +## Funai MStar +**Used in:** MStar-based Funai & Funai-made Philips TVs (USA market) +**Notes:** Inner SoC part is extracted with mstar_secure_old + ## Funai UPG **Used in:** Some Funai TVs **Notes:** **Depends on keys** - see keys.rs. @@ -107,6 +111,8 @@ Tip: if you have split ROM (.ROM-00 and .ROM-01), extract both into the same fol ## MStar upgrade bin (Secure, old) **Used in:** Older MStar-based TVs with Secure upgrade mode (encrypted+signed) **Notes:** Only default upgrade key is supported. This use the extractor above after decrypting. +**Options:** +`mstar_secure_old:keep_decrypted` - Keep decrypted file (it will be deleted by default). ## MediaTek BDP **Used in:** Many MediaTek-based Blu-Ray players (LG, Samsung, Philips, Panasonic...) diff --git a/src/formats.rs b/src/formats.rs index 8b99139..45cb162 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -23,6 +23,7 @@ pub mod rvp; pub mod funai_upg; pub mod funai_upg_phl; pub mod funai_bdp; +pub mod funai_mstar; pub mod pana_dvd; pub mod android_ota_payload; pub mod bdl; @@ -59,11 +60,6 @@ pub fn get_registry() -> Vec { detector_func: crate::formats::mstar::is_mstar_file, extractor_func: crate::formats::mstar::extract_mstar, }, - Format { - name: "mstar_secure_old", - detector_func: crate::formats::mstar_secure_old::is_mstar_secure_old_file, - extractor_func: crate::formats::mstar_secure_old::extract_mstar_secure_old, - }, Format { name: "samsung_old", detector_func: crate::formats::samsung_old::is_samsung_old_dir, @@ -144,6 +140,11 @@ pub fn get_registry() -> Vec { detector_func: crate::formats::funai_bdp::is_funai_bdp_file, extractor_func: crate::formats::funai_bdp::extract_funai_bdp, }, + Format { + name: "funai_mstar", // ORDER: needs to be placed BELOW mstar_secure_old + detector_func: crate::formats::funai_mstar::is_funai_mstar_file, //because, it can end with mstar_secure_old payload, but because it is not aligned to the start of the file, extraction will fail + extractor_func: crate::formats::funai_mstar::extract_funai_mstar, + }, Format { name: "pana_dvd", detector_func: crate::formats::pana_dvd::is_pana_dvd_file, @@ -221,8 +222,8 @@ pub fn get_registry() -> Vec { }, Format { name: "philips_bdp", - detector_func: crate::formats::philips_bdp::is_philips_bdp_file, - extractor_func: crate::formats::philips_bdp::extract_philips_bdp, + detector_func: crate::formats::philips_bdp::is_philips_bdp_file, //ORDER: needs to be placed below mtk_bdp + extractor_func: crate::formats::philips_bdp::extract_philips_bdp, //because, it can end with mtk_bdp payload, but because it is not aligned to the start of the file, extraction will fail }, Format { name: "mtk_bdp", @@ -244,5 +245,10 @@ pub fn get_registry() -> Vec { detector_func: crate::formats::onkyo::is_onkyo_file, extractor_func: crate::formats::onkyo::extract_onkyo, }, + Format { + name: "mstar_secure_old", + detector_func: crate::formats::mstar_secure_old::is_mstar_secure_old_file, + extractor_func: crate::formats::mstar_secure_old::extract_mstar_secure_old, + }, ] } \ No newline at end of file diff --git a/src/formats/funai_mstar/include.rs b/src/formats/funai_mstar/include.rs new file mode 100644 index 0000000..a38f79d --- /dev/null +++ b/src/formats/funai_mstar/include.rs @@ -0,0 +1,42 @@ +use std::collections::HashMap; + +#[derive(Debug)] +pub struct InfoStruct { + pub file_code: String, + pub brand_name: String, + pub model_name: String, + pub soc_version: String, + pub frc_version: String, + pub soc_size: usize, + pub frc60_size: usize, + pub frc120_size: usize, +} +impl InfoStruct { + pub fn from_str(str: String) -> Option { + let lines: Vec<&str> = str.lines().map(|l| l.trim()).collect(); + let mut map = HashMap::new(); + + for line in lines { + //skip markers + if line == "#@INFO" || line == "#@END" { + continue; + } + + //split KEY=VALUE + let (key, value) = line.split_once('=')?; + map.insert(key, value); + } + + Some(Self { + file_code: map.get("FileCode")?.to_string(), + brand_name: map.get("BrandName")?.to_string(), + model_name: map.get("ModelName")?.to_string(), + soc_version: map.get("SoC_Version")?.to_string(), + frc_version: map.get("FRC_Version")?.to_string(), + + soc_size: map.get("SoC_Size")?.parse().ok()?, + frc60_size: map.get("FRC60_Size")?.parse().ok()?, + frc120_size: map.get("FRC120_Size")?.parse().ok()?, + }) + } +} \ No newline at end of file diff --git a/src/formats/funai_mstar/mod.rs b/src/formats/funai_mstar/mod.rs new file mode 100644 index 0000000..f69ace2 --- /dev/null +++ b/src/formats/funai_mstar/mod.rs @@ -0,0 +1,87 @@ +mod include; +use std::any::Any; +use crate::{AppContext, InputTarget}; + +use std::path::Path; +use std::fs::{self, File, OpenOptions}; +use std::io::{Write, Seek, SeekFrom}; + +use crate::utils::common; +use crate::formats::mstar_secure_old::{is_mstar_secure_old_file, extract_mstar_secure_old}; +use include::*; + +struct FunaiMstarCtx { + data_offset: u64, + info_str: String, +} + +pub fn is_funai_mstar_file(app_ctx: &AppContext) -> Result>, Box> { + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; + + let mut info = common::read_file(&file, 0, 0x1000)?; //try at start of file + if info.starts_with(b"#@INFO") { + return Ok(Some(Box::new(FunaiMstarCtx {data_offset: 0x1000, info_str: common::string_from_bytes(&info)}))) + } + + //try at end of file (variant 2) + let file_size = file.metadata()?.len(); + if file_size < 0x1000 { + return Ok(None); + } + info = common::read_file(&file, file_size - 0x1000, 0x1000)?; + if info.starts_with(b"#@INFO") { + return Ok(Some(Box::new(FunaiMstarCtx {data_offset: 0, info_str: common::string_from_bytes(&info)}))) + } else { + return Ok(None) + } +} + +pub fn extract_funai_mstar(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { + let mut file = app_ctx.file().ok_or("Extractor expected file")?; + let ctx = ctx.downcast::().expect("Missing context"); + + let info = InfoStruct::from_str(ctx.info_str).unwrap(); + println!("File info -\nFile code: {}\nBrand name: {}\nModel name: {}\nSoC Version: {}\nFRC Version: {}", + info.file_code, info.brand_name, info.model_name, info.soc_version, info.frc_version); + + let payloads: Vec<(&str, usize)> = vec![("SoC", info.soc_size), ("FRC60", info.frc60_size), ("FRC120", info.frc120_size)]; + + file.seek(SeekFrom::Start(ctx.data_offset))?; + + let mut p_i = 0; + for (name, size) in payloads { + if size == 0 { + continue + } + println!("\n#{} - {}, Size: {}", p_i+1, name, size); + + let data = common::read_exact(&mut file, size)?; + + 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().write(true).create(true).open(&output_path)?; + out_file.write_all(&data)?; + + println!("- Saved file!"); + + //extract SoC which (should be) mstar_secure_old, this is just a simple container for that format ( so we will go funai_mstar -> mstar_secure_old -> mstar (DUMB?) ) + if name == "SoC" { + let r_out_file = File::open(&output_path)?; + let in_ctx: AppContext = AppContext { + input: InputTarget::File(r_out_file), + output_dir: app_ctx.output_dir.join("SoC"), + options: app_ctx.options.clone() + }; + + //do check and extarct + if let Some(result) = is_mstar_secure_old_file(&in_ctx)? { + println!("- Extracting mstar_secure_old..."); + extract_mstar_secure_old(&in_ctx, result)?; + }; + + p_i += 1; + } + } + + Ok(()) +} \ No newline at end of file diff --git a/src/formats/mstar/mod.rs b/src/formats/mstar/mod.rs index 0ea2c88..b105712 100644 --- a/src/formats/mstar/mod.rs +++ b/src/formats/mstar/mod.rs @@ -134,6 +134,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box Path::new(&app_ctx.output_dir).join(format!("_unknown_{}.bin", offset)) } else { println!("- Warning, unknown destination - skipping..."); + i += 1; continue; } } else { diff --git a/src/formats/mstar_secure_old/mod.rs b/src/formats/mstar_secure_old/mod.rs index c672007..9f2abc2 100644 --- a/src/formats/mstar_secure_old/mod.rs +++ b/src/formats/mstar_secure_old/mod.rs @@ -41,7 +41,7 @@ pub fn extract_mstar_secure_old(app_ctx: &AppContext, ctx: Box) -> Resu let enc_data = common::read_file(&mut file, hdr.file_data_offset as u64, hdr.file_data_len as usize)?; - println!("Decrypting..."); + println!("- Decrypting..."); let dec_data = decrypt_aes128_ecb(&MSTAR_DEFAULT_UPGRADE_KEY, &enc_data)?; let output_path = Path::new(&app_ctx.output_dir).join("_decrypted.bin"); @@ -49,8 +49,6 @@ pub fn extract_mstar_secure_old(app_ctx: &AppContext, ctx: Box) -> Resu let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?; out_file.write_all(&dec_data)?; - println!("- Saved decrypted file!"); - //run standard mstar ext into same directory let r_out_file = File::open(&output_path)?; let in_ctx: AppContext = AppContext { @@ -66,5 +64,10 @@ pub fn extract_mstar_secure_old(app_ctx: &AppContext, ctx: Box) -> Resu return Err("detection failed on decrypted data".into()); } + //delete decrypted file unless asked not to + if !app_ctx.has_option("mstar_secure_old:keep_decrypted") { + fs::remove_file(&output_path)?; + } + Ok(()) } \ No newline at end of file