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 binrw::BinRead;
13use binrw::binrw;
14use bitflags::bitflags;
15
16#[binrw]
17#[derive(Debug)]
18struct TextureAttribute(u32);
19
20bitflags! {
22 impl TextureAttribute : u32 {
23 const DISCARD_PER_FRAME = 0x1;
24 const DISCARD_PER_MAP = 0x2;
25
26 const MANAGED = 0x4;
27 const USER_MANAGED = 0x8;
28 const CPU_READ = 0x10;
29 const LOCATION_MAIN = 0x20;
30 const NO_GPU_READ = 0x40;
31 const ALIGNED_SIZE = 0x80;
32 const EDGE_CULLING = 0x100;
33 const LOCATION_ONION = 0x200;
34 const READ_WRITE = 0x400;
35 const IMMUTABLE = 0x800;
36
37 const TEXTURE_RENDER_TARGET = 0x100000;
38 const TEXTURE_DEPTH_STENCIL = 0x200000;
39 const TEXTURE_TYPE1_D = 0x400000;
40 const TEXTURE_TYPE2_D = 0x800000;
41 const TEXTURE_TYPE3_D = 0x1000000;
42 const TEXTURE_TYPE_CUBE = 0x2000000;
43 const TEXTURE_TYPE_MASK = 0x3C00000;
44 const TEXTURE_SWIZZLE = 0x4000000;
45 const TEXTURE_NO_TILED = 0x8000000;
46 const TEXTURE_NO_SWIZZLE = 0x80000000;
47 }
48}
49
50#[binrw]
51#[brw(repr = u32)]
52#[derive(Debug)]
53enum TextureFormat {
54 B4G4R4A4 = 0x1440,
55 B8G8R8A8 = 0x1450,
56 BC1 = 0x3420,
57 BC3 = 0x3431,
58 BC5 = 0x6230,
59}
60
61#[binrw]
62#[derive(Debug)]
63#[allow(dead_code)]
64#[brw(little)]
65struct TexHeader {
66 attribute: TextureAttribute,
67 format: TextureFormat,
68
69 width: u16,
70 height: u16,
71 depth: u16,
72 mip_levels: u16,
73
74 lod_offsets: [u32; 3],
75 offset_to_surface: [u32; 13],
76}
77
78#[repr(C)]
79#[derive(Clone, Copy)]
80pub enum TextureType {
81 TwoDimensional,
82 ThreeDimensional,
83}
84
85pub struct Texture {
86 pub texture_type: TextureType,
88 pub width: u32,
90 pub height: u32,
92 pub depth: u32,
94 pub rgba: Vec<u8>,
96}
97
98type DecodeFunction = fn(&[u8], usize, usize, &mut [u32]) -> Result<(), &'static str>;
99
100impl Texture {
101 pub fn from_existing(buffer: ByteSpan) -> Option<Texture> {
103 let mut cursor = Cursor::new(buffer);
104 let header = TexHeader::read(&mut cursor).ok()?;
105
106 cursor
107 .seek(SeekFrom::Start(std::mem::size_of::<TexHeader>() as u64))
108 .ok()?;
109
110 let mut src = vec![0u8; buffer.len() - std::mem::size_of::<TexHeader>()];
111 cursor.read_exact(src.as_mut_slice()).ok()?;
112
113 let mut dst: Vec<u8>;
114
115 match header.format {
116 TextureFormat::B4G4R4A4 => {
117 dst =
118 vec![
119 0u8;
120 header.width as usize * header.height as usize * header.depth as usize * 4
121 ];
122
123 let mut offset = 0;
124 let mut dst_offset = 0;
125
126 for _ in 0..header.width as usize * header.height as usize {
127 let short: u16 = ((src[offset] as u16) << 8) | src[offset + 1] as u16;
128
129 let src_b = short & 0xF;
130 let src_g = (short >> 4) & 0xF;
131 let src_r = (short >> 8) & 0xF;
132 let src_a = (short >> 12) & 0xF;
133
134 dst[dst_offset] = (17 * src_r) as u8;
135 dst[dst_offset + 1] = (17 * src_g) as u8;
136 dst[dst_offset + 2] = (17 * src_b) as u8;
137 dst[dst_offset + 3] = (17 * src_a) as u8;
138
139 offset += 2;
140 dst_offset += 4;
141 }
142 }
143 TextureFormat::B8G8R8A8 => {
144 dst =
145 vec![
146 0u8;
147 header.width as usize * header.height as usize * header.depth as usize * 4
148 ];
149
150 let mut offset = 0;
151
152 for _ in 0..header.width as usize * header.height as usize * header.depth as usize {
153 let src_b = src[offset];
154 let src_g = src[offset + 1];
155 let src_r = src[offset + 2];
156 let src_a = src[offset + 3];
157
158 dst[offset] = src_r;
159 dst[offset + 1] = src_g;
160 dst[offset + 2] = src_b;
161 dst[offset + 3] = src_a;
162
163 offset += 4;
164 }
165 }
166 TextureFormat::BC1 => {
167 dst = Texture::decode(
168 &src,
169 header.width as usize,
170 header.height as usize * header.depth as usize,
171 decode_bc1,
172 );
173 }
174 TextureFormat::BC3 => {
175 dst = Texture::decode(
176 &src,
177 header.width as usize,
178 header.height as usize * header.depth as usize,
179 decode_bc3,
180 );
181 }
182 TextureFormat::BC5 => {
183 dst = Texture::decode(
184 &src,
185 header.width as usize,
186 header.height as usize * header.depth as usize,
187 decode_bc5,
188 );
189 }
190 }
191
192 Some(Texture {
193 texture_type: if header.attribute.contains(TextureAttribute::TEXTURE_TYPE3_D) {
194 TextureType::ThreeDimensional
195 } else {
196 TextureType::TwoDimensional
197 },
198 width: header.width as u32,
199 height: header.height as u32,
200 depth: header.depth as u32,
201 rgba: dst,
202 })
203 }
204
205 fn decode(src: &[u8], width: usize, height: usize, decode_func: DecodeFunction) -> Vec<u8> {
206 let mut image: Vec<u32> = vec![0; width * height];
207 decode_func(src, width, height, &mut image).unwrap();
208
209 image
210 .iter()
211 .flat_map(|x| {
212 let v = x.to_le_bytes();
213 [v[2], v[1], v[0], v[3]]
214 })
215 .collect::<Vec<u8>>()
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use std::fs::read;
222 use std::path::PathBuf;
223
224 use super::*;
225
226 #[test]
227 fn test_invalid() {
228 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
229 d.push("resources/tests");
230 d.push("random");
231
232 Texture::from_existing(&read(d).unwrap());
234 }
235}