use log::debug; use std::{ env::args, fs::File, io::{BufReader, Cursor, Read}, }; use unity_tools::{ common_strings::COMMON_STRINGS, helper::{Endianness, ReadExt}, unityfs::UnityFS, }; fn main() -> anyhow::Result<()> { env_logger::init_from_env("LOG"); let file = BufReader::new(File::open(args().nth(1).unwrap())?); let mut fs = UnityFS::open(file)?; for node in fs.nodes().to_vec() { if node.name.ends_with(".resource") || node.name.ends_with("resS") { continue; } let mut cab = fs.read(&node)?; // let mut writer = File::create(format!("/tmp/{}", node.name))?; // std::io::copy(&mut cab, &mut writer)?; // continue; let mut metadata_size = cab.read_u32_be()?; let mut file_size = cab.read_u32_be()? as u64; let format = cab.read_u32_be()?; let mut data_offset = cab.read_u32_be()? as u64; debug!("format={format}"); assert!(format >= 9); let e = match cab.read_u32_be()? { 0 => Endianness::Little, _ => Endianness::Big, }; debug!("endianess={e:?}"); if format >= 22 { metadata_size = cab.read_u32(e)?; file_size = cab.read_u64(e)?; data_offset = cab.read_u64(e)?; cab.read_u64(e)?; } debug!("metadata_size={metadata_size}"); debug!("file_size={file_size}"); debug!("data_offset={data_offset}"); let generator_version = cab.read_cstr()?; let target_platform = cab.read_u32_le()?; debug!("generator_version={generator_version:?}"); debug!("target_platform={target_platform}"); let has_type_trees = cab.read_u8()? != 0; let num_types = cab.read_u32(e)?; debug!("has_type_trees={has_type_trees:?}"); debug!("num_types={num_types}"); for _ in 0..num_types { let class_id = cab.read_i32(e)?; let stripped_type = cab.read_u8()? != 0; let script_index = cab.read_i16(e)?; let mut script_id = 0; // TODO reftype if class_id == 114 { script_id = cab.read_u128_be()?; } let _old_hash = cab.read_u128_be()?; eprintln!("class_id={class_id}"); eprintln!("stripped_type={stripped_type}"); eprintln!("script_index={script_index}"); eprintln!("script_id={script_id}"); if has_type_trees { let num_nodes = cab.read_u32(e)?; let size = cab.read_u32(e)?; eprintln!("tree:num_nodes={num_nodes}"); eprintln!("tree:size={size}"); let mut node_data = vec![0u8; num_nodes as usize * 32]; cab.read_exact(&mut node_data)?; let mut node_data = Cursor::new(node_data); let mut string_data = vec![0u8; size as usize]; cab.read_exact(&mut string_data)?; let get_string = |off: u32| { let data = if off & 0x80000000 != 0 { let off = off & 0x7fffffff; &COMMON_STRINGS[(off & 0x7fffffff) as usize..] } else { &string_data[off as usize..] }; String::from_utf8( data.iter() .copied() .take_while(|e| *e != 0) .collect::>(), ) }; let mut nodes = Vec::new(); for _ in 0..num_nodes { nodes.push(TypeTreeNode { version: node_data.read_u16(e)?, level: node_data.read_u8()?, type_flags: node_data.read_u8()?, type_string: get_string(node_data.read_u32(e)?)?, name_string: get_string(node_data.read_u32(e)?)?, byte_size: node_data.read_i32(e)?, index: node_data.read_i32(e)?, flags: node_data.read_i32(e)?, ref_type_hash: node_data.read_u64(e)?, }); } eprintln!("{nodes:#?}"); if format >= 21 { let num_deps = cab.read_u32(e)?; for _ in 0..num_deps { cab.read_u32(e)?; } } } if format > 21 { cab.read_u32_be()?; } } // let num_objects = cab.read_u32_le()?; // debug!("num_objects={num_objects}"); // for _ in 0..num_objects {} } Ok(()) } #[derive(Debug)] struct TypeTreeNode { version: u16, level: u8, type_flags: u8, type_string: String, name_string: String, byte_size: i32, index: i32, flags: i32, ref_type_hash: u64, }