mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-16 05:04:22 +02:00
improve append file handling + misc change
This commit is contained in:
@@ -45,6 +45,10 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box<dyn Any>) ->
|
|||||||
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!");
|
||||||
|
|||||||
@@ -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],
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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!");
|
||||||
|
|||||||
@@ -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!");
|
||||||
|
|||||||
@@ -92,6 +92,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 {
|
||||||
entry = pkg_info_reader.read_le()?;
|
entry = pkg_info_reader.read_le()?;
|
||||||
@@ -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!");
|
||||||
|
|||||||
@@ -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
@@ -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!");
|
||||||
|
|||||||
@@ -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)?;
|
||||||
@@ -216,8 +217,7 @@ 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!");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user