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)] #[derive(BinRead)]
pub struct Header { pub struct Header {
_magic_bytes: [u8; 16], _magic_bytes: [u8; 16], // INVINCIBLE_IMAGE
pub file_version: [u8; 4], pub file_infos: [u8; 8], // 0=format version/key id, 1,2,3=unknown, 4-7=unused/reserved
_unk1: u32, ver1_bytes: [u8; 16], // actual firmware version
ver1_bytes: [u8; 16], ver2_bytes: [u8; 16], // Netcast platform version
ver2_bytes: [u8; 16], _unk1: [u8; 3],
_unk2: u16, pub chunk_size: u32,
_type: u8, pub chunk_count: u8, // 0 means not chunked
pub keep_size: u32,
_unk3: u8,
pub data_start_offset: u32, pub data_start_offset: u32,
pub data_size: u32, pub data_size: u32,
_data_size_2: u32, _file_size: u32,
pub skip_size: u32, pub signature_size: u32,
_unk4: u16, _flags: [u8; 4], // 0,1=unknown, 2=Encryption method, 3=Hash method
_encryption_method: u8, // 0x01 - AES128, 0x02 - AES256 ver3_bytes: [u8; 16], // model like in ROM filename, or sometimes just "A"?
_hash_type: u8, // 0x01 - MD5, 0x02 - SHA1 ver4_bytes: [u8; 16], // LG SDK version
ver3_bytes: [u8; 16], _unk2: [u8; 11],
ver4_bytes: [u8; 16], pub payload_count: u8, // if 0, entire data is a single payload
_unk6: [u8; 11],
pub payload_count: u8,
} }
impl Header { impl Header {
pub fn ver1(&self) -> String { 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 std::io::{Write, Read, Seek, SeekFrom, Cursor};
use binrw::BinReaderExt; use binrw::BinReaderExt;
use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::utils::aes::{decrypt_aes128_cbc_pcks7};
use crate::utils::common; use crate::utils::common;
use include::*; 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 mut file = app_ctx.file().ok_or("Extractor expected file")?;
let header: Header = file.read_le()?; 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: {}", println!("File info -\nFile Version: {}\nVersion(1): {}\nVersion(2): {}\nVersion(3): {}\nVersion(4): {}\nData size: {}\nChunk count: {}\nChunk 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); 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(); let mut entries: Vec<Entry> = Vec::new();
for i in 0..header.payload_count { for i in 0..header.payload_count {
let entry: Entry = file.read_le()?; let entry: Entry = file.read_le()?;
println!("{}. {}, Start offset: {}, Size: {}", println!("{}. {} - Start offset: {}, Size: {}",
i + 1, entry.name(), entry.start_offset, entry.size); i + 1, entry.name(), entry.start_offset, entry.size);
entries.push(entry); entries.push(entry);
} }
if header.file_version[0] != 3 { let (aes_key, aes_iv) = match header.file_infos[0] {
return Err("Unsupported format version! (Only 3 is supported right now)".into()); 3 => (V3_KEY, V3_IV),
} _ => return Err("Unsupported format version! (Unknown key)".into())
};
let mut encrypted_data = Vec::new();
let mut buffer = vec![0u8; header.keep_size as usize];
file.seek(SeekFrom::Start(header.data_start_offset.into()))?; file.seek(SeekFrom::Start(header.data_start_offset.into()))?;
loop { let mut encrypted_data = Vec::with_capacity(header.data_size as usize);
let bytes_read = file.read(&mut buffer)?;
if bytes_read == 0 { if header.chunk_count == 0 { //not chunked, read all data
break; // EOF 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]); encrypted_data.extend_from_slice(&buffer[..bytes_read]);
file.seek(SeekFrom::Current(header.skip_size.into()))?; remain -= bytes_read;
file.seek(SeekFrom::Current(header.signature_size.into()))?; // skip signature in each chunk
}
} }
println!("\nDecrypting data..."); 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 data_reader = Cursor::new(decrypted_data);
let mut i = 1; for (i , entry) in entries.iter().enumerate() {
for entry in entries { println!("\n({}/{}) - {}, Size: {}, Start offset: {}", i+1, header.payload_count, entry.name(), entry.size, entry.start_offset);
println!("\n({}/{}) - {}, Size: {}", i, header.payload_count, entry.name(), entry.size);
let data = common::read_exact(&mut data_reader, entry.size as usize)?; 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"); let output_path = Path::new(&app_ctx.output_dir).join(entry.name() + ".bin");
fs::create_dir_all(&app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; 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)?; out_file.write_all(&data)?;
println!("- Saved file!"); println!("- Saved file!");
i += 1;
} }
Ok(()) Ok(())