improvements to invincible_image

This commit is contained in:
theubusu
2026-04-22 16:35:38 +02:00
parent a3fbb9e76f
commit 550d9270c4
2 changed files with 44 additions and 40 deletions
+14 -18
View File
@@ -7,26 +7,22 @@ pub static V3_IV: [u8; 16] = [0xe3, 0x9f, 0x36, 0x39, 0x56, 0x9a, 0x6b, 0x8d, 0
#[derive(BinRead)]
pub struct Header {
_magic_bytes: [u8; 16],
pub file_version: [u8; 4],
_unk1: u32,
ver1_bytes: [u8; 16],
ver2_bytes: [u8; 16],
_unk2: u16,
_type: u8,
pub keep_size: u32,
_unk3: u8,
_magic_bytes: [u8; 16], // INVINCIBLE_IMAGE
pub file_infos: [u8; 8], // 0=format version/key id, 1,2,3=unknown, 4-7=unused/reserved
ver1_bytes: [u8; 16], // actual firmware version
ver2_bytes: [u8; 16], // Netcast platform version
_unk1: [u8; 3],
pub chunk_size: u32,
pub chunk_count: u8, // 0 means not chunked
pub data_start_offset: u32,
pub data_size: u32,
_data_size_2: u32,
pub skip_size: u32,
_unk4: u16,
_encryption_method: u8, // 0x01 - AES128, 0x02 - AES256
_hash_type: u8, // 0x01 - MD5, 0x02 - SHA1
ver3_bytes: [u8; 16],
ver4_bytes: [u8; 16],
_unk6: [u8; 11],
pub payload_count: u8,
_file_size: u32,
pub signature_size: u32,
_flags: [u8; 4], // 0,1=unknown, 2=Encryption method, 3=Hash method
ver3_bytes: [u8; 16], // model like in ROM filename, or sometimes just "A"?
ver4_bytes: [u8; 16], // LG SDK version
_unk2: [u8; 11],
pub payload_count: u8, // if 0, entire data is a single payload
}
impl Header {
pub fn ver1(&self) -> String {
+30 -22
View File
@@ -7,7 +7,7 @@ use std::fs::{self, OpenOptions};
use std::io::{Write, Read, Seek, SeekFrom, Cursor};
use binrw::BinReaderExt;
use crate::utils::aes::{decrypt_aes128_cbc_nopad};
use crate::utils::aes::{decrypt_aes128_cbc_pcks7};
use crate::utils::common;
use include::*;
@@ -26,54 +26,62 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Res
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let header: Header = file.read_le()?;
println!("File info:\nFile Version: {}.{}\nVersion(1): {}\nVersion(2): {}\nVersion(3): {}\nVersion(4): {}\nData size: {}\nData start offset: {}\nKeep data size: {}\nSkip data size: {}\n\nPayload Count: {}",
header.file_version[0], header.file_version[1], header.ver1(), header.ver2(), header.ver3(), header.ver4(), header.data_size, header.data_start_offset, header.keep_size, header.skip_size, header.payload_count);
println!("File info -\nFile Version: {}\nVersion(1): {}\nVersion(2): {}\nVersion(3): {}\nVersion(4): {}\nData size: {}\nChunk count: {}\nChunk size: {}\n\nPayload Count: {}",
header.file_infos[0], header.ver1(), header.ver2(), header.ver3(), header.ver4(), header.data_size, header.chunk_count, header.chunk_size, header.payload_count);
let mut entries: Vec<Entry> = Vec::new();
for i in 0..header.payload_count {
let entry: Entry = file.read_le()?;
println!("{}. {}, Start offset: {}, Size: {}",
println!("{}. {} - Start offset: {}, Size: {}",
i + 1, entry.name(), entry.start_offset, entry.size);
entries.push(entry);
}
if header.file_version[0] != 3 {
return Err("Unsupported format version! (Only 3 is supported right now)".into());
}
let mut encrypted_data = Vec::new();
let mut buffer = vec![0u8; header.keep_size as usize];
let (aes_key, aes_iv) = match header.file_infos[0] {
3 => (V3_KEY, V3_IV),
_ => return Err("Unsupported format version! (Unknown key)".into())
};
file.seek(SeekFrom::Start(header.data_start_offset.into()))?;
loop {
let bytes_read = file.read(&mut buffer)?;
if bytes_read == 0 {
break; // EOF
let mut encrypted_data = Vec::with_capacity(header.data_size as usize);
if header.chunk_count == 0 { //not chunked, read all data
encrypted_data = common::read_exact(&mut file, header.data_size as usize)?;
} else {
let mut buffer = vec![0u8; header.chunk_size as usize];
let mut remain = header.data_size as usize;
for _ in 0..header.chunk_count {
let read_size = remain.min(buffer.len());
let bytes_read = file.read(&mut buffer[..read_size])?;
encrypted_data.extend_from_slice(&buffer[..bytes_read]);
remain -= bytes_read;
file.seek(SeekFrom::Current(header.signature_size.into()))?; // skip signature in each chunk
}
encrypted_data.extend_from_slice(&buffer[..bytes_read]);
file.seek(SeekFrom::Current(header.skip_size.into()))?;
}
println!("\nDecrypting data...");
let decrypted_data = decrypt_aes128_cbc_nopad(&encrypted_data, &V3_KEY, &V3_IV)?;
let decrypted_data = decrypt_aes128_cbc_pcks7(&encrypted_data, &aes_key, &aes_iv)?;
let mut data_reader = Cursor::new(decrypted_data);
let mut i = 1;
for entry in entries {
println!("\n({}/{}) - {}, Size: {}", i, header.payload_count, entry.name(), entry.size);
for (i , entry) in entries.iter().enumerate() {
println!("\n({}/{}) - {}, Size: {}, Start offset: {}", i+1, header.payload_count, entry.name(), entry.size, entry.start_offset);
let data = common::read_exact(&mut data_reader, entry.size as usize)?;
let output_path = Path::new(&app_ctx.output_dir).join(entry.name() + ".bin");
fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.seek(SeekFrom::Start(entry.start_offset.into()))?;
out_file.write_all(&data)?;
println!("- Saved file!");
i += 1;
}
Ok(())