From e2a8c695d74112a949c9e02bfad6f453a6769ee1 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sat, 17 May 2025 12:32:23 -0400 Subject: [PATCH] Add a more exhaustive list of texture formats, support BC7 textures As seen in certain bgpart model textures. --- src/bcn/bc7.rs | 333 +++++++++++++++++++++++++++++++++++++++++++ src/bcn/bitreader.rs | 73 ++++++++++ src/bcn/consts.rs | 168 ++++++++++++++++++++++ src/bcn/mod.rs | 5 + src/tex.rs | 63 ++++++-- 5 files changed, 631 insertions(+), 11 deletions(-) create mode 100644 src/bcn/bc7.rs create mode 100644 src/bcn/bitreader.rs create mode 100644 src/bcn/consts.rs diff --git a/src/bcn/bc7.rs b/src/bcn/bc7.rs new file mode 100644 index 0000000..4094aad --- /dev/null +++ b/src/bcn/bc7.rs @@ -0,0 +1,333 @@ +// SPDX-FileCopyrightText: 2023 Rudolf Kolbe +// SPDX-License-Identifier: MIT + +use super::bitreader::BitReader; +use super::color::color; +use super::consts::{S_BPTC_A2, S_BPTC_A3, S_BPTC_FACTORS, S_BPTC_P2, S_BPTC_P3}; +use core::mem::swap; + +struct Bc7ModeInfo { + num_subsets: usize, + partition_bits: usize, + rotation_bits: usize, + index_selection_bits: usize, + color_bits: usize, + alpha_bits: usize, + endpoint_pbits: usize, + shared_pbits: usize, + index_bits: [usize; 2], +} + +static S_BP7_MODE_INFO: [Bc7ModeInfo; 8] = [ + // +---------------------------- num subsets + // | +------------------------- partition bits + // | | +---------------------- rotation bits + // | | | +------------------- index selection bits + // | | | | +---------------- color bits + // | | | | | +------------- alpha bits + // | | | | | | +---------- endpoint P-bits + // | | | | | | | +------- shared P-bits + // | | | | | | | | +-- 2x index bits + // { 3, 4, 0, 0, 4, 0, 1, 0, { 3, 0 } }, // 0 + // { 2, 6, 0, 0, 6, 0, 0, 1, { 3, 0 } }, // 1 + // { 3, 6, 0, 0, 5, 0, 0, 0, { 2, 0 } }, // 2 + // { 2, 6, 0, 0, 7, 0, 1, 0, { 2, 0 } }, // 3 + // { 1, 0, 2, 1, 5, 6, 0, 0, { 2, 3 } }, // 4 + // { 1, 0, 2, 0, 7, 8, 0, 0, { 2, 2 } }, // 5 + // { 1, 0, 0, 0, 7, 7, 1, 0, { 4, 0 } }, // 6 + // { 2, 6, 0, 0, 5, 5, 1, 0, { 2, 0 } }, // 7 + Bc7ModeInfo { + num_subsets: 3, + partition_bits: 4, + rotation_bits: 0, + index_selection_bits: 0, + color_bits: 4, + alpha_bits: 0, + endpoint_pbits: 1, + shared_pbits: 0, + index_bits: [3, 0], + }, + Bc7ModeInfo { + num_subsets: 2, + partition_bits: 6, + rotation_bits: 0, + index_selection_bits: 0, + color_bits: 6, + alpha_bits: 0, + endpoint_pbits: 0, + shared_pbits: 1, + index_bits: [3, 0], + }, + Bc7ModeInfo { + num_subsets: 3, + partition_bits: 6, + rotation_bits: 0, + index_selection_bits: 0, + color_bits: 5, + alpha_bits: 0, + endpoint_pbits: 0, + shared_pbits: 0, + index_bits: [2, 0], + }, + Bc7ModeInfo { + num_subsets: 2, + partition_bits: 6, + rotation_bits: 0, + index_selection_bits: 0, + color_bits: 7, + alpha_bits: 0, + endpoint_pbits: 1, + shared_pbits: 0, + index_bits: [2, 0], + }, + Bc7ModeInfo { + num_subsets: 1, + partition_bits: 0, + rotation_bits: 2, + index_selection_bits: 1, + color_bits: 5, + alpha_bits: 6, + endpoint_pbits: 0, + shared_pbits: 0, + index_bits: [2, 3], + }, + Bc7ModeInfo { + num_subsets: 1, + partition_bits: 0, + rotation_bits: 2, + index_selection_bits: 0, + color_bits: 7, + alpha_bits: 8, + endpoint_pbits: 0, + shared_pbits: 0, + index_bits: [2, 2], + }, + Bc7ModeInfo { + num_subsets: 1, + partition_bits: 0, + rotation_bits: 0, + index_selection_bits: 0, + color_bits: 7, + alpha_bits: 7, + endpoint_pbits: 1, + shared_pbits: 0, + index_bits: [4, 0], + }, + Bc7ModeInfo { + num_subsets: 2, + partition_bits: 6, + rotation_bits: 0, + index_selection_bits: 0, + color_bits: 5, + alpha_bits: 5, + endpoint_pbits: 1, + shared_pbits: 0, + index_bits: [2, 0], + }, +]; + +#[inline] +fn expand_quantized(v: u8, bits: usize) -> u8 { + let s = ((v as u16) << (8 - bits as u16)) as u8; + s | s.overflowing_shr(bits as u32).0 +} + +pub fn decode_bc7_block(data: &[u8], outbuf: &mut [u32]) { + let mut bit = BitReader::new(data, 0); + let mode = { + let mut mode = 0; + while 0 == bit.read(1) && mode < 8 { + mode += 1; + } + mode + }; + + if mode == 8 { + outbuf[0..16].fill(0); + return; + } + + let mi: &Bc7ModeInfo = &S_BP7_MODE_INFO[mode]; + let mode_pbits: usize = if 0 != mi.endpoint_pbits { + mi.endpoint_pbits + } else { + mi.shared_pbits + }; + + let partition_set_idx: usize = bit.read(mi.partition_bits) as usize; + let rotation_mode: u8 = bit.read(mi.rotation_bits) as u8; + let index_selection_mode: usize = bit.read(mi.index_selection_bits) as usize; + + let mut ep_r: [u8; 6] = [0; 6]; + let mut ep_g: [u8; 6] = [0; 6]; + let mut ep_b: [u8; 6] = [0; 6]; + let mut ep_a: [u8; 6] = [0; 6]; + + (0..mi.num_subsets).for_each(|ii| { + ep_r[ii * 2] = (bit.read(mi.color_bits) << mode_pbits) as u8; + ep_r[ii * 2 + 1] = (bit.read(mi.color_bits) << mode_pbits) as u8; + }); + + (0..mi.num_subsets).for_each(|ii| { + ep_g[ii * 2] = (bit.read(mi.color_bits) << mode_pbits) as u8; + ep_g[ii * 2 + 1] = (bit.read(mi.color_bits) << mode_pbits) as u8; + }); + + (0..mi.num_subsets).for_each(|ii| { + ep_b[ii * 2] = (bit.read(mi.color_bits) << mode_pbits) as u8; + ep_b[ii * 2 + 1] = (bit.read(mi.color_bits) << mode_pbits) as u8; + }); + + if mi.alpha_bits > 0 { + (0..mi.num_subsets).for_each(|ii| { + ep_a[ii * 2] = (bit.read(mi.alpha_bits) << mode_pbits) as u8; + ep_a[ii * 2 + 1] = (bit.read(mi.alpha_bits) << mode_pbits) as u8; + }); + } else { + ep_a = [0xff; 6]; + } + + if 0 != mode_pbits { + (0..mi.num_subsets).for_each(|ii| { + let pda: u8 = bit.read(mode_pbits) as u8; + let pdb: u8 = if 0 == mi.shared_pbits { + bit.read(mode_pbits) as u8 + } else { + pda + }; + + ep_r[ii * 2] |= pda; + ep_r[ii * 2 + 1] |= pdb; + ep_g[ii * 2] |= pda; + ep_g[ii * 2 + 1] |= pdb; + ep_b[ii * 2] |= pda; + ep_b[ii * 2 + 1] |= pdb; + ep_a[ii * 2] |= pda; + ep_a[ii * 2 + 1] |= pdb; + }); + } + + let color_bits: usize = mi.color_bits + mode_pbits; + + (0..mi.num_subsets).for_each(|ii| { + ep_r[ii * 2] = expand_quantized(ep_r[ii * 2], color_bits); + ep_r[ii * 2 + 1] = expand_quantized(ep_r[ii * 2 + 1], color_bits); + ep_g[ii * 2] = expand_quantized(ep_g[ii * 2], color_bits); + ep_g[ii * 2 + 1] = expand_quantized(ep_g[ii * 2 + 1], color_bits); + ep_b[ii * 2] = expand_quantized(ep_b[ii * 2], color_bits); + ep_b[ii * 2 + 1] = expand_quantized(ep_b[ii * 2 + 1], color_bits); + }); + + if mi.alpha_bits > 0 { + let alpha_bits = mi.alpha_bits + mode_pbits; + + (0..mi.num_subsets).for_each(|ii| { + ep_a[ii * 2] = expand_quantized(ep_a[ii * 2], alpha_bits); + ep_a[ii * 2 + 1] = expand_quantized(ep_a[ii * 2 + 1], alpha_bits); + }); + } + + let has_index_bits1: bool = 0 != mi.index_bits[1]; + + let factors: [[u8; 16]; 2] = [ + S_BPTC_FACTORS[mi.index_bits[0] - 2], + if has_index_bits1 { + S_BPTC_FACTORS[mi.index_bits[1] - 2] + } else { + S_BPTC_FACTORS[mi.index_bits[0] - 2] + }, + ]; + + let mut offset: [usize; 2] = [0, mi.num_subsets * (16 * mi.index_bits[0] - 1)]; + + (0..4_usize).for_each(|yy| { + (0..4_usize).for_each(|xx| { + let idx = yy * 4 + xx; + + let mut subset_index: usize = 0; + let mut index_anchor: usize = 0; + match mi.num_subsets { + 2 => { + subset_index = (S_BPTC_P2[partition_set_idx] >> idx) & 1; + index_anchor = if 0 != subset_index { + S_BPTC_A2[partition_set_idx] + } else { + 0 + }; + } + 3 => { + subset_index = (S_BPTC_P3[partition_set_idx] >> (2 * idx)) & 3; + index_anchor = if 0 != subset_index { + S_BPTC_A3[subset_index - 1][partition_set_idx] + } else { + 0 + }; + } + _ => {} + } + + let anchor = idx == index_anchor; + let num: [usize; 2] = [ + (mi.index_bits[0] - anchor as usize), + if has_index_bits1 { + mi.index_bits[1] - anchor as usize + } else { + 0 + }, + ]; + + let index: [usize; 2] = { + let index_0 = bit.peek(offset[0], num[0]) as usize; + [ + index_0, + if has_index_bits1 { + bit.peek(offset[1], num[1]) as usize + } else { + index_0 + }, + ] + }; + + offset[0] += num[0]; + offset[1] += num[1]; + + // index selection mode 0 or 1 + // !index_selection_mode == 1-index_selection_mode + let fc: u16 = factors[index_selection_mode][index[index_selection_mode]] as u16; + let fa: u16 = factors[1 - index_selection_mode][index[1 - index_selection_mode]] as u16; + + let fca: u16 = 64 - fc; + let fcb: u16 = fc; + let faa: u16 = 64 - fa; + let fab: u16 = fa; + + subset_index *= 2; + let mut rr: u8 = + ((ep_r[subset_index] as u16 * fca + ep_r[subset_index + 1] as u16 * fcb + 32) >> 6) + as u8; + let mut gg: u8 = + ((ep_g[subset_index] as u16 * fca + ep_g[subset_index + 1] as u16 * fcb + 32) >> 6) + as u8; + let mut bb: u8 = + ((ep_b[subset_index] as u16 * fca + ep_b[subset_index + 1] as u16 * fcb + 32) >> 6) + as u8; + let mut aa: u8 = + ((ep_a[subset_index] as u16 * faa + ep_a[subset_index + 1] as u16 * fab + 32) >> 6) + as u8; + + match rotation_mode { + 1 => { + swap(&mut aa, &mut rr); + } + 2 => { + swap(&mut aa, &mut gg); + } + 3 => { + swap(&mut aa, &mut bb); + } + _ => {} + } + outbuf[idx] = color(rr, gg, bb, aa); + }); + }); +} diff --git a/src/bcn/bitreader.rs b/src/bcn/bitreader.rs new file mode 100644 index 0000000..1685581 --- /dev/null +++ b/src/bcn/bitreader.rs @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2023 Rudolf Kolbe +// SPDX-License-Identifier: MIT + +#[inline] +fn getbits_raw(buf: &[u8], bit_offset: usize, num_bits: usize, dst: &mut [u8]) { + let bytes_offset = bit_offset / 8; + let bytes_end: usize = (bit_offset + num_bits).div_ceil(8); + dst[0..(bytes_end - bytes_offset)].copy_from_slice(&buf[bytes_offset..bytes_end]); +} + +#[inline] +pub fn getbits(buf: &[u8], bit_offset: usize, num_bits: usize) -> i32 { + let shift = bit_offset % 8; + + let mut raw = [0u8; 4]; + getbits_raw(buf, bit_offset, num_bits, &mut raw); + + // shift the bits we don't need out + i32::from_le_bytes(raw) >> shift & ((1 << num_bits) - 1) +} + +#[inline] +pub fn getbits64(buf: &[u8], bit: isize, len: usize) -> u64 { + let mask: u64 = if len == 64 { + 0xffffffffffffffff + } else { + (1 << len) - 1 + }; + if len == 0 { + 0 + } else if bit >= 64 { + u64::from_le_bytes(buf[8..16].try_into().unwrap()) >> (bit - 64) & mask + } else if bit <= 0 { + u64::from_le_bytes(buf[..8].try_into().unwrap()) << 0u64.overflowing_sub(bit as u64).0 + & mask + } else if bit as usize + len <= 64 { + u64::from_le_bytes(buf[..8].try_into().unwrap()) >> bit & mask + } else { + u64::from_le_bytes(buf[..8].try_into().unwrap()) >> bit + | u64::from_le_bytes(buf[8..16].try_into().unwrap()) << (64 - bit) & mask + } +} + +pub struct BitReader<'a> { + data: &'a [u8], + bit_pos: usize, +} + +impl BitReader<'_> { + #[inline] + pub const fn new(data: &[u8], bit_pos: usize) -> BitReader { + BitReader { data, bit_pos } + } + + #[inline] + pub fn read(&mut self, num_bits: usize) -> u16 { + let ret = self.peek(0, num_bits); + self.bit_pos += num_bits; + ret + } + + #[inline] + pub fn peek(&self, offset: usize, num_bits: usize) -> u16 { + let bit_pos = self.bit_pos + offset; + let shift = bit_pos & 7; + + let mut raw = [0u8; 4]; + getbits_raw(self.data, bit_pos, num_bits, &mut raw); + let data: u32 = u32::from_le_bytes(raw); + + (data >> shift as u32) as u16 & ((1 << num_bits as u16) - 1) + } +} diff --git a/src/bcn/consts.rs b/src/bcn/consts.rs new file mode 100644 index 0000000..6d3c0ca --- /dev/null +++ b/src/bcn/consts.rs @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText: 2023 Rudolf Kolbe +// SPDX-License-Identifier: MIT + +// BC 6 & 7 +pub(crate) static S_BPTC_A2: [usize; 64] = [ + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 8, 2, 2, 8, 8, 15, 2, 8, + 2, 2, 8, 8, 2, 2, 15, 15, 6, 8, 2, 8, 15, 15, 2, 8, 2, 2, 2, 15, 15, 6, 6, 2, 6, 8, 15, 15, 2, + 2, 15, 15, 15, 15, 15, 2, 2, 15, +]; + +// BC7 +pub(crate) static S_BPTC_A3: [[usize; 64]; 2] = [ + [ + 3, 3, 15, 15, 8, 3, 15, 15, 8, 8, 6, 6, 6, 5, 3, 3, 3, 3, 8, 15, 3, 3, 6, 10, 5, 8, 8, 6, + 8, 5, 15, 15, 8, 15, 3, 5, 6, 10, 8, 15, 15, 3, 15, 5, 15, 15, 15, 15, 3, 15, 5, 5, 5, 8, + 5, 10, 5, 10, 8, 13, 15, 12, 3, 3, + ], + [ + 15, 8, 8, 3, 15, 15, 3, 8, 15, 15, 15, 15, 15, 15, 15, 8, 15, 8, 15, 3, 15, 8, 15, 8, 3, + 15, 6, 10, 15, 15, 10, 8, 15, 3, 15, 10, 10, 8, 9, 10, 6, 15, 8, 15, 3, 6, 6, 8, 15, 3, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 3, 15, 15, 8, + ], +]; + +// BC 6 & 7 +pub(crate) static S_BPTC_FACTORS: [[u8; 16]; 3] = [ + [0, 21, 43, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 9, 18, 27, 37, 46, 55, 64, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64], +]; + +// BC 6 & 7 +pub(crate) static S_BPTC_P2: [usize; 64] = [ + // 3210 0000000000 1111111111 2222222222 3333333333 + 0xcccc, // 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 + 0x8888, // 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 + 0xeeee, // 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 + 0xecc8, // 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1 + 0xc880, // 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1 + 0xfeec, // 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 + 0xfec8, // 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 + 0xec80, // 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1 + 0xc800, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1 + 0xffec, // 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + 0xfe80, // 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1 + 0xe800, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1 + 0xffe8, // 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + 0xff00, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 + 0xfff0, // 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + 0xf000, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 + 0xf710, // 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1 + 0x008e, // 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 + 0x7100, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0 + 0x08ce, // 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0 + 0x008c, // 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 + 0x7310, // 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0 + 0x3100, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 + 0x8cce, // 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1 + 0x088c, // 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 + 0x3110, // 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 + 0x6666, // 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0 + 0x366c, // 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0 + 0x17e8, // 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0 + 0x0ff0, // 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 + 0x718e, // 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0 + 0x399c, // 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0 + 0xaaaa, // 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 + 0xf0f0, // 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 + 0x5a5a, // 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0 + 0x33cc, // 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0 + 0x3c3c, // 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0 + 0x55aa, // 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0 + 0x9696, // 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1 + 0xa55a, // 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1 + 0x73ce, // 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0 + 0x13c8, // 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 + 0x324c, // 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0 + 0x3bdc, // 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 + 0x6996, // 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 + 0xc33c, // 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1 + 0x9966, // 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1 + 0x0660, // 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 + 0x0272, // 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 + 0x04e4, // 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 + 0x4e40, // 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0 + 0x2720, // 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0 + 0xc936, // 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1 + 0x936c, // 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1 + 0x39c6, // 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0 + 0x639c, // 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0 + 0x9336, // 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1 + 0x9cc6, // 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1 + 0x817e, // 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1 + 0xe718, // 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1 + 0xccf0, // 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 + 0x0fcc, // 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 + 0x7744, // 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0 + 0xee22, // 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1 +]; + +// BC7 +pub(crate) static S_BPTC_P3: [usize; 64] = [ + // 76543210 0000 1111 2222 3333 4444 5555 6666 7777 + 0xaa685050, // 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 1, 2, 2, 2, 2 + 0x6a5a5040, // 0, 0, 0, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1 + 0x5a5a4200, // 0, 0, 0, 0, 2, 0, 0, 1, 2, 2, 1, 1, 2, 2, 1, 1 + 0x5450a0a8, // 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 1, 0, 1, 1, 1 + 0xa5a50000, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2 + 0xa0a05050, // 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 2, 2 + 0x5555a0a0, // 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 + 0x5a5a5050, // 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1 + 0xaa550000, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2 + 0xaa555500, // 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2 + 0xaaaa5500, // 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2 + 0x90909090, // 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2 + 0x94949494, // 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2 + 0xa4a4a4a4, // 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2 + 0xa9a59450, // 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2 + 0x2a0a4250, // 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0, 2, 2, 2, 0 + 0xa5945040, // 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2 + 0x0a425054, // 0, 1, 1, 1, 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0 + 0xa5a5a500, // 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2 + 0x55a0a0a0, // 0, 0, 2, 2, 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1 + 0xa8a85454, // 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2 + 0x6a6a4040, // 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 2, 1 + 0xa4a45000, // 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2 + 0x1a1a0500, // 0, 0, 0, 0, 1, 1, 0, 0, 2, 2, 1, 0, 2, 2, 1, 0 + 0x0050a4a4, // 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1, 0, 0, 0, 0 + 0xaaa59090, // 0, 0, 1, 2, 0, 0, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2 + 0x14696914, // 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1, 0, 1, 1, 0 + 0x69691400, // 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1 + 0xa08585a0, // 0, 0, 2, 2, 1, 1, 0, 2, 1, 1, 0, 2, 0, 0, 2, 2 + 0xaa821414, // 0, 1, 1, 0, 0, 1, 1, 0, 2, 0, 0, 2, 2, 2, 2, 2 + 0x50a4a450, // 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1 + 0x6a5a0200, // 0, 0, 0, 0, 2, 0, 0, 0, 2, 2, 1, 1, 2, 2, 2, 1 + 0xa9a58000, // 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 2, 2, 2 + 0x5090a0a8, // 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 2, 0, 0, 1, 1 + 0xa8a09050, // 0, 0, 1, 1, 0, 0, 1, 2, 0, 0, 2, 2, 0, 2, 2, 2 + 0x24242424, // 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0 + 0x00aa5500, // 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0 + 0x24924924, // 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0 + 0x24499224, // 0, 1, 2, 0, 2, 0, 1, 2, 1, 2, 0, 1, 0, 1, 2, 0 + 0x50a50a50, // 0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2, 0, 0, 1, 1 + 0x500aa550, // 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1 + 0xaaaa4444, // 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2 + 0x66660000, // 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1 + 0xa5a0a5a0, // 0, 0, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2, 1, 1, 2, 2 + 0x50a050a0, // 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1 + 0x69286928, // 0, 2, 2, 0, 1, 2, 2, 1, 0, 2, 2, 0, 1, 2, 2, 1 + 0x44aaaa44, // 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 1 + 0x66666600, // 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1 + 0xaa444444, // 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2 + 0x54a854a8, // 0, 2, 2, 2, 0, 1, 1, 1, 0, 2, 2, 2, 0, 1, 1, 1 + 0x95809580, // 0, 0, 0, 2, 1, 1, 1, 2, 0, 0, 0, 2, 1, 1, 1, 2 + 0x96969600, // 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2 + 0xa85454a8, // 0, 2, 2, 2, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2 + 0x80959580, // 0, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2 + 0xaa141414, // 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2 + 0x96960000, // 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2 + 0xaaaa1414, // 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2 + 0xa05050a0, // 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2 + 0xa0a5a5a0, // 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2 + 0x96000000, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2 + 0x40804080, // 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1 + 0xa9a8a9a8, // 0, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2 + 0xaaaaaa44, // 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 + 0x2a4a5254, // 0, 1, 1, 1, 2, 0, 1, 1, 2, 2, 0, 1, 2, 2, 2, 0 +]; diff --git a/src/bcn/mod.rs b/src/bcn/mod.rs index 811cd01..5ddab47 100644 --- a/src/bcn/mod.rs +++ b/src/bcn/mod.rs @@ -6,13 +6,18 @@ extern crate alloc; mod bc1; mod bc3; mod bc5; +mod bc7; +mod bitreader; mod color; +mod consts; mod macros; pub use bc1::decode_bc1_block; pub use bc3::decode_bc3_block; pub use bc5::decode_bc5_block; +pub use bc7::decode_bc7_block; macros::block_decoder!(decode_bc1, 4, 4, 8, decode_bc1_block); macros::block_decoder!(decode_bc3, 4, 4, 16, decode_bc3_block); macros::block_decoder!(decode_bc5, 4, 4, 16, decode_bc5_block); +macros::block_decoder!(decode_bc7, 4, 4, 16, decode_bc7_block); diff --git a/src/tex.rs b/src/tex.rs index 546171d..b4e448c 100644 --- a/src/tex.rs +++ b/src/tex.rs @@ -9,6 +9,7 @@ use crate::ByteSpan; use crate::bcn::decode_bc1; use crate::bcn::decode_bc3; use crate::bcn::decode_bc5; +use crate::bcn::decode_bc7; use binrw::BinRead; use binrw::binrw; use bitflags::bitflags; @@ -39,6 +40,7 @@ bitflags! { 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; @@ -47,15 +49,45 @@ bitflags! { } } +// From https://github.com/aers/FFXIVClientStructs/blob/344f5d488197e9c9d5fd78e92439e7104f25e2e0/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Texture.cs#L97 #[binrw] #[brw(repr = u32)] #[derive(Debug)] enum TextureFormat { - B4G4R4A4 = 0x1440, - B8G8R8A8 = 0x1450, - BC1 = 0x3420, - BC3 = 0x3431, - BC5 = 0x6230, + 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] @@ -101,7 +133,7 @@ impl Texture { /// Reads an existing TEX file pub fn from_existing(buffer: ByteSpan) -> Option { let mut cursor = Cursor::new(buffer); - let header = TexHeader::read(&mut cursor).ok()?; + let header = TexHeader::read(&mut cursor).unwrap(); cursor .seek(SeekFrom::Start(std::mem::size_of::() as u64)) @@ -113,7 +145,7 @@ impl Texture { let mut dst: Vec; match header.format { - TextureFormat::B4G4R4A4 => { + TextureFormat::B4G4R4A4_UNORM => { dst = vec![ 0u8; @@ -140,7 +172,7 @@ impl Texture { dst_offset += 4; } } - TextureFormat::B8G8R8A8 => { + TextureFormat::B8G8R8A8_UNORM => { dst = vec![ 0u8; @@ -163,7 +195,7 @@ impl Texture { offset += 4; } } - TextureFormat::BC1 => { + TextureFormat::BC1_UNORM => { dst = Texture::decode( &src, header.width as usize, @@ -171,7 +203,7 @@ impl Texture { decode_bc1, ); } - TextureFormat::BC3 => { + TextureFormat::BC3_UNORM => { dst = Texture::decode( &src, header.width as usize, @@ -179,7 +211,7 @@ impl Texture { decode_bc3, ); } - TextureFormat::BC5 => { + TextureFormat::BC5_UNORM => { dst = Texture::decode( &src, header.width as usize, @@ -187,6 +219,15 @@ impl Texture { decode_bc5, ); } + TextureFormat::BC7_UNORM => { + dst = Texture::decode( + &src, + header.width as usize, + header.height as usize * header.depth as usize, + decode_bc7, + ); + } + _ => panic!("Unsupported texture format {:?}!", header.format), } Some(Texture {