use std::collections::BTreeMap; use eframe::Storage; use globset::Glob; use objdiff_core::{ config::ScratchConfig, diff::{ ArmArchVersion, ArmR9Usage, DiffObjConfig, FunctionRelocDiffs, MipsAbi, MipsInstrCategory, X86Formatter, }, }; use typed_path::{Utf8PlatformPathBuf, Utf8UnixPathBuf}; use crate::app::{AppConfig, CONFIG_KEY, ObjectConfig}; #[derive(Clone, serde::Deserialize, serde::Serialize)] pub struct AppConfigVersion { pub version: u32, } impl Default for AppConfigVersion { fn default() -> Self { Self { version: 3 } } } /// Deserialize the AppConfig from storage, handling upgrades from older versions. pub fn deserialize_config(storage: &dyn Storage) -> Option { let str = storage.get_string(CONFIG_KEY)?; match ron::from_str::(&str) { Ok(version) => match version.version { 3 => from_str::(&str), 2 => from_str::(&str).map(|c| c.into_config()), 1 => from_str::(&str).map(|c| c.into_config()), _ => { log::warn!("Unknown config version: {}", version.version); None } }, Err(e) => { log::warn!("Failed to decode config version: {e}"); // Try to decode as v0 from_str::(&str).map(|c| c.into_config()) } } } fn from_str(str: &str) -> Option where T: serde::de::DeserializeOwned { match ron::from_str(str) { Ok(config) => Some(config), Err(err) => { log::warn!("Failed to decode config: {err}"); None } } } #[derive(serde::Deserialize, serde::Serialize)] pub struct ScratchConfigV2 { #[serde(default)] pub platform: Option, #[serde(default)] pub compiler: Option, #[serde(default)] pub c_flags: Option, #[serde(default)] pub ctx_path: Option, #[serde(default)] pub build_ctx: Option, #[serde(default)] pub preset_id: Option, } impl ScratchConfigV2 { fn into_config(self) -> ScratchConfig { ScratchConfig { platform: self.platform, compiler: self.compiler, c_flags: self.c_flags, ctx_path: self.ctx_path.map(Utf8UnixPathBuf::from), build_ctx: self.build_ctx, preset_id: self.preset_id, } } } #[derive(serde::Deserialize, serde::Serialize)] pub struct ObjectConfigV2 { pub name: String, pub target_path: Option, pub base_path: Option, pub reverse_fn_order: Option, pub complete: Option, pub scratch: Option, pub source_path: Option, #[serde(default)] pub symbol_mappings: BTreeMap, } impl ObjectConfigV2 { fn into_config(self) -> ObjectConfig { ObjectConfig { name: self.name, target_path: self.target_path.map(Utf8PlatformPathBuf::from), base_path: self.base_path.map(Utf8PlatformPathBuf::from), reverse_fn_order: self.reverse_fn_order, complete: self.complete, hidden: false, scratch: self.scratch.map(|scratch| scratch.into_config()), source_path: None, symbol_mappings: self.symbol_mappings, } } } #[derive(serde::Deserialize, serde::Serialize)] pub struct AppConfigV2 { pub version: u32, #[serde(default)] pub custom_make: Option, #[serde(default)] pub custom_args: Option>, #[serde(default)] pub selected_wsl_distro: Option, #[serde(default)] pub project_dir: Option, #[serde(default)] pub target_obj_dir: Option, #[serde(default)] pub base_obj_dir: Option, #[serde(default)] pub selected_obj: Option, #[serde(default = "bool_true")] pub build_base: bool, #[serde(default)] pub build_target: bool, #[serde(default = "bool_true")] pub rebuild_on_changes: bool, #[serde(default)] pub auto_update_check: bool, #[serde(default)] pub watch_patterns: Vec, #[serde(default)] pub recent_projects: Vec, #[serde(default)] pub diff_obj_config: DiffObjConfigV1, } impl AppConfigV2 { fn into_config(self) -> AppConfig { log::info!("Upgrading configuration from v2"); AppConfig { custom_make: self.custom_make, custom_args: self.custom_args, selected_wsl_distro: self.selected_wsl_distro, project_dir: self.project_dir.map(Utf8PlatformPathBuf::from), target_obj_dir: self.target_obj_dir.map(Utf8PlatformPathBuf::from), base_obj_dir: self.base_obj_dir.map(Utf8PlatformPathBuf::from), selected_obj: self.selected_obj.map(|obj| obj.into_config()), build_base: self.build_base, build_target: self.build_target, rebuild_on_changes: self.rebuild_on_changes, auto_update_check: self.auto_update_check, watch_patterns: self.watch_patterns, recent_projects: self.recent_projects, diff_obj_config: self.diff_obj_config.into_config(), ..Default::default() } } } #[derive(serde::Deserialize, serde::Serialize)] pub struct ScratchConfigV1 { #[serde(default)] pub platform: Option, #[serde(default)] pub compiler: Option, #[serde(default)] pub c_flags: Option, #[serde(default)] pub ctx_path: Option, #[serde(default)] pub build_ctx: bool, } impl ScratchConfigV1 { fn into_config(self) -> ScratchConfig { ScratchConfig { platform: self.platform, compiler: self.compiler, c_flags: self.c_flags, ctx_path: self.ctx_path.map(Utf8UnixPathBuf::from), build_ctx: self.build_ctx.then_some(true), preset_id: None, } } } #[derive(serde::Deserialize, serde::Serialize)] pub struct ObjectConfigV1 { pub name: String, pub target_path: Option, pub base_path: Option, pub reverse_fn_order: Option, pub complete: Option, pub scratch: Option, pub source_path: Option, } impl ObjectConfigV1 { fn into_config(self) -> ObjectConfig { ObjectConfig { name: self.name, target_path: self.target_path.map(Utf8PlatformPathBuf::from), base_path: self.base_path.map(Utf8PlatformPathBuf::from), reverse_fn_order: self.reverse_fn_order, complete: self.complete, scratch: self.scratch.map(|scratch| scratch.into_config()), source_path: None, ..Default::default() } } } #[derive(serde::Deserialize, serde::Serialize)] #[serde(default)] pub struct DiffObjConfigV1 { pub relax_reloc_diffs: bool, #[serde(default = "bool_true")] pub space_between_args: bool, pub combine_data_sections: bool, // x86 pub x86_formatter: X86Formatter, // MIPS pub mips_abi: MipsAbi, pub mips_instr_category: MipsInstrCategory, // ARM pub arm_arch_version: ArmArchVersion, pub arm_unified_syntax: bool, pub arm_av_registers: bool, pub arm_r9_usage: ArmR9Usage, pub arm_sl_usage: bool, pub arm_fp_usage: bool, pub arm_ip_usage: bool, } impl Default for DiffObjConfigV1 { fn default() -> Self { Self { relax_reloc_diffs: false, space_between_args: true, combine_data_sections: false, x86_formatter: Default::default(), mips_abi: Default::default(), mips_instr_category: Default::default(), arm_arch_version: Default::default(), arm_unified_syntax: true, arm_av_registers: false, arm_r9_usage: Default::default(), arm_sl_usage: false, arm_fp_usage: false, arm_ip_usage: false, } } } impl DiffObjConfigV1 { fn into_config(self) -> DiffObjConfig { DiffObjConfig { function_reloc_diffs: if self.relax_reloc_diffs { FunctionRelocDiffs::None } else { FunctionRelocDiffs::default() }, space_between_args: self.space_between_args, combine_data_sections: self.combine_data_sections, x86_formatter: self.x86_formatter, mips_abi: self.mips_abi, mips_instr_category: self.mips_instr_category, arm_arch_version: self.arm_arch_version, arm_unified_syntax: self.arm_unified_syntax, arm_av_registers: self.arm_av_registers, arm_r9_usage: self.arm_r9_usage, arm_sl_usage: self.arm_sl_usage, arm_fp_usage: self.arm_fp_usage, arm_ip_usage: self.arm_ip_usage, ..Default::default() } } } #[inline] fn bool_true() -> bool { true } #[derive(serde::Deserialize, serde::Serialize)] pub struct AppConfigV1 { pub version: u32, #[serde(default)] pub custom_make: Option, #[serde(default)] pub custom_args: Option>, #[serde(default)] pub selected_wsl_distro: Option, #[serde(default)] pub project_dir: Option, #[serde(default)] pub target_obj_dir: Option, #[serde(default)] pub base_obj_dir: Option, #[serde(default)] pub selected_obj: Option, #[serde(default = "bool_true")] pub build_base: bool, #[serde(default)] pub build_target: bool, #[serde(default = "bool_true")] pub rebuild_on_changes: bool, #[serde(default)] pub auto_update_check: bool, #[serde(default)] pub watch_patterns: Vec, #[serde(default)] pub recent_projects: Vec, #[serde(default)] pub diff_obj_config: DiffObjConfigV1, } impl AppConfigV1 { fn into_config(self) -> AppConfig { log::info!("Upgrading configuration from v1"); AppConfig { custom_make: self.custom_make, custom_args: self.custom_args, selected_wsl_distro: self.selected_wsl_distro, project_dir: self.project_dir.map(Utf8PlatformPathBuf::from), target_obj_dir: self.target_obj_dir.map(Utf8PlatformPathBuf::from), base_obj_dir: self.base_obj_dir.map(Utf8PlatformPathBuf::from), selected_obj: self.selected_obj.map(|obj| obj.into_config()), build_base: self.build_base, build_target: self.build_target, rebuild_on_changes: self.rebuild_on_changes, auto_update_check: self.auto_update_check, watch_patterns: self.watch_patterns, recent_projects: self.recent_projects, diff_obj_config: self.diff_obj_config.into_config(), ..Default::default() } } } #[derive(serde::Deserialize, serde::Serialize)] pub struct ObjectConfigV0 { pub name: String, pub target_path: String, pub base_path: String, pub reverse_fn_order: Option, } impl ObjectConfigV0 { fn into_config(self) -> ObjectConfig { ObjectConfig { name: self.name, target_path: Some(Utf8PlatformPathBuf::from(self.target_path)), base_path: Some(Utf8PlatformPathBuf::from(self.base_path)), reverse_fn_order: self.reverse_fn_order, ..Default::default() } } } #[derive(serde::Deserialize, serde::Serialize)] pub struct AppConfigV0 { pub custom_make: Option, pub selected_wsl_distro: Option, pub project_dir: Option, pub target_obj_dir: Option, pub base_obj_dir: Option, pub selected_obj: Option, pub build_target: bool, pub auto_update_check: bool, pub watch_patterns: Vec, } impl AppConfigV0 { fn into_config(self) -> AppConfig { log::info!("Upgrading configuration from v0"); AppConfig { custom_make: self.custom_make, selected_wsl_distro: self.selected_wsl_distro, project_dir: self.project_dir.map(Utf8PlatformPathBuf::from), target_obj_dir: self.target_obj_dir.map(Utf8PlatformPathBuf::from), base_obj_dir: self.base_obj_dir.map(Utf8PlatformPathBuf::from), selected_obj: self.selected_obj.map(|obj| obj.into_config()), build_target: self.build_target, auto_update_check: self.auto_update_check, watch_patterns: self.watch_patterns, ..Default::default() } } }