use std::collections::HashMap; use std::io::Cursor; use std::rc::Rc; use crate::sprites::{CachedSprite, Frame}; /// A list of assets loaded into memory. #[derive(Debug)] pub(crate) struct Assets { // sounds: TODO sprites: HashMap, } impl Assets { pub(crate) fn sprites(&self) -> &HashMap { &self.sprites } } /// Load all static assets into an `Assets` structure pub(crate) fn load_assets() -> Assets { use Frame::*; let mut sprites = HashMap::new(); sprites.insert(Blipjoy1, load_pcx(include_bytes!("assets/blipjoy1.pcx"))); sprites.insert(Blipjoy2, load_pcx(include_bytes!("assets/blipjoy2.pcx"))); sprites.insert(Ferris1, load_pcx(include_bytes!("assets/ferris1.pcx"))); sprites.insert(Ferris2, load_pcx(include_bytes!("assets/ferris2.pcx"))); sprites.insert(Cthulhu1, load_pcx(include_bytes!("assets/cthulhu1.pcx"))); sprites.insert(Cthulhu2, load_pcx(include_bytes!("assets/cthulhu2.pcx"))); sprites.insert(Player1, load_pcx(include_bytes!("assets/player1.pcx"))); sprites.insert(Player2, load_pcx(include_bytes!("assets/player2.pcx"))); sprites.insert(Shield1, load_pcx(include_bytes!("assets/shield.pcx"))); sprites.insert(Bullet1, load_pcx(include_bytes!("assets/bullet1.pcx"))); sprites.insert(Bullet2, load_pcx(include_bytes!("assets/bullet2.pcx"))); sprites.insert(Bullet3, load_pcx(include_bytes!("assets/bullet3.pcx"))); sprites.insert(Bullet4, load_pcx(include_bytes!("assets/bullet4.pcx"))); sprites.insert(Bullet5, load_pcx(include_bytes!("assets/bullet5.pcx"))); sprites.insert(Laser1, load_pcx(include_bytes!("assets/laser1.pcx"))); sprites.insert(Laser2, load_pcx(include_bytes!("assets/laser2.pcx"))); sprites.insert(Laser3, load_pcx(include_bytes!("assets/laser3.pcx"))); sprites.insert(Laser4, load_pcx(include_bytes!("assets/laser4.pcx"))); sprites.insert(Laser5, load_pcx(include_bytes!("assets/laser5.pcx"))); sprites.insert(Laser6, load_pcx(include_bytes!("assets/laser6.pcx"))); sprites.insert(Laser7, load_pcx(include_bytes!("assets/laser7.pcx"))); sprites.insert(Laser8, load_pcx(include_bytes!("assets/laser8.pcx"))); Assets { sprites } } /// Convert PCX data to raw pixels fn load_pcx(pcx: &[u8]) -> CachedSprite { let mut reader = pcx::Reader::new(Cursor::new(pcx)).unwrap(); let width = reader.width() as usize; let height = reader.height() as usize; let mut result = Vec::new(); if reader.is_paletted() { // Read the raw pixel data let mut buffer = Vec::new(); buffer.resize_with(width * height, Default::default); for y in 0..height { let a = y as usize * width; let b = a + width; reader.next_row_paletted(&mut buffer[a..b]).unwrap(); } // Read the pallete let mut palette = Vec::new(); let palette_length = reader.palette_length().unwrap() as usize; palette.resize_with(palette_length * 3, Default::default); reader.read_palette(&mut palette).unwrap(); // Copy to result with an alpha component let pixels = buffer .into_iter() .flat_map(|pal| { let i = pal as usize * 3; &palette[i..i + 3] }) .cloned() .collect::>(); result.extend_from_slice(&pixels); } else { for _ in 0..height { // Read the raw pixel data let mut buffer = Vec::new(); buffer.resize_with(width * 3, Default::default); reader.next_row_rgb(&mut buffer[..]).unwrap(); // Copy to result with an alpha component let pixels = buffer .chunks(3) .flat_map(|rgb| { let mut rgb = rgb.to_vec(); rgb.push(255); rgb }) .collect::>(); result.extend_from_slice(&pixels); } } (width, height, Rc::new(result)) } #[cfg(test)] mod tests { use super::*; #[test] fn test_pcx() { let pixels = load_pcx(include_bytes!("assets/blipjoy1.pcx")); let expected = vec![ 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, ]; assert_eq!(pixels.0, 10, "Width differs"); assert_eq!(pixels.1, 8, "Height differs"); assert_eq!(Rc::try_unwrap(pixels.2).unwrap(), expected, "Pixels differ"); } }