2025-10-06 22:44:30 +02:00
|
|
|
use std::fs::File;
|
|
|
|
|
use std::path::{Path};
|
|
|
|
|
use std::fs::{self, OpenOptions};
|
|
|
|
|
use std::io::{Write, Seek, SeekFrom};
|
2025-10-10 22:04:51 +02:00
|
|
|
use binrw::{BinRead, BinReaderExt};
|
|
|
|
|
|
2025-11-26 17:29:25 +01:00
|
|
|
use crate::utils::common;
|
2025-10-06 22:44:30 +02:00
|
|
|
|
2025-10-10 22:04:51 +02:00
|
|
|
#[derive(BinRead)]
|
|
|
|
|
struct CommonHeader {
|
|
|
|
|
#[br(count = 4)] _magic_bytes: Vec<u8>,
|
|
|
|
|
file_size: u32,
|
|
|
|
|
pak_count: u32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(BinRead)]
|
|
|
|
|
struct Pak {
|
|
|
|
|
offset : u32,
|
|
|
|
|
size : u32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(BinRead)]
|
|
|
|
|
struct PakHeader {
|
|
|
|
|
#[br(count = 4)] pak_name_bytes: Vec<u8>,
|
|
|
|
|
stored_size: u32,
|
|
|
|
|
#[br(count = 15)] platform_id_bytes: Vec<u8>,
|
|
|
|
|
}
|
|
|
|
|
impl PakHeader {
|
|
|
|
|
fn pak_name(&self) -> String {
|
|
|
|
|
common::string_from_bytes(&self.pak_name_bytes)
|
|
|
|
|
}
|
|
|
|
|
fn platform_id(&self) -> String {
|
|
|
|
|
common::string_from_bytes(&self.platform_id_bytes)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-06 22:44:30 +02:00
|
|
|
pub fn is_epk1_file(file: &File) -> bool {
|
|
|
|
|
let header = common::read_file(&file, 0, 4).expect("Failed to read from file.");
|
2025-10-07 16:00:32 +02:00
|
|
|
if header == b"epak" {
|
2025-10-06 22:44:30 +02:00
|
|
|
true
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2025-10-07 16:00:32 +02:00
|
|
|
println!("\nBig endian EPK1 detected.");
|
2025-10-06 22:44:30 +02:00
|
|
|
epk1_type = "be";
|
|
|
|
|
} else if init_pak_count < 21 {
|
2025-10-07 16:00:32 +02:00
|
|
|
println!("\nLittle endian EPK1 detected.");
|
2025-10-06 22:44:30 +02:00
|
|
|
epk1_type = "le";
|
|
|
|
|
} else {
|
2025-10-07 16:00:32 +02:00
|
|
|
//println!("\nEPK1(new) detected.");
|
2025-10-06 22:44:30 +02:00
|
|
|
//epk1_type = "new";
|
2025-10-07 16:00:32 +02:00
|
|
|
println!("\nNot supported!");
|
2025-10-06 22:44:30 +02:00
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file.seek(SeekFrom::Start(0))?;
|
|
|
|
|
|
|
|
|
|
let mut paks: Vec<Pak> = Vec::new();
|
|
|
|
|
|
|
|
|
|
if epk1_type == "be" {
|
2025-10-10 22:04:51 +02:00
|
|
|
let header: CommonHeader = file.read_be()?;
|
2025-10-06 22:44:30 +02:00
|
|
|
|
|
|
|
|
for _i in 0..10 { //header can fit max 10 pak entries
|
2025-10-10 22:04:51 +02:00
|
|
|
let pak: Pak = file.read_be()?;
|
2025-10-06 22:44:30 +02:00
|
|
|
|
2025-10-10 22:04:51 +02:00
|
|
|
if pak.offset == 0 && pak.size == 0 {
|
2025-10-06 22:44:30 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-10 22:04:51 +02:00
|
|
|
paks.push(Pak { offset: pak.offset, size: pak.size });
|
2025-10-06 22:44:30 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-10 22:04:51 +02:00
|
|
|
assert!(header.pak_count as usize == paks.len(), "Paks count in header does not match the amount of non empty pak entries!");
|
2025-10-06 22:44:30 +02:00
|
|
|
|
|
|
|
|
let version = common::read_exact(&mut file, 4)?;
|
|
|
|
|
|
2025-10-07 16:00:32 +02:00
|
|
|
println!("EPK info:\nFile size: {}\nPak count: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
|
2025-10-10 22:04:51 +02:00
|
|
|
header.file_size, header.pak_count, version[1], version[2], version[3]);
|
2025-10-06 22:44:30 +02:00
|
|
|
|
|
|
|
|
} else if epk1_type == "le" {
|
2025-10-10 22:04:51 +02:00
|
|
|
let header: CommonHeader = file.read_le()?;
|
2025-10-06 22:44:30 +02:00
|
|
|
|
|
|
|
|
for _i in 0..20 { //header can fit max 20 pak entries
|
2025-10-10 22:04:51 +02:00
|
|
|
let pak: Pak = file.read_le()?;
|
2025-10-06 22:44:30 +02:00
|
|
|
|
2025-10-10 22:04:51 +02:00
|
|
|
if pak.offset == 0 && pak.size == 0 {
|
2025-10-06 22:44:30 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-10 22:04:51 +02:00
|
|
|
paks.push(Pak { offset: pak.offset, size: pak.size });
|
2025-10-06 22:44:30 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-10 22:04:51 +02:00
|
|
|
assert!(header.pak_count as usize == paks.len(), "Paks count in header does not match the amount of non empty pak entries!");
|
2025-10-06 22:44:30 +02:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2025-10-07 16:00:32 +02:00
|
|
|
println!("EPK info:\nFile size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
|
2025-10-10 22:04:51 +02:00
|
|
|
header.file_size, header.pak_count, ota_id, version[2], version[1], version[0]);
|
2025-10-06 22:44:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i, pak) in paks.iter().enumerate() {
|
2025-10-10 22:04:51 +02:00
|
|
|
file.seek(SeekFrom::Start(pak.offset as u64))?;
|
|
|
|
|
let pak_header: PakHeader = if epk1_type == "be" {file.read_be()?} else {file.read_le()?};
|
2025-10-06 22:44:30 +02:00
|
|
|
|
2025-10-10 22:04:51 +02:00
|
|
|
let data = common::read_file(&file, pak.offset as u64 + 128, pak.size as usize - 128)?;
|
2025-10-06 22:44:30 +02:00
|
|
|
|
2025-10-10 22:04:51 +02:00
|
|
|
println!("\nPak {}: {}, Offset: {}, Size: {}, Platform: {}", i + 1, pak_header.pak_name(), pak.offset, pak.size, pak_header.platform_id());
|
2025-10-06 22:44:30 +02:00
|
|
|
|
2025-10-10 22:04:51 +02:00
|
|
|
let output_path = Path::new(&output_folder).join(pak_header.pak_name() + ".bin");
|
2025-10-06 22:44:30 +02:00
|
|
|
|
|
|
|
|
fs::create_dir_all(&output_folder)?;
|
|
|
|
|
let mut out_file = OpenOptions::new()
|
|
|
|
|
.write(true)
|
|
|
|
|
.create(true)
|
|
|
|
|
.open(output_path)?;
|
|
|
|
|
|
2025-10-10 22:04:51 +02:00
|
|
|
out_file.write_all(&data[..pak_header.stored_size as usize])?;
|
2025-10-06 22:44:30 +02:00
|
|
|
|
2025-10-07 16:00:32 +02:00
|
|
|
println!("- Saved file!");
|
2025-10-06 22:44:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println!("\nExtraction finished!");
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|