gba.rsannotatedgba.rssource284 lines · 10.5 KB · raw
1use crate::app::widgets::{BiosErrorStrings, OptionalPathSelector, RenderErrorEffect};
2use crate::app::{App, OpenWindow, widgets};
3use egui::{Context, Slider, Window};
4use gba_config::{GbaAspectRatio, GbaAudioInterpolation, GbaColorCorrection, GbaSaveMemory};
5use jgenesis_native_config::gba::GameBoyAdvanceAppConfig;
6use jgenesis_native_driver::extensions::Console;
7use rfd::FileDialog;
8use std::path::PathBuf;
9
10mod helptext;
11
12impl App {
13    pub(super) fn render_gba_general_settings(&mut self, ctx: &Context) {
14        const WINDOW: OpenWindow = OpenWindow::GbaGeneral;
15
16        let mut open = true;
17
18        Window::new(WINDOW.title()).open(&mut open).resizable(false).show(ctx, |ui| {
19            let rect = ui
20                .add(OptionalPathSelector::new(
21                    "BIOS path",
22                    &mut self.config.game_boy_advance.bios_path,
23                    pick_bios_path,
24                ))
25                .interact_rect;
26            if ui.rect_contains_pointer(rect) {
27                self.state.help_text.insert(WINDOW, helptext::BIOS_PATH);
28            }
29
30            ui.add_space(5.0);
31
32            let rect = ui
33                .checkbox(
34                    &mut self.config.game_boy_advance.skip_bios_animation,
35                    "Skip BIOS intro animation",
36                )
37                .interact_rect;
38            if ui.rect_contains_pointer(rect) {
39                self.state.help_text.insert(WINDOW, helptext::SKIP_BIOS_ANIMATION);
40            }
41
42            ui.add_space(5.0);
43
44            let rect = ui
45                .group(|ui| {
46                    ui.label("Save memory type");
47
48                    for (value, label) in [
49                        (None, "Auto-detect"),
50                        (Some(GbaSaveMemory::Sram), "SRAM"),
51                        (Some(GbaSaveMemory::EepromUnknownSize), "EEPROM - Auto-detect size"),
52                        (Some(GbaSaveMemory::Eeprom512), "EEPROM - 512 bytes"),
53                        (Some(GbaSaveMemory::Eeprom8K), "EEPROM - 8 KB"),
54                        (Some(GbaSaveMemory::FlashRom64K), "Flash ROM - 64 KB"),
55                        (Some(GbaSaveMemory::FlashRom128K), "Flash ROM - 128 KB"),
56                        (Some(GbaSaveMemory::None), "No save memory"),
57                    ] {
58                        ui.radio_value(
59                            &mut self.config.game_boy_advance.forced_save_memory_type,
60                            value,
61                            label,
62                        );
63                    }
64                })
65                .response
66                .interact_rect;
67            if ui.rect_contains_pointer(rect) {
68                self.state.help_text.insert(WINDOW, helptext::SAVE_MEMORY_TYPE);
69            }
70
71            self.render_help_text(ui, WINDOW);
72        });
73
74        if !open {
75            self.state.open_windows.remove(&WINDOW);
76        }
77    }
78
79    pub(super) fn render_gba_video_settings(&mut self, ctx: &Context) {
80        const WINDOW: OpenWindow = OpenWindow::GbaVideo;
81
82        let mut open = true;
83
84        Window::new(WINDOW.title()).open(&mut open).resizable(false).show(ctx, |ui| {
85            let rect = ui
86                .group(|ui| {
87                    ui.label("Aspect ratio");
88
89                    ui.horizontal(|ui| {
90                        for (value, label) in [
91                            (GbaAspectRatio::SquarePixels, "Square pixels"),
92                            (GbaAspectRatio::Stretched, "Stretched"),
93                        ] {
94                            ui.radio_value(
95                                &mut self.config.game_boy_advance.aspect_ratio,
96                                value,
97                                label,
98                            );
99                        }
100                    });
101                })
102                .response
103                .interact_rect;
104            if ui.rect_contains_pointer(rect) {
105                self.state.help_text.insert(WINDOW, helptext::ASPECT_RATIO);
106            }
107
108            let rect = ui
109                .group(|ui| {
110                    ui.label("Color correction");
111
112                    ui.horizontal(|ui| {
113                        for (value, label) in [
114                            (GbaColorCorrection::None, "None"),
115                            (GbaColorCorrection::GbaLcd, "Game Boy Advance LCD"),
116                        ] {
117                            ui.radio_value(
118                                &mut self.config.game_boy_advance.color_correction,
119                                value,
120                                label,
121                            );
122                        }
123                    });
124
125                    ui.add_enabled_ui(
126                        self.config.game_boy_advance.color_correction == GbaColorCorrection::GbaLcd,
127                        |ui| {
128                            ui.horizontal(|ui| {
129                                ui.label("Console screen gamma");
130
131                                ui.add(
132                                    Slider::new(
133                                        &mut self.config.game_boy_advance.color_correction_gamma,
134                                        0.1..=5.0,
135                                    )
136                                    .custom_formatter(|value, _| format!("{value:.1}")),
137                                );
138
139                                if ui.button("Default").clicked() {
140                                    self.config.game_boy_advance.color_correction_gamma =
141                                        GameBoyAdvanceAppConfig::default().color_correction_gamma;
142                                }
143                            });
144                        },
145                    );
146                })
147                .response
148                .interact_rect;
149            if ui.rect_contains_pointer(rect) {
150                self.state.help_text.insert(WINDOW, helptext::COLOR_CORRECTION);
151            }
152
153            ui.add_space(5.0);
154
155            let rect = ui
156                .checkbox(
157                    &mut self.config.game_boy_advance.frame_blending,
158                    "Frame blending enabled",
159                )
160                .interact_rect;
161            if ui.rect_contains_pointer(rect) {
162                self.state.help_text.insert(WINDOW, helptext::FRAME_BLENDING);
163            }
164
165            self.render_help_text(ui, WINDOW);
166        });
167
168        if !open {
169            self.state.open_windows.remove(&WINDOW);
170        }
171    }
172
173    pub(super) fn render_gba_audio_settings(&mut self, ctx: &Context) {
174        const WINDOW: OpenWindow = OpenWindow::GbaAudio;
175
176        let mut open = true;
177        Window::new(WINDOW.title()).open(&mut open).resizable(false).show(ctx, |ui| {
178            ui.group(|ui| {
179                let rect = ui
180                    .scope(|ui| {
181                        ui.label("Audio interpolation");
182
183                        for (value, label) in [
184                            (GbaAudioInterpolation::NearestNeighbor, "Nearest neighbor (Native)"),
185                            (GbaAudioInterpolation::CubicHermite, "Cubic Hermite"),
186                            (GbaAudioInterpolation::WindowedSinc, "Windowed sinc"),
187                        ] {
188                            ui.radio_value(
189                                &mut self.config.game_boy_advance.audio_interpolation,
190                                value,
191                                label,
192                            );
193                        }
194                    })
195                    .response
196                    .interact_rect;
197                if ui.rect_contains_pointer(rect) {
198                    self.state.help_text.insert(WINDOW, helptext::AUDIO_INTERPOLATION);
199                }
200
201                let rect = ui
202                    .add_enabled_ui(
203                        self.config.game_boy_advance.audio_interpolation
204                            != GbaAudioInterpolation::NearestNeighbor,
205                        |ui| {
206                            ui.checkbox(
207                                &mut self.config.game_boy_advance.psg_low_pass,
208                                "Apply low-pass filter to PSG",
209                            );
210                        },
211                    )
212                    .response
213                    .interact_rect;
214                if ui.rect_contains_pointer(rect) {
215                    self.state.help_text.insert(WINDOW, helptext::PSG_LPF);
216                }
217            });
218
219            let rect = ui
220                .group(|ui| {
221                    ui.label("Enabled audio channels");
222
223                    ui.checkbox(
224                        &mut self.config.game_boy_advance.pulse_1_enabled,
225                        "Channel 1 (Pulse with sweep)",
226                    );
227                    ui.checkbox(
228                        &mut self.config.game_boy_advance.pulse_2_enabled,
229                        "Channel 2 (Pulse)",
230                    );
231                    ui.checkbox(
232                        &mut self.config.game_boy_advance.wavetable_enabled,
233                        "Channel 3 (Wavetable)",
234                    );
235                    ui.checkbox(
236                        &mut self.config.game_boy_advance.noise_enabled,
237                        "Channel 4 (Noise)",
238                    );
239                    ui.checkbox(
240                        &mut self.config.game_boy_advance.pcm_a_enabled,
241                        "Channel A (Direct Sound)",
242                    );
243                    ui.checkbox(
244                        &mut self.config.game_boy_advance.pcm_b_enabled,
245                        "Channel B (Direct Sound)",
246                    );
247                })
248                .response
249                .interact_rect;
250            if ui.rect_contains_pointer(rect) {
251                self.state.help_text.insert(WINDOW, helptext::AUDIO_CHANNELS_ENABLED);
252            }
253
254            self.render_help_text(ui, WINDOW);
255        });
256
257        if !open {
258            self.state.open_windows.remove(&WINDOW);
259        }
260    }
261
262    pub(super) fn render_gba_bios_error(
263        &mut self,
264        ctx: &Context,
265        open: &mut bool,
266    ) -> RenderErrorEffect {
267        widgets::render_bios_error(
268            ctx,
269            open,
270            BiosErrorStrings {
271                title: "Missing GBA BIOS",
272                text: "No GBA BIOS path is configured. A BIOS ROM is required for GBA emulation.",
273                button_label: "Configure GBA BIOS path",
274            },
275            &mut self.config.game_boy_advance.bios_path,
276            Console::GameBoyAdvance,
277            pick_bios_path,
278        )
279    }
280}
281
282fn pick_bios_path() -> Option<PathBuf> {
283    FileDialog::new().add_filter("bin", &["bin"]).add_filter("All Files", &["*"]).pick_file()
284}