freenukum 0.3.5

A clone of the 1991 DOS game Duke Nukem 1
Documentation
use crate::Result;
use anyhow::anyhow;
use sdl2::{
    pixels::{Color, PixelFormatEnum},
    render::Canvas,
    surface::Surface,
};
use std::io::Read;

#[derive(Clone, Copy)]
pub struct TileHeader {
    pub tiles: u8,
    pub width: u8,
    pub height: u8,
}

impl TileHeader {
    pub fn load_from<R: Read>(r: &mut R) -> Result<Self> {
        let mut buf = [0u8; 3];
        r.read_exact(&mut buf)?;
        Ok(TileHeader {
            tiles: buf[0],
            width: buf[1],
            height: buf[2],
        })
    }
}

pub fn load<'t, R: Read>(
    r: &mut R,
    header: TileHeader,
    has_transparency: bool,
) -> Result<Surface<'t>> {
    let width: u32 = header.width as u32 * 8;
    let height: u32 = header.height as u32;

    let surface = Surface::new(width, height, PixelFormatEnum::RGBA8888)
        .map_err(|s| anyhow!(s))?;
    let mut data: Vec<u8> =
        Vec::with_capacity(width as usize * height as usize * 4);

    let mut readbuf = [0u8; 5];

    let num_loads = width as usize * height as usize / 8;

    for _ in 0..num_loads {
        r.read_exact(&mut readbuf)?;

        let opaque_row = readbuf[0];
        let blue_row = readbuf[1];
        let green_row = readbuf[2];
        let red_row = readbuf[3];
        let bright_row = readbuf[4];

        for i in 0..8 {
            let bright_pixel = (bright_row >> (7 - i)) & 1;
            let red_pixel = (red_row >> (7 - i)) & 1;
            let green_pixel = (green_row >> (7 - i)) & 1;
            let blue_pixel = (blue_row >> (7 - i)) & 1;
            let opaque_pixel = if has_transparency {
                (opaque_row >> (7 - i)) & 1
            } else {
                1
            };
            let ugly_yellow = if red_pixel == 1
                && green_pixel == 1
                && blue_pixel == 0
                && bright_pixel == 0
            {
                1
            } else {
                0
            };

            data.push(0x54 * (red_pixel * 2 + bright_pixel));
            data.push(
                0x54 * (green_pixel * 2 + bright_pixel - ugly_yellow),
            );
            data.push(0x54 * (blue_pixel * 2 + bright_pixel));
            data.push(opaque_pixel * 0xff);
        }
    }

    use crate::graphics::SurfaceExt;
    let mut canvas =
        Canvas::from_surface(surface).map_err(|s| anyhow!(s))?;
    canvas.set_draw_color(Color::RGBA(0, 0, 0, 0));
    canvas.clear();
    canvas.set_data(&data, width, height)?;
    let surface = canvas.into_surface();

    Ok(surface)
}