proton_launch/command/
backup.rs1use std::{
2 collections::HashSet,
3 fs::File,
4 path::{Path, PathBuf},
5};
6
7use crate::{paths::Paths, steam::SteamData};
8
9use super::{Runnable, RunnableResult};
10
11#[derive(Debug, Clone)]
12#[cfg_attr(feature = "commandline", derive(clap::Args))]
13pub struct Backup {
14 exe: PathBuf,
16
17 #[cfg_attr(feature = "commandline", clap(short, long))]
20 save_name: Option<String>,
21}
22
23impl Runnable for Backup {
24 fn run(&self, paths: &Paths, _steam_data: &SteamData) -> RunnableResult<()> {
25 let save_name = self
26 .save_name
27 .as_deref()
28 .unwrap_or_else(|| self.exe.file_stem().unwrap().to_str().unwrap());
29 let global_compat_dir = paths.compat_dir(save_name);
30 let r = find_new_files(&global_compat_dir).unwrap();
31 let f = File::create(format!("{}.backup", save_name)).unwrap();
32 let w = zstd::Encoder::new(f, 3).unwrap().auto_finish();
33 let mut t = tar::Builder::new(w);
34 for f in r {
35 let path = f.strip_prefix(&global_compat_dir).unwrap();
36 t.append_path_with_name(&f, path).unwrap();
37 }
38 t.finish().unwrap();
39 Ok(())
40 }
41}
42
43fn find_new_files(compat_dir: &Path) -> std::io::Result<Vec<PathBuf>> {
44 let mut v = Vec::new();
45 let tracked_files = compat_dir.join("tracked_files");
46 let tracked_files = std::fs::read_to_string(tracked_files)?;
47 let tracked_files: HashSet<&str> = tracked_files.lines().collect();
48 let prefix = compat_dir.join("pfx");
49
50 for entry in walkdir::WalkDir::new(&prefix) {
51 let entry = entry?;
52 let path = entry.path();
53 if entry.file_type().is_file() {
54 let path = path.strip_prefix(&prefix).unwrap();
55 if !tracked_files.contains(path.to_str().unwrap())
56 && path.to_string_lossy().contains("users")
57 {
58 v.push(entry.path().to_path_buf());
59 }
60 }
61 }
62
63 Ok(v)
64}