1
Fork 0
mirror of https://github.com/redstrate/Physis.git synced 2025-04-23 13:17:44 +00:00

Decompress and return rgba data for textures

This commit is contained in:
Joshua Goins 2022-08-11 15:03:12 -04:00
parent 0210479fd3
commit 6554dee2f1
4 changed files with 79 additions and 59 deletions

16
Cargo.lock generated
View file

@ -351,6 +351,12 @@ version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b" checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b"
[[package]]
name = "libm"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565"
[[package]] [[package]]
name = "libz-sys" name = "libz-sys"
version = "1.1.8" version = "1.1.8"
@ -452,6 +458,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"sha1_smol", "sha1_smol",
"texpresso",
] ]
[[package]] [[package]]
@ -647,6 +654,15 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "texpresso"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8277e703c934b9693d0773d5749faacc6366b3d81d012da556a4cfd4ab87f336"
dependencies = [
"libm",
]
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.15.0" version = "0.15.0"

View file

@ -52,4 +52,7 @@ sha1_smol = "1.0.0"
glam = "0.21.3" glam = "0.21.3"
# needed for c-style bitflags used in some formats (such as tex files) # needed for c-style bitflags used in some formats (such as tex files)
bitflags = "1.3" bitflags = "1.3"
# needed for dxt/bc decompression
texpresso = "2.0.1"

View file

@ -28,7 +28,7 @@ struct StandardFileBlock {
num_blocks: u32, num_blocks: u32,
} }
#[derive(BinRead)] #[derive(BinRead, Debug)]
struct TextureLodBlock { struct TextureLodBlock {
compressed_offset: u32, compressed_offset: u32,
compressed_size: u32, compressed_size: u32,
@ -88,7 +88,7 @@ pub struct ModelFileBlock {
pub edge_geometry_enabled: bool, pub edge_geometry_enabled: bool,
} }
#[derive(BinRead)] #[derive(BinRead, Debug)]
struct TextureBlock { struct TextureBlock {
#[br(pad_before = 8)] #[br(pad_before = 8)]
num_blocks: u32, num_blocks: u32,
@ -103,7 +103,7 @@ struct TextureBlock {
struct FileInfo { struct FileInfo {
size: u32, size: u32,
file_type: FileType, file_type: FileType,
file_size: i32, file_size: u32,
#[br(if (file_type == FileType::Standard))] #[br(if (file_type == FileType::Standard))]
standard_info: Option<StandardFileBlock>, standard_info: Option<StandardFileBlock>,
@ -331,12 +331,15 @@ impl DatFile {
let texture_file_info = file_info.texture_info.as_ref().unwrap(); let texture_file_info = file_info.texture_info.as_ref().unwrap();
// write the header if it exists // write the header if it exists
if texture_file_info.lods[0].compressed_offset != 0 { let mipmap_size = texture_file_info.lods[0].compressed_size;
let original_pos = self.file.stream_position().unwrap(); if mipmap_size != 0 {
let original_pos = self.file.stream_position().ok()?;
self.file.seek(SeekFrom::Start(offset + file_info.size as u64)).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]; 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); data.append(&mut header);
self.file.seek(SeekFrom::Start(original_pos)).ok()?; 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); 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 { for _ in 0..texture_file_info.lods[i as usize].block_count {
data.append(&mut read_data_block(&self.file, running_block_total).unwrap()); let original_pos = self.file.stream_position().ok()?;
self.file.seek(SeekFrom::Start(running_block_total)).ok()?;
running_block_total += i16::read(&mut self.file).unwrap() as u64; 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;
} }
} }

View file

@ -1,8 +1,10 @@
use std::io::Cursor; use std::cmp::min;
use std::io::{Cursor, Read, Seek, SeekFrom};
use binrw::binread; use binrw::binread;
use crate::gamedata::MemoryBuffer; use crate::gamedata::MemoryBuffer;
use binrw::BinRead; use binrw::BinRead;
use bitflags::bitflags; 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) // Attributes and Format are adapted from Lumina (https://github.com/NotAdam/Lumina/blob/master/src/Lumina/Data/Files/TexFile.cs)
bitflags! { bitflags! {
@ -35,51 +37,13 @@ bitflags! {
} }
} }
bitflags! { #[binread]
#[binread] #[br(repr = u32)]
struct TextureFormat : u32 { #[derive(Debug)]
const TypeShift = 0xC; enum TextureFormat {
const TypeMask = 0xF000; B8G8R8A8 = 0x1450,
const ComponentShift = 0x8; BC1 = 0x3420,
const ComponentMask = 0xF00; BC5 = 0x3431
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] #[binread]
@ -98,7 +62,7 @@ struct TexHeader {
} }
pub struct Texture { pub struct Texture {
rgba: Vec<u8>
} }
impl Texture { impl Texture {
@ -106,8 +70,38 @@ impl Texture {
let mut cursor = Cursor::new(buffer); let mut cursor = Cursor::new(buffer);
let header = TexHeader::read(&mut cursor).unwrap(); 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::<u32>() as usize];
cursor.read_exact(src.as_mut_slice()).ok()?;
let mut dst : Vec<u8> = 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
})
} }
} }