use crate::{LayerMipmaps, NutexbFile, NutexbFooter, NutexbFormat};
use binrw::NullString;
use std::cmp::max;
use tegra_swizzle::{div_round_up, surface::BlockDim};
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Surface<T> {
pub width: u32,
pub height: u32,
pub depth: u32,
pub image_data: T,
pub mipmap_count: u32,
pub layer_count: u32,
pub image_format: NutexbFormat,
}
pub fn create_nutexb<T: AsRef<[u8]>, S: Into<String>>(
image: Surface<T>,
name: S,
) -> Result<NutexbFile, tegra_swizzle::SwizzleError> {
let width = image.width;
let height = image.height;
let depth = image.depth;
let image_format = image.image_format;
let bytes_per_pixel = image_format.bytes_per_pixel();
let block_dim = image_format.block_dim();
let mip_count = image.mipmap_count;
let layer_count = image.layer_count;
let layer_mipmaps = calculate_layer_mip_sizes(
width as usize,
height as usize,
depth as usize,
block_dim,
bytes_per_pixel as usize,
mip_count as usize,
layer_count as usize,
);
let data = tegra_swizzle::surface::swizzle_surface(
width as usize,
height as usize,
depth as usize,
image.image_data.as_ref(),
block_dim,
None,
bytes_per_pixel as usize,
mip_count as usize,
layer_count as usize,
)?;
let size = data.len() as u32;
let unk2 = unk2(depth, layer_count);
Ok(NutexbFile {
data,
layer_mipmaps,
footer: NutexbFooter {
string: NullString::from(name.into()),
width,
height,
depth,
image_format,
unk2,
mipmap_count: mip_count,
unk3: 0x1000,
layer_count,
data_size: size,
version: (1, 2),
},
})
}
fn unk2(depth: u32, layer_count: u32) -> u32 {
if depth > 1 {
8
} else if layer_count > 1 {
9
} else {
4
}
}
fn calculate_layer_mip_sizes(
width: usize,
height: usize,
depth: usize,
block_dim: BlockDim,
bytes_per_pixel: usize,
mip_count: usize,
layer_count: usize,
) -> Vec<LayerMipmaps> {
let layer = LayerMipmaps {
mipmap_sizes: (0..mip_count)
.into_iter()
.map(|mip| {
let mip_width = max(div_round_up(width >> mip, block_dim.width.get()), 1);
let mip_height = max(div_round_up(height >> mip, block_dim.height.get()), 1);
let mip_depth = max(div_round_up(depth >> mip, block_dim.depth.get()), 1);
(mip_width * mip_height * mip_depth * bytes_per_pixel) as u32
})
.collect(),
};
vec![layer; layer_count]
}
pub fn create_nutexb_unswizzled<T: AsRef<[u8]>, S: Into<String>>(
surface: &Surface<T>,
name: S,
) -> NutexbFile {
let width = surface.width;
let height = surface.height;
let depth = surface.depth;
let data = surface.image_data.as_ref().to_vec();
let image_format = surface.image_format;
let bytes_per_pixel = image_format.bytes_per_pixel();
let block_dim = image_format.block_dim();
let layer_mipmaps = calculate_layer_mip_sizes(
width as usize,
height as usize,
depth as usize,
block_dim,
bytes_per_pixel as usize,
1,
1,
);
let size = data.len() as u32;
NutexbFile {
data,
layer_mipmaps,
footer: NutexbFooter {
string: NullString::from(name.into()),
width,
height,
depth,
image_format,
unk2: 2,
mipmap_count: 1,
unk3: 0, layer_count: 1,
data_size: size,
version: (2, 0),
},
}
}