use crate::renderware::rw_file::RwFile;
use crate::renderware::utils::image_format_enums::{PaletteType, PlatformType};
use std::io::Result;
use texpresso;
use serde::Serialize;
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct RwTxd {
pub texture_dictionary: RwTextureDictionary,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct RwTextureDictionary {
pub texture_count: u16,
pub texture_natives: Vec<RwTextureNative>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct RwTextureNative {
pub platform_id: u32,
pub filter_mode: u8,
pub u_addressing: u8,
pub v_addressing: u8,
pub texture_name: String,
pub mask_name: String,
pub raster_format: u32,
pub d3d_format: String,
pub width: u16,
pub height: u16,
pub depth: u8,
pub mipmap_count: u8,
pub raster_type: u8,
pub alpha: bool,
pub cube_texture: bool,
pub auto_mip_maps: bool,
pub compressed: bool,
pub mipmaps: Vec<Vec<u8>>,
}
pub struct TxdParser<'a> {
file: RwFile<'a>,
}
impl<'a> TxdParser<'a> {
pub fn new(buffer: &'a [u8]) -> Self {
TxdParser {
file: RwFile::new(buffer),
}
}
pub fn parse(&mut self) -> Result<RwTxd> {
Ok(RwTxd {
texture_dictionary: self.read_texture_dictionary()?,
})
}
fn read_texture_dictionary(&mut self) -> Result<RwTextureDictionary> {
self.file.read_section_header()?; self.file.read_section_header()?;
let texture_count = self.file.get_stream().read_u16()?;
self.file.get_stream().skip(2)?;
let mut texture_natives = Vec::with_capacity(texture_count as usize);
for _ in 0..texture_count {
texture_natives.push(self.read_texture_native()?);
}
let size = self.file.read_section_header()?.section_size;
self.file.get_stream().skip(size as u64)?;
Ok(RwTextureDictionary {
texture_count,
texture_natives,
})
}
fn read_texture_native(&mut self) -> Result<RwTextureNative> {
self.file.read_section_header()?; self.file.read_section_header()?;
let platform_id = self.file.get_stream().read_u32()?;
let flags = self.file.get_stream().read_u32()?;
let filter_mode = (flags & 0xFF) as u8;
let u_addressing = ((flags & 0xF00) >> 8) as u8;
let v_addressing = ((flags & 0xF000) >> 12) as u8;
let texture_name = self.file.get_stream().read_string(32)?;
let mask_name = self.file.get_stream().read_string(32)?;
let raster_format = self.file.get_stream().read_u32()?;
let d3d_format = self.file.get_stream().read_string(4)?;
let width = self.file.get_stream().read_u16()?;
let height = self.file.get_stream().read_u16()?;
let depth = self.file.get_stream().read_u8()?;
let mipmap_count = self.file.get_stream().read_u8()?;
let raster_type = self.file.get_stream().read_u8()?;
let compression_flags = self.file.get_stream().read_u8()?;
let alpha = (compression_flags & (1 << 0)) != 0;
let cube_texture = (compression_flags & (1 << 1)) != 0;
let auto_mip_maps = (compression_flags & (1 << 2)) != 0;
let compressed = (compression_flags & (1 << 3)) != 0;
let palette_type = (raster_format >> 13) & 0b11;
let mut mipmaps = Vec::new();
let palette = if palette_type != PaletteType::PaletteNone as u32 {
self.read_palette(palette_type, depth)?
} else {
Vec::new()
};
for i in 0..mipmap_count {
let raster_size = self.file.get_stream().read_u32()?;
let raster = self.file.get_stream().read(raster_size as usize)?;
if i == 0 {
let bitmap = if !palette.is_empty() {
vec![]
} else if platform_id == PlatformType::D3d8 as u32 && compression_flags != 0 {
self.get_bitmap_with_dxt(&format!("DXT{}", compression_flags), &raster, width, height)?
} else if platform_id == PlatformType::D3d9 as u32 && compressed {
self.get_bitmap_with_dxt(&d3d_format, &raster, width, height)?
} else {
vec![]
};
mipmaps.push(bitmap);
}
}
let size = self.file.read_section_header()?.section_size;
self.file.get_stream().skip(size as u64)?;
Ok(RwTextureNative {
platform_id,
filter_mode,
u_addressing,
v_addressing,
texture_name,
mask_name,
raster_format,
d3d_format,
width,
height,
depth,
mipmap_count,
raster_type,
alpha,
cube_texture,
auto_mip_maps,
compressed,
mipmaps,
})
}
fn read_palette(&mut self, palette_type: u32, depth: u8) -> Result<Vec<u8>> {
let size = if palette_type == PaletteType::Palette8 as u32 { 1024 } else if depth == 4 { 64 } else { 128 };
self.file.get_stream().read(size)
}
fn get_bitmap_with_dxt(&self, dxt_type: &str, raster: &[u8], width: u16, height: u16) -> Result<Vec<u8>> {
let format = match dxt_type {
"DXT1" => texpresso::Format::Bc1,
"DXT2" => texpresso::Format::Bc2,
"DXT3" => texpresso::Format::Bc2,
"DXT4" => texpresso::Format::Bc3,
"DXT5" => texpresso::Format::Bc3,
_ => return Ok(Vec::new())
};
let mut decoded = vec![0; width as usize * height as usize * 4];
format.decompress(raster, width as usize, height as usize, &mut decoded);
Ok(decoded)
}
}