texture2ddecoder/
unitycrunch.rs

1pub(crate) mod crn_decomp;
2pub(crate) mod crn_unpacker;
3use super::crnlib::{CrnFormat, CrnTextureInfo};
4use crate::bcn;
5use crate::{decode_etc1, decode_etc2_rgb, decode_etc2_rgba8};
6extern crate alloc;
7
8struct CrunchDecodeHandler {
9    format: CrnFormat,
10    dxt_data: alloc::vec::Vec<u8>,
11}
12
13fn unity_crunch_unpack_level(
14    data: &[u8],
15    data_size: u32,
16    level_index: u32,
17) -> Result<CrunchDecodeHandler, &'static str> {
18    let mut tex_info: CrnTextureInfo = CrnTextureInfo::default();
19    if !tex_info.crnd_get_texture_info(data, data_size) {
20        return Err("Invalid crunch texture encoding.");
21    }
22    if tex_info.faces != 1 {
23        return Err("Texture2D must only have 1 number of faces.");
24    }
25    let mut p_context: crn_unpacker::CrnUnpacker<'_> =
26        crn_decomp::crnd_unpack_begin(data, data_size)?;
27    let width = core::cmp::max(1, tex_info.width >> level_index);
28    let height = core::cmp::max(1, tex_info.height >> level_index);
29    let blocks_x: u32 = core::cmp::max(1, (width + 3) >> 2);
30    let blocks_y: u32 = core::cmp::max(1, (height + 3) >> 2);
31    let row_pitch: u32 = blocks_x * crn_decomp::crnd_get_bytes_per_dxt_block(&mut tex_info.format)?;
32    let total_face_size: u32 = row_pitch * blocks_y;
33    match p_context.crnd_unpack_level(total_face_size, row_pitch, level_index) {
34        Ok(res) => Ok(CrunchDecodeHandler {
35            format: tex_info.format,
36            dxt_data: res,
37        }),
38        Err(err) => Err(err),
39    }
40}
41
42pub fn decode_unity_crunch(
43    data: &[u8],
44    width: usize,
45    height: usize,
46    image: &mut [u32],
47) -> Result<(), &'static str> {
48    let handler = unity_crunch_unpack_level(data, data.len() as u32, 0)?;
49    match handler.format {
50        CrnFormat::Dxt1 => bcn::decode_bc1(&handler.dxt_data, width, height, image),
51
52        CrnFormat::Etc1 | CrnFormat::Etc1s => decode_etc1(&handler.dxt_data, width, height, image),
53
54        CrnFormat::CCrnfmtDxt5
55        | CrnFormat::Dxt5CcxY
56        | CrnFormat::Dxt5XGbr
57        | CrnFormat::Dxt5Agbr
58        | CrnFormat::Dxt5XGxR => bcn::decode_bc3(&handler.dxt_data, width, height, image),
59
60        CrnFormat::Dxt5a => bcn::decode_bc4(&handler.dxt_data, width, height, image),
61
62        CrnFormat::DxnXy | CrnFormat::DxnYx => {
63            bcn::decode_bc5(&handler.dxt_data, width, height, image)
64        }
65
66        CrnFormat::Etc2 => decode_etc2_rgb(&handler.dxt_data, width, height, image),
67
68        CrnFormat::Etc2a | CrnFormat::Etc2as => {
69            decode_etc2_rgba8(&handler.dxt_data, width, height, image)
70        }
71
72        _ => Err("Invalid crunch format."),
73    }
74}