v0_14.rsannotatedv0_14.rssource166 lines · 5.5 KB · raw
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}