diff --git a/README.md b/README.md index 5bc6c44..22c2a71 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,8 @@ Options: **Used in:** Panasonic TVs **Notes:** Pre-2011 files are not supported. **Options:** -`sddl_sec:save_extra` - Save SDIT.FDI and .TXT files that are not extracted by default. +`sddl_sec:save_extra` - Save SDIT.FDI and .TXT files that are not extracted by default. +`sddl_sec:split_peaks` - Split PEAKS module into partitions (only on older files). ## SLP **Used in:** Samsung Tizen-based NX series cameras diff --git a/src/formats/sddl_sec/mod.rs b/src/formats/sddl_sec/mod.rs index 76009b1..6bf6c63 100644 --- a/src/formats/sddl_sec/mod.rs +++ b/src/formats/sddl_sec/mod.rs @@ -1,5 +1,6 @@ //sddl_dec 6.0 https://github.com/theubusu/sddl_dec mod include; +mod util; use std::any::Any; use crate::AppContext; @@ -12,6 +13,7 @@ use crate::utils::common; use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; use crate::utils::compression::{decompress_zlib}; use include::*; +use util::split_peaks_file; pub fn is_sddl_sec_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -67,6 +69,7 @@ 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 split_peaks = app_ctx.options.iter().any(|e| e == "sddl_sec:split_peaks"); let mut secfile_hdr_reader = Cursor::new(decipher(&common::read_exact(&mut file, 32)?)); let secfile_header: SecHeader = secfile_hdr_reader.read_be()?; @@ -106,6 +109,8 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), println!("\nModule #{}/{} - {}, Target ID: {}, Segment count: {}, Version: {}", i+1, &modules.len(), module.module_name(), module.target_id, module.num_of_txx, module.version_string()); + let mut final_out_path: Option = None; + for i in 0..module.num_of_txx { let (module_file, module_data) = get_sec_file(&file)?; if !module_file.name().starts_with(&module.module_name()) { @@ -147,13 +152,21 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box) -> Result<(), } else { output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", module.module_name())); } + final_out_path = Some(output_path.clone()); let data = common::read_exact(&mut content_reader, content_header.size as usize)?; - let mut out_file = OpenOptions::new().read(true).write(true).create(true).open(output_path)?; + let mut out_file = OpenOptions::new().read(true).write(true).create(true).open(&output_path)?; out_file.seek(SeekFrom::Start(content_header.dest_offset() as u64))?; out_file.write_all(&data)?; } + + if split_peaks && module.module_name() == "PEAKS" { + println!("\n- Splitting PEAKS"); + if let Some(ref path) = final_out_path { + split_peaks_file(path, &app_ctx.output_dir)?; + } + } } Ok(()) diff --git a/src/formats/sddl_sec/util.rs b/src/formats/sddl_sec/util.rs new file mode 100644 index 0000000..6e90091 --- /dev/null +++ b/src/formats/sddl_sec/util.rs @@ -0,0 +1,83 @@ +use crate::utils::common; +use std::{fs::{self, File, OpenOptions}, io::Write, path::{Path, PathBuf}}; + +pub fn split_peaks_file(path: &PathBuf, out_path: &PathBuf) -> Result<(), Box> { + let mut file = File::open(path)?; + let file_size = file.metadata()?.len(); + let output_folder = Path::new(&out_path).join("PEAKS"); + + let args_bytes = common::read_file(&mut file, 0, 0x210)?; + if !args_bytes.starts_with(b"D50 ") { + println!("- Splitting PEAKS is not supported on this file..."); + return Ok(()); + } + let args = common::string_from_bytes(&args_bytes[16..]); + + let mut root: Option = None; + let mut parts = Vec::<(String, u64, Option<&str>)>::new(); + + //parse parts + for line in args.lines() { + let line = line.trim(); + if line.is_empty() { + continue; + } + + //get the root partition, strip /dev/ + if let Some(rest) = line.strip_prefix("root=") { + let dev = rest.trim(); + if let Some(name) = dev.strip_prefix("/dev/") { + root = Some(name.to_string()); + } + } + + //get fmaX=XXXXXk partitions + if let Some((name, size_part)) = line.split_once('=') { + if name.starts_with("fma") { + let (size_str, flag) = if let Some((size, flag)) = size_part.split_once(':') { + (size.trim(), Some(flag.trim())) + } else { + (size_part.trim(), None) + }; + + if let Some(num) = size_str.strip_suffix('k') { + let kb: u64 = num.parse().map_err(|_| format!("Bad size: {}", size_str))?; + parts.push((name.to_string(), kb * 1024, flag)); + } + + if let Some(num) = size_str.strip_suffix('M') { + let mb: u64 = num.parse().map_err(|_| format!("Bad size: {}", size_str))?; + parts.push((name.to_string(), mb * 1048576, flag)); + } + } + } + } + + let root = root.ok_or("Failed to get root partition!")?; + println!("Root - {}", root); + let root_index = parts.iter().position(|(n, _s, _f)| n == &root).ok_or("Root partition not found in partition list!")?; + + let mut tsize: u64 = 0; + + //read parts from file + let start_index = root_index - 1; // root is always the second partition in PEAKS, so we start from the previous one. + for (part_name, part_size, part_flag) in parts.iter().skip(start_index) { + if tsize >= file_size { + break + } + + println!("- {} - Size: {}{}", part_name, part_size, if let Some(part_flag) = part_flag {format!(", Flag: {}", part_flag)} else {format!("")}); + + tsize += part_size; + + let data = common::read_exact(&mut file, *part_size as usize)?; + + let output_path = Path::new(&output_folder).join(format!("{}.bin", part_name)); + fs::create_dir_all(&output_folder)?; + + let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; + out_file.write_all(&data)?; + } + + Ok(()) +} \ No newline at end of file