2026-02-17 17:28:59 +01:00
mod include ;
2026-02-05 15:18:26 +01:00
use std ::any ::Any ;
2026-02-17 17:28:59 +01:00
use crate ::AppContext ;
2026-02-05 15:18:26 +01:00
2026-02-17 17:28:59 +01:00
use std ::path ::Path ;
2025-10-06 22:44:30 +02:00
use std ::fs ::{ self , OpenOptions };
use std ::io ::{ Write , Seek , SeekFrom };
2026-02-17 17:28:59 +01:00
use binrw ::BinReaderExt ;
2025-10-10 22:04:51 +02:00
2025-11-26 17:29:25 +01:00
use crate ::utils ::common ;
2026-02-17 17:28:59 +01:00
use include ::* ;
2026-01-24 16:36:20 +01:00
2026-02-05 18:53:35 +01:00
pub fn is_epk1_file ( app_ctx : & AppContext ) -> Result < Option < Box < dyn Any >> , Box < dyn std ::error ::Error >> {
2026-02-06 16:27:13 +01:00
let file = match app_ctx . file () { Some ( f ) => f , None => return Ok ( None )};
2026-02-06 15:08:38 +01:00
let epk2_magic = common ::read_file ( & file , 12 , 4 ) ? ; //for epk2b
let epak_magic = common ::read_file ( & file , 0 , 4 ) ? ;
2026-01-24 16:36:20 +01:00
if epak_magic == b "epak" && epk2_magic != b "EPK2" {
2026-02-05 15:18:26 +01:00
Ok ( Some ( Box ::new (())))
2025-10-06 22:44:30 +02:00
} else {
2026-02-05 15:18:26 +01:00
Ok ( None )
2025-10-06 22:44:30 +02:00
}
}
2026-02-06 16:27:13 +01:00
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" ) ? ;
2025-10-06 22:44:30 +02:00
//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-02-06 15:08:38 +01:00
let output_path = Path ::new ( & app_ctx . output_dir ). join ( pak_header . pak_name () + ".bin" );
fs ::create_dir_all ( & app_ctx . output_dir ) ? ;
2026-01-24 16:36:20 +01:00
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 (())
}