aboutsummaryrefslogtreecommitdiff
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
downloadunity-tools-41f35c442c821b3fdab7e8605b0583ba5ce38d74.tar
unity-tools-41f35c442c821b3fdab7e8605b0583ba5ce38d74.tar.bz2
unity-tools-41f35c442c821b3fdab7e8605b0583ba5ce38d74.tar.zst
lz4 fails for some reason
-rw-r--r--.gitignore2
-rw-r--r--Cargo.lock333
-rw-r--r--Cargo.toml12
-rw-r--r--src/helper.rs43
-rw-r--r--src/main.rs133
5 files changed, 523 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0f285c7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+/samples
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..fe97625
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,333 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
+dependencies = [
+ "anstyle",
+ "once_cell",
+ "windows-sys",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
+
+[[package]]
+name = "assetdebundle"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "env_logger",
+ "log",
+ "lz4",
+ "lz4_flex",
+ "lzma",
+]
+
+[[package]]
+name = "byteorder"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
+
+[[package]]
+name = "cc"
+version = "1.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+
+[[package]]
+name = "env_filter"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "humantime",
+ "log",
+]
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "libc"
+version = "0.2.169"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
+
+[[package]]
+name = "log"
+version = "0.4.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
+
+[[package]]
+name = "lz4"
+version = "1.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a20b523e860d03443e98350ceaac5e71c6ba89aea7d960769ec3ce37f4de5af4"
+dependencies = [
+ "lz4-sys",
+]
+
+[[package]]
+name = "lz4-sys"
+version = "1.11.1+lz4-1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "lz4_flex"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5"
+dependencies = [
+ "twox-hash",
+]
+
+[[package]]
+name = "lzma"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "782ba3f542e8bc1349386c15e9dc3119ae6da96479f96b3863cc7a88bbdfd4e4"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "once_cell"
+version = "1.20.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
+
+[[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "twox-hash"
+version = "1.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
+dependencies = [
+ "cfg-if",
+ "static_assertions",
+]
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..72785f4
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "assetdebundle"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+log = "0.4.25"
+env_logger = "0.11.6"
+anyhow = "1.0.95"
+lz4_flex = "0.11.3"
+lzma = "0.2.2"
+lz4 = "1.28.1"
diff --git a/src/helper.rs b/src/helper.rs
new file mode 100644
index 0000000..79c2872
--- /dev/null
+++ b/src/helper.rs
@@ -0,0 +1,43 @@
+use std::io::{Read, Result};
+
+pub trait ReadExt {
+ fn read_u16_be(&mut self) -> Result<u16>;
+ fn read_u32_be(&mut self) -> Result<u32>;
+ fn read_u64_be(&mut self) -> Result<u64>;
+ fn read_u128_be(&mut self) -> Result<u128>;
+ fn read_cstr(&mut self) -> Result<String>;
+}
+impl<T: Read> ReadExt for T {
+ fn read_u16_be(&mut self) -> Result<u16> {
+ let mut buf = [0; 2];
+ self.read_exact(&mut buf)?;
+ Ok(u16::from_be_bytes(buf))
+ }
+ fn read_u32_be(&mut self) -> Result<u32> {
+ let mut buf = [0; 4];
+ self.read_exact(&mut buf)?;
+ Ok(u32::from_be_bytes(buf))
+ }
+ fn read_u64_be(&mut self) -> Result<u64> {
+ let mut buf = [0; 8];
+ self.read_exact(&mut buf)?;
+ Ok(u64::from_be_bytes(buf))
+ }
+ fn read_u128_be(&mut self) -> Result<u128> {
+ let mut buf = [0; 16];
+ self.read_exact(&mut buf)?;
+ Ok(u128::from_be_bytes(buf))
+ }
+ fn read_cstr(&mut self) -> Result<String> {
+ let mut buf = [0; 1];
+ let mut s = Vec::new();
+ loop {
+ self.read_exact(&mut buf)?;
+ if buf[0] == 0 {
+ break;
+ }
+ s.push(buf[0]);
+ }
+ Ok(String::from_utf8_lossy(&s).to_string())
+ }
+}
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!(),
+ }
+ }
+}