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"
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"

View file

@ -53,3 +53,6 @@ glam = "0.21.3"
# needed for c-style bitflags used in some formats (such as tex files)
bitflags = "1.3"
# needed for dxt/bc decompression
texpresso = "2.0.1"

View file

@ -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<StandardFileBlock>,
@ -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;
}
}

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 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<u8>
}
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::<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
})
}
}