use tracing::{debug, warn};
#[derive(Debug, Clone)]
pub(crate) struct Palette {
colors: [[u8; 3]; 256],
}
impl Palette {
pub(crate) fn system_default() -> Self {
let mut colors = [[0u8; 3]; 256];
colors[0] = [0, 0, 0]; colors[1] = [128, 0, 0]; colors[2] = [0, 128, 0]; colors[3] = [128, 128, 0]; colors[4] = [0, 0, 128]; colors[5] = [128, 0, 128]; colors[6] = [0, 128, 128]; colors[7] = [192, 192, 192]; colors[8] = [192, 220, 192]; colors[9] = [166, 202, 240]; colors[246] = [255, 251, 240]; colors[247] = [160, 160, 164]; colors[248] = [128, 128, 128]; colors[249] = [255, 0, 0]; colors[250] = [0, 255, 0]; colors[251] = [255, 255, 0]; colors[252] = [0, 0, 255]; colors[253] = [255, 0, 255]; colors[254] = [0, 255, 255]; colors[255] = [255, 255, 255]; Self { colors }
}
pub(crate) fn process_update(&mut self, data: &[u8]) {
const UPDATE_TYPE_PALETTE: u16 = 0x0002;
const PALETTE_ENTRY_COUNT: usize = 256;
const PALETTE_ENTRY_SIZE: usize = 3;
if data.len() < 8 {
warn!("Palette update too short: {} bytes", data.len());
return;
}
let update_type = u16::from_le_bytes([data[0], data[1]]);
if update_type != UPDATE_TYPE_PALETTE {
warn!(update_type, "Ignoring palette update with unexpected type");
return;
}
let number_colors = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
if number_colors != 256 {
warn!(number_colors, "Ignoring palette update with unexpected entry count");
return;
}
let entry_data = &data[8..];
let required_len = PALETTE_ENTRY_COUNT * PALETTE_ENTRY_SIZE;
if entry_data.len() < required_len {
warn!(
"Palette data truncated: expected {} bytes for {} colors, got {}",
required_len,
PALETTE_ENTRY_COUNT,
entry_data.len()
);
return;
}
for (color, rgb) in self.colors.iter_mut().zip(entry_data.chunks_exact(PALETTE_ENTRY_SIZE)) {
*color = [rgb[0], rgb[1], rgb[2]];
}
debug!("Updated palette with {} colors", PALETTE_ENTRY_COUNT);
}
pub(crate) fn colors(&self) -> &[[u8; 3]; 256] {
&self.colors
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn processes_spec_palette_data() {
let mut data = vec![0; 8 + 256 * 3];
data[0..2].copy_from_slice(&0x0002u16.to_le_bytes());
data[4..8].copy_from_slice(&256u32.to_le_bytes());
data[8..11].copy_from_slice(&[0x10, 0x20, 0x30]);
data[11..14].copy_from_slice(&[0x40, 0x50, 0x60]);
let mut palette = Palette::system_default();
palette.process_update(&data);
assert_eq!(palette.colors()[0], [0x10, 0x20, 0x30]);
assert_eq!(palette.colors()[1], [0x40, 0x50, 0x60]);
}
}