diff --git a/Cargo.lock b/Cargo.lock index 80a0925..9570288 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,6 +351,12 @@ version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b" +[[package]] +name = "libm" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" + [[package]] name = "libz-sys" version = "1.1.8" @@ -452,6 +458,7 @@ dependencies = [ "serde", "serde_json", "sha1_smol", + "texpresso", ] [[package]] @@ -647,6 +654,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "texpresso" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8277e703c934b9693d0773d5749faacc6366b3d81d012da556a4cfd4ab87f336" +dependencies = [ + "libm", +] + [[package]] name = "textwrap" version = "0.15.0" diff --git a/Cargo.toml b/Cargo.toml index 6f2b6d1..f5633c5 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,4 +52,7 @@ sha1_smol = "1.0.0" glam = "0.21.3" # needed for c-style bitflags used in some formats (such as tex files) -bitflags = "1.3" \ No newline at end of file +bitflags = "1.3" + +# needed for dxt/bc decompression +texpresso = "2.0.1" \ No newline at end of file diff --git a/src/dat.rs b/src/dat.rs index 5f750ed..2c58ce8 100755 --- a/src/dat.rs +++ b/src/dat.rs @@ -28,7 +28,7 @@ struct StandardFileBlock { num_blocks: u32, } -#[derive(BinRead)] +#[derive(BinRead, Debug)] struct TextureLodBlock { compressed_offset: u32, compressed_size: u32, @@ -88,7 +88,7 @@ pub struct ModelFileBlock { pub edge_geometry_enabled: bool, } -#[derive(BinRead)] +#[derive(BinRead, Debug)] struct TextureBlock { #[br(pad_before = 8)] num_blocks: u32, @@ -103,7 +103,7 @@ struct TextureBlock { struct FileInfo { size: u32, file_type: FileType, - file_size: i32, + file_size: u32, #[br(if (file_type == FileType::Standard))] standard_info: Option, @@ -331,12 +331,15 @@ impl DatFile { let texture_file_info = file_info.texture_info.as_ref().unwrap(); // write the header if it exists - if texture_file_info.lods[0].compressed_offset != 0 { - let original_pos = self.file.stream_position().unwrap(); + let mipmap_size = texture_file_info.lods[0].compressed_size; + if mipmap_size != 0 { + let original_pos = self.file.stream_position().ok()?; self.file.seek(SeekFrom::Start(offset + file_info.size as u64)).ok()?; + let mut header = vec![0u8; texture_file_info.lods[0].compressed_offset as usize]; - self.file.read(&mut header).ok()?; + self.file.read_exact(&mut header).ok()?; + data.append(&mut header); self.file.seek(SeekFrom::Start(original_pos)).ok()?; @@ -346,9 +349,13 @@ impl DatFile { let mut running_block_total = (texture_file_info.lods[i as usize].compressed_offset as u64) + offset + (file_info.size as u64); for _ in 0..texture_file_info.lods[i as usize].block_count { - data.append(&mut read_data_block(&self.file, running_block_total).unwrap()); - self.file.seek(SeekFrom::Start(running_block_total)).ok()?; - running_block_total += i16::read(&mut self.file).unwrap() as u64; + let original_pos = self.file.stream_position().ok()?; + + data.append(&mut read_data_block(&self.file, running_block_total)?); + + self.file.seek(SeekFrom::Start(original_pos)).ok()?; + + running_block_total += i16::read(&mut self.file).ok()? as u64; } } diff --git a/src/tex.rs b/src/tex.rs index 0e14230..cf2a72e 100644 --- a/src/tex.rs +++ b/src/tex.rs @@ -1,8 +1,10 @@ -use std::io::Cursor; +use std::cmp::min; +use std::io::{Cursor, Read, Seek, SeekFrom}; use binrw::binread; use crate::gamedata::MemoryBuffer; use binrw::BinRead; use bitflags::bitflags; +use texpresso::Format; // Attributes and Format are adapted from Lumina (https://github.com/NotAdam/Lumina/blob/master/src/Lumina/Data/Files/TexFile.cs) bitflags! { @@ -35,51 +37,13 @@ bitflags! { } } -bitflags! { - #[binread] - struct TextureFormat : u32 { - const TypeShift = 0xC; - const TypeMask = 0xF000; - const ComponentShift = 0x8; - const ComponentMask = 0xF00; - const BppShift = 0x4; - const BppMask = 0xF0; - const EnumShift = 0x0; - const EnumMask = 0xF; - const TypeInteger = 0x1; - const TypeFloat = 0x2; - const TypeDxt = 0x3; - const TypeBc123 = 0x3; - const TypeDepthStencil = 0x4; - const TypeSpecial = 0x5; - const TypeBc57 = 0x6; - - const L8 = 0x1130; - const A8 = 0x1131; - const B4G4R4A4 = 0x1440; - const B5G5R5A1 = 0x1441; - const B8G8R8A8 = 0x1450; - const B8G8R8X8 = 0x1451; - - const R32F = 0x2150; - const R16G16F = 0x2250; - const R32G32F = 0x2260; - const R16G16B16A16F = 0x2460; - const R32G32B32A32F = 0x2470; - - const BC1 = 0x3420; - const BC2 = 0x3430; - const BC3 = 0x3431; - const BC5 = 0x6230; - const BC7 = 0x6432; - - const D16 = 0x4140; - const D24S8 = 0x4250; - - const Null = 0x5100; - const Shadow16 = 0x5140; - const Shadow24 = 0x5150; - } +#[binread] +#[br(repr = u32)] +#[derive(Debug)] +enum TextureFormat { + B8G8R8A8 = 0x1450, + BC1 = 0x3420, + BC5 = 0x3431 } #[binread] @@ -98,7 +62,7 @@ struct TexHeader { } pub struct Texture { - + rgba: Vec } impl Texture { @@ -106,8 +70,38 @@ impl Texture { let mut cursor = Cursor::new(buffer); let header = TexHeader::read(&mut cursor).unwrap(); - println!("{:#?}", header); + // TODO: Adapted from Lumina, but this really can be written better... + let mut texture_data_size = vec![]; + texture_data_size.resize(min(13, header.mip_levels as usize), 0); + let size = texture_data_size.len(); + for i in 0..size - 1 { + texture_data_size[i] = header.offset_to_surface[i + 1] - header.offset_to_surface[i]; + } + texture_data_size[size - 1] = (buffer.len() - header.offset_to_surface[size - 1] as usize) as u32; - None + cursor.seek(SeekFrom::Start(header.offset_to_surface[0] as u64)).ok()?; + + let mut src = vec![0u8; texture_data_size.iter().sum::() as usize]; + cursor.read_exact(src.as_mut_slice()).ok()?; + + let mut dst : Vec = vec![0u8; (header.width as usize * header.height as usize * 4) as usize]; + + match header.format { + TextureFormat::B8G8R8A8 => { + dst.copy_from_slice(&src); + } + TextureFormat::BC1 => { + let format = Format::Bc1; + format.decompress(&src, header.width as usize, header.height as usize, dst.as_mut_slice()); + } + TextureFormat::BC5 => { + let format = Format::Bc3; + format.decompress(&src, header.width as usize, header.height as usize, dst.as_mut_slice()); + } + } + + Some(Texture { + rgba: dst + }) } } \ No newline at end of file