use std::collections::hash_map::DefaultHasher; use std::fs::File; use std::hash::{Hash, Hasher}; use std::io::ErrorKind; use std::path::Path; use ttmp::ttmp_extractor::TtmpExtractor; pub fn main() { let arg = std::env::args().skip(1).next().unwrap(); let file = File::open(&arg).unwrap(); let mut extractor = TtmpExtractor::new(file).unwrap(); extractor.extract_all(|file| { if file.file.full_path.contains("../") { return Err(std::io::Error::new(ErrorKind::Other, "tried to escape directory")); } let group = file.group.map(|s| { let mut hasher = DefaultHasher::default(); s.hash(&mut hasher); hasher.finish() }).unwrap_or(0); let option = file.option.map(|s| { let mut hasher = DefaultHasher::default(); s.hash(&mut hasher); hasher.finish() }).unwrap_or(0); let path = Path::new(".") .join(format!("g{}", group)) .join(format!("o{}", option)) .join(&file.file.full_path); if let Some(parent) = path.parent() { std::fs::create_dir_all(parent)?; } println!("extracting {}", path.to_string_lossy()); Ok(Box::new(File::create(path)?)) }).unwrap(); } // use std::fs::File; // use std::io::{Cursor, Read, Seek, SeekFrom, Write}; // use std::path::Path; // // use flate2::read::DeflateDecoder; // use sqpack::{DatBlockHeader, DatStdFileBlockInfos, FileKind, LodBlock, ModelBlock, SqPackFileInfoHeader}; // use sqpack::binrw::{BinRead, BinWriterExt, VecArgs}; // use sqpack::model::SqPackFileInfo; // // use ttmp::tracking_reader::TrackingReader; // // const MAX_LODS: usize = 3; // // pub fn main() { // let arg = std::env::args().skip(1).next().unwrap(); // let file = File::open(&arg).unwrap(); // let (manifest, mut zip) = ttmp::from_reader(file).unwrap(); // let mut data_file = TrackingReader::new(zip.by_name("TTMPD.mpd").unwrap()); // // let mut all_files = Vec::new(); // // if let Some(pages) = &manifest.mod_pack_pages { // for page in pages { // for group in &page.mod_groups { // for option in &group.option_list { // for file in &option.mods_jsons { // all_files.push(file); // } // } // } // } // } // // if let Some(list) = &manifest.simple_mods_list { // all_files.extend(list); // } // // all_files.sort_unstable_by_key(|&file| file.mod_offset); // // let mut buf = [0; 4092]; // for file in all_files { // data_file.read = 0; // let path = &file.full_path; // println!("extracting {}", path); // // if let Some(parent) = Path::new("./").join(Path::new(path)).parent() { // std::fs::create_dir_all(parent).unwrap(); // } // // let expected = file.mod_size; // let mut file = File::create(path).unwrap(); // // let info: SqPackFileInfoHeader = read_struct(&mut data_file, &mut buf); // // match info.kind { // FileKind::Empty => todo!(), // FileKind::Standard => { // let std_info: SqPackFileInfo = read_struct(&mut data_file, &mut buf); // let blocks: Vec = (0..std_info.number_of_blocks) // .map(|_| read_struct(&mut data_file, &mut buf)) // .collect(); // // let skip_amt = info.size as usize // - std::mem::size_of::() // - std::mem::size_of::() // - std::mem::size_of::() * std_info.number_of_blocks as usize; // skip(&mut data_file, &mut buf, skip_amt); // // for block in blocks { // read_block_into(&mut data_file, &mut file, &mut buf, block.compressed_size as usize); // } // } // FileKind::Model => { // let model_info: ModelBlock = read_struct(&mut data_file, &mut buf); // dbg!(&model_info); // // let block_counts = &model_info.block_num; // let total_blocks = block_counts.stack // + block_counts.runtime // + block_counts.vertex_buffer.iter().sum::() // + block_counts.edge_geometry_vertex_buffer.iter().sum::() // + block_counts.index_buffer.iter().sum::(); // let block_sizes: Vec = read_vec(&mut data_file, total_blocks as usize); // // let skip_amt = info.size as usize // - std::mem::size_of::() // - std::mem::size_of::() // - total_blocks as usize * std::mem::size_of::(); // skip(&mut data_file, &mut buf, skip_amt); // // file.seek(SeekFrom::Start(0x44)).unwrap(); // // let stack_size = read_blocks_into( // model_info.block_num.stack, // model_info.block_index.stack, // model_info.offset.stack, // &block_sizes, // &mut data_file, // &mut file, // &mut buf, // ); // // let runtime_size = read_blocks_into( // model_info.block_num.runtime, // model_info.block_index.runtime, // model_info.offset.runtime, // &block_sizes, // &mut data_file, // &mut file, // &mut buf, // ); // // let mut vertex_data_offsets = [0u32; MAX_LODS]; // let mut vertex_buffer_sizes = [0u32; MAX_LODS]; // // let mut index_data_offsets = [0u32; MAX_LODS]; // let mut index_buffer_sizes = [0u32; MAX_LODS]; // // for lod_index in 0..MAX_LODS { // // Vertex buffer // let block_count = model_info.block_num.vertex_buffer[lod_index]; // if block_count != 0 { // if lod_index == 0 || block_count > 0 { // vertex_data_offsets[lod_index] = file.stream_position().unwrap().try_into().unwrap(); // } // // vertex_buffer_sizes[lod_index] = read_blocks_into( // block_count, // model_info.block_index.vertex_buffer[lod_index], // // offset + model_info.offset.vertex_buffer[lod_index], // model_info.offset.vertex_buffer[lod_index], // &block_sizes, // &mut data_file, // &mut file, // &mut buf, // ); // } // // // Edge geometry vertex buffer // let block_count = model_info.block_num.edge_geometry_vertex_buffer[lod_index]; // if block_count != 0 { // read_blocks_into( // block_count, // model_info.block_index.edge_geometry_vertex_buffer[lod_index], // // offset + model_info.offset.edge_geometry_vertex_buffer[lod_index], // model_info.offset.edge_geometry_vertex_buffer[lod_index], // &block_sizes, // &mut data_file, // &mut file, // &mut buf, // ); // } // // // Index buffer // let block_count = model_info.block_num.index_buffer[lod_index]; // if block_count != 0 { // if lod_index == 0 || block_count > 0 { // index_data_offsets[lod_index] = file.stream_position().unwrap().try_into().unwrap(); // } // // index_buffer_sizes[lod_index] = read_blocks_into( // block_count, // model_info.block_index.index_buffer[lod_index], // // offset + model_info.offset.index_buffer[lod_index], // model_info.offset.index_buffer[lod_index], // &block_sizes, // &mut data_file, // &mut file, // &mut buf, // ); // } // } // // // Write out the header now we've collected the info for it. // file.seek(SeekFrom::Start(0)).unwrap(); // file.write_le(&model_info.version).unwrap(); // file.write_le(&stack_size).unwrap(); // file.write_le(&runtime_size).unwrap(); // file.write_le(&model_info.vertex_declaration_num).unwrap(); // file.write_le(&model_info.material_num).unwrap(); // file.write_le(&vertex_data_offsets).unwrap(); // file.write_le(&index_data_offsets).unwrap(); // file.write_le(&vertex_buffer_sizes).unwrap(); // file.write_le(&index_buffer_sizes).unwrap(); // file.write_le(&model_info.num_lods).unwrap(); // file.write_le(&model_info.index_buffer_streaming_enabled).unwrap(); // file.write_le(&model_info.edge_geometry_enabled).unwrap(); // file.write_le(&0u8).unwrap(); // } // FileKind::Texture => { // let std_info: SqPackFileInfo = read_struct(&mut data_file, &mut buf); // let blocks: Vec = (0..std_info.number_of_blocks) // .map(|_| read_struct(&mut data_file, &mut buf)) // .collect(); // // let sub_block_count = blocks // .iter() // .fold(0, |acc, block| acc + block.block_count); // let sub_block_sizes: Vec = read_vec(&mut data_file, sub_block_count as usize); // // let skip_amt = info.size as usize // - std::mem::size_of::() // - std::mem::size_of::() // - std::mem::size_of::() * std_info.number_of_blocks as usize // - std::mem::size_of::() * sub_block_sizes.len(); // skip(&mut data_file, &mut buf, skip_amt); // // let mip_map_size = blocks[0].compressed_offset; // if mip_map_size > 0 { // let mut reader = (&mut data_file).take(mip_map_size as u64); // std::io::copy(&mut reader, &mut file).unwrap(); // } // // let mut sub_block = 0; // for block in blocks { // for _ in 0..block.block_count { // read_block_into(&mut data_file, &mut file, &mut buf, sub_block_sizes[sub_block] as usize); // sub_block += 1; // } // } // } // } // // if data_file.read < expected { // let to_skip = expected - data_file.read; // skip(&mut data_file, &mut buf, to_skip); // } // } // } // // fn read_block_into(reader: &mut R, writer: &mut W, buf: &mut [u8], size: usize) -> u64 { // let header: DatBlockHeader = read_struct(reader, buf); // // let (read, actual) = if header.compressed_size == 32_000 { // // uncompressed // let mut reader = reader.take(header.uncompressed_size as u64); // let read = std::io::copy(&mut reader, writer).unwrap(); // (read, read) // } else { // // compressed // let reader = reader.take(header.compressed_size as u64); // let mut decoder = DeflateDecoder::new(reader); // let read = std::io::copy(&mut decoder, writer).unwrap(); // (header.compressed_size as u64, read) // }; // // if (header.size as usize + read as usize) < size as usize { // let to_skip = size // - header.size as usize // - read as usize; // skip(reader, buf, to_skip); // } // // actual // } // // fn read_blocks_into( // block_count: u16, // block_index: u16, // _section_offset: u32, // block_sizes: &[u16], // reader: &mut impl Read, // writer: &mut impl Write, // buf: &mut [u8], // ) -> u32 { // let sizes = &block_sizes[block_index as usize..block_index as usize + block_count as usize]; // // let mut total_read = 0u32; // for &size in sizes { // let bytes_read = read_block_into(reader, writer, buf, size as usize); // total_read += bytes_read as u32; // } // // total_read as u32 // } // // fn read_struct(reader: &mut R, buf: &mut [u8]) -> S // where S::Args: Default, // { // let size = std::mem::size_of::(); // reader.read_exact(&mut buf[..size]).unwrap(); // S::read(&mut Cursor::new(&buf[..size])).unwrap() // } // // fn skip(reader: &mut R, buf: &mut [u8], size: usize) { // let mut left = size; // while left > 0 { // let to_read = std::cmp::min(left, buf.len()); // left -= reader.read(&mut buf[..to_read]).unwrap(); // } // } // // fn read_vec, R: Read>(reader: &mut R, amount: usize) -> Vec { // let mut buf = vec![0; amount * std::mem::size_of::()]; // reader.read_exact(&mut buf).unwrap(); // >::read_args( // &mut Cursor::new(buf), // VecArgs { // count: amount, // inner: (), // }, // ).unwrap() // }