funai_bdp: add support of uncmpr(sparse like) and decompressing image rom

This commit is contained in:
theubusu
2026-04-08 16:11:09 +02:00
parent 2fc231135b
commit 73709c87cd
2 changed files with 125 additions and 5 deletions
+53 -1
View File
@@ -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<CmprEntry>,
}
#[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<ImageRomEntry>,
}
#[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],
}
+72 -4
View File
@@ -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<dyn Any>) -> 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<Vec<u8>, Box<dyn std::error::Error>> {
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<u8> = 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<Vec<u8>, Box<dyn std::error::Error>> {
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<u8> = 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)
}