config.rsannotatedconfig.rssource593 lines · 25.6 KB · raw
1use gb_config::GbcColorCorrection;
2use gb_core::api::GameBoyEmulatorConfig;
3use gba_config::GbaColorCorrection;
4use gba_core::api::{GbaAudioConfig, GbaEmulatorConfig};
5use genesis_config::GenesisEmulatorConfig;
6use genesis_config::cheats::GenesisCheats;
7use genesis_config::{
8    S32XVoidColor, S32XVoidColorType, Sega32XEmulatorConfig, SegaCdEmulatorConfig,
9};
10use genesis_core::api::GenesisHardware;
11use jgenesis_common::frontend::{ColorCorrection, FiniteF32};
12use jgenesis_native_config::common::{
13    ConfigSavePath, HideMouseCursor, PauseEmulator, SavePath, WindowSize,
14};
15use jgenesis_native_config::input::mappings::PceInputConfig;
16use jgenesis_native_config::input::mappings::{
17    GameBoyInputConfig, GbaInputConfig, GenesisInputConfig, HotkeyConfig, NesInputConfig,
18    SmsGgInputConfig, SnesInputConfig,
19};
20use jgenesis_native_config::{AppConfig, EguiTheme};
21use jgenesis_proc_macros::ConfigDisplay;
22use jgenesis_renderer::config::{PreprocessShader, PrescaleMode, RendererConfig};
23use nes_core::api::NesEmulatorConfig;
24use pce_core::api::PceEmulatorConfig;
25use smsgg_config::cheats::SmsGgCheats;
26use smsgg_core::{SmsGgEmulatorConfig, SmsGgHardware};
27use snes_core::api::{CoprocessorRomFn, CoprocessorRoms, SnesEmulatorConfig};
28use std::fs;
29use std::num::NonZeroU8;
30use std::path::{Path, PathBuf};
31
32#[derive(Debug, Clone, ConfigDisplay)]
33pub struct CommonConfig {
34    #[cfg_display(path)]
35    pub rom_file_path: PathBuf,
36    pub mute_audio: bool,
37    pub audio_output_frequency: u64,
38    pub audio_sync: bool,
39    pub audio_dynamic_resampling_ratio: bool,
40    pub audio_buffer_size: u32,
41    pub audio_hardware_queue_size: u32,
42    pub audio_gain_db: f64,
43    pub save_path: SavePath,
44    pub state_path: SavePath,
45    #[cfg_display(debug_fmt)]
46    pub window_size: Option<WindowSize>,
47    #[cfg_display(indent_nested)]
48    pub renderer_config: RendererConfig,
49    pub fast_forward_multiplier: u64,
50    pub rewind_buffer_length_seconds: u64,
51    pub load_recent_state_at_launch: bool,
52    pub launch_in_fullscreen: bool,
53    pub borderless_window: bool,
54    pub initial_window_size: NonZeroU8,
55    pub axis_deadzone: i16,
56    #[cfg_display(indent_nested)]
57    pub hotkey_config: HotkeyConfig,
58    pub pause_emulator: PauseEmulator,
59    pub hide_mouse_cursor: HideMouseCursor,
60    pub egui_theme: EguiTheme,
61}
62
63#[derive(Debug, Clone, ConfigDisplay)]
64pub struct SmsGgConfig {
65    #[cfg_display(indent_nested)]
66    pub common: CommonConfig,
67    #[cfg_display(indent_nested)]
68    pub inputs: SmsGgInputConfig,
69    #[cfg_display(debug_fmt)]
70    pub hardware: Option<SmsGgHardware>,
71    #[cfg_display(indent_nested)]
72    pub emulator_config: SmsGgEmulatorConfig,
73    pub sms_boot_from_bios: bool,
74    pub gg_boot_from_bios: bool,
75    pub run_without_cartridge: bool,
76    #[cfg_display(path)]
77    pub sms_bios_path: Option<PathBuf>,
78    #[cfg_display(path)]
79    pub gg_bios_path: Option<PathBuf>,
80}
81
82#[derive(Debug, Clone, ConfigDisplay)]
83pub struct GenesisConfig {
84    #[cfg_display(indent_nested)]
85    pub common: CommonConfig,
86    // Disc path for Sega CD boot-from-cartridge (Mode 1)
87    // Cartridge path for boot-from-disc (Mode 2)
88    #[cfg_display(path)]
89    pub secondary_path: Option<PathBuf>,
90    #[cfg_display(indent_nested)]
91    pub inputs: GenesisInputConfig,
92    pub hardware: GenesisHardware,
93    #[cfg_display(indent_nested)]
94    pub emulator_config: GenesisEmulatorConfig,
95    #[cfg_display(path)]
96    pub scd_us_bios_path: Option<PathBuf>,
97    #[cfg_display(path)]
98    pub scd_eu_bios_path: Option<PathBuf>,
99    #[cfg_display(path)]
100    pub scd_jp_bios_path: Option<PathBuf>,
101    pub scd_per_region_bios: bool,
102    pub scd_run_without_disc: bool,
103}
104
105#[derive(Debug, Clone, ConfigDisplay)]
106pub struct SegaCdConfig {
107    #[cfg_display(indent_nested)]
108    pub genesis: GenesisConfig,
109    #[cfg_display(indent_nested)]
110    pub emulator_config: SegaCdEmulatorConfig,
111    #[cfg_display(path)]
112    pub bios_file_path: Option<PathBuf>,
113    #[cfg_display(path)]
114    pub eu_bios_file_path: Option<PathBuf>,
115    #[cfg_display(path)]
116    pub jp_bios_file_path: Option<PathBuf>,
117    pub per_region_bios: bool,
118    pub run_without_disc: bool,
119}
120
121#[derive(Debug, Clone, ConfigDisplay)]
122pub struct Sega32XConfig {
123    #[cfg_display(indent_nested)]
124    pub genesis: GenesisConfig,
125    #[cfg_display(indent_nested)]
126    pub emulator_config: Sega32XEmulatorConfig,
127}
128
129#[derive(Debug, Clone, ConfigDisplay)]
130pub struct NesConfig {
131    #[cfg_display(indent_nested)]
132    pub common: CommonConfig,
133    #[cfg_display(indent_nested)]
134    pub inputs: NesInputConfig,
135    #[cfg_display(indent_nested)]
136    pub emulator_config: NesEmulatorConfig,
137}
138
139#[derive(Debug, Clone, ConfigDisplay)]
140pub struct SnesConfig {
141    #[cfg_display(indent_nested)]
142    pub common: CommonConfig,
143    #[cfg_display(indent_nested)]
144    pub inputs: SnesInputConfig,
145    #[cfg_display(indent_nested)]
146    pub emulator_config: SnesEmulatorConfig,
147    #[cfg_display(path)]
148    pub dsp1_rom_path: Option<PathBuf>,
149    #[cfg_display(path)]
150    pub dsp2_rom_path: Option<PathBuf>,
151    #[cfg_display(path)]
152    pub dsp3_rom_path: Option<PathBuf>,
153    #[cfg_display(path)]
154    pub dsp4_rom_path: Option<PathBuf>,
155    #[cfg_display(path)]
156    pub st010_rom_path: Option<PathBuf>,
157    #[cfg_display(path)]
158    pub st011_rom_path: Option<PathBuf>,
159    #[cfg_display(path)]
160    pub st018_rom_path: Option<PathBuf>,
161}
162
163impl SnesConfig {
164    pub(crate) fn to_coprocessor_roms(&self) -> CoprocessorRoms {
165        let dsp1 = self.dsp1_rom_path.clone().map(coprocessor_read_fn);
166        let dsp2 = self.dsp2_rom_path.clone().map(coprocessor_read_fn);
167        let dsp3 = self.dsp3_rom_path.clone().map(coprocessor_read_fn);
168        let dsp4 = self.dsp4_rom_path.clone().map(coprocessor_read_fn);
169        let st010 = self.st010_rom_path.clone().map(coprocessor_read_fn);
170        let st011 = self.st011_rom_path.clone().map(coprocessor_read_fn);
171        let st018 = self.st018_rom_path.clone().map(coprocessor_read_fn);
172
173        CoprocessorRoms { dsp1, dsp2, dsp3, dsp4, st010, st011, st018 }
174    }
175}
176
177fn coprocessor_read_fn(path: PathBuf) -> Box<CoprocessorRomFn> {
178    Box::new(move || fs::read(&path).map_err(|err| (err, path.display().to_string())))
179}
180
181#[derive(Debug, Clone, ConfigDisplay)]
182pub struct GameBoyConfig {
183    #[cfg_display(indent_nested)]
184    pub common: CommonConfig,
185    #[cfg_display(indent_nested)]
186    pub inputs: GameBoyInputConfig,
187    #[cfg_display(indent_nested)]
188    pub emulator_config: GameBoyEmulatorConfig,
189    pub dmg_boot_rom: bool,
190    pub cgb_boot_rom: bool,
191    #[cfg_display(path)]
192    pub dmg_boot_rom_path: Option<PathBuf>,
193    #[cfg_display(path)]
194    pub cgb_boot_rom_path: Option<PathBuf>,
195}
196
197#[derive(Debug, Clone, ConfigDisplay)]
198pub struct GameBoyAdvanceConfig {
199    #[cfg_display(indent_nested)]
200    pub common: CommonConfig,
201    #[cfg_display(indent_nested)]
202    pub inputs: GbaInputConfig,
203    #[cfg_display(indent_nested)]
204    pub emulator_config: GbaEmulatorConfig,
205    #[cfg_display(path)]
206    pub bios_path: Option<PathBuf>,
207    pub solar_brightness_step: u8,
208    pub solar_min_brightness: u8,
209    pub solar_max_brightness: u8,
210}
211
212#[derive(Debug, Clone, ConfigDisplay)]
213pub struct PcEngineConfig {
214    #[cfg_display(indent_nested)]
215    pub common: CommonConfig,
216    #[cfg_display(indent_nested)]
217    pub inputs: PceInputConfig,
218    #[cfg_display(indent_nested)]
219    pub emulator_config: PceEmulatorConfig,
220}
221
222pub trait AppConfigExt {
223    #[must_use]
224    fn common_config(&self, path: PathBuf) -> CommonConfig;
225
226    #[must_use]
227    fn genesis_config(
228        &self,
229        path: PathBuf,
230        secondary_path: Option<PathBuf>,
231        hardware: Option<GenesisHardware>,
232        cheats: &GenesisCheats,
233    ) -> Box<GenesisConfig>;
234
235    #[must_use]
236    fn smsgg_config(
237        &self,
238        path: PathBuf,
239        hardware: Option<SmsGgHardware>,
240        cheats: &SmsGgCheats,
241    ) -> Box<SmsGgConfig>;
242
243    #[must_use]
244    fn nes_config(&self, path: PathBuf) -> Box<NesConfig>;
245
246    #[must_use]
247    fn snes_config(&self, path: PathBuf) -> Box<SnesConfig>;
248
249    #[must_use]
250    fn gb_config(&self, path: PathBuf) -> Box<GameBoyConfig>;
251
252    #[must_use]
253    fn gba_config(&self, path: PathBuf) -> Box<GameBoyAdvanceConfig>;
254
255    #[must_use]
256    fn pce_config(&self, path: PathBuf) -> Box<PcEngineConfig>;
257}
258
259impl AppConfigExt for AppConfig {
260    fn common_config(&self, path: PathBuf) -> CommonConfig {
261        fn save_path(path: ConfigSavePath, custom_path: &Path) -> SavePath {
262            match path {
263                ConfigSavePath::RomFolder => SavePath::RomFolder,
264                ConfigSavePath::EmulatorFolder => SavePath::EmulatorFolder,
265                ConfigSavePath::Custom => SavePath::Custom(custom_path.into()),
266            }
267        }
268
269        CommonConfig {
270            rom_file_path: path,
271            mute_audio: self.common.mute_audio,
272            audio_output_frequency: self.common.audio_output_frequency,
273            audio_sync: self.common.audio_sync,
274            audio_dynamic_resampling_ratio: self.common.audio_dynamic_resampling_ratio,
275            audio_buffer_size: self.common.audio_buffer_size,
276            audio_hardware_queue_size: self.common.audio_hardware_queue_size,
277            audio_gain_db: self.common.audio_gain_db,
278            save_path: save_path(self.common.save_path, &self.common.custom_save_path),
279            state_path: save_path(self.common.state_path, &self.common.custom_state_path),
280            window_size: self.common.window_size(),
281            renderer_config: RendererConfig {
282                wgpu_backend: self.common.wgpu_backend,
283                wgpu_power_preference: self.common.wgpu_power_preference,
284                vsync_mode: self.common.vsync_mode,
285                frame_time_sync: self.common.frame_time_sync,
286                prescale_mode: if self.common.auto_prescale {
287                    PrescaleMode::Auto
288                } else {
289                    PrescaleMode::Manual {
290                        width: self.common.prescale_width,
291                        height: self.common.prescale_height,
292                    }
293                },
294                scanlines_enabled: self.common.scanlines_enabled,
295                scanlines_brightness: self.common.scanlines_brightness,
296                force_integer_height_scaling: self.common.force_integer_height_scaling,
297                filter_mode: self.common.filter_mode,
298                supersample_minification: self.common.supersample_minification,
299                preprocess_shader: self.common.preprocess_shader,
300                anti_dither_shader: self.common.anti_dither_shader,
301                frame_rotation: self.common.frame_rotation,
302                ntsc_config: self.common.ntsc,
303            },
304            fast_forward_multiplier: self.common.fast_forward_multiplier,
305            rewind_buffer_length_seconds: self.common.rewind_buffer_length_seconds,
306            load_recent_state_at_launch: self.common.load_recent_state_at_launch,
307            launch_in_fullscreen: self.common.launch_in_fullscreen,
308            borderless_window: self.common.borderless_window,
309            initial_window_size: self.common.initial_window_size,
310            axis_deadzone: self.input.axis_deadzone,
311            hotkey_config: self.input.hotkeys.clone(),
312            pause_emulator: self.common.pause_emulator,
313            hide_mouse_cursor: self.common.hide_mouse_cursor,
314            egui_theme: self.egui_theme,
315        }
316    }
317
318    fn genesis_config(
319        &self,
320        path: PathBuf,
321        secondary_path: Option<PathBuf>,
322        hardware: Option<GenesisHardware>,
323        cheats: &GenesisCheats,
324    ) -> Box<GenesisConfig> {
325        Box::new(GenesisConfig {
326            common: self.common_config(path),
327            secondary_path,
328            inputs: self.input.genesis.clone(),
329            hardware: hardware.unwrap_or(GenesisHardware::Standalone),
330            emulator_config: GenesisEmulatorConfig {
331                sega_cd: SegaCdEmulatorConfig {
332                    pcm_interpolation: self.sega_cd.pcm_interpolation,
333                    enable_ram_cartridge: self.sega_cd.enable_ram_cartridge,
334                    load_disc_into_ram: self.sega_cd.load_disc_into_ram,
335                    disc_drive_speed: self.sega_cd.disc_drive_speed,
336                    sub_cpu_divider: self.sega_cd.sub_cpu_divider,
337                    pcm_lpf_enabled: self.sega_cd.pcm_lpf_enabled,
338                    pcm_lpf_cutoff: self.sega_cd.pcm_lpf_cutoff,
339                    apply_genesis_lpf_to_pcm: self.sega_cd.apply_genesis_lpf_to_pcm,
340                    apply_genesis_lpf_to_cd_da: self.sega_cd.apply_genesis_lpf_to_cd_da,
341                    pcm_enabled: self.sega_cd.pcm_enabled,
342                    cd_audio_enabled: self.sega_cd.cd_audio_enabled,
343                    pcm_volume_adjustment_db: self.sega_cd.pcm_volume_adjustment_db,
344                    cd_volume_adjustment_db: self.sega_cd.cd_volume_adjustment_db,
345                },
346                sega_32x: Sega32XEmulatorConfig {
347                    sh2_clock_multiplier: self.sega_32x.sh2_clock_multiplier,
348                    video_out: self.sega_32x.video_out,
349                    darken_genesis_colors: self.sega_32x.darken_genesis_colors,
350                    color_tint: self.sega_32x.color_tint,
351                    show_high_priority: self.sega_32x.show_high_priority,
352                    show_low_priority: self.sega_32x.show_low_priority,
353                    void_color: match self.sega_32x.void_color_type {
354                        S32XVoidColorType::PaletteRam => {
355                            S32XVoidColor::PaletteRam { idx: self.sega_32x.void_palette_index }
356                        }
357                        S32XVoidColorType::Direct => {
358                            let [r, g, b] = self.sega_32x.void_direct;
359                            S32XVoidColor::Direct { r, g, b, a: self.sega_32x.void_direct_priority }
360                        }
361                    },
362                    apply_genesis_lpf_to_pwm: self.sega_32x.apply_genesis_lpf_to_pwm,
363                    pwm_resampling: self.sega_32x.pwm_resampling,
364                    pwm_enabled: self.sega_32x.pwm_enabled,
365                    pwm_volume_adjustment_db: self.sega_32x.pwm_volume_adjustment_db,
366                },
367                forced_timing_mode: self.genesis.forced_timing_mode,
368                forced_region: self.genesis.forced_region,
369                allow_opposing_joypad_directions: self.genesis.allow_opposing_joypad_directions,
370                mega_mouse_sensitivity: self.genesis.mega_mouse_sensitivity,
371                auto_3_button_mode: self.genesis.auto_3_button_mode,
372                aspect_ratio: self.genesis.aspect_ratio,
373                force_square_pixels_in_h40: self.genesis.force_square_pixels_in_h40,
374                anamorphic_widescreen: self.genesis.anamorphic_widescreen,
375                adjust_aspect_ratio_in_2x_resolution: self
376                    .genesis
377                    .adjust_aspect_ratio_in_2x_resolution,
378                remove_sprite_limits: self.genesis.remove_sprite_limits,
379                m68k_clock_divider: self.genesis.m68k_clock_divider,
380                non_linear_color_scale: self.genesis.non_linear_color_scale,
381                deinterlace: self.genesis.deinterlace,
382                render_vertical_border: self.genesis.render_vertical_border,
383                render_horizontal_border: self.genesis.render_horizontal_border,
384                plane_a_enabled: self.genesis.plane_a_enabled,
385                plane_b_enabled: self.genesis.plane_b_enabled,
386                sprites_enabled: self.genesis.sprites_enabled,
387                window_enabled: self.genesis.window_enabled,
388                quantize_ym2612_output: self.genesis.quantize_ym2612_output,
389                emulate_ym2612_ladder_effect: self.genesis.emulate_ym2612_ladder_effect,
390                opn2_busy_behavior: self.genesis.opn2_busy_behavior,
391                genesis_lpf_enabled: self.genesis.genesis_lpf_enabled,
392                genesis_lpf_cutoff: self.genesis.genesis_lpf_cutoff,
393                ym2612_2nd_lpf_enabled: self.genesis.ym2612_2nd_lpf_enabled,
394                ym2612_2nd_lpf_cutoff: self.genesis.ym2612_2nd_lpf_cutoff,
395                ym2612_channels_enabled: self.genesis.ym2612_channels_enabled,
396                ym2612_enabled: self.genesis.ym2612_enabled,
397                psg_enabled: self.genesis.psg_enabled,
398                ym2612_volume_adjustment_db: self.genesis.ym2612_volume_adjustment_db,
399                psg_volume_adjustment_db: self.genesis.psg_volume_adjustment_db,
400                cheat_codes: cheats.to_memory_override_vec(),
401            },
402            scd_us_bios_path: self.sega_cd.bios_path.clone(),
403            scd_eu_bios_path: self.sega_cd.eu_bios_path.clone(),
404            scd_jp_bios_path: self.sega_cd.jp_bios_path.clone(),
405            scd_per_region_bios: self.sega_cd.per_region_bios,
406            scd_run_without_disc: false,
407        })
408    }
409
410    fn smsgg_config(
411        &self,
412        path: PathBuf,
413        hardware: Option<SmsGgHardware>,
414        cheats: &SmsGgCheats,
415    ) -> Box<SmsGgConfig> {
416        Box::new(SmsGgConfig {
417            common: self.common_config(path),
418            inputs: self.input.smsgg.clone(),
419            hardware,
420            emulator_config: SmsGgEmulatorConfig {
421                sms_timing_mode: self.smsgg.sms_timing_mode,
422                sms_model: self.smsgg.sms_model,
423                forced_psg_version: self.smsgg.psg_version,
424                remove_sprite_limit: self.smsgg.remove_sprite_limit,
425                sms_aspect_ratio: self.smsgg.sms_aspect_ratio,
426                gg_aspect_ratio: self.smsgg.gg_aspect_ratio,
427                forced_region: self.smsgg.forced_region,
428                sms_crop_vertical_border: self.smsgg.sms_crop_vertical_border,
429                sms_crop_left_border: self.smsgg.sms_crop_left_border,
430                gg_frame_blending: self.smsgg.gg_frame_blending,
431                gg_use_sms_resolution: self.smsgg.gg_use_sms_resolution,
432                fm_sound_unit_enabled: self.smsgg.fm_sound_unit_enabled,
433                z80_divider: self.smsgg.z80_divider,
434                allow_opposing_joypad_directions: self.smsgg.allow_opposing_joypad_directions,
435                cheat_codes: cheats.to_memory_override_vec(),
436            },
437            sms_boot_from_bios: self.smsgg.sms_boot_from_bios,
438            gg_boot_from_bios: self.smsgg.gg_boot_from_bios,
439            run_without_cartridge: false,
440            sms_bios_path: self.smsgg.sms_bios_path.clone(),
441            gg_bios_path: self.smsgg.gg_bios_path.clone(),
442        })
443    }
444
445    fn nes_config(&self, path: PathBuf) -> Box<NesConfig> {
446        Box::new(NesConfig {
447            common: self.common_config(path),
448            inputs: self.input.nes.clone(),
449            emulator_config: NesEmulatorConfig {
450                forced_timing_mode: self.nes.forced_timing_mode,
451                aspect_ratio: self.nes.aspect_ratio,
452                palette: self.nes.palette,
453                ntsc_crop_vertical_overscan: self.nes.ntsc_crop_vertical_overscan,
454                overscan: self.nes.overscan,
455                emulate_ntsc_output: self.common.preprocess_shader
456                    == PreprocessShader::NtscComposite,
457                remove_sprite_limit: self.nes.remove_sprite_limit,
458                pal_black_border: self.nes.pal_black_border,
459                silence_ultrasonic_triangle_output: self.nes.silence_ultrasonic_triangle_output,
460                audio_resampler: self.nes.audio_resampler,
461                audio_refresh_rate_adjustment: self.nes.audio_60hz_hack,
462                allow_opposing_joypad_directions: self.nes.allow_opposing_joypad_directions,
463                dma_dummy_joy_reads: self.nes.dma_dummy_joy_reads,
464            },
465        })
466    }
467
468    fn snes_config(&self, path: PathBuf) -> Box<SnesConfig> {
469        Box::new(SnesConfig {
470            common: self.common_config(path),
471            inputs: self.input.snes.clone(),
472            emulator_config: SnesEmulatorConfig {
473                forced_timing_mode: self.snes.forced_timing_mode,
474                aspect_ratio: self.snes.aspect_ratio,
475                deinterlace: self.snes.deinterlace,
476                audio_interpolation: self.snes.audio_interpolation,
477                audio_60hz_hack: self.snes.audio_60hz_hack,
478                gsu_overclock_factor: self.snes.gsu_overclock_factor,
479                allow_opposing_joypad_directions: self.snes.allow_opposing_joypad_directions,
480                mouse_sensitivity: self.snes.mouse_sensitivity,
481            },
482            dsp1_rom_path: self.snes.dsp1_rom_path.clone(),
483            dsp2_rom_path: self.snes.dsp2_rom_path.clone(),
484            dsp3_rom_path: self.snes.dsp3_rom_path.clone(),
485            dsp4_rom_path: self.snes.dsp4_rom_path.clone(),
486            st010_rom_path: self.snes.st010_rom_path.clone(),
487            st011_rom_path: self.snes.st011_rom_path.clone(),
488            st018_rom_path: self.snes.st018_rom_path.clone(),
489        })
490    }
491
492    fn gb_config(&self, path: PathBuf) -> Box<GameBoyConfig> {
493        let color_correction = match self.game_boy.gbc_color_correction {
494            GbcColorCorrection::None => ColorCorrection::None,
495            GbcColorCorrection::GbcLcd => ColorCorrection::GbcLcd {
496                screen_gamma: convert_color_correct_gamma(self.game_boy.gbc_correction_gamma),
497            },
498            GbcColorCorrection::GbaLcd => ColorCorrection::GbaLcd {
499                screen_gamma: convert_color_correct_gamma(self.game_boy.gba_correction_gamma),
500            },
501        };
502
503        Box::new(GameBoyConfig {
504            common: self.common_config(path),
505            inputs: self.input.game_boy.clone(),
506            emulator_config: GameBoyEmulatorConfig {
507                force_dmg_mode: self.game_boy.force_dmg_mode,
508                force_cgb_mode: self.game_boy.force_cgb_mode,
509                pretend_to_be_gba: self.game_boy.pretend_to_be_gba,
510                aspect_ratio: self.game_boy.aspect_ratio,
511                gb_palette: self.game_boy.gb_palette,
512                gb_custom_palette: self.game_boy.gb_custom_palette,
513                gbc_color_correction: color_correction,
514                frame_blending: self.game_boy.frame_blending,
515                audio_resampler: self.game_boy.audio_resampler,
516                audio_60hz_hack: self.game_boy.audio_60hz_hack,
517                allow_opposing_joypad_directions: self.game_boy.allow_opposing_joypad_directions,
518            },
519            dmg_boot_rom: self.game_boy.dmg_boot_rom,
520            cgb_boot_rom: self.game_boy.cgb_boot_rom,
521            dmg_boot_rom_path: self.game_boy.dmg_boot_rom_path.clone(),
522            cgb_boot_rom_path: self.game_boy.cgb_boot_rom_path.clone(),
523        })
524    }
525
526    fn gba_config(&self, path: PathBuf) -> Box<GameBoyAdvanceConfig> {
527        let color_correction = match self.game_boy_advance.color_correction {
528            GbaColorCorrection::None => ColorCorrection::None,
529            GbaColorCorrection::GbaLcd => ColorCorrection::GbaLcd {
530                screen_gamma: convert_color_correct_gamma(
531                    self.game_boy_advance.color_correction_gamma,
532                ),
533            },
534        };
535
536        Box::new(GameBoyAdvanceConfig {
537            common: self.common_config(path),
538            inputs: self.input.game_boy_advance.clone(),
539            emulator_config: GbaEmulatorConfig {
540                skip_bios_animation: self.game_boy_advance.skip_bios_animation,
541                aspect_ratio: self.game_boy_advance.aspect_ratio,
542                color_correction,
543                frame_blending: self.game_boy_advance.frame_blending,
544                forced_save_memory_type: self.game_boy_advance.forced_save_memory_type,
545                allow_opposing_joypad_directions: self
546                    .game_boy_advance
547                    .allow_opposing_joypad_directions,
548                audio: GbaAudioConfig {
549                    audio_interpolation: self.game_boy_advance.audio_interpolation,
550                    psg_low_pass: self.game_boy_advance.psg_low_pass,
551                    pulse_1_enabled: self.game_boy_advance.pulse_1_enabled,
552                    pulse_2_enabled: self.game_boy_advance.pulse_2_enabled,
553                    wavetable_enabled: self.game_boy_advance.wavetable_enabled,
554                    noise_enabled: self.game_boy_advance.noise_enabled,
555                    pcm_a_enabled: self.game_boy_advance.pcm_a_enabled,
556                    pcm_b_enabled: self.game_boy_advance.pcm_b_enabled,
557                },
558            },
559            bios_path: self.game_boy_advance.bios_path.clone(),
560            solar_brightness_step: self.game_boy_advance.solar_brightness_step,
561            solar_min_brightness: self.game_boy_advance.solar_min_brightness,
562            solar_max_brightness: self.game_boy_advance.solar_max_brightness,
563        })
564    }
565
566    fn pce_config(&self, path: PathBuf) -> Box<PcEngineConfig> {
567        Box::new(PcEngineConfig {
568            common: self.common_config(path),
569            inputs: self.input.pc_engine.clone(),
570            emulator_config: PceEmulatorConfig {
571                region: self.pc_engine.region,
572                cpu_fast_clock_divider: self.pc_engine.cpu_fast_clock_divider,
573                aspect_ratio: self.pc_engine.aspect_ratio,
574                palette: self.pc_engine.palette,
575                crop_overscan: self.pc_engine.crop_overscan,
576                remove_sprite_limits: self.pc_engine.remove_sprite_limits,
577                audio_resampler: self.pc_engine.audio_resampler,
578                input_device: self.pc_engine.input_device,
579                turbo_tap_connected: self.pc_engine.turbo_tap_connected,
580                allow_opposing_joypad_directions: self.pc_engine.allow_opposing_joypad_directions,
581                allow_simultaneous_run_select: self.pc_engine.allow_simultaneous_run_select,
582            },
583        })
584    }
585}
586
587fn convert_color_correct_gamma(gamma: f64) -> FiniteF32 {
588    FiniteF32::try_from(gamma as f32).unwrap_or(fallback_color_correct_gamma())
589}
590
591fn fallback_color_correct_gamma() -> FiniteF32 {
592    FiniteF32::try_from(2.2).unwrap()
593}