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 PakHeader {
#[br(count = 4)] pak_name_bytes : Vec < u8 > ,
2026-01-24 16:36:20 +01:00
image_size : u32 ,
#[br(count = 64)] platform_id_bytes : Vec < u8 > ,
#[br(count = 56)] _reserved : Vec < u8 > ,
2025-10-10 22:04:51 +02:00
}
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 )
}
}
2026-01-24 16:36:20 +01:00
#[derive(BinRead)]
struct Pak {
offset : u32 ,
size : u32 ,
}
2025-10-06 22:44:30 +02:00
pub fn is_epk1_file ( file : & File ) -> bool {
2026-01-24 16:36:20 +01:00
let epk2_magic = common ::read_file ( & file , 12 , 4 ). expect ( "Failed to read from file." ); //for epk2b
let epak_magic = common ::read_file ( & file , 0 , 4 ). expect ( "Failed to read from file." );
if epak_magic == b "epak" && epk2_magic != b "EPK2" {
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! ( " \n Big endian EPK1 detected." );
2025-10-06 22:44:30 +02:00
epk1_type = "be" ;
2026-01-24 16:36:20 +01:00
} else if init_pak_count < 33 {
2025-10-07 16:00:32 +02:00
println! ( " \n Little endian EPK1 detected." );
2025-10-06 22:44:30 +02:00
epk1_type = "le" ;
} else {
2026-01-24 16:36:20 +01:00
println! ( " \n Unknown EPK1 variant!" );
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 () ? ;
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
}
2026-01-24 16:36:20 +01:00
assert! ( header . pak_count as usize == paks . len (), "Paks count in header({}) does not match the amount of non empty pak entries({})!" , header . pak_count , paks . len ());
2025-10-06 22:44:30 +02:00
let version = common ::read_exact ( & mut file , 4 ) ? ;
2026-01-24 16:36:20 +01:00
println! ( "EPK info - \n Data size: {} \n Pak count: {} \n Version: {: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
2026-01-24 16:36:20 +01:00
//this is to make an odd variant with 32 max pak entries work
let header_size_bytes = common ::read_file ( & file , 12 , 4 ) ? ; //offset of first entry, can be treated as header size
let header_size = u32 ::from_le_bytes ( header_size_bytes . try_into (). unwrap ());
let max_pak_count = ( header_size - 48 ) / 8 ; //header size minus common header + ota id (48) divide by size of pak entry (8).
assert! ( max_pak_count < 128 , "Unreasonable calculated pak count {}!!" , max_pak_count );
2025-10-06 22:44:30 +02:00
2026-01-24 16:36:20 +01:00
for _i in 0 .. max_pak_count {
let pak : Pak = file . read_le () ? ;
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
}
2026-01-24 16:36:20 +01:00
assert! ( header . pak_count as usize == paks . len (), "Paks count in header({}) does not match the amount of non empty pak entries({})!" , header . pak_count , paks . len ());
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 );
2026-01-24 16:36:20 +01:00
println! ( "EPK info - \n Data size: {} \n Header size: {} \n Pak count: {} \n OTA ID: {} \n Version: {:02x?} . {:02x?} . {:02x?} " ,
header . file_size , header_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 () {
2026-01-24 16:36:20 +01: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
2026-01-24 16:36:20 +01:00
println! ( " \n ( {} / {} ) - {} , Offset: {} , Size: {} , Platform: {} " ,
i + 1 , paks . len (), pak_header . pak_name (), pak . offset , pak_header . image_size , pak_header . platform_id ());
2025-10-06 22:44:30 +02:00
2026-01-24 16:36:20 +01:00
let data = common ::read_exact ( & mut file , pak_header . image_size as usize ) ? ;
2025-10-06 22:44:30 +02:00
2026-01-24 16:36:20 +01:00
let output_path = Path ::new ( & output_folder ). join ( pak_header . 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 ) ? ;
2025-10-06 22:44:30 +02:00
2026-01-24 16:36:20 +01:00
println! ( "- Saved file!" );
}
2025-10-06 22:44:30 +02:00
println! ( " \n Extraction finished!" );
Ok (())
}