earthbound-battle-backgrounds 0.1.0

Emulate and render the battle backgrounds from EarthBound / Mother 2.
Documentation
use crate::rom::block::Block;
use crate::rom::{self};

#[derive(Debug)]
pub struct BackgroundPalette {
    colors: Vec<Vec<u32>>,
    bits_per_pixel: u8,
    address: usize,
}

impl BackgroundPalette {
    pub fn new(index: usize, bits_per_pixel: u8) -> Self {
        let mut palette = BackgroundPalette {
            colors: vec![],
            bits_per_pixel,
            address: 0,
        };

        palette.read(index);

        palette
    }

    fn read(&mut self, index: usize) {
        let mut pointer = rom::read_block(0xDAD9 + index * 4);
        let address = rom::snes_to_hex(pointer.read_int32() as usize, true);
        let data = rom::read_block(address);
        self.address = address;
        self.read_palette(data, self.bits_per_pixel, 1);
    }

    /// Gets an array of colors representing one of this palette's subpalettes.
    ///
    /// `palette` - The index of the subpalette to retrieve.
    ///
    /// Returns an array containing the colors of the specified subpalette.
    #[allow(unused)]
    pub fn get_colors(&self, palette: usize) -> &[u32] {
        &self.colors[palette]
    }

    pub fn get_color_matrix(&self) -> &[Vec<u32>] {
        &self.colors
    }

    /// Internal function - reads palette data from the given block into this
    /// palette's colors array.
    ///
    /// - `block` - Block to read palette data from.
    /// - `bitsPerPixel` - Bit depth: Must be either 2 or 4.
    /// - `count` - Number of subpalettes to read.
    fn read_palette(&mut self, mut block: Block, bits_per_pixel: u8, count: usize) {
        assert!(
            self.bits_per_pixel == 2 || self.bits_per_pixel == 4,
            "Palette error: Incorrect color depth specified"
        );
        assert!(
            count > 0,
            "Palette error: Must specify positive number of subpalettes"
        );
        self.colors = vec![Vec::new(); count];
        let power = 2usize.pow(bits_per_pixel.into());
        for palette in 0..count {
            self.colors[palette] = vec![0; power];
            for i in 0..power {
                let clr16 = block.read_double_short() as u32;
                let b = ((clr16 >> 10) & 31) * 8;
                let g = ((clr16 >> 5) & 31) * 8;
                let r = (clr16 & 31) * 8;
                // convert RGB to color int
                // this code is straight out of Android: http://git.io/F1lZtw
                self.colors[palette][i] = (0xFF << 24) | (r << 16) | (g << 8) | b
            }
        }
    }
}