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-06-05 21:02:40 +08:00
|
|
|
use log::info;
|
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
|
2026-02-24 21:07:37 +01:00
|
|
|
let epk1_type: Epk1Type;
|
2025-10-06 22:44:30 +02:00
|
|
|
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 {
|
2026-06-05 21:02:40 +08:00
|
|
|
info!("\nBig endian EPK1 detected.");
|
2026-02-24 21:07:37 +01:00
|
|
|
epk1_type = Epk1Type::BigEndian;
|
2026-01-24 16:36:20 +01:00
|
|
|
} else if init_pak_count < 33 {
|
2026-06-05 21:02:40 +08:00
|
|
|
info!("\nLittle endian EPK1 detected.");
|
2026-02-24 21:07:37 +01:00
|
|
|
epk1_type = Epk1Type::LittleEndian;
|
2025-10-06 22:44:30 +02:00
|
|
|
} else {
|
2026-02-17 18:34:46 +01:00
|
|
|
return Err("Unknown EPK1 variant!".into());
|
2025-10-06 22:44:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file.seek(SeekFrom::Start(0))?;
|
|
|
|
|
|
|
|
|
|
let mut paks: Vec<Pak> = Vec::new();
|
|
|
|
|
|
2026-02-24 21:07:37 +01:00
|
|
|
if epk1_type == Epk1Type::BigEndian {
|
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-02-17 18:34:46 +01:00
|
|
|
|
|
|
|
|
if header.pak_count as usize != paks.len() {
|
|
|
|
|
return Err(format!("Paks count in header({}) does not match the amount of non empty pak entries({})!", header.pak_count, paks.len()).into());
|
|
|
|
|
}
|
2025-10-06 22:44:30 +02:00
|
|
|
|
|
|
|
|
let version = common::read_exact(&mut file, 4)?;
|
|
|
|
|
|
2026-06-05 21:02:40 +08:00
|
|
|
info!("EPK info -\nData 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
|
|
|
|
2026-02-24 21:07:37 +01:00
|
|
|
} else if epk1_type == Epk1Type::LittleEndian {
|
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).
|
2026-02-17 18:34:46 +01:00
|
|
|
if max_pak_count > 128 {
|
|
|
|
|
return Err(format!("Unreasonable calculated pak count {}!!", max_pak_count).into());
|
|
|
|
|
}
|
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-02-17 18:34:46 +01:00
|
|
|
|
|
|
|
|
if header.pak_count as usize != paks.len() {
|
|
|
|
|
return Err(format!("Paks count in header({}) does not match the amount of non empty pak entries({})!", header.pak_count, paks.len()).into());
|
|
|
|
|
}
|
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-06-05 21:02:40 +08:00
|
|
|
info!("EPK info -\nData size: {}\nHeader size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
|
2026-01-24 16:36:20 +01:00
|
|
|
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))?;
|
2026-02-24 21:07:37 +01:00
|
|
|
let pak_header: PakHeader = if epk1_type == Epk1Type::BigEndian {file.read_be()?} else {file.read_le()?};
|
2025-10-06 22:44:30 +02:00
|
|
|
|
2026-06-05 21:02:40 +08:00
|
|
|
info!("\n({}/{}) - {}, Offset: {}, Size: {}, Platform: {}",
|
2026-01-24 16:36:20 +01:00
|
|
|
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-06-05 21:02:40 +08:00
|
|
|
info!("- Saved file!");
|
2026-01-24 16:36:20 +01:00
|
|
|
}
|
2025-10-06 22:44:30 +02:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|