1use crate::input::GenericInput; 2use crate::input::mappings::{GenesisControllerMapping, HotkeyConfig, SnesControllerMapping}; 3use crate::{AppConfig, RomSearchDirectory}; 4use serde::Deserialize; 5 6pub fn migrate_config_0_14_0(config: &mut AppConfig, config_str: &str) { 7 // rom_search_dirs field changed from Vec<String> to Vec<RomSearchDirectory> 8 migrate_0_14_0_rom_search_dirs(config, config_str); 9 10 // New buttons/hotkeys for Mega Mouse and SNES Mouse 11 migrate_0_14_0_mouse_inputs(config); 12 13 // SNES Super Scope input config format changed 14 migrate_0_14_0_super_scope_inputs(config, config_str); 15} 16 17fn migrate_0_14_0_rom_search_dirs(config: &mut AppConfig, config_str: &str) { 18 #[derive(Deserialize)] 19 struct OldConfig { 20 rom_search_dirs: Vec<String>, 21 } 22 23 let Ok(old_config) = toml::from_str::<OldConfig>(config_str) else { return }; 24 25 if !old_config.rom_search_dirs.is_empty() && config.rom_search_dirs.is_empty() { 26 log::info!("Converting rom_search_dirs to new config format"); 27 28 config.rom_search_dirs = old_config 29 .rom_search_dirs 30 .into_iter() 31 .map(|path| RomSearchDirectory { path: path.into(), recursive: false }) 32 .collect(); 33 } 34} 35 36fn migrate_0_14_0_mouse_inputs(config: &mut AppConfig) { 37 let defaults = GenesisControllerMapping::keyboard_wasd(); 38 for (mapping, default) in [ 39 (&mut config.input.genesis.mapping_1.p1.mega_mouse_left, defaults.mega_mouse_left), 40 (&mut config.input.genesis.mapping_1.p1.mega_mouse_right, defaults.mega_mouse_right), 41 (&mut config.input.genesis.mapping_1.p1.mega_mouse_middle, defaults.mega_mouse_middle), 42 (&mut config.input.genesis.mapping_1.p1.mega_mouse_start, defaults.mega_mouse_start), 43 ( 44 &mut config.input.snes.mapping_1.p1.mouse_left, 45 SnesControllerMapping::default().mouse_left, 46 ), 47 ( 48 &mut config.input.snes.mapping_1.p1.mouse_right, 49 SnesControllerMapping::default().mouse_right, 50 ), 51 ( 52 &mut config.input.hotkeys.mapping_1.cancel_mouse_input, 53 HotkeyConfig::default().mapping_1.cancel_mouse_input, 54 ), 55 ] { 56 if mapping.is_none() { 57 *mapping = default; 58 } 59 } 60} 61 62fn migrate_0_14_0_super_scope_inputs(config: &mut AppConfig, config_str: &str) { 63 #[derive(Default, Deserialize)] 64 struct OldSuperScopeConfig { 65 fire: Option<Vec<GenericInput>>, 66 cursor: Option<Vec<GenericInput>>, 67 pause: Option<Vec<GenericInput>>, 68 turbo_toggle: Option<Vec<GenericInput>>, 69 } 70 71 #[derive(Default, Deserialize)] 72 struct OldSnesInputMapping { 73 super_scope: OldSuperScopeConfig, 74 } 75 76 #[derive(Default, Deserialize)] 77 #[serde(default)] 78 struct OldSnesInputConfig { 79 mapping_1: OldSnesInputMapping, 80 mapping_2: OldSnesInputMapping, 81 } 82 83 #[derive(Deserialize)] 84 struct OldInputConfig { 85 snes: OldSnesInputConfig, 86 } 87 88 #[derive(Deserialize)] 89 struct OldConfig { 90 input: OldInputConfig, 91 } 92 93 let Ok(old_config) = toml::from_str::<OldConfig>(config_str) else { return }; 94 95 for (new_mapping, old_mapping) in [ 96 (&mut config.input.snes.mapping_1.p2, &old_config.input.snes.mapping_1.super_scope), 97 (&mut config.input.snes.mapping_2.p2, &old_config.input.snes.mapping_2.super_scope), 98 ] { 99 for (new_field, old_field) in [ 100 (&mut new_mapping.super_scope_fire, &old_mapping.fire), 101 (&mut new_mapping.super_scope_cursor, &old_mapping.cursor), 102 (&mut new_mapping.super_scope_pause, &old_mapping.pause), 103 (&mut new_mapping.super_scope_turbo_toggle, &old_mapping.turbo_toggle), 104 ] { 105 if new_field.is_none() { 106 new_field.clone_from(old_field); 107 } 108 } 109 } 110} 111 112#[cfg(test)] 113mod tests { 114 use super::*; 115 use crate::input::KeyboardInput; 116 use crate::input::mappings::SnesControllerMapping; 117 use sdl3::keyboard::Keycode; 118 use sdl3::mouse::MouseButton; 119 120 #[test] 121 fn migrate_super_scope_config() { 122 let config_str = r#" 123[[input.snes.mapping_1.super_scope.fire]] 124type = "Mouse" 125button = "Left" 126 127[[input.snes.mapping_1.super_scope.cursor]] 128type = "Mouse" 129button = "Right" 130 131[[input.snes.mapping_1.super_scope.pause]] 132type = "Mouse" 133button = "Middle" 134 135[[input.snes.mapping_1.super_scope.turbo_toggle]] 136type = "Keyboard" 137key = "T" 138 "#; 139 140 let mut config = AppConfig::default(); 141 config.input.snes.mapping_1.p2 = SnesControllerMapping::default(); 142 assert!(config.input.snes.mapping_1.p2.super_scope_fire.is_none()); 143 assert!(config.input.snes.mapping_1.p2.super_scope_cursor.is_none()); 144 assert!(config.input.snes.mapping_1.p2.super_scope_pause.is_none()); 145 assert!(config.input.snes.mapping_1.p2.super_scope_turbo_toggle.is_none()); 146 147 migrate_config_0_14_0(&mut config, config_str); 148 149 assert_eq!( 150 config.input.snes.mapping_1.p2.super_scope_fire, 151 Some(vec![GenericInput::Mouse(MouseButton::Left)]) 152 ); 153 assert_eq!( 154 config.input.snes.mapping_1.p2.super_scope_cursor, 155 Some(vec![GenericInput::Mouse(MouseButton::Right)]) 156 ); 157 assert_eq!( 158 config.input.snes.mapping_1.p2.super_scope_pause, 159 Some(vec![GenericInput::Mouse(MouseButton::Middle)]) 160 ); 161 assert_eq!( 162 config.input.snes.mapping_1.p2.super_scope_turbo_toggle, 163 Some(vec![GenericInput::Keyboard(KeyboardInput::Keycode(Keycode::T))]) 164 ); 165 } 166}