diff --git a/src/ppu.rs b/src/ppu.rs index 995bfb27c34d2b3ed7a24bf5dddf5788ec61987d..1cd40a9fe18fe5fc17ecc91f4cab0a79a9517b41 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -144,6 +144,24 @@ impl PaletteInfo { } } + pub fn from_colors_hex(name: &str, colors_hex: &str) -> Self { + let colors = Self::parse_colors_hex(colors_hex); + Self::new(name, colors) + } + + pub fn parse_colors_hex(colors_hex: &str) -> Palette { + let mut colors = [[0u8; RGB_SIZE]; PALETTE_SIZE]; + for (index, color) in colors_hex.split(',').enumerate() { + let color = color.trim(); + let color = u32::from_str_radix(color, 16).unwrap_or(0); + let r = ((color >> 16) & 0xff) as u8; + let g = ((color >> 8) & 0xff) as u8; + let b = (color & 0xff) as u8; + colors[index] = [r, g, b]; + } + colors + } + pub fn name(&self) -> &String { &self.name } @@ -151,6 +169,24 @@ impl PaletteInfo { pub fn colors(&self) -> &Palette { &self.colors } + + pub fn colors_hex(&self) -> String { + let mut buffer = String::new(); + let mut is_first = true; + for color in self.colors.iter() { + let r = color[0]; + let g = color[1]; + let b = color[2]; + let color = (r as u32) << 16 | (g as u32) << 8 | b as u32; + if is_first { + is_first = false; + } else { + buffer.push(','); + } + buffer.push_str(format!("{:06x}", color).as_str()); + } + buffer + } } /// Represents a tile within the Game Boy context, diff --git a/src/py.rs b/src/py.rs index 87aadc16174436c7095a943a90439cd9435bb006..4b18b1a7aaa88640e99678cc4d9877c7759f6472 100644 --- a/src/py.rs +++ b/src/py.rs @@ -3,7 +3,7 @@ use pyo3::{prelude::*, types::PyBytes}; use crate::{ gb::{GameBoy as GameBoyBase, GameBoyMode}, info::Info, - ppu::{DISPLAY_HEIGHT, DISPLAY_WIDTH}, + ppu::{PaletteInfo, DISPLAY_HEIGHT, DISPLAY_WIDTH}, }; #[pyclass] @@ -57,6 +57,11 @@ impl GameBoy { pybytes.into() } + pub fn set_palette_colors(&mut self, colors_hex: &str) { + let palette = PaletteInfo::from_colors_hex("default", colors_hex); + self.system.ppu().set_palette_colors(palette.colors()); + } + pub fn ppu_enabled(&self) -> bool { self.system.ppu_enabled() } diff --git a/src/python/boytacean/__init__.py b/src/python/boytacean/__init__.py index adc3074f27b2e94643011a0702886c5dc5b81953..1b4517ad7755dda1ef43ee65e4b43fa9675eddc5 100644 --- a/src/python/boytacean/__init__.py +++ b/src/python/boytacean/__init__.py @@ -1 +1,9 @@ -from .gb import * +from .gb import ( + DISPLAY_WIDTH, + DISPLAY_HEIGHT, + CPU_FREQ, + GameBoy, + GameBoyMode, + GameBoyRust, +) +from .palettes import PALETTES diff --git a/src/python/boytacean/gb.py b/src/python/boytacean/gb.py index 12c477c2db88b6800fcd823f50e4c9c2568f28b6..9451ffdbe3e8ddd62c2f9d159996b4d70c0bc9ad 100644 --- a/src/python/boytacean/gb.py +++ b/src/python/boytacean/gb.py @@ -2,6 +2,8 @@ from enum import Enum from PIL.Image import Image, frombytes +from .palettes import PALETTES + from .boytacean import DISPLAY_WIDTH, DISPLAY_HEIGHT, CPU_FREQ, GameBoy as GameBoyRust @@ -76,6 +78,15 @@ This is a [Game Boy](https://en.wikipedia.org/wiki/Game_Boy) emulator built usin image = self.image() image.save(filename, format=format) + def set_palette(self, name: str): + if not name in PALETTES: + raise ValueError(f"Unknown palette: {name}") + palette = PALETTES[name] + self.set_palette_colors(palette) + + def set_palette_colors(self, colors_hex: str): + self._system.set_palette_colors(colors_hex) + @property def ppu_enabled(self) -> bool: return self._system.ppu_enabled() diff --git a/src/python/boytacean/palettes.py b/src/python/boytacean/palettes.py new file mode 100644 index 0000000000000000000000000000000000000000..7f11977727f4f0faa097f358e567ac328da613a8 --- /dev/null +++ b/src/python/boytacean/palettes.py @@ -0,0 +1,17 @@ +BASIC_PALETTE = "ffffff,c0c0c0,606060,000000" +HOGWARDS_PALETTE = "b6a571,8b7e56,554d35,201d13" +CHRISTMAS_PALETTE = "e8e7df,8bab95,9e5c5e,534d57" +GOLDSILVER_PALETTE = "c5c66d,97a1b0,585e67,235229" +PACMAN_PALETTE = "ffff00,ffb897,3732ff,000000" +MARIOBROS_PALETTE = "f7cec3,cc9e22,923404,000000" +POKEMON_PALETTE = "f87800,b86000,783800,000000" + +PALETTES = { + "basic": BASIC_PALETTE, + "hogwards": HOGWARDS_PALETTE, + "christmas": CHRISTMAS_PALETTE, + "goldsilver": GOLDSILVER_PALETTE, + "pacman": PACMAN_PALETTE, + "mariobros": MARIOBROS_PALETTE, + "pokemon": POKEMON_PALETTE, +}