diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..eb131dd --- /dev/null +++ b/src/main.rs @@ -0,0 +1,133 @@ +pub mod helper; + +use anyhow::{Result, anyhow}; +use helper::ReadExt; +use std::{ + env::args, + fs::File, + io::{BufReader, Cursor, Read, Seek, SeekFrom}, +}; + +fn main() -> anyhow::Result<()> { + let mut file = BufReader::new(File::open(args().nth(1).unwrap())?); + + let signature = file.read_cstr()?; + let file_version = file.read_u32_be()?; + let player_version = file.read_cstr()?; + let unity_version = file.read_cstr()?; + let size = file.read_u64_be()?; + let meta_comp_size = file.read_u32_be()?; + let meta_decomp_size = file.read_u32_be()?; + let flags = file.read_u32_be()?; + + let meta_comp_scheme = CompressionScheme::from_flag_num((flags & 0x3f) as u8) + .ok_or(anyhow!("unknown block compression"))?; + let eof_meta = flags & 0x80 != 0; + + eprintln!("signature={signature:?}"); + eprintln!("file_version={file_version:?}"); + eprintln!("player_version={player_version:?}"); + eprintln!("unity_version={unity_version:?}"); + eprintln!("size={size:?}"); + eprintln!("meta_comp_size={meta_comp_size:?}"); + eprintln!("meta_decomp_size={meta_decomp_size:?}"); + eprintln!("flags={flags:?}"); + eprintln!("meta_comp_scheme={meta_comp_scheme:?}"); + eprintln!("eof_meta={eof_meta:?}"); + + let restore_position = if eof_meta { + let pos = file.stream_position()?; + file.seek(SeekFrom::End(-(meta_comp_size as i64)))?; + Some(pos) + } else { + None + }; + + let mut block = vec![0u8; meta_comp_size as usize]; + file.read_exact(&mut block)?; + + if let Some(pos) = restore_position { + file.seek(SeekFrom::Start(pos))?; + } + + let block = meta_comp_scheme.decompress(block, meta_decomp_size as usize)?; + let mut block = Cursor::new(block); + + let guid = block.read_u128_be()?; + eprintln!("guid={guid:032x}"); + + let num_blocks = block.read_u32_be()?; + eprintln!("num_blocks={num_blocks:?}"); + let mut blocks = Vec::new(); + for _ in 0..num_blocks { + let block_decomp_size = block.read_u32_be()?; + let block_comp_size = block.read_u32_be()?; + let block_flags = block.read_u16_be()?; + let block_comp_scheme = CompressionScheme::from_flag_num((block_flags & 0x3f) as u8) + .ok_or(anyhow!("unknown block compression"))?; + blocks.push((block_comp_size, block_decomp_size, block_comp_scheme)) + } + + let num_nodes = block.read_u32_be()?; + eprintln!("num_nodes={num_nodes:?}"); + let mut nodes = Vec::new(); + for _ in 0..num_nodes { + let offset = block.read_u64_be()?; + let size = block.read_u64_be()?; + let status = block.read_u32_be()?; + let name = block.read_cstr()?; + nodes.push((offset, size, status, name)) + } + + let mut all_blocks = Vec::new(); + for (comp_size, decomp_size, comp_scheme) in blocks { + let mut comp_buf = vec![0u8; comp_size as usize]; + file.read_exact(&mut comp_buf)?; + let decomp_buf = comp_scheme.decompress(comp_buf, decomp_size as usize)?; + assert_eq!(decomp_size, decomp_buf.len() as u32); + all_blocks.extend_from_slice(&decomp_buf); + } + + eprintln!("{nodes:#?}"); + + Ok(()) +} + +#[derive(Debug, Clone, Copy, PartialEq)] +enum CompressionScheme { + None, + LZMA, + LZ4, + LZ4HC, + LZHAM, +} +impl CompressionScheme { + pub fn from_flag_num(n: u8) -> Option<CompressionScheme> { + Some(match n & 0x3f { + 0 => CompressionScheme::None, + 1 => CompressionScheme::LZMA, + 2 => CompressionScheme::LZ4, + 3 => CompressionScheme::LZ4HC, + 4 => CompressionScheme::LZHAM, + _ => return None, + }) + } + pub fn decompress(&self, block: Vec<u8>, decomp_size: usize) -> Result<Vec<u8>> { + match self { + CompressionScheme::None => Ok(block), + CompressionScheme::LZMA => { + let mut r = lzma::Reader::from(Cursor::new(block))?; + let mut buf = Vec::new(); + r.read_to_end(&mut buf)?; + Ok(buf) + } + // CompressionScheme::LZ4HC | CompressionScheme::LZ4 => { + // Ok(lz4_flex::block::decompress(&block, decomp_size).context("lz4 decomp")?) + // } + CompressionScheme::LZ4HC | CompressionScheme::LZ4 => { + Ok(lz4::block::decompress(&block, Some(decomp_size as i32))?) + } + CompressionScheme::LZHAM => todo!(), + } + } +} |