tgbr/
config.rs

1use meru_interface::{Color, File};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5#[derive(Clone, JsonSchema, Serialize, Deserialize)]
6pub struct Config {
7    /// Hardware model
8    pub model: Model,
9    /// Boot ROM
10    pub boot_rom: BootRom,
11    /// Custom boot ROM
12    pub custom_boot_roms: CustomBootRoms,
13    /// Game Boy Palette
14    pub palette: PaletteSelect,
15    /// Custom palette
16    pub custom_palette: Palette,
17    /// Color Correction
18    pub color_correction: bool,
19}
20
21impl Default for Config {
22    fn default() -> Self {
23        Self {
24            model: Model::Auto,
25            boot_rom: BootRom::Internal,
26            custom_boot_roms: CustomBootRoms::default(),
27            palette: PaletteSelect::Pocket,
28            custom_palette: PALETTE_GRAYSCALE,
29            color_correction: true,
30        }
31    }
32}
33
34#[derive(PartialEq, Eq, Clone, Copy, Debug, JsonSchema, Serialize, Deserialize)]
35pub enum Model {
36    Auto,
37    #[serde(rename = "Game Boy")]
38    Dmg,
39    #[serde(rename = "Game Boy Color")]
40    Cgb,
41    #[serde(rename = "Super Game Boy")]
42    Sgb,
43    #[serde(rename = "Super Game Boy 2")]
44    Sgb2,
45    #[serde(rename = "Game Boy Advance")]
46    Agb,
47}
48
49impl Model {
50    pub fn is_cgb(&self) -> bool {
51        match self {
52            Model::Cgb | Model::Agb => true,
53            Model::Sgb | Model::Sgb2 | Model::Dmg => false,
54            Model::Auto => panic!(),
55        }
56    }
57}
58
59#[derive(Clone, PartialEq, Eq, JsonSchema, Serialize, Deserialize)]
60pub enum BootRom {
61    None,
62    Internal,
63    Custom,
64}
65
66#[derive(Clone, Default, JsonSchema, Serialize, Deserialize)]
67pub struct CustomBootRoms {
68    /// Game Boy
69    pub dmg: Option<File>,
70    /// Game Boy Color
71    pub cgb: Option<File>,
72    // pub sgb: Option<File>,
73    // pub sgb2: Option<File>,
74    // pub agb: Option<File>,
75}
76
77#[rustfmt::skip]
78const BOOT_ROMS: &[(&str, &[u8])] = &[
79    ("DMG", include_bytes!("../assets/sameboy-bootroms/dmg_boot.bin")),
80    ("CGB", include_bytes!("../assets/sameboy-bootroms/cgb_boot.bin")),
81    ("SGB", include_bytes!("../assets/sameboy-bootroms/sgb_boot.bin")),
82    ("SGB2",include_bytes!("../assets/sameboy-bootroms/sgb2_boot.bin")),
83    ("AGB", include_bytes!("../assets/sameboy-bootroms/agb_boot.bin")),
84];
85
86#[derive(Default, Clone)]
87pub struct BootRoms {
88    pub dmg: Option<Vec<u8>>,
89    pub cgb: Option<Vec<u8>>,
90    pub sgb: Option<Vec<u8>>,
91    pub sgb2: Option<Vec<u8>>,
92    pub agb: Option<Vec<u8>>,
93}
94
95impl BootRoms {
96    pub fn get(&self, model: Model) -> Option<&[u8]> {
97        match model {
98            Model::Dmg => self.dmg.as_deref(),
99            Model::Cgb => self.cgb.as_deref(),
100            Model::Sgb => self.sgb.as_deref(),
101            Model::Sgb2 => self.sgb2.as_deref(),
102            Model::Agb => self.agb.as_deref(),
103            Model::Auto => panic!(),
104        }
105    }
106}
107
108impl Config {
109    pub fn boot_roms(&self) -> Result<BootRoms, std::io::Error> {
110        Ok(match self.boot_rom {
111            BootRom::None => BootRoms::default(),
112            BootRom::Internal => {
113                let lookup = |name: &str| {
114                    BOOT_ROMS
115                        .iter()
116                        .find(|(n, _)| *n == name)
117                        .map(|(_, b)| b.to_vec())
118                };
119                BootRoms {
120                    dmg: lookup("DMG"),
121                    cgb: lookup("CGB"),
122                    sgb: lookup("SGB"),
123                    sgb2: lookup("SGB2"),
124                    agb: lookup("AGB"),
125                }
126            }
127            BootRom::Custom => {
128                let load = |file: &Option<File>| -> Result<Option<Vec<u8>>, std::io::Error> {
129                    file.as_ref().map(|r| r.data()).transpose()
130                };
131                BootRoms {
132                    dmg: load(&self.custom_boot_roms.dmg)?,
133                    cgb: load(&self.custom_boot_roms.cgb)?,
134                    sgb: None,
135                    sgb2: None,
136                    agb: None,
137                    // sgb: load(&self.custom_boot_roms.sgb),
138                    // sgb2: load(&self.custom_boot_roms.sgb2),
139                    // agb: load(&self.custom_boot_roms.agb),
140                }
141            }
142        })
143    }
144
145    pub fn palette(&self) -> &Palette {
146        self.palette.get_palette().unwrap_or(&self.custom_palette)
147    }
148}
149
150#[derive(Clone, PartialEq, Eq, JsonSchema, Serialize, Deserialize)]
151pub enum PaletteSelect {
152    #[serde(rename = "Game Boy")]
153    Dmg,
154    #[serde(rename = "Game Boy Pocket")]
155    Pocket,
156    #[serde(rename = "Game Boy Light")]
157    Light,
158    Grayscale,
159    Custom,
160}
161
162pub type Palette = [Color; 4];
163
164impl PaletteSelect {
165    pub fn get_palette(&self) -> Option<&Palette> {
166        Some(match self {
167            PaletteSelect::Dmg => &PALETTE_DMG,
168            PaletteSelect::Pocket => &PALETTE_POCKET,
169            PaletteSelect::Light => &PALETTE_LIGHT,
170            PaletteSelect::Grayscale => &PALETTE_GRAYSCALE,
171            PaletteSelect::Custom => None?,
172        })
173    }
174}
175
176pub const PALETTE_DMG: Palette = [
177    Color::new(120, 128, 16),
178    Color::new(92, 120, 64),
179    Color::new(56, 88, 76),
180    Color::new(40, 64, 56),
181];
182
183pub const PALETTE_POCKET: Palette = [
184    Color::new(200, 200, 168),
185    Color::new(164, 164, 140),
186    Color::new(104, 104, 84),
187    Color::new(40, 40, 20),
188];
189
190pub const PALETTE_LIGHT: Palette = [
191    Color::new(0, 178, 132),
192    Color::new(0, 156, 116),
193    Color::new(0, 104, 74),
194    Color::new(0, 80, 56),
195];
196
197pub const PALETTE_GRAYSCALE: Palette = [
198    Color::new(255, 255, 255),
199    Color::new(170, 170, 170),
200    Color::new(85, 85, 85),
201    Color::new(0, 0, 0),
202];