diff --git a/src/formats/rvp/include.rs b/src/formats/rvp/include.rs index 0429955..633d53e 100644 --- a/src/formats/rvp/include.rs +++ b/src/formats/rvp/include.rs @@ -1,3 +1,56 @@ +use binrw::BinRead; + +pub static KNOWN_MODULES: &[&str] = &[ + "Host", + "EMMA", + "EOPE", + "Writer1", + "Writer2", + "unknown", + "FrontMicom", + "Kernel", + "RootFS", + "FontData", + "MultiBoot", + "AquosAudio", +]; + +#[derive(PartialEq)] +pub enum HeaderType { + RVP, + MVP, +} + +#[derive(BinRead)] +pub struct RVPHeader { + _crc: u32, + _sum: u32, + pub force: u32, //3 = force + pub year: u32, //as hex + version_info_bytes: [u8; 48], //EUC_JP encoded as fullwidth +} +impl RVPHeader { + pub fn version_info(&self) -> String { + eucjp_fullwidth_to_ascii(&self.version_info_bytes) + } +} + +// HAX +fn eucjp_fullwidth_to_ascii(data: &[u8]) -> String { + let mut out = String::new(); + + let mut i = 0; + while i + 1 < data.len() { + if data[i] == 0xA3 { + let c = (data[i + 1] - 0x80) as char; + out.push(c); + } + i += 2; + } + + out +} + pub fn decrypt_xor(data: &[u8]) -> Vec { let key_bytes = b"\xCC\xF0\xC8\xC4\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA"; data.iter() diff --git a/src/formats/rvp/mod.rs b/src/formats/rvp/mod.rs index 1f896fc..492eb5f 100644 --- a/src/formats/rvp/mod.rs +++ b/src/formats/rvp/mod.rs @@ -5,12 +5,13 @@ use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Read, Cursor, Seek}; +use binrw::BinReaderExt; use crate::utils::common; use include::*; pub struct RvpContext { - header_offset: u64, + header_type: HeaderType, } pub fn is_rvp_file(app_ctx: &AppContext) -> Result>, Box> { @@ -18,7 +19,7 @@ pub fn is_rvp_file(app_ctx: &AppContext) -> Result>, Box Result>, Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; let ctx = ctx.downcast::().expect("Missing context"); - file.seek(std::io::SeekFrom::Start(ctx.header_offset))?; + if ctx.header_type == HeaderType::RVP { + let header: RVPHeader = file.read_be()?; + println!("RVP Info -\nVersion: {}\nYear: {:x}\nForce: {}", header.version_info(), header.year, header.force); - let mut obf_data = Vec::new(); //we sadly cannot deXOR on the fly because of its 32 byte pattern + } else if ctx.header_type == HeaderType::MVP { + file.seek(std::io::SeekFrom::Start(36))?; + } + + let mut obf_data = Vec::new(); file.read_to_end(&mut obf_data)?; println!("DeXORing data.."); + let data = decrypt_xor(&obf_data); + let data_size = data.len(); let mut data_reader = Cursor::new(data); - let _unknown = common::read_exact(&mut data_reader, 256)?; //seems to be mostly the same between files + let module_count: u32 = data_reader.read_le()?; //little endian?? + println!("Module count: {}", module_count); + + //follows table of sizes of modules, structure is static for given module + let mut module_names: Vec<&str> = Vec::new(); + for i in 0..63 { + if i >= KNOWN_MODULES.len() { + break + } + let module_size: u32 = data_reader.read_be()?; + if module_size == 0 { + continue + } + if module_size as usize > data_size { + break + } + module_names.push(KNOWN_MODULES[i]); + } + + data_reader.seek(std::io::SeekFrom::Start(256))?; + + for i in 0..module_count as usize { + let module_name = if i >= module_names.len() { + "unknown" + } else { + module_names[i] + }; - let mut i = 0; - while (data_reader.position() as usize) < data_reader.get_ref().len() { - i += 1; let header_size_bytes = common::read_exact(&mut data_reader, 4)?; let header_size = u32::from_be_bytes(header_size_bytes.try_into().unwrap()); - println!("\n#{} - Offset: {}, Header size: {}", i, data_reader.position() - 4, header_size); + println!("\n({}/{}) - {}, Offset: {}, Header size: {}", i+1, module_count, module_name, data_reader.position() - 4, header_size); let hdr = common::read_exact(&mut data_reader, header_size as usize)?; let size; let mut name = String::new(); - if i == 1 { //first entry always has this big header + if i == 0 { //first entry is always HOST module (SEINE) let hdr_string = String::from_utf8_lossy(&hdr); let lines: Vec = hdr_string.lines().map(|l| l.trim().to_string()).collect(); - //1. maybe target? always "ALL" in japanese, "BD-HP50" in usa hp50 - //2. always "host" - //3. unknown, like "0B01" - //4. unknown, like "00000111" - //5. always "FFFF" - //6. always "FFFF" - //7. always "3" or "0" - //8. always "88005004" - //9. always "88005004" - //10. always "88005000" - //11. size as decimal string, like "12320768" - //12. unknown, differing hex string like "F8833EE1" - //13. crc32 checksum like "3827E120" - let target = &lines[0]; + + //BEAUTIFUL + println!("ModelName: {}\nFileName: {}\nModelID: {}\nNewUpdate: {}\nNewMajorVer: {}\nNewMinorVer: {}\nForcedFlag: {}\nStartAddress: {}\nJumpAddress: {}\nMagicAddress: {}\nTotalSize: {}\nTotalSum: {}\nTotalCrc: {}", + lines[0], lines[1], lines[2], lines[3], lines[4], lines[5], lines[6], lines[7], lines[8], lines[9], lines[10], lines[11], lines[12]); + size = lines[10].parse().unwrap(); - let crc32 = &lines[12]; - println!("Target: {}", target); - println!("CRC32: {}", crc32); } else if header_size == 32 { let hdr_string = String::from_utf8_lossy(&hdr); @@ -84,32 +104,26 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box) -> Result<(), Box = hdr_string.lines().map(|l| l.trim().to_string()).collect(); //1. - name, like "L12_110.IMG" //2. - size in hex string like "001E6388" - //3. - unknown - single number like "3" + //3. - unknown - single number like "3" (force flag?) //4. - crc32 checksum like "0BC0F6F7" //5. - unknown - like "00011200" -- this line is not present when size is 40 but were not using it anyway so whatever name = lines[0].clone(); size = u32::from_str_radix(&lines[1], 16).unwrap(); - let crc32 = &lines[3]; - println!("File name: {}", name); - println!("CRC32: {}", crc32); + println!("Name: {}", name); } else if header_size == 16 { // 4 bytes CRC32 // 4 bytes unknown "FF FF FF FF" // 4 bytes size // 4 bytes unknown "00 00 00 00" - let crc32 = hex::encode_upper(&hdr[0..4]); size = u32::from_be_bytes(hdr[8..12].try_into().unwrap()); - println!("CRC32: {}", crc32); } else { println!("Unsupported header size!"); @@ -118,7 +132,7 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box) -> Result<(), Box