improve append file handling + misc change

This commit is contained in:
theubusu
2026-02-24 21:07:37 +01:00
parent a6567ae532
commit 4d86513bab
9 changed files with 56 additions and 35 deletions
+5 -4
View File
@@ -44,7 +44,11 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box<dyn Any>) ->
let operation_count = partition.operations.len(); let operation_count = partition.operations.len();
println!("\n#{} - {}, Size: {}, Operations: {}", println!("\n#{} - {}, Size: {}, Operations: {}",
i + 1, partition.partition_name, partition.new_partition_info.unwrap().size.unwrap(), operation_count); 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() { for (i, operation) in partition.operations.into_iter().enumerate() {
let operation_name_str = match install_operation::Type::try_from(operation.r#type) { let operation_name_str = match install_operation::Type::try_from(operation.r#type) {
Ok(t) => t.as_str_name(), Ok(t) => t.as_str_name(),
@@ -76,9 +80,6 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box<dyn Any>) ->
break 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)?; out_file.write_all(&out_data)?;
} }
println!("\n-- Saved!"); println!("\n-- Saved!");
+6
View File
@@ -1,6 +1,12 @@
use crate::utils::common; use crate::utils::common;
use binrw::BinRead; use binrw::BinRead;
#[derive(PartialEq)]
pub enum Epk1Type {
LittleEndian,
BigEndian,
}
#[derive(BinRead)] #[derive(BinRead)]
pub struct CommonHeader { pub struct CommonHeader {
_magic_bytes: [u8; 4], _magic_bytes: [u8; 4],
+6 -6
View File
@@ -25,16 +25,16 @@ pub fn is_epk1_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dy
pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?; let mut file = app_ctx.file().ok_or("Extractor expected file")?;
//check type of epk1 //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_bytes = common::read_file(&file, 8, 4)?;
let init_pak_count = u32::from_le_bytes(init_pak_count_bytes.try_into().unwrap()); let init_pak_count = u32::from_le_bytes(init_pak_count_bytes.try_into().unwrap());
if init_pak_count > 256 { if init_pak_count > 256 {
println!("\nBig endian EPK1 detected."); println!("\nBig endian EPK1 detected.");
epk1_type = "be"; epk1_type = Epk1Type::BigEndian;
} else if init_pak_count < 33 { } else if init_pak_count < 33 {
println!("\nLittle endian EPK1 detected."); println!("\nLittle endian EPK1 detected.");
epk1_type = "le"; epk1_type = Epk1Type::LittleEndian;
} else { } else {
return Err("Unknown EPK1 variant!".into()); return Err("Unknown EPK1 variant!".into());
} }
@@ -43,7 +43,7 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let mut paks: Vec<Pak> = Vec::new(); let mut paks: Vec<Pak> = Vec::new();
if epk1_type == "be" { if epk1_type == Epk1Type::BigEndian {
let header: CommonHeader = file.read_be()?; let header: CommonHeader = file.read_be()?;
for _i in 0..10 { //header can fit max 10 pak entries 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<dyn Any>) -> Result<(), Box<
println!("EPK info -\nData size: {}\nPak count: {}\nVersion: {:02x?}.{:02x?}.{:02x?}", println!("EPK info -\nData size: {}\nPak count: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
header.file_size, header.pak_count, version[1], version[2], version[3]); 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()?; let header: CommonHeader = file.read_le()?;
//this is to make an odd variant with 32 max pak entries work //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<dyn Any>) -> Result<(), Box<
for (i, pak) in paks.iter().enumerate() { for (i, pak) in paks.iter().enumerate() {
file.seek(SeekFrom::Start(pak.offset as u64))?; 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: {}", println!("\n({}/{}) - {}, Offset: {}, Size: {}, Platform: {}",
i + 1, paks.len(), pak_header.pak_name(), pak.offset, pak_header.image_size, pak_header.platform_id()); i + 1, paks.len(), pak_header.pak_name(), pak.offset, pak_header.image_size, pak_header.platform_id());
+4 -3
View File
@@ -98,6 +98,10 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
println!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}", 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()); 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 i in 0..pak_header.segment_count {
// for first segment we already read the header so skip doing that for it // for first segment we already read the header so skip doing that for it
if i > 0 { if i > 0 {
@@ -137,9 +141,6 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let segment_data = common::read_exact(&mut file, actual_segment_size as usize)?; 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 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)?; out_file.write_all(&out_data)?;
println!("-- Saved to file!"); println!("-- Saved to file!");
+4 -3
View File
@@ -52,6 +52,10 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
println!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}", 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()); 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 i in 0..pak_header.segment_count {
// for first segment we already read the header so skip doing that for it // for first segment we already read the header so skip doing that for it
if i > 0 { if i > 0 {
@@ -74,9 +78,6 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
pak_header.segment_size 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])?; out_file.write_all(&out_data[..segment_limit as usize])?;
println!("-- Saved to file!"); println!("-- Saved to file!");
+4 -3
View File
@@ -91,6 +91,10 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
println!("\n({}) - {}, Size: {}, Segments: {}", println!("\n({}) - {}, Size: {}, Segments: {}",
pak_i, entry.package_name(), entry.package_size, entry.segment_count); 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 { for i in 0..entry.segment_count {
if i > 0 { if i > 0 {
@@ -104,9 +108,6 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let encrypted_data = common::read_exact(&mut file, entry.segment_size as usize + extra_segment_size)?; 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 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..])?; out_file.write_all(&out_data[extra_segment_size..])?;
println!("-- Saved to file!"); println!("-- Saved to file!");
+11
View File
@@ -1,3 +1,14 @@
#[derive(PartialEq)]
pub enum CompressionType {
None,
Lzma,
DoubleLzma,
Lz4,
Lzo,
Sparse,
}
pub fn parse_number(s: &str) -> Option<u64> { pub fn parse_number(s: &str) -> Option<u64> {
if let Some(hex_str) = s.strip_prefix("0x") { if let Some(hex_str) = s.strip_prefix("0x") {
u64::from_str_radix(hex_str, 16).ok() u64::from_str_radix(hex_str, 16).ok()
+13 -13
View File
@@ -69,7 +69,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
// "unknown" // "unknown"
//}; //};
let mut partname = "unknown"; let mut partname = "unknown";
let mut compression = "none"; let mut compression: CompressionType = CompressionType::None;
let mut lz4_expect_size = 0; let mut lz4_expect_size = 0;
let mut j = i + 1; let mut j = i + 1;
@@ -77,20 +77,20 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
while j < lines.len() && !lines[j].starts_with("filepartload") { while j < lines.len() && !lines[j].starts_with("filepartload") {
//get compression method //get compression method
if lines[j].starts_with("mscompress7"){ if lines[j].starts_with("mscompress7"){
if compression == "none" { if compression == CompressionType::None {
compression = "lzma"; compression = CompressionType::Lzma;
} else if compression == "lzma" { } else if compression == CompressionType::Lzma {
//thank the turks //thank the turks
compression = "double_lzma"; compression = CompressionType::DoubleLzma;
} }
} }
if lines[j].starts_with("lz4"){ if lines[j].starts_with("lz4"){
compression = "lz4"; compression = CompressionType::Lz4;
let parts: Vec<&str> = lines[j].split_whitespace().collect(); let parts: Vec<&str> = lines[j].split_whitespace().collect();
lz4_expect_size = parse_number(parts[5]).unwrap_or(0); lz4_expect_size = parse_number(parts[5]).unwrap_or(0);
} }
if lines[j].starts_with("mmc unlzo"){ if lines[j].starts_with("mmc unlzo"){
compression = "lzo"; compression = CompressionType::Lzo;
let parts: Vec<&str> = lines[j].split_whitespace().collect(); let parts: Vec<&str> = lines[j].split_whitespace().collect();
// get part name from mmc unlzo // get part name from mmc unlzo
if partname == "unknown" { if partname == "unknown" {
@@ -98,7 +98,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
} }
} }
if lines[j].starts_with("sparse_write"){ 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(); let parts: Vec<&str> = lines[j].split_whitespace().collect();
// get part name from sparse_write // get part name from sparse_write
if partname == "unknown" { if partname == "unknown" {
@@ -133,24 +133,24 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let out_data; 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" { if compression == CompressionType::Lzma {
println!("- Decompressing LZMA..."); println!("- Decompressing LZMA...");
out_data = decompress_lzma(&data)?; out_data = decompress_lzma(&data)?;
} else if compression == "double_lzma" { } else if compression == CompressionType::DoubleLzma {
println!("- Decompressing LZMA (Pass 1)..."); println!("- Decompressing LZMA (Pass 1)...");
let pass_1 = decompress_lzma(&data)?; let pass_1 = decompress_lzma(&data)?;
println!("- Decompressing LZMA (Pass 2)..."); println!("- Decompressing LZMA (Pass 2)...");
out_data = decompress_lzma(&pass_1)?; out_data = decompress_lzma(&pass_1)?;
} else if compression == "lz4" { } else if compression == CompressionType::Lz4 {
println!("- Decompressing lz4, expected size: {}", lz4_expect_size); println!("- Decompressing lz4, expected size: {}", lz4_expect_size);
out_data = decompress_lz4(&data, lz4_expect_size.try_into().unwrap())?; out_data = decompress_lz4(&data, lz4_expect_size.try_into().unwrap())?;
} else if compression == "lzo" { } else if compression == CompressionType::Lzo {
println!("- Decompessing LZO.."); println!("- Decompessing LZO..");
unlzop_to_file(&data, output_path)?; unlzop_to_file(&data, output_path)?;
println!("-- Saved file!"); println!("-- Saved file!");
i += 1; i += 1;
continue continue
} else if compression == "sparse" { } else if compression == CompressionType::Sparse {
println!("- Unsparsing..."); println!("- Unsparsing...");
unsparse_to_file(&data, output_path)?; unsparse_to_file(&data, output_path)?;
println!("-- Saved file!"); println!("-- Saved file!");
+3 -3
View File
@@ -196,6 +196,7 @@ fn extract_main(file_reader: &mut Cursor<Vec<u8>>, key: [u8; 8], output_path: &P
} }
let mut maine_i = 0; 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 { for entry in &main_entries {
maine_i += 1; maine_i += 1;
let mut data = common::read_exact(file_reader, entry.size as usize)?; let mut data = common::read_exact(file_reader, entry.size as usize)?;
@@ -215,9 +216,8 @@ fn extract_main(file_reader: &mut Cursor<Vec<u8>>, key: [u8; 8], output_path: &P
print!("\nMAIN ({}/{}) - ", maine_i, main_entries.len()); print!("\nMAIN ({}/{}) - ", maine_i, main_entries.len());
let decompressed_data = decompress_data(&data)?; let decompressed_data = decompress_data(&data)?;
let mut out_file = OpenOptions::new().append(true).create(true).open(&output_path)?; main_out_file.write_all(&decompressed_data)?;
out_file.write_all(&decompressed_data)?;
println!("-- Saved to MAIN!"); println!("-- Saved to MAIN!");
} }