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}