1use meru_interface::{Color, File};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5#[derive(Clone, JsonSchema, Serialize, Deserialize)]
6pub struct Config {
7 pub model: Model,
9 pub boot_rom: BootRom,
11 pub custom_boot_roms: CustomBootRoms,
13 pub palette: PaletteSelect,
15 pub custom_palette: Palette,
17 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 pub dmg: Option<File>,
70 pub cgb: Option<File>,
72 }
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 }
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];