use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LauncherConfig { pub core_binary: String, pub bridge_binary: String, pub core_database_url: String, pub core_data_dir: String, pub core_listen_addr: String, pub bridge_listen_addr: String, pub bridge_captures_dir: String, pub bridge_core_url: String, pub bridge_tls_enabled: bool, /// Path to the built openfut_hook.dll (Windows DLL for Proton injection). pub hook_dll_path: String, /// FIFA 23 game folder inside the Proton prefix (where the DLL is deployed). pub fifa_game_dir: String, /// IP the hook DLL redirects EA hostnames to (written to openfut.cfg). pub hook_redirect_ip: String, } impl Default for LauncherConfig { fn default() -> Self { let base = dirs::home_dir() .map(|h| h.join("Documents/OpenFUT")) .unwrap_or_default(); Self { core_binary: base .join("openfut-core/target/release/openfut-core") .to_string_lossy() .into(), bridge_binary: base .join("openfut-bridge/target/release/openfut-bridge") .to_string_lossy() .into(), core_database_url: "sqlite://openfut.db".into(), core_data_dir: base .join("openfut-core/data") .to_string_lossy() .into(), core_listen_addr: "127.0.0.1:8080".into(), bridge_listen_addr: "0.0.0.0:8765".into(), bridge_captures_dir: base .join("openfut-bridge/captures") .to_string_lossy() .into(), bridge_core_url: "http://127.0.0.1:8080".into(), bridge_tls_enabled: true, hook_dll_path: dirs::home_dir() .map(|h| h.join("Documents/openfut-launcher/openfut-hook/target/x86_64-pc-windows-gnu/release/openfut_hook.dll")) .unwrap_or_default() .to_string_lossy() .into(), fifa_game_dir: dirs::home_dir() .map(|h| h.join(".steam/steam/steamapps/common/FIFA 23")) .unwrap_or_default() .to_string_lossy() .into(), hook_redirect_ip: "127.0.0.1".into(), } } } impl LauncherConfig { pub fn config_path() -> std::path::PathBuf { dirs::config_dir() .unwrap_or_else(|| std::path::PathBuf::from(".")) .join("openfut-launcher") .join("config.json") } pub fn load() -> Self { let path = Self::config_path(); std::fs::read_to_string(&path) .ok() .and_then(|s| serde_json::from_str(&s).ok()) .unwrap_or_default() } pub fn save(&self) { let path = Self::config_path(); if let Some(parent) = path.parent() { let _ = std::fs::create_dir_all(parent); } if let Ok(json) = serde_json::to_string_pretty(self) { let _ = std::fs::write(path, json); } } pub fn core_env(&self) -> Vec<(String, String)> { vec![ ("DATABASE_URL".into(), self.core_database_url.clone()), ("DATA_DIR".into(), self.core_data_dir.clone()), ("LISTEN_ADDR".into(), self.core_listen_addr.clone()), ("RUST_LOG".into(), "openfut_core=info,tower_http=info".into()), ] } pub fn bridge_env(&self) -> Vec<(String, String)> { vec![ ("CORE_URL".into(), self.bridge_core_url.clone()), ("LISTEN_ADDR".into(), self.bridge_listen_addr.clone()), ("CAPTURES_DIR".into(), self.bridge_captures_dir.clone()), ("TLS_ENABLED".into(), self.bridge_tls_enabled.to_string()), ("RUST_LOG".into(), "openfut_bridge=info".into()), ] } }