aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-02-09 14:20:02 +0100
committermetamuffin <metamuffin@disroot.org>2025-02-09 14:20:02 +0100
commit41f35c442c821b3fdab7e8605b0583ba5ce38d74 (patch)
treeae8dc41167811e19ace23d5a5e88e55115f22865 /src/main.rs
downloadunity-tools-41f35c442c821b3fdab7e8605b0583ba5ce38d74.tar
unity-tools-41f35c442c821b3fdab7e8605b0583ba5ce38d74.tar.bz2
unity-tools-41f35c442c821b3fdab7e8605b0583ba5ce38d74.tar.zst
lz4 fails for some reason
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs133
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!(),
+ }
+ }
+}