345 lines
14 KiB
Rust
345 lines
14 KiB
Rust
|
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<DatStdFileBlockInfos> = (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::<SqPackFileInfoHeader>()
|
||
|
// - std::mem::size_of::<SqPackFileInfo>()
|
||
|
// - std::mem::size_of::<DatStdFileBlockInfos>() * 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::<u16>()
|
||
|
// + block_counts.edge_geometry_vertex_buffer.iter().sum::<u16>()
|
||
|
// + block_counts.index_buffer.iter().sum::<u16>();
|
||
|
// let block_sizes: Vec<u16> = read_vec(&mut data_file, total_blocks as usize);
|
||
|
//
|
||
|
// let skip_amt = info.size as usize
|
||
|
// - std::mem::size_of::<SqPackFileInfoHeader>()
|
||
|
// - std::mem::size_of::<ModelBlock>()
|
||
|
// - total_blocks as usize * std::mem::size_of::<u16>();
|
||
|
// 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<LodBlock> = (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<u16> = read_vec(&mut data_file, sub_block_count as usize);
|
||
|
//
|
||
|
// let skip_amt = info.size as usize
|
||
|
// - std::mem::size_of::<SqPackFileInfoHeader>()
|
||
|
// - std::mem::size_of::<SqPackFileInfo>()
|
||
|
// - std::mem::size_of::<LodBlock>() * std_info.number_of_blocks as usize
|
||
|
// - std::mem::size_of::<u16>() * 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<R: Read, W: Write>(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<S: BinRead, R: Read>(reader: &mut R, buf: &mut [u8]) -> S
|
||
|
// where S::Args: Default,
|
||
|
// {
|
||
|
// let size = std::mem::size_of::<S>();
|
||
|
// reader.read_exact(&mut buf[..size]).unwrap();
|
||
|
// S::read(&mut Cursor::new(&buf[..size])).unwrap()
|
||
|
// }
|
||
|
//
|
||
|
// fn skip<R: Read>(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<S: BinRead<Args=()>, R: Read>(reader: &mut R, amount: usize) -> Vec<S> {
|
||
|
// let mut buf = vec![0; amount * std::mem::size_of::<S>()];
|
||
|
// reader.read_exact(&mut buf).unwrap();
|
||
|
// <Vec<S>>::read_args(
|
||
|
// &mut Cursor::new(buf),
|
||
|
// VecArgs {
|
||
|
// count: amount,
|
||
|
// inner: (),
|
||
|
// },
|
||
|
// ).unwrap()
|
||
|
// }
|