Files
achievement-watcher/src/main.rs
T
2025-01-22 17:35:49 +01:00

141 lines
4.3 KiB
Rust

use dirs;
use ini::Ini;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json;
use std::{env, fs};
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct JsonGame {
game: JsonGameContent,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct JsonGameContent {
game_name: String,
game_version: String,
available_game_stats: JsonAvailableGameStats,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct JsonAvailableGameStats {
achievements: Vec<JsonAchievement>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct JsonAchievement {
name: String,
defaultvalue: i32,
display_name: String,
hidden: i32,
description: String,
icon: String,
icongray: String,
}
#[tokio::main]
async fn main() {
let client = Client::new();
let config_home = dirs::config_dir().unwrap().to_str().unwrap().to_string();
let api_key =
fs::read_to_string(format!("{config_home}/achievement-watcher/api-key.txt")).unwrap();
let api_key = api_key.trim();
let wine_home = env::var("WINEPREFIX").unwrap_or(env::var("HOME").unwrap() + ".wine");
let dirs = vec![
wine_home.clone() + "/drive_c/users/Public/Documents/Steam/CODEX",
wine_home.clone() + "/drive_c/users/Public/Documents/Steam/RUNE",
];
let ini_games = get_achievement_data_all(dirs);
println!("ini games: {:#?}", ini_games);
let game_data = get_steam_data(&client, api_key, "2310", "en").await;
println!("game_data: {:#?}", game_data);
}
async fn get_steam_data(client: &Client, key: &str, appid: &str, lang: &str) -> JsonGame {
let url = format!("https://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v0002/?key={key}&appid={appid}&l={lang}&format=json");
let json = client.get(url).send().await.unwrap().text().await.unwrap();
// fs::write("out.json", &json).unwrap();
// let json = fs::read_to_string("out.json").unwrap();
serde_json::from_str(&json).unwrap()
}
#[derive(Debug)]
struct IniAchievement {
name: String,
achieved: bool,
current_progress: u32,
max_progress: u32,
unlock_time: u32,
}
#[derive(Debug)]
struct IniGame {
id: u32,
achievements: Vec<IniAchievement>,
}
impl IniGame {
fn new(id: u32) -> Self {
Self {
id,
achievements: Vec::new(),
}
}
}
impl IniAchievement {
fn new(name: String) -> Self {
Self {
name,
achieved: false,
current_progress: 0,
max_progress: 0,
unlock_time: 0,
}
}
}
fn get_achievement_data_all(dirs: Vec<String>) -> Vec<IniGame> {
let mut ini_games = Vec::new();
for dir in dirs {
let game_dirs = fs::read_dir(dir).unwrap();
for game_dir in game_dirs {
let game_dir = game_dir.unwrap();
let mut ini_game = IniGame::new(
game_dir
.file_name()
.to_str()
.unwrap()
.to_string()
.parse()
.unwrap(),
);
let game_dir_path = game_dir.path().to_str().unwrap().to_string();
let i = Ini::load_from_file(format!("{game_dir_path}/achievements.ini",)).unwrap();
for (sec, prop) in i.iter() {
if let Some(section) = sec {
if section == "SteamAchievements" {
continue;
}
let mut ini_achievement = IniAchievement::new(section.to_string());
for (k, v) in prop.iter() {
match k {
"Achieved" => ini_achievement.achieved = v.parse::<u32>().unwrap() == 1,
"CurProgress" => ini_achievement.current_progress = v.parse().unwrap(),
"MaxProgress" => ini_achievement.max_progress = v.parse().unwrap(),
"UnlockTime" => ini_achievement.unlock_time = v.parse().unwrap(),
s => panic!("unexpected achievement stat {s}"),
}
}
ini_game.achievements.push(ini_achievement);
}
}
ini_games.push(ini_game);
}
}
ini_games
}