1
Fork 0
mirror of https://github.com/redstrate/Physis.git synced 2025-05-18 16:07:46 +00:00

Add a more exhaustive list of texture formats, support BC7 textures

As seen in certain bgpart model textures.
This commit is contained in:
Joshua Goins 2025-05-17 12:32:23 -04:00
parent a905234a18
commit e2a8c695d7
5 changed files with 631 additions and 11 deletions

333
src/bcn/bc7.rs Normal file
View file

@ -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);
});
});
}

73
src/bcn/bitreader.rs Normal file
View file

@ -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)
}
}

168
src/bcn/consts.rs Normal file
View file

@ -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
];

View file

@ -6,13 +6,18 @@ extern crate alloc;
mod bc1; mod bc1;
mod bc3; mod bc3;
mod bc5; mod bc5;
mod bc7;
mod bitreader;
mod color; mod color;
mod consts;
mod macros; mod macros;
pub use bc1::decode_bc1_block; pub use bc1::decode_bc1_block;
pub use bc3::decode_bc3_block; pub use bc3::decode_bc3_block;
pub use bc5::decode_bc5_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_bc1, 4, 4, 8, decode_bc1_block);
macros::block_decoder!(decode_bc3, 4, 4, 16, decode_bc3_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_bc5, 4, 4, 16, decode_bc5_block);
macros::block_decoder!(decode_bc7, 4, 4, 16, decode_bc7_block);

View file

@ -9,6 +9,7 @@ use crate::ByteSpan;
use crate::bcn::decode_bc1; use crate::bcn::decode_bc1;
use crate::bcn::decode_bc3; use crate::bcn::decode_bc3;
use crate::bcn::decode_bc5; use crate::bcn::decode_bc5;
use crate::bcn::decode_bc7;
use binrw::BinRead; use binrw::BinRead;
use binrw::binrw; use binrw::binrw;
use bitflags::bitflags; use bitflags::bitflags;
@ -39,6 +40,7 @@ bitflags! {
const TEXTURE_TYPE1_D = 0x400000; const TEXTURE_TYPE1_D = 0x400000;
const TEXTURE_TYPE2_D = 0x800000; const TEXTURE_TYPE2_D = 0x800000;
const TEXTURE_TYPE3_D = 0x1000000; const TEXTURE_TYPE3_D = 0x1000000;
const TEXTURE_TYPE2_D_ARRAY = 0x10000000;
const TEXTURE_TYPE_CUBE = 0x2000000; const TEXTURE_TYPE_CUBE = 0x2000000;
const TEXTURE_TYPE_MASK = 0x3C00000; const TEXTURE_TYPE_MASK = 0x3C00000;
const TEXTURE_SWIZZLE = 0x4000000; 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] #[binrw]
#[brw(repr = u32)] #[brw(repr = u32)]
#[derive(Debug)] #[derive(Debug)]
enum TextureFormat { enum TextureFormat {
B4G4R4A4 = 0x1440, L8_UNORM = 0x1130,
B8G8R8A8 = 0x1450, A8_UNORM = 0x1131,
BC1 = 0x3420, R8_UNORM = 0x1132,
BC3 = 0x3431, R8_UINT = 0x1133,
BC5 = 0x6230, 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] #[binrw]
@ -101,7 +133,7 @@ impl Texture {
/// Reads an existing TEX file /// Reads an existing TEX file
pub fn from_existing(buffer: ByteSpan) -> Option<Texture> { pub fn from_existing(buffer: ByteSpan) -> Option<Texture> {
let mut cursor = Cursor::new(buffer); let mut cursor = Cursor::new(buffer);
let header = TexHeader::read(&mut cursor).ok()?; let header = TexHeader::read(&mut cursor).unwrap();
cursor cursor
.seek(SeekFrom::Start(std::mem::size_of::<TexHeader>() as u64)) .seek(SeekFrom::Start(std::mem::size_of::<TexHeader>() as u64))
@ -113,7 +145,7 @@ impl Texture {
let mut dst: Vec<u8>; let mut dst: Vec<u8>;
match header.format { match header.format {
TextureFormat::B4G4R4A4 => { TextureFormat::B4G4R4A4_UNORM => {
dst = dst =
vec![ vec![
0u8; 0u8;
@ -140,7 +172,7 @@ impl Texture {
dst_offset += 4; dst_offset += 4;
} }
} }
TextureFormat::B8G8R8A8 => { TextureFormat::B8G8R8A8_UNORM => {
dst = dst =
vec![ vec![
0u8; 0u8;
@ -163,7 +195,7 @@ impl Texture {
offset += 4; offset += 4;
} }
} }
TextureFormat::BC1 => { TextureFormat::BC1_UNORM => {
dst = Texture::decode( dst = Texture::decode(
&src, &src,
header.width as usize, header.width as usize,
@ -171,7 +203,7 @@ impl Texture {
decode_bc1, decode_bc1,
); );
} }
TextureFormat::BC3 => { TextureFormat::BC3_UNORM => {
dst = Texture::decode( dst = Texture::decode(
&src, &src,
header.width as usize, header.width as usize,
@ -179,7 +211,7 @@ impl Texture {
decode_bc3, decode_bc3,
); );
} }
TextureFormat::BC5 => { TextureFormat::BC5_UNORM => {
dst = Texture::decode( dst = Texture::decode(
&src, &src,
header.width as usize, header.width as usize,
@ -187,6 +219,15 @@ impl Texture {
decode_bc5, 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 { Some(Texture {