fix: use the worst hack to correct a TexTools mistake

This commit is contained in:
Anna 2022-12-21 20:54:09 -05:00
parent 05a704df75
commit af175a6509
2 changed files with 48 additions and 27 deletions

View File

@ -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"] }

View File

@ -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<T> WriteSeek for T
where T: Write + Seek {}
where T: Write + Seek + Read {}
pub struct TtmpExtractor<R> {
manifest: ManifestKind,
@ -128,7 +128,7 @@ impl<R: Read + Seek> TtmpExtractor<R> {
}
impl<R: Read> TtmpExtractor<R> {
pub fn extract_one_into<W: Write + Seek>(mod_file: &ModFile, mut reader: R, mut writer: W) -> Result<()> {
pub fn extract_one_into<W: Read + Write + Seek>(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<R: Read> TtmpExtractor<R> {
Ok(())
}
fn extract_model_file<T: Read, W: Write + Seek>(info: &SqPackFileInfoHeader, mut reader: T, mut writer: W, buf: &mut [u8]) -> Result<()> {
fn extract_model_file<T: Read, W: Read + Write + Seek>(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<R: Read> TtmpExtractor<R> {
- std::mem::size_of::<u16>() * 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<R: Read> TtmpExtractor<R> {
model_info.offset.stack,
&block_sizes,
&mut reader,
&mut writer,
&mut temp,
buf,
)?;
@ -210,7 +213,7 @@ impl<R: Read> TtmpExtractor<R> {
model_info.offset.runtime,
&block_sizes,
&mut reader,
&mut writer,
&mut temp,
buf,
)?;
@ -225,7 +228,7 @@ impl<R: Read> TtmpExtractor<R> {
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<R: Read> TtmpExtractor<R> {
model_info.offset.vertex_buffer[lod_index],
&block_sizes,
&mut reader,
&mut writer,
&mut temp,
buf,
)?;
}
@ -248,7 +251,7 @@ impl<R: Read> TtmpExtractor<R> {
model_info.offset.edge_geometry_vertex_buffer[lod_index],
&block_sizes,
&mut reader,
&mut writer,
&mut temp,
buf,
)?;
}
@ -257,7 +260,7 @@ impl<R: Read> TtmpExtractor<R> {
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<R: Read> TtmpExtractor<R> {
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(())
}