From ac96e7be69d849a59b8f6f24e7414906e68b3c51 Mon Sep 17 00:00:00 2001 From: Novattz Date: Tue, 23 Dec 2025 01:59:06 +0100 Subject: [PATCH] smokeapi config backend implementation #67 --- src-tauri/src/main.rs | 25 ++++++ src-tauri/src/smokeapi_config.rs | 128 +++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 src-tauri/src/smokeapi_config.rs diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index fa06810..7b738ef 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -8,6 +8,7 @@ mod dlc_manager; mod installer; mod searcher; mod unlockers; +mod smokeapi_config; use dlc_manager::DlcInfoWithState; use installer::{Game, InstallerAction, InstallerType}; @@ -434,6 +435,27 @@ async fn install_cream_with_dlcs_command( } } +#[tauri::command] +fn read_smokeapi_config(game_path: String) -> Result, String> { + info!("Reading SmokeAPI config for: {}", game_path); + smokeapi_config::read_config(&game_path) +} + +#[tauri::command] +fn write_smokeapi_config( + game_path: String, + config: smokeapi_config::SmokeAPIConfig, +) -> Result<(), String> { + info!("Writing SmokeAPI config for: {}", game_path); + smokeapi_config::write_config(&game_path, &config) +} + +#[tauri::command] +fn delete_smokeapi_config(game_path: String) -> Result<(), String> { + info!("Deleting SmokeAPI config for: {}", game_path); + smokeapi_config::delete_config(&game_path) +} + fn setup_logging() -> Result<(), Box> { use log::LevelFilter; use log4rs::append::file::FileAppender; @@ -491,6 +513,9 @@ fn main() { get_all_dlcs_command, clear_caches, abort_dlc_fetch, + read_smokeapi_config, + write_smokeapi_config, + delete_smokeapi_config, ]) .setup(|app| { info!("Tauri application setup"); diff --git a/src-tauri/src/smokeapi_config.rs b/src-tauri/src/smokeapi_config.rs new file mode 100644 index 0000000..4680a87 --- /dev/null +++ b/src-tauri/src/smokeapi_config.rs @@ -0,0 +1,128 @@ +use log::{info, warn}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::fs; +use std::path::Path; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SmokeAPIConfig { + #[serde(rename = "$schema")] + pub schema: String, + #[serde(rename = "$version")] + pub version: u32, + pub logging: bool, + pub log_steam_http: bool, + pub default_app_status: String, + pub override_app_status: HashMap, + pub override_dlc_status: HashMap, + pub auto_inject_inventory: bool, + pub extra_inventory_items: Vec, + pub extra_dlcs: HashMap, +} + +impl Default for SmokeAPIConfig { + fn default() -> Self { + Self { + schema: "https://raw.githubusercontent.com/acidicoala/SmokeAPI/refs/tags/v4.0.0/res/SmokeAPI.schema.json".to_string(), + version: 4, + logging: false, + log_steam_http: false, + default_app_status: "unlocked".to_string(), + override_app_status: HashMap::new(), + override_dlc_status: HashMap::new(), + auto_inject_inventory: true, + extra_inventory_items: Vec::new(), + extra_dlcs: HashMap::new(), + } + } +} + +// Read SmokeAPI config from a game directory +// Returns None if the config doesn't exist +pub fn read_config(game_path: &str) -> Result, String> { + info!("Reading SmokeAPI config from: {}", game_path); + + // Find the SmokeAPI DLL location in the game directory + let config_path = find_smokeapi_config_path(game_path)?; + + if !config_path.exists() { + info!("No SmokeAPI config found at: {}", config_path.display()); + return Ok(None); + } + + let content = fs::read_to_string(&config_path) + .map_err(|e| format!("Failed to read SmokeAPI config: {}", e))?; + + let config: SmokeAPIConfig = serde_json::from_str(&content) + .map_err(|e| format!("Failed to parse SmokeAPI config: {}", e))?; + + info!("Successfully read SmokeAPI config"); + Ok(Some(config)) +} + +// Write SmokeAPI config to a game directory +pub fn write_config(game_path: &str, config: &SmokeAPIConfig) -> Result<(), String> { + info!("Writing SmokeAPI config to: {}", game_path); + + let config_path = find_smokeapi_config_path(game_path)?; + + let content = serde_json::to_string_pretty(config) + .map_err(|e| format!("Failed to serialize SmokeAPI config: {}", e))?; + + fs::write(&config_path, content) + .map_err(|e| format!("Failed to write SmokeAPI config: {}", e))?; + + info!("Successfully wrote SmokeAPI config to: {}", config_path.display()); + Ok(()) +} + +// Delete SmokeAPI config from a game directory +pub fn delete_config(game_path: &str) -> Result<(), String> { + info!("Deleting SmokeAPI config from: {}", game_path); + + let config_path = find_smokeapi_config_path(game_path)?; + + if config_path.exists() { + fs::remove_file(&config_path) + .map_err(|e| format!("Failed to delete SmokeAPI config: {}", e))?; + info!("Successfully deleted SmokeAPI config"); + } else { + info!("No SmokeAPI config to delete"); + } + + Ok(()) +} + +// Find the path where SmokeAPI.config.json should be located +// This is in the same directory as the SmokeAPI DLL files +fn find_smokeapi_config_path(game_path: &str) -> Result { + let game_path_obj = Path::new(game_path); + + // Search for steam_api*.dll files with _o.dll backups (indicating SmokeAPI installation) + let mut smokeapi_dir: Option = None; + + // Use walkdir to search recursively + for entry in walkdir::WalkDir::new(game_path_obj) + .max_depth(5) + .into_iter() + .filter_map(Result::ok) + { + let path = entry.path(); + let filename = path.file_name().unwrap_or_default().to_string_lossy(); + + // Look for steam_api*_o.dll (backup files created by SmokeAPI) + if filename.starts_with("steam_api") && filename.ends_with("_o.dll") { + smokeapi_dir = path.parent().map(|p| p.to_path_buf()); + break; + } + } + + // If we found a SmokeAPI directory, return the config path + if let Some(dir) = smokeapi_dir { + Ok(dir.join("SmokeAPI.config.json")) + } else { + // Fallback to game root directory + warn!("Could not find SmokeAPI DLL directory, using game root"); + Ok(game_path_obj.join("SmokeAPI.config.json")) + } +} \ No newline at end of file