diff --git a/src/formats.rs b/src/formats.rs index 541440c..87d1020 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -1,4 +1,5 @@ pub mod mstar; pub mod samsung_old; pub mod tpv_timg; -pub mod pfl_upg; \ No newline at end of file +pub mod pfl_upg; +pub mod epk1; \ No newline at end of file diff --git a/src/formats/epk1.rs b/src/formats/epk1.rs new file mode 100644 index 0000000..d5553eb --- /dev/null +++ b/src/formats/epk1.rs @@ -0,0 +1,153 @@ +use std::fs::File; +use std::path::{Path}; +use std::fs::{self, OpenOptions}; +use std::io::{Write, Seek, SeekFrom}; + +use crate::common; + +pub fn is_epk1_file(file: &File) -> bool { + let header = common::read_file(&file, 0, 4).expect("Failed to read from file."); + let header_string = String::from_utf8_lossy(&header); + + if header_string == "epak"{ + true + } else { + false + } +} + +struct Pak { + offset : u32, + size : u32, +} + +pub fn extract_epk1(mut file: &File, output_folder: &str) -> Result<(), Box> { + + //check type of epk1 + let epk1_type; + 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!("Big endian EPK1 detected."); + epk1_type = "be"; + } else if init_pak_count < 21 { + println!("Little endian EPK1 detected."); + epk1_type = "le"; + } else { + //println!("EPK1(new) detected."); + //epk1_type = "new"; + println!("Not supported!"); + return Ok(()); + } + + file.seek(SeekFrom::Start(0))?; + + let mut paks: Vec = Vec::new(); + + if epk1_type == "be" { + //epak magic + let _epak = common::read_exact(&mut file, 4)?; + + //file size + let file_size_bytes = common::read_exact(&mut file, 4)?; + let file_size = u32::from_be_bytes(file_size_bytes.try_into().unwrap()); + + //pak count + let pak_count_bytes = common::read_exact(&mut file, 4)?; + let pak_count = u32::from_be_bytes(pak_count_bytes.try_into().unwrap()); + + for _i in 0..10 { //header can fit max 10 pak entries + let pak_offset_bytes = common::read_exact(&mut file, 4)?; + let pak_offset = u32::from_be_bytes(pak_offset_bytes.try_into().unwrap()); + + let pak_size_bytes = common::read_exact(&mut file, 4)?; + let pak_size = u32::from_be_bytes(pak_size_bytes.try_into().unwrap()); + + if pak_offset == 0 && pak_size == 0 { + continue; + } + + paks.push(Pak { offset: pak_offset, size: pak_size }); + } + + assert!(pak_count as usize == paks.len(), "Paks count in header does not match the amount of non empty pak entries!"); + + let version = common::read_exact(&mut file, 4)?; + + println!("EPK info:\nFile size: {}\nPak count: {}\nVersion: {:02x?}.{:02x?}.{:02x?}\n", + file_size, pak_count, version[1], version[2], version[3]); + + } else if epk1_type == "le" { + //epak magic + let _epak = common::read_exact(&mut file, 4)?; + + //file size + let file_size_bytes = common::read_exact(&mut file, 4)?; + let file_size = u32::from_le_bytes(file_size_bytes.try_into().unwrap()); + + //pak count + let pak_count_bytes = common::read_exact(&mut file, 4)?; + let pak_count = u32::from_le_bytes(pak_count_bytes.try_into().unwrap()); + + for _i in 0..20 { //header can fit max 20 pak entries + let pak_offset_bytes = common::read_exact(&mut file, 4)?; + let pak_offset = u32::from_le_bytes(pak_offset_bytes.try_into().unwrap()); + + let pak_size_bytes = common::read_exact(&mut file, 4)?; + let pak_size = u32::from_le_bytes(pak_size_bytes.try_into().unwrap()); + + if pak_offset == 0 && pak_size == 0 { + continue; + } + + paks.push(Pak { offset: pak_offset, size: pak_size }); + } + + assert!(pak_count as usize == paks.len(), "Paks count in header does not match the amount of non empty pak entries!"); + + let version = common::read_exact(&mut file, 4)?; + + let ota_id_bytes = common::read_exact(&mut file, 32)?; + let ota_id = common::string_from_bytes(&ota_id_bytes); + + println!("EPK info:\nFile size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}\n", + file_size, pak_count, ota_id, version[2], version[1], version[0]); + } + + for (i, pak) in paks.iter().enumerate() { + let pak_name_bytes = common::read_file(&file, pak.offset as u64, 4)?; + let pak_name = common::string_from_bytes(&pak_name_bytes); + + let pak_actual_size_bytes = common::read_file(&file, pak.offset as u64 + 4, 4)?; + let pak_actual_size; + if epk1_type == "be" { + pak_actual_size = u32::from_be_bytes(pak_actual_size_bytes.try_into().unwrap()); + } else { + pak_actual_size = u32::from_le_bytes(pak_actual_size_bytes.try_into().unwrap()); + } + + let pak_platform_bytes = common::read_file(&file, pak.offset as u64 + 8, 15)?; + let pak_platform = common::string_from_bytes(&pak_platform_bytes); + + let data = common::read_file(&file, pak.offset as u64, pak.size as usize)?; + + println!("- Pak {}: {}, Offset: {}, Size: {}, Platform: {}", i + 1, pak_name, pak.offset, pak.size, pak_platform); + + let output_path = Path::new(&output_folder).join(pak_name + ".bin"); + + fs::create_dir_all(&output_folder)?; + let mut out_file = OpenOptions::new() + .write(true) + .create(true) + .open(output_path)?; + + out_file.write_all(&data[128..pak_actual_size as usize + 128])?; + + println!("-- Saved file!"); + } + + println!("\nExtraction finished!"); + + Ok(()) +} \ No newline at end of file diff --git a/src/formats/mstar.rs b/src/formats/mstar.rs index 97340ae..de3b6f5 100644 --- a/src/formats/mstar.rs +++ b/src/formats/mstar.rs @@ -132,28 +132,28 @@ pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box {}", offset, size, partname); + println!("- Part: Offset: {}, Size: {} --> {}", offset, size, partname); if partname == "unknown"{ - println!("- Unknown destination, skipping!"); + println!("-- Unknown destination, skipping!"); } else { let data = common::read_file(&file, offset, size.try_into().unwrap())?; let out_data; if compression == "lzma" { - println!("- Decompressing LZMA..."); + println!("-- Decompressing LZMA..."); out_data = decompress_lzma(&data)?; } else if compression == "double_lzma" { - println!("- Decompressing LZMA (Pass 1)..."); + println!("-- Decompressing LZMA (Pass 1)..."); let pass_1 = decompress_lzma(&data)?; - println!("- Decompressing LZMA (Pass 2)..."); + println!("-- Decompressing LZMA (Pass 2)..."); out_data = decompress_lzma(&pass_1)?; } else if compression == "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())?; } else if compression == "lzo" { // nothing in rust to parse lzo file with header - println!("- lzo compression is not supported yet!!..."); + println!("-- lzo compression is not supported yet!!..."); out_data = data; }else { out_data = data; diff --git a/src/formats/pfl_upg.rs b/src/formats/pfl_upg.rs index 074e568..49f17bf 100644 --- a/src/formats/pfl_upg.rs +++ b/src/formats/pfl_upg.rs @@ -168,7 +168,7 @@ pub fn extract_pfl_upg(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Result<(), Bo let file = File::open(&path)?; let filename = path.file_name().unwrap().to_str().unwrap(); let file_size = file.metadata()?.len(); - println!("File: {}", filename); + println!("- File: {}", filename); let data = common::read_file(&file, 0, file_size.try_into().unwrap())?; let salt = &data[8..16]; @@ -126,10 +126,10 @@ pub fn extract_samsung_old(path: &PathBuf, output_folder: &str) -> Result<(), Bo //println!("IV: {:02x?}", iv_md5); let end = file_size - 260; - println!("- Decrypting file..."); + println!("-- Decrypting file..."); let decrypted_data = decrypt_aes(&data[16..end.try_into().unwrap()], &key_md5, &iv_md5)?; - println!("- DeXORing file..."); + println!("-- DeXORing file..."); let xor_key = fw_info.split_whitespace().next().unwrap(); let out_data = decrypt_xor(&decrypted_data, xor_key); @@ -137,7 +137,7 @@ pub fn extract_samsung_old(path: &PathBuf, output_folder: &str) -> Result<(), Bo fs::create_dir_all(&output_folder)?; let mut out_file = OpenOptions::new() - .append(true) + .write(true) .create(true) .open(output_path)?; diff --git a/src/formats/tpv_timg.rs b/src/formats/tpv_timg.rs index d3b2edd..cc88f4f 100644 --- a/src/formats/tpv_timg.rs +++ b/src/formats/tpv_timg.rs @@ -70,17 +70,17 @@ pub fn extract_tpv_timg(mut file: &File, output_folder: &str) -> Result<(), Box< //actual data let data = common::read_exact(&mut file, size as usize)?; - println!("PIMG - Name: {} Size: {} Dest: {} Compression: {}", name, size, dev, comp_type); + println!("- PIMG: Name: {} Size: {} Dest: {} Compression: {}", name, size, dev, comp_type); let out_data; if comp_type == "gzip" { - println!("- Decompressing gzip..."); + println!("-- Decompressing gzip..."); out_data = decompress_gzip(&data)?; } else if comp_type == "none" { out_data = data; } else { - println!("- Warning: unsupported compression type!"); + println!("-- Warning: unsupported compression type!"); out_data = data; } @@ -88,13 +88,13 @@ pub fn extract_tpv_timg(mut file: &File, output_folder: &str) -> Result<(), Box< fs::create_dir_all(&output_folder)?; let mut out_file = OpenOptions::new() - .append(true) + .write(true) .create(true) .open(output_path)?; out_file.write_all(&out_data)?; - println!("- Saved file!"); + println!("-- Saved file!"); } println!(); diff --git a/src/main.rs b/src/main.rs index e3c0f8c..0ab48d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,6 +39,10 @@ fn main() -> Result<(), Box> { println!("TPV TIMG file detected!"); println!(); formats::tpv_timg::extract_tpv_timg(&file, &output_path)?; + } else if formats::epk1::is_epk1_file(&file) { + println!("EPK1 file detected!"); + println!(); + formats::epk1::extract_epk1(&file, &output_path)?; } else if formats::pfl_upg::is_pfl_upg_file(&file) { println!("PFL UPG file detected!"); println!();