diff --git a/src/formats/funai_bdp/include.rs b/src/formats/funai_bdp/include.rs index 846015e..01da080 100644 --- a/src/formats/funai_bdp/include.rs +++ b/src/formats/funai_bdp/include.rs @@ -6,10 +6,62 @@ pub struct IndexTableEntry { name_bytes: [u8; 32], pub offset: u32, pub size: u32, - _unk: u32, + _checksum: u32, } impl IndexTableEntry { pub fn name(&self) -> String { common::string_from_bytes(&self.name_bytes) } +} + +//cmpr + +#[derive(BinRead)] +pub struct CmprHeader { + pub out_checksum: u32, + pub count: u32, + pub data_size: u32, + #[br(count=count)] pub entries: Vec, +} + +#[derive(BinRead)] +pub struct CmprEntry { + pub size: u32, + pub mode: u32, + pub fill: u32, +} + +//dumb check, since this information is not stored in header +pub fn is_cmpr(data: &[u8], size: u32) -> bool { + if data.len() < 0x20+12 {return false}; + + let count = u32::from_le_bytes(data[0x20+4..0x20+8].try_into().unwrap()); + let data_size = u32::from_le_bytes(data[0x20+8..0x20+12].try_into().unwrap()); + + if count > size || data_size > size {return false}; + + return (size - 0x20 - (count*12) - 12) == data_size; +} + +//image rom +#[derive(BinRead)] +pub struct ImageRomHeader { + pub count: u32, + _unk: u32, + _pad1: [u8; 8], + _entry_table_sha1: [u8; 20], + _pad2: [u8; 12], + pub start_offset: u32, + _pad3: [u8; 12], + #[br(count=count)] pub entries: Vec, +} + +#[derive(BinRead)] +pub struct ImageRomEntry { + _decompressed_sha1: [u8; 20], + _compressed_sha1: [u8; 20], + _pad1: [u8; 8], + pub offset: u32, //relative to start_offset in header + pub size: u32, + _pad2: [u8; 8], } \ No newline at end of file diff --git a/src/formats/funai_bdp/mod.rs b/src/formats/funai_bdp/mod.rs index d45f993..fd0824a 100644 --- a/src/formats/funai_bdp/mod.rs +++ b/src/formats/funai_bdp/mod.rs @@ -7,10 +7,11 @@ use std::fs::{self, OpenOptions}; use std::io::{Cursor, Read, Seek, SeekFrom, Write}; use binrw::BinReaderExt; -use crate::utils::common; +use crate::utils::common::{self, read_exact}; use crate::formats::funai_upg::funai_des::funai_des_decrypt; use include::*; use crate::keys; +use crate::utils::compression::decompress_zlib; pub struct FunaiBdpContext { key: u32, @@ -58,18 +59,85 @@ pub fn extract_funai_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), println!("\n({}/{}) - {}, Offset: {}, Size: {}", i +1, index_entry_count, entry.name(), entry.offset, entry.size); file_reader.seek(SeekFrom::Start(entry.offset as u64))?; - let data = common::read_exact(&mut file_reader, entry.size as usize)?; + let mut data = common::read_exact(&mut file_reader, entry.size as usize)?; + + if is_cmpr(&data, entry.size) { + println!("- cmpr detected!, 'uncompressing' data..."); + data = uncmpr_data(&data)?; + + } else if entry.name().ends_with("_image_rom") { + println!("- Decompressing image ROM..."); + data = uncomp_image_rom(&data)?; + + } else {//strip partition name at start + data = data[0x20..].to_vec() + } let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", entry.name())); fs::create_dir_all(&app_ctx.output_dir)?; let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; - //at start of location there is an additional 0x20 with the entry's name - out_file.write_all(&data[0x20..])?; + out_file.write_all(&data)?; println!("-- Saved file!"); } Ok(()) +} + +pub fn uncmpr_data(data: &[u8]) -> Result, Box> { + let mut data_reader = Cursor::new(data); + let _part_name = read_exact(&mut data_reader, 0x20)?; + let cmpr_header: CmprHeader = data_reader.read_le()?; + println!("[cmpr] out chk: {:02x}, count: {}, data size: {}", + cmpr_header.out_checksum, cmpr_header.count, cmpr_header.data_size); + + let mut out_data: Vec = Vec::new(); + + for (i, entry) in cmpr_header.entries.iter().enumerate() { + println!("[cmpr] ({}/{}) size: {}, mode: {}, fill: {:02x}", + i+1, cmpr_header.count, entry.size, entry.mode, entry.fill); + + let mut data; + if entry.mode == 0 { //raw data + data = read_exact(&mut data_reader, entry.size as usize)?; + + } else if entry.mode == 1 { //fill data + data = vec![entry.fill as u8; entry.size as usize]; + + } else { + return Err("invalid/unknown entry mode value!".into()); + }; + + out_data.append(&mut data); + } + + Ok(out_data) +} + +pub fn uncomp_image_rom(data: &[u8]) -> Result, Box> { + let mut data_reader = Cursor::new(data); + let _part_name = read_exact(&mut data_reader, 0x20)?; + let header: ImageRomHeader = data_reader.read_le()?; + println!("[rom] count: {}", header.count); + + let mut out_data: Vec = Vec::new(); + + for (i, entry) in header.entries.iter().enumerate() { + let offset = header.start_offset + entry.offset; + + println!("[rom] ({}/{}) offset: {}, size: {}", + i+1, header.count, offset, entry.size); + + data_reader.seek(SeekFrom::Start(offset as u64))?; + let compr_data = read_exact(&mut data_reader, entry.size as usize)?; + + println!("[rom] - Decompressing..."); + let mut decomp_data = decompress_zlib(&compr_data)?; + + out_data.append(&mut decomp_data); + } + + Ok(out_data) } \ No newline at end of file