diff --git a/CHANGELOG.md b/CHANGELOG.md index 10f25b8..148b428 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ The Linux and Mac downloads are now provided in `.tar.gz` format to better preserve the files' executable permissions. +* Added: + * Paths may now use the `` placeholder. + This is supported in Steam, GOG, and Lutris roots. + For Steam roots, this also supports shortcuts to non-Steam games, + where the placeholder will map to the shortcut's dynamic app ID. * Fixed: * Files on Windows network shares were not backed up correctly. For example, a file identified as `\\localhost\share\test.txt` diff --git a/src/resource/manifest.rs b/src/resource/manifest.rs index 7fb62a1..882f8ff 100644 --- a/src/resource/manifest.rs +++ b/src/resource/manifest.rs @@ -16,6 +16,7 @@ pub mod placeholder { pub const GAME: &str = ""; pub const BASE: &str = ""; pub const HOME: &str = ""; + pub const STORE_GAME_ID: &str = ""; pub const STORE_USER_ID: &str = ""; pub const OS_USER_NAME: &str = ""; pub const WIN_APP_DATA: &str = ""; @@ -349,6 +350,12 @@ impl<'a> IdSet<'a> { .chain(std::iter::once(shortcut)) .flatten() } + + pub fn gog(&'a self) -> impl Iterator + 'a { + std::iter::once(self.gog) + .chain(self.gog_extra.iter().map(|x| Some(*x))) + .flatten() + } } #[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] diff --git a/src/scan.rs b/src/scan.rs index c8dff7a..47c5e2d 100644 --- a/src/scan.rs +++ b/src/scan.rs @@ -385,6 +385,35 @@ pub fn parse_paths( } } + let paths = if path.contains(STORE_GAME_ID) { + let mut expanded = HashSet::new(); + + for (p, c) in paths { + match root.store() { + Store::Gog => { + for id in ids.gog() { + expanded.insert((p.replace(STORE_GAME_ID, &id.to_string()), c)); + } + } + Store::Lutris => { + if let Some(id) = ids.lutris.as_ref() { + expanded.insert((p.replace(STORE_GAME_ID, id), c)); + } + } + Store::Steam => { + for id in ids.steam(steam_shortcut.map(|x| x.id)) { + expanded.insert((p.replace(STORE_GAME_ID, &id.to_string()), c)); + } + } + _ => continue, + } + } + + expanded + } else { + paths + }; + paths .into_iter() // This excludes `SKIP` and any other unmatched placeholders.