use super::{Deswizzler, Format, SwizzleError, Swizzler};
pub struct Xbox360;
impl Swizzler for Xbox360 {
fn swizzle<T: Format>(
source: &mut [u8],
dest: &mut [u8],
dimentions: (usize, usize, usize),
format: T,
align_resolution: bool,
) -> Result<(), SwizzleError> {
x360::do_swizzle(source, dest, dimentions, format, false, align_resolution)
}
}
impl Deswizzler for Xbox360 {
fn deswizzle<T: Format>(
source: &mut [u8],
dest: &mut [u8],
dimentions: (usize, usize, usize),
format: T,
align_resolution: bool,
) -> Result<(), SwizzleError> {
x360::do_swizzle(source, dest, dimentions, format, true, align_resolution)
}
}
mod x360 {
use crate::swizzle::{Format, SwizzleError, TextureSlice};
pub fn do_swizzle<T: Format>(
source: &mut [u8],
dest: &mut [u8],
dimensions: (usize, usize, usize),
format: T,
unswizzle: bool,
align_resolution: bool,
) -> Result<(), SwizzleError> {
if format.x360_swap() {
swap_byte_order_x360(source);
}
untile_x360_image_data(
&source,
dest,
dimensions,
format.pixel_block_size(),
format.block_size(),
unswizzle,
)?;
if format.x360_swap() {
for chunk in dest.chunks_exact_mut(4) {
let (x, y, z, w) = (chunk[1], chunk[2], chunk[3], chunk[0]);
chunk[0] = x;
chunk[1] = y;
chunk[2] = z;
chunk[3] = w;
}
}
Ok(())
}
pub fn swap_byte_order_x360(image_data: &mut [u8]) {
for chunk in image_data.chunks_mut(2) {
chunk.swap(0, 1);
}
}
fn xg_address_2d_tiled_x(
block_offset: usize,
width_in_blocks: usize,
texel_byte_pitch: usize,
) -> usize {
let aligned_width = (width_in_blocks + 31) & !31;
let log_bpp =
(texel_byte_pitch >> 2) + ((texel_byte_pitch >> 1) >> (texel_byte_pitch >> 2));
let offset_byte = block_offset << log_bpp;
let offset_tile =
((offset_byte & !0xFFF) >> 3) + ((offset_byte & 0x700) >> 2) + (offset_byte & 0x3F);
let offset_macro = offset_tile >> (7 + log_bpp);
let macro_x = (offset_macro % (aligned_width >> 5)) << 2;
let tile = (((offset_tile >> (5 + log_bpp)) & 2) + (offset_byte >> 6)) & 3;
let macro_ = (macro_x + tile) << 3;
let micro = ((((offset_tile >> 1) & !0xF) + (offset_tile & 0xF))
& ((texel_byte_pitch << 3) - 1))
>> log_bpp;
macro_ + micro
}
fn xg_address_2d_tiled_y(
block_offset: usize,
width_in_blocks: usize,
texel_byte_pitch: usize,
) -> usize {
let aligned_width = (width_in_blocks + 31) & !31;
let log_bpp =
(texel_byte_pitch >> 2) + ((texel_byte_pitch >> 1) >> (texel_byte_pitch >> 2));
let offset_byte = block_offset << log_bpp;
let offset_tile =
((offset_byte & !0xFFF) >> 3) + ((offset_byte & 0x700) >> 2) + (offset_byte & 0x3F);
let offset_macro = offset_tile >> (7 + log_bpp);
let macro_y = (offset_macro / (aligned_width >> 5)) << 2;
let tile = ((offset_tile >> (6 + log_bpp)) & 1) + ((offset_byte & 0x800) >> 10);
let macro_ = (macro_y + tile) << 3;
let micro = (((offset_tile & (((texel_byte_pitch << 6) - 1) & !0x1F))
+ ((offset_tile & 0xF) << 1))
>> (3 + log_bpp))
& !1;
macro_ + micro + ((offset_tile & 0x10) >> 4)
}
fn untile_x360_image_data(
image_data: &[u8],
dest: &mut [u8],
dimensions: (usize, usize, usize),
block_pixel_size: usize,
texel_byte_pitch: usize,
deswizzle: bool,
) -> Result<(), SwizzleError> {
let (image_width, image_height, image_depth) = dimensions;
let width_in_blocks = image_width / block_pixel_size;
let height_in_blocks = image_height / block_pixel_size;
let padded_width_in_blocks = (width_in_blocks + 31) & !31;
let padded_height_in_blocks = (height_in_blocks + 31) & !31;
let slice_size = padded_width_in_blocks * padded_height_in_blocks * texel_byte_pitch;
for slice in 0..image_depth {
let Some(slice_src) = image_data.get(slice * slice_size..) else {
return Err(SwizzleError::OutOfBounds(TextureSlice::Source));
};
let Some(slice_dest) = dest.get_mut(slice * slice_size..) else {
return Err(SwizzleError::OutOfBounds(TextureSlice::Dest));
};
for j in 0..padded_height_in_blocks {
for i in 0..padded_width_in_blocks {
let block_offset = j * padded_width_in_blocks + i;
let x = xg_address_2d_tiled_x(
block_offset,
padded_width_in_blocks,
texel_byte_pitch,
);
let y = xg_address_2d_tiled_y(
block_offset,
padded_width_in_blocks,
texel_byte_pitch,
);
let src_byte_offset = block_offset * texel_byte_pitch;
let dest_byte_offset = (y * width_in_blocks + x) * texel_byte_pitch;
if dest_byte_offset + texel_byte_pitch > slice_dest.len()
|| src_byte_offset + texel_byte_pitch > slice_src.len()
{
continue;
}
if deswizzle {
match slice_src.get(src_byte_offset..src_byte_offset + texel_byte_pitch) {
Some(source) => {
if source.iter().all(|&b| b == 0) {
continue;
}
slice_dest[dest_byte_offset..dest_byte_offset + texel_byte_pitch]
.copy_from_slice(source);
}
None => {
continue;
}
}
} else {
match slice_src.get(dest_byte_offset..dest_byte_offset + texel_byte_pitch) {
Some(source) => {
slice_dest[src_byte_offset..src_byte_offset + texel_byte_pitch]
.copy_from_slice(source);
}
None => {
continue;
}
}
}
}
}
}
Ok(())
}
}