From 4d86513babe08ba3f6238da4cbadcfde36ea616f Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Tue, 24 Feb 2026 21:07:37 +0100 Subject: [PATCH] improve append file handling + misc change --- src/formats/android_ota_payload/mod.rs | 9 +++++---- src/formats/epk1/include.rs | 6 ++++++ src/formats/epk1/mod.rs | 12 ++++++------ src/formats/epk2/mod.rs | 7 ++++--- src/formats/epk2b/mod.rs | 7 ++++--- src/formats/epk3/mod.rs | 7 ++++--- src/formats/mstar/include.rs | 11 +++++++++++ src/formats/mstar/mod.rs | 26 +++++++++++++------------- src/formats/pana_dvd/mod.rs | 6 +++--- 9 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/formats/android_ota_payload/mod.rs b/src/formats/android_ota_payload/mod.rs index dd721e7..c5b72ef 100644 --- a/src/formats/android_ota_payload/mod.rs +++ b/src/formats/android_ota_payload/mod.rs @@ -44,7 +44,11 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box) -> let operation_count = partition.operations.len(); println!("\n#{} - {}, Size: {}, Operations: {}", i + 1, partition.partition_name, partition.new_partition_info.unwrap().size.unwrap(), operation_count); - + + fs::create_dir_all(&app_ctx.output_dir)?; + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", partition.partition_name)); + let mut out_file = OpenOptions::new().write(true).create(true).truncate(true).open(output_path)?; + for (i, operation) in partition.operations.into_iter().enumerate() { let operation_name_str = match install_operation::Type::try_from(operation.r#type) { Ok(t) => t.as_str_name(), @@ -76,9 +80,6 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box) -> break } - 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)?; } println!("\n-- Saved!"); diff --git a/src/formats/epk1/include.rs b/src/formats/epk1/include.rs index 43cad85..ab7f794 100644 --- a/src/formats/epk1/include.rs +++ b/src/formats/epk1/include.rs @@ -1,6 +1,12 @@ use crate::utils::common; use binrw::BinRead; +#[derive(PartialEq)] +pub enum Epk1Type { + LittleEndian, + BigEndian, +} + #[derive(BinRead)] pub struct CommonHeader { _magic_bytes: [u8; 4], diff --git a/src/formats/epk1/mod.rs b/src/formats/epk1/mod.rs index dad0ef9..0e6e15f 100644 --- a/src/formats/epk1/mod.rs +++ b/src/formats/epk1/mod.rs @@ -25,16 +25,16 @@ pub fn is_epk1_file(app_ctx: &AppContext) -> Result>, Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; //check type of epk1 - let epk1_type; + let epk1_type: Epk1Type; let init_pak_count_bytes = common::read_file(&file, 8, 4)?; let init_pak_count = u32::from_le_bytes(init_pak_count_bytes.try_into().unwrap()); if init_pak_count > 256 { println!("\nBig endian EPK1 detected."); - epk1_type = "be"; + epk1_type = Epk1Type::BigEndian; } else if init_pak_count < 33 { println!("\nLittle endian EPK1 detected."); - epk1_type = "le"; + epk1_type = Epk1Type::LittleEndian; } else { return Err("Unknown EPK1 variant!".into()); } @@ -43,7 +43,7 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let mut paks: Vec = Vec::new(); - if epk1_type == "be" { + if epk1_type == Epk1Type::BigEndian { let header: CommonHeader = file.read_be()?; for _i in 0..10 { //header can fit max 10 pak entries @@ -63,7 +63,7 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< println!("EPK info -\nData size: {}\nPak count: {}\nVersion: {:02x?}.{:02x?}.{:02x?}", header.file_size, header.pak_count, version[1], version[2], version[3]); - } else if epk1_type == "le" { + } else if epk1_type == Epk1Type::LittleEndian { let header: CommonHeader = file.read_le()?; //this is to make an odd variant with 32 max pak entries work @@ -97,7 +97,7 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< for (i, pak) in paks.iter().enumerate() { file.seek(SeekFrom::Start(pak.offset as u64))?; - let pak_header: PakHeader = if epk1_type == "be" {file.read_be()?} else {file.read_le()?}; + let pak_header: PakHeader = if epk1_type == Epk1Type::BigEndian {file.read_be()?} else {file.read_le()?}; println!("\n({}/{}) - {}, Offset: {}, Size: {}, Platform: {}", i + 1, paks.len(), pak_header.pak_name(), pak.offset, pak_header.image_size, pak_header.platform_id()); diff --git a/src/formats/epk2/mod.rs b/src/formats/epk2/mod.rs index 62f5996..127ef35 100644 --- a/src/formats/epk2/mod.rs +++ b/src/formats/epk2/mod.rs @@ -98,6 +98,10 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< println!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}", pak_n + 1, paks.len(), pak.name, pak_header.image_size, pak_header.segment_count, pak_header.platform_id()); + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", pak.name)); + fs::create_dir_all(&app_ctx.output_dir)?; + let mut out_file = OpenOptions::new().write(true).create(true).truncate(true).open(output_path)?; + for i in 0..pak_header.segment_count { // for first segment we already read the header so skip doing that for it if i > 0 { @@ -137,9 +141,6 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< 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 mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?; out_file.write_all(&out_data)?; println!("-- Saved to file!"); diff --git a/src/formats/epk2b/mod.rs b/src/formats/epk2b/mod.rs index 6d5735d..3cd5602 100644 --- a/src/formats/epk2b/mod.rs +++ b/src/formats/epk2b/mod.rs @@ -52,6 +52,10 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box println!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}", i + 1, paks.len(), pak_header.pak_name(), pak_header.image_size, pak_header.segment_count, pak_header.platform_id()); + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", pak_header.pak_name())); + fs::create_dir_all(&app_ctx.output_dir)?; + let mut out_file = OpenOptions::new().write(true).create(true).truncate(true).open(output_path)?; + for i in 0..pak_header.segment_count { // for first segment we already read the header so skip doing that for it if i > 0 { @@ -74,9 +78,6 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box 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 mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?; out_file.write_all(&out_data[..segment_limit as usize])?; println!("-- Saved to file!"); diff --git a/src/formats/epk3/mod.rs b/src/formats/epk3/mod.rs index 00a09d3..6fceb40 100644 --- a/src/formats/epk3/mod.rs +++ b/src/formats/epk3/mod.rs @@ -91,6 +91,10 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< println!("\n({}) - {}, Size: {}, Segments: {}", pak_i, entry.package_name(), entry.package_size, entry.segment_count); + + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", entry.package_name())); + fs::create_dir_all(&app_ctx.output_dir)?; + let mut out_file = OpenOptions::new().write(true).create(true).truncate(true).open(output_path)?; for i in 0..entry.segment_count { if i > 0 { @@ -104,9 +108,6 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< 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 mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?; out_file.write_all(&out_data[extra_segment_size..])?; println!("-- Saved to file!"); diff --git a/src/formats/mstar/include.rs b/src/formats/mstar/include.rs index f51b1b4..6e5aed3 100644 --- a/src/formats/mstar/include.rs +++ b/src/formats/mstar/include.rs @@ -1,3 +1,14 @@ + +#[derive(PartialEq)] +pub enum CompressionType { + None, + Lzma, + DoubleLzma, + Lz4, + Lzo, + Sparse, +} + pub fn parse_number(s: &str) -> Option { if let Some(hex_str) = s.strip_prefix("0x") { u64::from_str_radix(hex_str, 16).ok() diff --git a/src/formats/mstar/mod.rs b/src/formats/mstar/mod.rs index 54d94ca..f208c2e 100644 --- a/src/formats/mstar/mod.rs +++ b/src/formats/mstar/mod.rs @@ -69,7 +69,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box // "unknown" //}; let mut partname = "unknown"; - let mut compression = "none"; + let mut compression: CompressionType = CompressionType::None; let mut lz4_expect_size = 0; let mut j = i + 1; @@ -77,20 +77,20 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box while j < lines.len() && !lines[j].starts_with("filepartload") { //get compression method if lines[j].starts_with("mscompress7"){ - if compression == "none" { - compression = "lzma"; - } else if compression == "lzma" { + if compression == CompressionType::None { + compression = CompressionType::Lzma; + } else if compression == CompressionType::Lzma { //thank the turks - compression = "double_lzma"; + compression = CompressionType::DoubleLzma; } } if lines[j].starts_with("lz4"){ - compression = "lz4"; + compression = CompressionType::Lz4; let parts: Vec<&str> = lines[j].split_whitespace().collect(); lz4_expect_size = parse_number(parts[5]).unwrap_or(0); } if lines[j].starts_with("mmc unlzo"){ - compression = "lzo"; + compression = CompressionType::Lzo; let parts: Vec<&str> = lines[j].split_whitespace().collect(); // get part name from mmc unlzo if partname == "unknown" { @@ -98,7 +98,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box } } if lines[j].starts_with("sparse_write"){ - compression = "sparse"; //its not really compression but anyway + compression = CompressionType::Sparse; //its not really compression but anyway let parts: Vec<&str> = lines[j].split_whitespace().collect(); // get part name from sparse_write if partname == "unknown" { @@ -133,24 +133,24 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let out_data; let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", partname)); - if compression == "lzma" { + if compression == CompressionType::Lzma { println!("- Decompressing LZMA..."); out_data = decompress_lzma(&data)?; - } else if compression == "double_lzma" { + } else if compression == CompressionType::DoubleLzma { println!("- Decompressing LZMA (Pass 1)..."); let pass_1 = decompress_lzma(&data)?; println!("- Decompressing LZMA (Pass 2)..."); out_data = decompress_lzma(&pass_1)?; - } else if compression == "lz4" { + } else if compression == CompressionType::Lz4 { println!("- Decompressing lz4, expected size: {}", lz4_expect_size); out_data = decompress_lz4(&data, lz4_expect_size.try_into().unwrap())?; - } else if compression == "lzo" { + } else if compression == CompressionType::Lzo { println!("- Decompessing LZO.."); unlzop_to_file(&data, output_path)?; println!("-- Saved file!"); i += 1; continue - } else if compression == "sparse" { + } else if compression == CompressionType::Sparse { println!("- Unsparsing..."); unsparse_to_file(&data, output_path)?; println!("-- Saved file!"); diff --git a/src/formats/pana_dvd/mod.rs b/src/formats/pana_dvd/mod.rs index 0ecd408..ec84510 100644 --- a/src/formats/pana_dvd/mod.rs +++ b/src/formats/pana_dvd/mod.rs @@ -196,6 +196,7 @@ fn extract_main(file_reader: &mut Cursor>, key: [u8; 8], output_path: &P } let mut maine_i = 0; + let mut main_out_file = OpenOptions::new().write(true).create(true).truncate(true).open(&output_path)?; for entry in &main_entries { maine_i += 1; let mut data = common::read_exact(file_reader, entry.size as usize)?; @@ -215,9 +216,8 @@ fn extract_main(file_reader: &mut Cursor>, key: [u8; 8], output_path: &P print!("\nMAIN ({}/{}) - ", maine_i, main_entries.len()); let decompressed_data = decompress_data(&data)?; - - let mut out_file = OpenOptions::new().append(true).create(true).open(&output_path)?; - out_file.write_all(&decompressed_data)?; + + main_out_file.write_all(&decompressed_data)?; println!("-- Saved to MAIN!"); }