1
Fork 0
mirror of https://github.com/redstrate/Physis.git synced 2025-04-22 12:47:45 +00:00
physis/src/tex.rs

128 lines
3.6 KiB
Rust
Raw Normal View History

use crate::gamedata::MemoryBuffer;
2022-08-16 11:52:07 -04:00
use binrw::binread;
use binrw::BinRead;
2022-08-11 09:23:15 -04:00
use bitflags::bitflags;
2022-08-16 11:52:07 -04:00
use std::cmp::min;
use std::io::{Cursor, Read, Seek, SeekFrom};
use texpresso::Format;
2022-08-11 09:23:15 -04:00
// Attributes and Format are adapted from Lumina (https://github.com/NotAdam/Lumina/blob/master/src/Lumina/Data/Files/TexFile.cs)
bitflags! {
#[binread]
2022-09-15 16:26:31 -04:00
#[allow(non_upper_case_globals)]
2022-08-11 09:23:15 -04:00
struct TextureAttribute : u32 {
const DiscardPerFrame = 0x1;
const DiscardPerMap = 0x2;
const Managed = 0x4;
const UserManaged = 0x8;
const CpuRead = 0x10;
const LocationMain = 0x20;
const NoGpuRead = 0x40;
const AlignedSize = 0x80;
const EdgeCulling = 0x100;
const LocationOnion = 0x200;
const ReadWrite = 0x400;
const Immutable = 0x800;
const TextureRenderTarget = 0x100000;
const TextureDepthStencil = 0x200000;
const TextureType1D = 0x400000;
const TextureType2D = 0x800000;
const TextureType3D = 0x1000000;
const TextureTypeCube = 0x2000000;
const TextureTypeMask = 0x3C00000;
const TextureSwizzle = 0x4000000;
const TextureNoTiled = 0x8000000;
const TextureNoSwizzle = 0x80000000;
}
}
#[binread]
#[br(repr = u32)]
#[derive(Debug)]
enum TextureFormat {
B8G8R8A8 = 0x1450,
BC1 = 0x3420,
2022-08-16 11:52:07 -04:00
BC5 = 0x3431,
2022-08-11 09:23:15 -04:00
}
#[binread]
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
struct TexHeader {
2022-08-16 11:52:07 -04:00
attribute: TextureAttribute,
2022-08-11 09:23:15 -04:00
format: TextureFormat,
2022-08-16 11:52:07 -04:00
width: u16,
height: u16,
depth: u16,
mip_levels: u16,
2022-08-16 11:52:07 -04:00
lod_offsets: [u32; 3],
offset_to_surface: [u32; 13],
}
pub struct Texture {
2022-08-11 15:48:28 -04:00
pub width: u32,
pub height: u32,
2022-08-16 11:52:07 -04:00
pub rgba: Vec<u8>,
}
impl Texture {
pub fn from_existing(buffer: &MemoryBuffer) -> Option<Texture> {
let mut cursor = Cursor::new(buffer);
let header = TexHeader::read(&mut cursor).unwrap();
// 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];
}
2022-08-16 11:52:07 -04:00
texture_data_size[size - 1] =
(buffer.len() - header.offset_to_surface[size - 1] as usize) as u32;
2022-08-16 11:52:07 -04:00
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()?;
2022-08-16 11:52:07 -04:00
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;
2022-08-16 11:52:07 -04:00
format.decompress(
&src,
header.width as usize,
header.height as usize,
dst.as_mut_slice(),
);
}
TextureFormat::BC5 => {
let format = Format::Bc3;
2022-08-16 11:52:07 -04:00
format.decompress(
&src,
header.width as usize,
header.height as usize,
dst.as_mut_slice(),
);
}
}
Some(Texture {
2022-08-11 15:47:02 -04:00
width: header.width as u32,
height: header.height as u32,
2022-08-16 11:52:07 -04:00
rgba: dst,
})
}
2022-08-16 11:52:07 -04:00
}