mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-15 21:00:03 +02:00
improvements to RVP/MVP
This commit is contained in:
@@ -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<u8> {
|
pub fn decrypt_xor(data: &[u8]) -> Vec<u8> {
|
||||||
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";
|
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()
|
data.iter()
|
||||||
|
|||||||
+52
-38
@@ -5,12 +5,13 @@ use crate::AppContext;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::fs::{self, OpenOptions};
|
use std::fs::{self, OpenOptions};
|
||||||
use std::io::{Write, Read, Cursor, Seek};
|
use std::io::{Write, Read, Cursor, Seek};
|
||||||
|
use binrw::BinReaderExt;
|
||||||
|
|
||||||
use crate::utils::common;
|
use crate::utils::common;
|
||||||
use include::*;
|
use include::*;
|
||||||
|
|
||||||
pub struct RvpContext {
|
pub struct RvpContext {
|
||||||
header_offset: u64,
|
header_type: HeaderType,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_rvp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
pub fn is_rvp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||||
@@ -18,7 +19,7 @@ pub fn is_rvp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn
|
|||||||
//MVP
|
//MVP
|
||||||
let header = common::read_file(&file, 0, 4)?;
|
let header = common::read_file(&file, 0, 4)?;
|
||||||
if header == b"UPDT" {
|
if header == b"UPDT" {
|
||||||
return Ok(Some(Box::new(RvpContext {header_offset: 36})))
|
return Ok(Some(Box::new(RvpContext {header_type: HeaderType::MVP})))
|
||||||
}
|
}
|
||||||
|
|
||||||
//RVP
|
//RVP
|
||||||
@@ -29,54 +30,73 @@ pub fn is_rvp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(Box::new(RvpContext {header_offset: 64})))
|
Ok(Some(Box::new(RvpContext {header_type: HeaderType::RVP})))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_rvp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn extract_rvp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
|
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
|
||||||
let ctx = ctx.downcast::<RvpContext>().expect("Missing context");
|
let ctx = ctx.downcast::<RvpContext>().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)?;
|
file.read_to_end(&mut obf_data)?;
|
||||||
println!("DeXORing data..");
|
println!("DeXORing data..");
|
||||||
|
|
||||||
let data = decrypt_xor(&obf_data);
|
let data = decrypt_xor(&obf_data);
|
||||||
|
let data_size = data.len();
|
||||||
let mut data_reader = Cursor::new(data);
|
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_bytes = common::read_exact(&mut data_reader, 4)?;
|
||||||
let header_size = u32::from_be_bytes(header_size_bytes.try_into().unwrap());
|
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 hdr = common::read_exact(&mut data_reader, header_size as usize)?;
|
||||||
|
|
||||||
let size;
|
let size;
|
||||||
let mut name = String::new();
|
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 hdr_string = String::from_utf8_lossy(&hdr);
|
||||||
let lines: Vec<String> = hdr_string.lines().map(|l| l.trim().to_string()).collect();
|
let lines: Vec<String> = 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"
|
//BEAUTIFUL
|
||||||
//3. unknown, like "0B01"
|
println!("ModelName: {}\nFileName: {}\nModelID: {}\nNewUpdate: {}\nNewMajorVer: {}\nNewMinorVer: {}\nForcedFlag: {}\nStartAddress: {}\nJumpAddress: {}\nMagicAddress: {}\nTotalSize: {}\nTotalSum: {}\nTotalCrc: {}",
|
||||||
//4. unknown, like "00000111"
|
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]);
|
||||||
//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];
|
|
||||||
size = lines[10].parse().unwrap();
|
size = lines[10].parse().unwrap();
|
||||||
let crc32 = &lines[12];
|
|
||||||
println!("Target: {}", target);
|
|
||||||
println!("CRC32: {}", crc32);
|
|
||||||
|
|
||||||
} else if header_size == 32 {
|
} else if header_size == 32 {
|
||||||
let hdr_string = String::from_utf8_lossy(&hdr);
|
let hdr_string = String::from_utf8_lossy(&hdr);
|
||||||
@@ -84,32 +104,26 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dy
|
|||||||
//1. CRC32 checksum like "34D0757C"
|
//1. CRC32 checksum like "34D0757C"
|
||||||
//2. unknown - "FFFFFFFF"
|
//2. unknown - "FFFFFFFF"
|
||||||
//3. size in hex string like "00040000"
|
//3. size in hex string like "00040000"
|
||||||
let crc32 = &lines[0];
|
|
||||||
size = u32::from_str_radix(&lines[2], 16).unwrap();
|
size = u32::from_str_radix(&lines[2], 16).unwrap();
|
||||||
println!("CRC32: {}", crc32);
|
|
||||||
|
|
||||||
} else if header_size == 48 || header_size == 44 || header_size == 40 {
|
} else if header_size == 48 || header_size == 44 || header_size == 40 { //for disk drive firmware
|
||||||
let hdr_string = String::from_utf8_lossy(&hdr);
|
let hdr_string = String::from_utf8_lossy(&hdr);
|
||||||
let lines: Vec<String> = hdr_string.lines().map(|l| l.trim().to_string()).collect();
|
let lines: Vec<String> = hdr_string.lines().map(|l| l.trim().to_string()).collect();
|
||||||
//1. - name, like "L12_110.IMG"
|
//1. - name, like "L12_110.IMG"
|
||||||
//2. - size in hex string like "001E6388"
|
//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"
|
//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
|
//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();
|
name = lines[0].clone();
|
||||||
size = u32::from_str_radix(&lines[1], 16).unwrap();
|
size = u32::from_str_radix(&lines[1], 16).unwrap();
|
||||||
let crc32 = &lines[3];
|
println!("Name: {}", name);
|
||||||
println!("File name: {}", name);
|
|
||||||
println!("CRC32: {}", crc32);
|
|
||||||
|
|
||||||
} else if header_size == 16 {
|
} else if header_size == 16 {
|
||||||
// 4 bytes CRC32
|
// 4 bytes CRC32
|
||||||
// 4 bytes unknown "FF FF FF FF"
|
// 4 bytes unknown "FF FF FF FF"
|
||||||
// 4 bytes size
|
// 4 bytes size
|
||||||
// 4 bytes unknown "00 00 00 00"
|
// 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());
|
size = u32::from_be_bytes(hdr[8..12].try_into().unwrap());
|
||||||
println!("CRC32: {}", crc32);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
println!("Unsupported header size!");
|
println!("Unsupported header size!");
|
||||||
@@ -118,7 +132,7 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dy
|
|||||||
|
|
||||||
println!("Size: {}", size);
|
println!("Size: {}", size);
|
||||||
let data = common::read_exact(&mut data_reader, size as usize)?;
|
let data = common::read_exact(&mut data_reader, size as usize)?;
|
||||||
let output_path = Path::new(&app_ctx.output_dir).join(if name=="" {format!("{}.bin", i)} else {format!("{}_{}.bin", i, name)});
|
let output_path = Path::new(&app_ctx.output_dir).join(if name=="" {format!("{}_{}.bin", i+1, module_name)} else {format!("{}_{}_{}", i+1, module_name, name)});
|
||||||
|
|
||||||
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)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user