dtmt/lib/dtmt-shared/src/lib.rs
Lucas Schwiderski 83de50409b
All checks were successful
lint/clippy Checking for common mistakes and opportunities for code improvement
build/msvc Build for the target platform: msvc
build/linux Build for the target platform: linux
Fix locating Steam game path
2025-04-21 23:55:55 +02:00

102 lines
2.6 KiB
Rust

use std::collections::HashMap;
use std::path::PathBuf;
use color_eyre::eyre::{OptionExt as _, WrapErr as _};
use color_eyre::Result;
use serde::{Deserialize, Serialize};
use steamlocate::SteamDir;
use time::OffsetDateTime;
pub use log::*;
mod log;
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ModConfigResources {
pub init: PathBuf,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub data: Option<PathBuf>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub localization: Option<PathBuf>,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ModOrder {
Before,
After,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum ModDependency {
ID(String),
Config { id: String, order: ModOrder },
}
// A bit dumb, but serde doesn't support literal values with the
// `default` attribute, only paths.
fn default_true() -> bool {
true
}
// Similarly dumb, as the `skip_serializing_if` attribute needs a function
fn is_true(val: &bool) -> bool {
*val
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ModConfig {
#[serde(skip)]
pub dir: PathBuf,
pub id: String,
pub name: String,
pub summary: String,
pub version: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub author: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<PathBuf>,
#[serde(default)]
pub categories: Vec<String>,
#[serde(default)]
pub packages: Vec<PathBuf>,
pub resources: ModConfigResources,
#[serde(default)]
pub depends: Vec<ModDependency>,
#[serde(default = "default_true", skip_serializing_if = "is_true")]
pub bundled: bool,
#[serde(default)]
pub name_overrides: HashMap<String, String>,
}
pub const STEAMAPP_ID: u32 = 1361210;
#[derive(Debug)]
pub struct GameInfo {
pub path: PathBuf,
pub last_updated: OffsetDateTime,
}
pub fn collect_game_info() -> Result<Option<GameInfo>> {
let dir = SteamDir::locate().wrap_err("Failed to locate Steam installation")?;
let found = dir
.find_app(STEAMAPP_ID)
.wrap_err("Failed to look up game by Steam app ID")?;
let Some((app, library)) = found else {
return Ok(None);
};
let last_updated = app
.last_updated
.ok_or_eyre("Missing field 'last_updated'")?;
Ok(Some(GameInfo {
path: library.path().join(app.install_dir),
last_updated: last_updated.into(),
}))
}