1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use crate::texture::Texture;
use crate::{texture_decoder, TextureParseError};
use byteorder::{LittleEndian, ReadBytesExt};
use encoding_rs::UTF_8;
use std::io::prelude::BufRead;
use std::io::{Cursor, Read, Seek, SeekFrom};

type Result<T> = std::result::Result<T, TextureParseError>;

#[allow(dead_code)]
pub struct Header {
    pub magic_id: u32,
    pub version: u16,
    pub texture_count: u16,
    pub texture_ptr: u32,
    pub texture_length: u32,
    pub hash_ptr: u32,
    pub texture_short_info_ptr: u32,
}

impl Header {
    pub fn new(reader: &mut Cursor<&[u8]>) -> Result<Self> {
        let magic_id = reader.read_u32::<LittleEndian>()?;
        let version = reader.read_u16::<LittleEndian>()?;
        let texture_count = reader.read_u16::<LittleEndian>()?;
        let texture_ptr = reader.read_u32::<LittleEndian>()?;
        let texture_length = reader.read_u32::<LittleEndian>()?;
        let hash_ptr = reader.read_u32::<LittleEndian>()?;
        let texture_short_info_ptr = reader.read_u32::<LittleEndian>()?;
        reader.seek(SeekFrom::Current(0x8))?; // Skip padding
        Ok(Header {
            magic_id,
            version,
            texture_count,
            texture_ptr,
            texture_length,
            hash_ptr,
            texture_short_info_ptr,
        })
    }
}

pub struct TextureInfo {
    pub filename_ptr: u32,
    pub texture_length: u32,
    pub texture_ptr: u32,
    pub pixel_format: u32,
    pub width: usize,
    pub height: usize,
    pub mipmap_level: u8,
    pub texture_type: u8,
    pub cube_dir: u16,
    pub bitmap_size_ptr: u32,
    pub file_time: u32,
}

impl TextureInfo {
    fn new(reader: &mut Cursor<&[u8]>) -> Result<Self> {
        let filename_ptr = reader.read_u32::<LittleEndian>()?;
        let texture_length = reader.read_u32::<LittleEndian>()?;
        let texture_ptr = reader.read_u32::<LittleEndian>()?;
        let pixel_format = reader.read_u32::<LittleEndian>()?;
        let width = reader.read_u16::<LittleEndian>()? as usize;
        let height = reader.read_u16::<LittleEndian>()? as usize;
        let mipmap_level = reader.read_u8()?;
        let texture_type = reader.read_u8()?;
        let cube_dir = reader.read_u16::<LittleEndian>()?;
        let bitmap_size_ptr = reader.read_u32::<LittleEndian>()?;
        let file_time = reader.read_u32::<LittleEndian>()?;
        Ok(TextureInfo {
            filename_ptr,
            texture_length,
            texture_ptr,
            pixel_format,
            width,
            height,
            mipmap_level,
            texture_type,
            cube_dir,
            bitmap_size_ptr,
            file_time,
        })
    }
}

pub fn read(file: &[u8]) -> Result<Vec<Texture>> {
    let mut reader = Cursor::new(file);

    let header = Header::new(&mut reader)?;

    // Read texture info
    let mut texture_info: Vec<TextureInfo> = Vec::new();
    for _ in 0..header.texture_count {
        texture_info.push(TextureInfo::new(&mut reader)?);
    }

    // Read texture
    let mut texture: Vec<Texture> = Vec::new();
    for i in 0..header.texture_count as usize {
        // Read filename
        reader.seek(SeekFrom::Start(texture_info[i].filename_ptr as u64))?;
        let mut filename_buffer: Vec<u8> = Vec::new();
        reader.read_until(0x0, &mut filename_buffer)?;
        filename_buffer.pop(); // Get rid of the null terminator.
        let (result, _, errors) = UTF_8.decode(filename_buffer.as_slice());
        if errors {
            return Err(TextureParseError::BadText);
        }
        let filename: String = result.into();

        // Read pixel data
        reader.seek(SeekFrom::Start(
            (header.texture_ptr + texture_info[i].texture_ptr) as u64,
        ))?;
        let mut pixel_data: Vec<u8> = vec![
            0;
            (texture_decoder::get_pixel_format_bpp(texture_info[i].pixel_format)
                * texture_info[i].width as f32
                * texture_info[i].height as f32) as usize
        ];
        reader.read_exact(&mut pixel_data)?;

        let width = texture_info[i].width;
        let height = texture_info[i].height;
        let pixel_format = texture_info[i].pixel_format;
        let pixel_data =
            texture_decoder::decode_pixel_data(&pixel_data, width, height, pixel_format)?;
        texture.push(Texture {
            filename,
            width,
            height,
            pixel_data,
        });
    }
    Ok(texture)
}