1#![allow(clippy::needless_range_loop)]
5
6use std::io::{Cursor, Read, Seek, SeekFrom};
7
8use crate::ByteSpan;
9use crate::bcn::decode_bc1;
10use crate::bcn::decode_bc3;
11use crate::bcn::decode_bc5;
12use crate::bcn::decode_bc7;
13use binrw::BinRead;
14use binrw::binrw;
15use bitflags::bitflags;
16
17#[binrw]
18#[derive(Debug)]
19struct TextureAttribute(u32);
20
21bitflags! {
23 impl TextureAttribute : u32 {
24 const DISCARD_PER_FRAME = 0x1;
25 const DISCARD_PER_MAP = 0x2;
26
27 const MANAGED = 0x4;
28 const USER_MANAGED = 0x8;
29 const CPU_READ = 0x10;
30 const LOCATION_MAIN = 0x20;
31 const NO_GPU_READ = 0x40;
32 const ALIGNED_SIZE = 0x80;
33 const EDGE_CULLING = 0x100;
34 const LOCATION_ONION = 0x200;
35 const READ_WRITE = 0x400;
36 const IMMUTABLE = 0x800;
37
38 const TEXTURE_RENDER_TARGET = 0x100000;
39 const TEXTURE_DEPTH_STENCIL = 0x200000;
40 const TEXTURE_TYPE1_D = 0x400000;
41 const TEXTURE_TYPE2_D = 0x800000;
42 const TEXTURE_TYPE3_D = 0x1000000;
43 const TEXTURE_TYPE2_D_ARRAY = 0x10000000;
44 const TEXTURE_TYPE_CUBE = 0x2000000;
45 const TEXTURE_TYPE_MASK = 0x3C00000;
46 const TEXTURE_SWIZZLE = 0x4000000;
47 const TEXTURE_NO_TILED = 0x8000000;
48 const TEXTURE_NO_SWIZZLE = 0x80000000;
49 }
50}
51
52#[binrw]
54#[brw(repr = u32)]
55#[derive(Debug)]
56#[allow(non_camel_case_types)] enum TextureFormat {
58 L8_UNORM = 0x1130,
59 A8_UNORM = 0x1131,
60 R8_UNORM = 0x1132,
61 R8_UINT = 0x1133,
62 R16_UINT = 0x1140,
63 R32_UINT = 0x1150,
64 R8G8_UNORM = 0x1240,
65 B4G4R4A4_UNORM = 0x1440,
66 B5G5R5A1_UNORM = 0x1441,
67 B8G8R8A8_UNORM = 0x1450,
68 B8G8R8X8_UNORM = 0x1451,
69 R16_FLOAT = 0x2140,
70 R32_FLOAT = 0x2150,
71 R16G16_FLOAT = 0x2250,
72 R32G32_FLOAT = 0x2260,
73 R11G11B10_FLOAT = 0x2350,
74 R16G16B16A16_FLOAT = 0x2460,
75 R32G32B32A32_FLOAT = 0x2470,
76 BC1_UNORM = 0x3420,
77 BC2_UNORM = 0x3430,
78 BC3_UNORM = 0x3431,
79 D16_UNORM = 0x4140,
80 D24_UNORM_S8_UINT = 0x4250,
81 D16_UNORM_2 = 0x5140,
82 D24_UNORM_S8_UINT_2 = 0x5150,
83 BC4_UNORM = 0x6120,
84 BC5_UNORM = 0x6230,
85 BC6H_SF16 = 0x6330,
86 BC7_UNORM = 0x6432,
87 R16_UNORM = 0x7140,
88 R16G16_UNORM = 0x7250,
89 R10G10B10A2_UNORM_2 = 0x7350,
90 R10G10B10A2_UNORM = 0x7450,
91 D24_UNORM_S8_UINT_3 = 0x8250,
92}
93
94#[binrw]
95#[derive(Debug)]
96#[allow(dead_code)]
97#[brw(little)]
98struct TexHeader {
99 attribute: TextureAttribute,
100 format: TextureFormat,
101
102 width: u16,
103 height: u16,
104 depth: u16,
105 mip_levels: u16,
106
107 lod_offsets: [u32; 3],
108 offset_to_surface: [u32; 13],
109}
110
111#[repr(C)]
112#[derive(Clone, Copy)]
113pub enum TextureType {
114 TwoDimensional,
115 ThreeDimensional,
116}
117
118pub struct Texture {
119 pub texture_type: TextureType,
121 pub width: u32,
123 pub height: u32,
125 pub depth: u32,
127 pub rgba: Vec<u8>,
129}
130
131type DecodeFunction = fn(&[u8], usize, usize, &mut [u32]) -> Result<(), &'static str>;
132
133impl Texture {
134 pub fn from_existing(buffer: ByteSpan) -> Option<Texture> {
136 let mut cursor = Cursor::new(buffer);
137 let header = TexHeader::read(&mut cursor).ok()?;
138
139 cursor
140 .seek(SeekFrom::Start(std::mem::size_of::<TexHeader>() as u64))
141 .ok()?;
142
143 let mut src = vec![0u8; buffer.len() - std::mem::size_of::<TexHeader>()];
144 cursor.read_exact(src.as_mut_slice()).ok()?;
145
146 let mut dst: Vec<u8>;
147
148 match header.format {
149 TextureFormat::B4G4R4A4_UNORM => {
150 dst =
151 vec![
152 0u8;
153 header.width as usize * header.height as usize * header.depth as usize * 4
154 ];
155
156 let mut offset = 0;
157 let mut dst_offset = 0;
158
159 for _ in 0..header.width as usize * header.height as usize {
160 let short: u16 = ((src[offset] as u16) << 8) | src[offset + 1] as u16;
161
162 let src_b = short & 0xF;
163 let src_g = (short >> 4) & 0xF;
164 let src_r = (short >> 8) & 0xF;
165 let src_a = (short >> 12) & 0xF;
166
167 dst[dst_offset] = (17 * src_r) as u8;
168 dst[dst_offset + 1] = (17 * src_g) as u8;
169 dst[dst_offset + 2] = (17 * src_b) as u8;
170 dst[dst_offset + 3] = (17 * src_a) as u8;
171
172 offset += 2;
173 dst_offset += 4;
174 }
175 }
176 TextureFormat::B8G8R8A8_UNORM => {
177 dst =
178 vec![
179 0u8;
180 header.width as usize * header.height as usize * header.depth as usize * 4
181 ];
182
183 let mut offset = 0;
184
185 for _ in 0..header.width as usize * header.height as usize * header.depth as usize {
186 let src_b = src[offset];
187 let src_g = src[offset + 1];
188 let src_r = src[offset + 2];
189 let src_a = src[offset + 3];
190
191 dst[offset] = src_r;
192 dst[offset + 1] = src_g;
193 dst[offset + 2] = src_b;
194 dst[offset + 3] = src_a;
195
196 offset += 4;
197 }
198 }
199 TextureFormat::BC1_UNORM => {
200 dst = Texture::decode(
201 &src,
202 header.width as usize,
203 header.height as usize * header.depth as usize,
204 decode_bc1,
205 );
206 }
207 TextureFormat::BC3_UNORM => {
208 dst = Texture::decode(
209 &src,
210 header.width as usize,
211 header.height as usize * header.depth as usize,
212 decode_bc3,
213 );
214 }
215 TextureFormat::BC5_UNORM => {
216 dst = Texture::decode(
217 &src,
218 header.width as usize,
219 header.height as usize * header.depth as usize,
220 decode_bc5,
221 );
222 }
223 TextureFormat::BC7_UNORM => {
224 dst = Texture::decode(
225 &src,
226 header.width as usize,
227 header.height as usize * header.depth as usize,
228 decode_bc7,
229 );
230 }
231 _ => panic!("Unsupported texture format {:?}!", header.format),
232 }
233
234 Some(Texture {
235 texture_type: if header.attribute.contains(TextureAttribute::TEXTURE_TYPE3_D) {
236 TextureType::ThreeDimensional
237 } else {
238 TextureType::TwoDimensional
239 },
240 width: header.width as u32,
241 height: header.height as u32,
242 depth: header.depth as u32,
243 rgba: dst,
244 })
245 }
246
247 fn decode(src: &[u8], width: usize, height: usize, decode_func: DecodeFunction) -> Vec<u8> {
248 let mut image: Vec<u32> = vec![0; width * height];
249 decode_func(src, width, height, &mut image).unwrap();
250
251 image
252 .iter()
253 .flat_map(|x| {
254 let v = x.to_le_bytes();
255 [v[2], v[1], v[0], v[3]]
256 })
257 .collect::<Vec<u8>>()
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use std::fs::read;
264 use std::path::PathBuf;
265
266 use super::*;
267
268 #[test]
269 fn test_invalid() {
270 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
271 d.push("resources/tests");
272 d.push("random");
273
274 Texture::from_existing(&read(d).unwrap());
276 }
277}