use super::{Gen2, SpeciesId, cached, lz, read};
use crate::games::pokemon::gen1::pic::Pic;
use crate::games::pokemon::gen1::{Bank, in_front_box};
use crate::io::{ByteSource, Reader};
use crate::media::{Image, Rgba, bgr555};
use crate::platforms::gb::FarPointer;
use crate::{Error, Result};
const UNOWN: u16 = 201;
const BACK_SIZE: u8 = 6;
const WHITE: Rgba = [0xFF, 0xFF, 0xFF, 0xFF];
const BLACK: Rgba = [0, 0, 0, 0xFF];
impl<S: ByteSource> Gen2<S> {
pub fn pokemon_sprite(&self, species: SpeciesId, back: bool, shiny: bool) -> Result<Image> {
let not_found = || Error::NotFound(format!("sprite of species {}", species.0));
let record = usize::from(species.0)
.checked_sub(1)
.and_then(|index| self.species().ok()?.get(index))
.ok_or_else(not_found)?;
let edition = self.edition;
let entry = match species.0 {
UNOWN => edition.unown_pics,
id => FarPointer::new(edition.pics.bank, edition.pics.address + (id - 1) * 6),
};
let entry = read(&self.source, entry, 6)?;
let pointer = Reader::new("Gen 2 pic table", &entry[if back { 3 } else { 0 }..]);
let bank = edition.pic_banks.actual(pointer.u8_at(0)?);
let tiles = lz::decompress(Bank::read(&self.source, bank)?.from(pointer.u16_at(1)?)?)?;
let (width, height) = match back {
true => (BACK_SIZE, BACK_SIZE),
false => record.pic_size,
};
let pic = untile(&tiles, width, height)?;
let [light, dark] = self.palettes()?[usize::from(species.0)][usize::from(shiny)];
let palette = [WHITE, light, dark, BLACK];
if back {
let side = u32::from(BACK_SIZE) * 8;
return Ok(Image::from_indices(side, side, &pic.pixels, &palette, true));
}
let side = 7 * 8;
Ok(Image::from_indices(
side,
side,
&in_front_box(&pic)?,
&palette,
true,
))
}
fn palettes(&self) -> Result<&[[[Rgba; 2]; 2]]> {
cached(&self.palettes, || {
let bytes = read(
&self.source,
self.edition.palettes,
(super::SPECIES + 1) * 8,
)?;
let color = |at: &[u8]| bgr555(u16::from_le_bytes([at[0], at[1]]));
Ok(bytes
.as_chunks::<8>()
.0
.iter()
.map(|entry| {
[
[color(&entry[0..]), color(&entry[2..])],
[color(&entry[4..]), color(&entry[6..])],
]
})
.collect())
})
.map(Vec::as_slice)
}
}
fn untile(tiles: &[u8], width: u8, height: u8) -> Result<Pic> {
let (columns, rows) = (usize::from(width), usize::from(height));
if tiles.len() < columns * rows * 16 {
return Err(Error::malformed(
"Gen 2 pic",
0,
"fewer tiles than its size",
));
}
let line = columns * 8;
let mut pixels = vec![0; line * rows * 8];
for (tile, bytes) in tiles
.as_chunks::<16>()
.0
.iter()
.take(columns * rows)
.enumerate()
{
let (x, y) = (tile / rows * 8, tile % rows * 8);
for (row, plane) in bytes.as_chunks::<2>().0.iter().enumerate() {
for bit in 0..8 {
let (low, high) = (plane[0] >> (7 - bit) & 1, plane[1] >> (7 - bit) & 1);
pixels[(y + row) * line + x + bit] = low | high << 1;
}
}
}
Ok(Pic {
width,
height,
pixels,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tiles_fill_columns_first() {
let mut tiles = vec![0; 32];
tiles[..16]
.iter_mut()
.step_by(2)
.for_each(|low| *low = 0xFF);
tiles[17] = 0x80;
let pic = untile(&tiles, 1, 2).unwrap();
assert_eq!((pic.pixels[0], pic.pixels[7 * 8 + 7]), (1, 1));
assert_eq!((pic.pixels[8 * 8], pic.pixels[8 * 8 + 1]), (2, 0));
assert!(untile(&tiles, 2, 2).is_err());
}
}