#![allow(clippy::needless_range_loop)]
use std::io::Cursor;
use crate::ByteBuffer;
use crate::ByteSpan;
use crate::ReadableFile;
use crate::bcn::decode_bc1;
use crate::bcn::decode_bc3;
use crate::bcn::decode_bc5;
use crate::bcn::decode_bc7;
use crate::common::Platform;
use binrw::BinRead;
use binrw::BinWrite;
use binrw::binrw;
use binrw::helpers::until_eof;
use bitflags::bitflags;
#[binrw]
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct TextureAttribute(u32);
bitflags! {
impl TextureAttribute : u32 {
const DISCARD_PER_FRAME = 0x1;
const DISCARD_PER_MAP = 0x2;
const MANAGED = 0x4;
const USER_MANAGED = 0x8;
const CPU_READ = 0x10;
const LOCATION_MAIN = 0x20;
const NO_GPU_READ = 0x40;
const ALIGNED_SIZE = 0x80;
const EDGE_CULLING = 0x100;
const LOCATION_ONION = 0x200;
const READ_WRITE = 0x400;
const IMMUTABLE = 0x800;
const TEXTURE_RENDER_TARGET = 0x100000;
const TEXTURE_DEPTH_STENCIL = 0x200000;
const TEXTURE_TYPE1_D = 0x400000;
const TEXTURE_TYPE2_D = 0x800000;
const TEXTURE_TYPE3_D = 0x1000000;
const TEXTURE_TYPE2_D_ARRAY = 0x10000000;
const TEXTURE_TYPE_CUBE = 0x2000000;
const TEXTURE_TYPE_MASK = 0x3C00000;
const TEXTURE_SWIZZLE = 0x4000000;
const TEXTURE_NO_TILED = 0x8000000;
const TEXTURE_NO_SWIZZLE = 0x80000000;
}
}
#[binrw]
#[brw(repr = u32)]
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
#[allow(non_camel_case_types)] pub enum TextureFormat {
L8_UNORM = 0x1130,
A8_UNORM = 0x1131,
R8_UNORM = 0x1132,
R8_UINT = 0x1133,
R16_UINT = 0x1140,
R32_UINT = 0x1150,
R8G8_UNORM = 0x1240,
B4G4R4A4_UNORM = 0x1440,
B5G5R5A1_UNORM = 0x1441,
B8G8R8A8_UNORM = 0x1450,
B8G8R8X8_UNORM = 0x1451,
R16_FLOAT = 0x2140,
R32_FLOAT = 0x2150,
R16G16_FLOAT = 0x2250,
R32G32_FLOAT = 0x2260,
R11G11B10_FLOAT = 0x2350,
R16G16B16A16_FLOAT = 0x2460,
R32G32B32A32_FLOAT = 0x2470,
BC1_UNORM = 0x3420,
BC2_UNORM = 0x3430,
BC3_UNORM = 0x3431,
D16_UNORM = 0x4140,
D24_UNORM_S8_UINT = 0x4250,
D16_UNORM_2 = 0x5140,
D24_UNORM_S8_UINT_2 = 0x5150,
BC4_UNORM = 0x6120,
BC5_UNORM = 0x6230,
BC6H_SF16 = 0x6330,
BC7_UNORM = 0x6432,
R16_UNORM = 0x7140,
R16G16_UNORM = 0x7250,
R10G10B10A2_UNORM_2 = 0x7350,
R10G10B10A2_UNORM = 0x7450,
D24_UNORM_S8_UINT_3 = 0x8250,
}
#[binrw]
#[derive(Debug)]
#[allow(dead_code)]
pub struct Texture {
pub attribute: TextureAttribute,
pub format: TextureFormat,
pub width: u16,
pub height: u16,
pub depth: u16,
pub mip_levels: u16,
lod_offsets: [u32; 3],
offset_to_surface: [u32; 13],
#[br(parse_with = until_eof)]
pub data: Vec<u8>,
}
type DecodeFunction = fn(&[u8], usize, usize, &mut [u32]) -> Result<(), &'static str>;
impl ReadableFile for Texture {
fn from_existing(platform: Platform, buffer: ByteSpan) -> Option<Self> {
let mut cursor = Cursor::new(buffer);
Self::read_options(&mut cursor, platform.endianness(), ()).ok()
}
}
impl Texture {
pub fn convert_existing(
src_platform: Platform,
buffer: ByteSpan,
dst_platform: Platform,
) -> ByteBuffer {
let mut src_cursor = Cursor::new(buffer);
let src_header =
Self::read_options(&mut src_cursor, src_platform.endianness(), ()).unwrap();
let mut dst_cursor = Cursor::new(buffer.to_vec());
src_header
.write_options(&mut dst_cursor, dst_platform.endianness(), ())
.unwrap();
dst_cursor.into_inner()
}
fn decode(src: &[u8], width: usize, height: usize, decode_func: DecodeFunction) -> Vec<u8> {
let mut image: Vec<u32> = vec![0; width * height];
decode_func(src, width, height, &mut image).unwrap();
image
.iter()
.flat_map(|x| {
let v = x.to_le_bytes();
[v[2], v[1], v[0], v[3]]
})
.collect::<Vec<u8>>()
}
pub fn to_rgba(&self) -> Option<Vec<u8>> {
match self.format {
TextureFormat::B4G4R4A4_UNORM => {
let mut dst =
vec![0u8; self.width as usize * self.height as usize * self.depth as usize * 4];
let mut offset = 0;
let mut dst_offset = 0;
for _ in 0..self.width as usize * self.height as usize {
let short: u16 =
((self.data[offset] as u16) << 8) | self.data[offset + 1] as u16;
let src_b = short & 0xF;
let src_g = (short >> 4) & 0xF;
let src_r = (short >> 8) & 0xF;
let src_a = (short >> 12) & 0xF;
dst[dst_offset] = (17 * src_r) as u8;
dst[dst_offset + 1] = (17 * src_g) as u8;
dst[dst_offset + 2] = (17 * src_b) as u8;
dst[dst_offset + 3] = (17 * src_a) as u8;
offset += 2;
dst_offset += 4;
}
Some(dst)
}
TextureFormat::B8G8R8A8_UNORM => {
let mut dst =
vec![0u8; self.width as usize * self.height as usize * self.depth as usize * 4];
let mut offset = 0;
for _ in 0..self.width as usize * self.height as usize * self.depth as usize {
let src_b = self.data[offset];
let src_g = self.data[offset + 1];
let src_r = self.data[offset + 2];
let src_a = self.data[offset + 3];
dst[offset] = src_r;
dst[offset + 1] = src_g;
dst[offset + 2] = src_b;
dst[offset + 3] = src_a;
offset += 4;
}
Some(dst)
}
TextureFormat::BC1_UNORM => Some(Texture::decode(
&self.data,
self.width as usize,
self.height as usize * self.depth as usize,
decode_bc1,
)),
TextureFormat::BC3_UNORM => Some(Texture::decode(
&self.data,
self.width as usize,
self.height as usize * self.depth as usize,
decode_bc3,
)),
TextureFormat::BC5_UNORM => Some(Texture::decode(
&self.data,
self.width as usize,
self.height as usize * self.depth as usize,
decode_bc5,
)),
TextureFormat::BC7_UNORM => Some(Texture::decode(
&self.data,
self.width as usize,
self.height as usize * self.depth as usize,
decode_bc7,
)),
_ => {
println!("Unsupported texture format {:?}!", self.format);
None
}
}
}
}
#[cfg(test)]
mod tests {
use crate::pass_random_invalid;
use super::*;
#[test]
fn test_invalid() {
pass_random_invalid::<Texture>();
}
}