Cleanup + add epk1 extractor

This commit is contained in:
theubusu
2025-10-06 22:44:30 +02:00
parent 7c2c47b44c
commit 6e33b3bc49
7 changed files with 181 additions and 20 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
pub mod mstar; pub mod mstar;
pub mod samsung_old; pub mod samsung_old;
pub mod tpv_timg; pub mod tpv_timg;
pub mod pfl_upg; pub mod pfl_upg;
pub mod epk1;
+153
View File
@@ -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<dyn std::error::Error>> {
//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<Pak> = 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(())
}
+7 -7
View File
@@ -132,28 +132,28 @@ pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box<dyn std
j += 1; j += 1;
} }
println!("Part - Offset: {}, Size: {} --> {}", offset, size, partname); println!("- Part: Offset: {}, Size: {} --> {}", offset, size, partname);
if partname == "unknown"{ if partname == "unknown"{
println!("- Unknown destination, skipping!"); println!("-- Unknown destination, skipping!");
} else { } else {
let data = common::read_file(&file, offset, size.try_into().unwrap())?; let data = common::read_file(&file, offset, size.try_into().unwrap())?;
let out_data; let out_data;
if compression == "lzma" { if compression == "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 == "double_lzma" {
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 == "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 == "lzo" {
// nothing in rust to parse lzo file with header // 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; out_data = data;
}else { }else {
out_data = data; out_data = data;
+6 -3
View File
@@ -168,7 +168,7 @@ pub fn extract_pfl_upg(mut file: &File, output_folder: &str) -> Result<(), Box<d
let _header_size = u32::from_le_bytes(file_header[68..72].try_into().unwrap()); let _header_size = u32::from_le_bytes(file_header[68..72].try_into().unwrap());
println!("File: {}, Size: {}", file_name, real_size); println!("- File: {}, Size: {}", file_name, real_size);
let data = common::read_exact(&mut data_reader, stored_size as usize)?; let data = common::read_exact(&mut data_reader, stored_size as usize)?;
@@ -180,14 +180,17 @@ pub fn extract_pfl_upg(mut file: &File, output_folder: &str) -> Result<(), Box<d
fs::create_dir_all(&output_folder)?; fs::create_dir_all(&output_folder)?;
let mut out_file = OpenOptions::new() let mut out_file = OpenOptions::new()
.append(true) .write(true)
.create(true) .create(true)
.open(output_path)?; .open(output_path)?;
out_file.write_all(&data[..real_size as usize])?; out_file.write_all(&data[..real_size as usize])?;
println!("- Saved file!"); println!("-- Saved file!");
} }
println!();
println!("Extraction finished!");
Ok(()) Ok(())
} }
+4 -4
View File
@@ -85,7 +85,7 @@ pub fn extract_samsung_old(path: &PathBuf, output_folder: &str) -> Result<(), Bo
let file = File::open(&path)?; let file = File::open(&path)?;
let filename = path.file_name().unwrap().to_str().unwrap(); let filename = path.file_name().unwrap().to_str().unwrap();
let file_size = file.metadata()?.len(); 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 data = common::read_file(&file, 0, file_size.try_into().unwrap())?;
let salt = &data[8..16]; 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); //println!("IV: {:02x?}", iv_md5);
let end = file_size - 260; 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)?; 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 xor_key = fw_info.split_whitespace().next().unwrap();
let out_data = decrypt_xor(&decrypted_data, xor_key); 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)?; fs::create_dir_all(&output_folder)?;
let mut out_file = OpenOptions::new() let mut out_file = OpenOptions::new()
.append(true) .write(true)
.create(true) .create(true)
.open(output_path)?; .open(output_path)?;
+5 -5
View File
@@ -70,17 +70,17 @@ pub fn extract_tpv_timg(mut file: &File, output_folder: &str) -> Result<(), Box<
//actual data //actual data
let data = common::read_exact(&mut file, size as usize)?; 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; let out_data;
if comp_type == "gzip" { if comp_type == "gzip" {
println!("- Decompressing gzip..."); println!("-- Decompressing gzip...");
out_data = decompress_gzip(&data)?; out_data = decompress_gzip(&data)?;
} else if comp_type == "none" { } else if comp_type == "none" {
out_data = data; out_data = data;
} else { } else {
println!("- Warning: unsupported compression type!"); println!("-- Warning: unsupported compression type!");
out_data = data; 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)?; fs::create_dir_all(&output_folder)?;
let mut out_file = OpenOptions::new() let mut out_file = OpenOptions::new()
.append(true) .write(true)
.create(true) .create(true)
.open(output_path)?; .open(output_path)?;
out_file.write_all(&out_data)?; out_file.write_all(&out_data)?;
println!("- Saved file!"); println!("-- Saved file!");
} }
println!(); println!();
+4
View File
@@ -39,6 +39,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("TPV TIMG file detected!"); println!("TPV TIMG file detected!");
println!(); println!();
formats::tpv_timg::extract_tpv_timg(&file, &output_path)?; 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) { } else if formats::pfl_upg::is_pfl_upg_file(&file) {
println!("PFL UPG file detected!"); println!("PFL UPG file detected!");
println!(); println!();