From af175a650930054f9daa167c5a70b40946e1e2f3 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 21 Dec 2022 20:54:09 -0500 Subject: [PATCH] fix: use the worst hack to correct a TexTools mistake --- Cargo.toml | 1 + src/ttmp_extractor.rs | 74 +++++++++++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e0b01b6..2057b10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ flate2 = "1" serde = { version = "1", features = ["derive"] } serde_json = "1" sha3 = "0.10" +tempfile = "3" thiserror = "1" zip = { version = "0.6", default-features = false, features = ["deflate"] } sqpack = { git = "https://git.anna.lgbt/ascclemens/sqpack-rs", features = ["read", "write"] } diff --git a/src/ttmp_extractor.rs b/src/ttmp_extractor.rs index 6f62105..c777379 100644 --- a/src/ttmp_extractor.rs +++ b/src/ttmp_extractor.rs @@ -4,7 +4,7 @@ use std::io::{Cursor, Read, Seek, SeekFrom, Write}; use flate2::read::DeflateDecoder; use sqpack::{DatBlockHeader, DatStdFileBlockInfos, FileKind, LodBlock, ModelBlock, SqPackFileInfo, SqPackFileInfoHeader}; -use sqpack::binrw::{BinRead, BinWriterExt, VecArgs}; +use sqpack::binrw::{BinRead, BinReaderExt, BinWriterExt, VecArgs}; use zip::ZipArchive; use crate::Error; @@ -15,10 +15,10 @@ use crate::tracking_reader::TrackingReader; use crate::util::{MAX_MODEL_LODS, read_struct}; #[doc(hidden)] -pub trait WriteSeek: Write + Seek {} +pub trait WriteSeek: Write + Seek + Read {} impl WriteSeek for T - where T: Write + Seek {} + where T: Write + Seek + Read {} pub struct TtmpExtractor { manifest: ManifestKind, @@ -128,7 +128,7 @@ impl TtmpExtractor { } impl TtmpExtractor { - pub fn extract_one_into(mod_file: &ModFile, mut reader: R, mut writer: W) -> Result<()> { + pub fn extract_one_into(mod_file: &ModFile, mut reader: R, mut writer: W) -> Result<()> { let mut reader = TrackingReader::new(&mut reader); let mut buf = [0; 4096]; let file = mod_file.file; @@ -175,7 +175,7 @@ impl TtmpExtractor { Ok(()) } - fn extract_model_file(info: &SqPackFileInfoHeader, mut reader: T, mut writer: W, buf: &mut [u8]) -> Result<()> { + fn extract_model_file(info: &SqPackFileInfoHeader, mut reader: T, mut writer: W, buf: &mut [u8]) -> Result<()> { let model_info: ModelBlock = read_struct(&mut reader, buf)?; let block_counts = &model_info.block_num; @@ -192,7 +192,10 @@ impl TtmpExtractor { - std::mem::size_of::() * total_blocks as usize; Self::skip(&mut reader, buf, skip_amt)?; - writer.seek(SeekFrom::Start(0x44)).map_err(Error::Io)?; + // we need to modify the mdl file because tt sucks ass, so write into + // a temp file first, then copy into the real writer + let mut temp = tempfile::tempfile().map_err(Error::Io)?; + temp.seek(SeekFrom::Start(0x44)).map_err(Error::Io)?; let stack_size = Self::read_blocks_into( model_info.block_num.stack, @@ -200,7 +203,7 @@ impl TtmpExtractor { model_info.offset.stack, &block_sizes, &mut reader, - &mut writer, + &mut temp, buf, )?; @@ -210,7 +213,7 @@ impl TtmpExtractor { model_info.offset.runtime, &block_sizes, &mut reader, - &mut writer, + &mut temp, buf, )?; @@ -225,7 +228,7 @@ impl TtmpExtractor { 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] = writer.stream_position().map_err(Error::Io)? as u32; + vertex_data_offsets[lod_index] = temp.stream_position().map_err(Error::Io)? as u32; } vertex_buffer_sizes[lod_index] = Self::read_blocks_into( @@ -234,7 +237,7 @@ impl TtmpExtractor { model_info.offset.vertex_buffer[lod_index], &block_sizes, &mut reader, - &mut writer, + &mut temp, buf, )?; } @@ -248,7 +251,7 @@ impl TtmpExtractor { model_info.offset.edge_geometry_vertex_buffer[lod_index], &block_sizes, &mut reader, - &mut writer, + &mut temp, buf, )?; } @@ -257,7 +260,7 @@ impl TtmpExtractor { 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] = writer.stream_position().map_err(Error::Io)? as u32; + index_data_offsets[lod_index] = temp.stream_position().map_err(Error::Io)? as u32; } index_buffer_sizes[lod_index] = Self::read_blocks_into( @@ -266,27 +269,44 @@ impl TtmpExtractor { model_info.offset.index_buffer[lod_index], &block_sizes, &mut reader, - &mut writer, + &mut temp, buf, )?; } } // Write out the header now we've collected the info for it. - writer.seek(SeekFrom::Start(0)).map_err(Error::Io)?; - writer.write_le(&model_info.version).map_err(Error::BinRwWrite)?; - writer.write_le(&stack_size).map_err(Error::BinRwWrite)?; - writer.write_le(&runtime_size).map_err(Error::BinRwWrite)?; - writer.write_le(&model_info.vertex_declaration_num).map_err(Error::BinRwWrite)?; - writer.write_le(&model_info.material_num).map_err(Error::BinRwWrite)?; - writer.write_le(&vertex_data_offsets).map_err(Error::BinRwWrite)?; - writer.write_le(&index_data_offsets).map_err(Error::BinRwWrite)?; - writer.write_le(&vertex_buffer_sizes).map_err(Error::BinRwWrite)?; - writer.write_le(&index_buffer_sizes).map_err(Error::BinRwWrite)?; - writer.write_le(&model_info.num_lods).map_err(Error::BinRwWrite)?; - writer.write_le(&model_info.index_buffer_streaming_enabled).map_err(Error::BinRwWrite)?; - writer.write_le(&model_info.edge_geometry_enabled).map_err(Error::BinRwWrite)?; - writer.write_le(&0u8).map_err(Error::BinRwWrite)?; + temp.seek(SeekFrom::Start(0)).map_err(Error::Io)?; + temp.write_le(&model_info.version).map_err(Error::BinRwWrite)?; + temp.write_le(&stack_size).map_err(Error::BinRwWrite)?; + temp.write_le(&runtime_size).map_err(Error::BinRwWrite)?; + temp.write_le(&model_info.vertex_declaration_num).map_err(Error::BinRwWrite)?; + temp.write_le(&model_info.material_num).map_err(Error::BinRwWrite)?; + temp.write_le(&vertex_data_offsets).map_err(Error::BinRwWrite)?; + temp.write_le(&index_data_offsets).map_err(Error::BinRwWrite)?; + temp.write_le(&vertex_buffer_sizes).map_err(Error::BinRwWrite)?; + temp.write_le(&index_buffer_sizes).map_err(Error::BinRwWrite)?; + // apparently textools only writes one lod, though the header says three + // temp.write_le(&model_info.num_lods).map_err(Error::BinRwWrite)?; + temp.write_le(&1u8).map_err(Error::BinRwWrite)?; + temp.write_le(&model_info.index_buffer_streaming_enabled).map_err(Error::BinRwWrite)?; + temp.write_le(&model_info.edge_geometry_enabled).map_err(Error::BinRwWrite)?; + temp.write_le(&0u8).map_err(Error::BinRwWrite)?; + + // fix textools lod number being wrong + let strings_length_offset = 0x44 + stack_size as u64 + 4; + temp.seek(SeekFrom::Start(strings_length_offset)).map_err(Error::Io)?; + let strings_length: u32 = temp.read_le().map_err(Error::BinRwRead)?; + let lod_offset = strings_length_offset + strings_length as u64 + 4 + 22; + temp.rewind().map_err(Error::Io)?; + + // copy bytes up to the offset + std::io::copy(&mut (&mut temp).take(lod_offset), &mut writer).map_err(Error::Io)?; + // write 1 lod + writer.write_le(&1u8).map_err(Error::BinRwWrite)?; + temp.seek(SeekFrom::Current(1)).map_err(Error::Io)?; + // copy bytes after the offset + std::io::copy(&mut temp, &mut writer).map_err(Error::Io)?; Ok(()) }