mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-10 04:22:13 +10:00
@ -2,7 +2,7 @@ use std::{collections::HashMap, path::PathBuf, str::FromStr};
|
|||||||
|
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
|
||||||
use crate::{database::db::GameVersion, error::backup_error::BackupError, process::process_manager::Platform};
|
use crate::{database::db::{GameVersion, DATA_ROOT_DIR}, error::backup_error::BackupError, process::process_manager::Platform};
|
||||||
|
|
||||||
use super::path::CommonPath;
|
use super::path::CommonPath;
|
||||||
|
|
||||||
@ -45,11 +45,11 @@ impl BackupManager<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait BackupHandler: Send + Sync {
|
pub trait BackupHandler: Send + Sync {
|
||||||
fn root_translate(&self, path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError>;
|
fn root_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> { Ok(DATA_ROOT_DIR.lock().unwrap().join("games")) }
|
||||||
fn game_translate(&self, _path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError> { Ok(PathBuf::from_str(&game.game_id).unwrap()) }
|
fn game_translate(&self, _path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError> { Ok(PathBuf::from_str(&game.game_id).unwrap()) }
|
||||||
fn base_translate(&self, path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError> { Ok(self.root_translate(path, game)?.join(self.game_translate(path, game)?)) }
|
fn base_translate(&self, path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError> { Ok(self.root_translate(path, game)?.join(self.game_translate(path, game)?)) }
|
||||||
fn home_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> { let c = CommonPath::Home.get().ok_or(BackupError::NotFound); println!("{:?}", c); c }
|
fn home_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> { let c = CommonPath::Home.get().ok_or(BackupError::NotFound); println!("{:?}", c); c }
|
||||||
fn store_user_id_translate(&self, path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError>;
|
fn store_user_id_translate(&self, _path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError> { PathBuf::from_str(&game.game_id).map_err(|_| BackupError::ParseError) }
|
||||||
fn os_user_name_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> { Ok(PathBuf::from_str(&whoami::username()).unwrap()) }
|
fn os_user_name_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> { Ok(PathBuf::from_str(&whoami::username()).unwrap()) }
|
||||||
fn win_app_data_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> { warn!("Unexpected Windows Reference in Backup <winAppData>"); Err(BackupError::InvalidSystem) }
|
fn win_app_data_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> { warn!("Unexpected Windows Reference in Backup <winAppData>"); Err(BackupError::InvalidSystem) }
|
||||||
fn win_local_app_data_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> { warn!("Unexpected Windows Reference in Backup <winLocalAppData>"); Err(BackupError::InvalidSystem) }
|
fn win_local_app_data_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> { warn!("Unexpected Windows Reference in Backup <winLocalAppData>"); Err(BackupError::InvalidSystem) }
|
||||||
@ -65,33 +65,38 @@ pub trait BackupHandler: Send + Sync {
|
|||||||
|
|
||||||
pub struct LinuxBackupManager {}
|
pub struct LinuxBackupManager {}
|
||||||
impl BackupHandler for LinuxBackupManager {
|
impl BackupHandler for LinuxBackupManager {
|
||||||
fn root_translate(&self, path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError> {
|
fn xdg_config_translate(&self, _path: &PathBuf,_game: &GameVersion) -> Result<PathBuf, BackupError> {
|
||||||
println!("Root translate");
|
Ok(CommonPath::Data.get().ok_or(BackupError::NotFound)?)
|
||||||
PathBuf::from_str("~").map_err(|_| BackupError::ParseError)
|
|
||||||
}
|
}
|
||||||
|
fn xdg_data_translate(&self, _path: &PathBuf,_game: &GameVersion) -> Result<PathBuf, BackupError> {
|
||||||
fn store_user_id_translate(&self, path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError> {
|
Ok(CommonPath::Config.get().ok_or(BackupError::NotFound)?)
|
||||||
println!("Store user id translate");
|
|
||||||
PathBuf::from_str("ID").map_err(|_| BackupError::ParseError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct WindowsBackupManager {}
|
pub struct WindowsBackupManager {}
|
||||||
impl BackupHandler for WindowsBackupManager {
|
impl BackupHandler for WindowsBackupManager {
|
||||||
fn root_translate(&self, path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError> {
|
fn win_app_data_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> {
|
||||||
todo!()
|
Ok(CommonPath::Config.get().ok_or(BackupError::NotFound)?)
|
||||||
}
|
}
|
||||||
|
fn win_local_app_data_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> {
|
||||||
|
Ok(CommonPath::DataLocal.get().ok_or(BackupError::NotFound)?)
|
||||||
|
}
|
||||||
|
fn win_local_app_data_low_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> {
|
||||||
|
Ok(CommonPath::DataLocalLow.get().ok_or(BackupError::NotFound)?)
|
||||||
|
}
|
||||||
|
fn win_dir_translate(&self, _path: &PathBuf,_game: &GameVersion) -> Result<PathBuf, BackupError> {
|
||||||
|
Ok(PathBuf::from_str("C:/Windows").unwrap())
|
||||||
|
}
|
||||||
|
fn win_documents_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> {
|
||||||
|
Ok(CommonPath::Document.get().ok_or(BackupError::NotFound)?)
|
||||||
|
|
||||||
|
}
|
||||||
|
fn win_program_data_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> {
|
||||||
|
Ok(PathBuf::from_str("C:/ProgramData").unwrap())
|
||||||
|
}
|
||||||
|
fn win_public_translate(&self, _path: &PathBuf, _game: &GameVersion) -> Result<PathBuf, BackupError> {
|
||||||
|
Ok(CommonPath::Public.get().ok_or(BackupError::NotFound)?)
|
||||||
|
|
||||||
fn store_user_id_translate(&self, path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError> {
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct MacBackupManager {}
|
pub struct MacBackupManager {}
|
||||||
impl BackupHandler for MacBackupManager {
|
impl BackupHandler for MacBackupManager {}
|
||||||
fn root_translate(&self, path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_user_id_translate(&self, path: &PathBuf, game: &GameVersion) -> Result<PathBuf, BackupError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,6 +3,5 @@ pub mod metadata;
|
|||||||
pub mod resolver;
|
pub mod resolver;
|
||||||
pub mod placeholder;
|
pub mod placeholder;
|
||||||
pub mod normalise;
|
pub mod normalise;
|
||||||
pub mod parse;
|
|
||||||
pub mod path;
|
pub mod path;
|
||||||
pub mod backup_manager;
|
pub mod backup_manager;
|
||||||
@ -1,7 +1,5 @@
|
|||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
use crate::process::process_manager::Platform;
|
|
||||||
|
|
||||||
pub const ALL: &[&str] = &[
|
pub const ALL: &[&str] = &[
|
||||||
ROOT,
|
ROOT,
|
||||||
GAME,
|
GAME,
|
||||||
@ -33,22 +31,21 @@ pub const AVOID_WILDCARDS: &[&str] = &[
|
|||||||
XDG_CONFIG,
|
XDG_CONFIG,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const ROOT: &str = "<root>";
|
pub const ROOT: &str = "<root>"; // a directory where games are installed (configured in backup tool)
|
||||||
pub const GAME: &str = "<game>";
|
pub const GAME: &str = "<game>"; // an installDir (if defined) or the game's canonical name in the manifest
|
||||||
pub const BASE: &str = "<base>";
|
pub const BASE: &str = "<base>"; // shorthand for <root>/<game> (unless overridden by store-specific rules)
|
||||||
pub const HOME: &str = "<home>";
|
pub const HOME: &str = "<home>"; // current user's home directory in the OS (~)
|
||||||
pub const STORE_USER_ID: &str = "<storeUserId>";
|
pub const STORE_USER_ID: &str = "<storeUserId>"; // a store-specific id from the manifest, corresponding to the root's store type
|
||||||
pub const OS_USER_NAME: &str = "<osUserName>";
|
pub const OS_USER_NAME: &str = "<osUserName>"; // current user's ID in the game store
|
||||||
pub const WIN_APP_DATA: &str = "<winAppData>";
|
pub const WIN_APP_DATA: &str = "<winAppData>"; // current user's name in the OS
|
||||||
pub const WIN_LOCAL_APP_DATA: &str = "<winLocalAppData>";
|
pub const WIN_LOCAL_APP_DATA: &str = "<winLocalAppData>"; // %APPDATA% on Windows
|
||||||
pub const WIN_LOCAL_APP_DATA_LOW: &str = "<winLocalAppDataLow>";
|
pub const WIN_LOCAL_APP_DATA_LOW: &str = "<winLocalAppDataLow>"; // %LOCALAPPDATA% on Windows
|
||||||
pub const WIN_DOCUMENTS: &str = "<winDocuments>";
|
pub const WIN_DOCUMENTS: &str = "<winDocuments>"; // <home>/AppData/LocalLow on Windows
|
||||||
pub const WIN_PUBLIC: &str = "<winPublic>";
|
pub const WIN_PUBLIC: &str = "<winPublic>"; // <home>/Documents (f.k.a. <home>/My Documents) or a localized equivalent on Windows
|
||||||
pub const WIN_PROGRAM_DATA: &str = "<winProgramData>";
|
pub const WIN_PROGRAM_DATA: &str = "<winProgramData>"; // %PUBLIC% on Windows
|
||||||
pub const WIN_DIR: &str = "<winDir>";
|
pub const WIN_DIR: &str = "<winDir>"; // %PROGRAMDATA% on Windows
|
||||||
pub const XDG_DATA: &str = "<xdgData>";
|
pub const XDG_DATA: &str = "<xdgData>"; // %WINDIR% on Windows
|
||||||
pub const XDG_CONFIG: &str = "<xdgConfig>";
|
pub const XDG_CONFIG: &str = "<xdgConfig>"; // $XDG_DATA_HOME on Linux
|
||||||
pub const SKIP: &str = "<skip>";
|
pub const SKIP: &str = "<skip>"; // $XDG_CONFIG_HOME on Linux
|
||||||
|
|
||||||
pub static OS_USERNAME: LazyLock<String> = LazyLock::new(|| whoami::username());
|
|
||||||
|
|
||||||
|
pub static OS_USERNAME: LazyLock<String> = LazyLock::new(|| whoami::username());
|
||||||
@ -1,6 +1,12 @@
|
|||||||
use std::{fs::File, io::Write, path::{Component, PathBuf}};
|
use std::{
|
||||||
|
fs::{create_dir_all, File},
|
||||||
|
io::{Read, Write},
|
||||||
|
path::PathBuf, thread::{sleep}, time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{backup_manager::BackupHandler, conditions::Condition, metadata::GameFile, placeholder::*};
|
use super::{
|
||||||
|
backup_manager::BackupHandler, conditions::Condition, metadata::GameFile, placeholder::*,
|
||||||
|
};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use rustix::path::Arg;
|
use rustix::path::Arg;
|
||||||
use tempfile::tempfile;
|
use tempfile::tempfile;
|
||||||
@ -62,6 +68,64 @@ pub fn resolve(meta: &mut CloudSaveMetadata) -> File {
|
|||||||
tarball.into_inner().unwrap().finish().unwrap()
|
tarball.into_inner().unwrap().finish().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extract(file: PathBuf) -> Result<(), BackupError> {
|
||||||
|
let tmpdir = tempfile::tempdir().unwrap();
|
||||||
|
|
||||||
|
// Reopen the file for reading
|
||||||
|
let file = File::open(file).unwrap();
|
||||||
|
|
||||||
|
let decompressor = zstd::Decoder::new(file).unwrap();
|
||||||
|
let mut f = tar::Archive::new(decompressor);
|
||||||
|
f.unpack(tmpdir.path()).unwrap();
|
||||||
|
|
||||||
|
let path = tmpdir.path();
|
||||||
|
|
||||||
|
let mut manifest = File::open(path.join("metadata")).unwrap();
|
||||||
|
|
||||||
|
let mut manifest_slice = Vec::new();
|
||||||
|
manifest.read_to_end(&mut manifest_slice).unwrap();
|
||||||
|
|
||||||
|
let manifest: CloudSaveMetadata = serde_json::from_slice(&manifest_slice).unwrap();
|
||||||
|
|
||||||
|
for file in manifest.files {
|
||||||
|
let current_path = path.join(file.id.as_ref().unwrap());
|
||||||
|
|
||||||
|
let manager = BackupManager::new();
|
||||||
|
let os = match file
|
||||||
|
.conditions
|
||||||
|
.iter()
|
||||||
|
.find_map(|p| match p {
|
||||||
|
super::conditions::Condition::Os(os) => Some(os),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
{
|
||||||
|
Some(os) => os,
|
||||||
|
None => {
|
||||||
|
warn!(
|
||||||
|
"File {:?} could not be replaced up because it did not provide an OS",
|
||||||
|
&file
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let handler = match manager.sources.get(&(manager.current_platform, os)) {
|
||||||
|
Some(h) => *h,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_path = parse_path(file.path.into(), handler, &manifest.game_version)?;
|
||||||
|
create_dir_all(&new_path.parent().unwrap()).unwrap();
|
||||||
|
|
||||||
|
println!("Current path {:?} copying to {:?}", ¤t_path, &new_path);
|
||||||
|
|
||||||
|
|
||||||
|
std::fs::copy(current_path, new_path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_path(
|
pub fn parse_path(
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
backup_handler: &dyn BackupHandler,
|
backup_handler: &dyn BackupHandler,
|
||||||
@ -71,23 +135,25 @@ pub fn parse_path(
|
|||||||
let mut s = PathBuf::new();
|
let mut s = PathBuf::new();
|
||||||
for component in path.components() {
|
for component in path.components() {
|
||||||
match component.as_str().unwrap() {
|
match component.as_str().unwrap() {
|
||||||
ROOT => { s.push(backup_handler.root_translate(&path, game)?)},
|
ROOT => s.push(backup_handler.root_translate(&path, game)?),
|
||||||
GAME => { s.push(backup_handler.game_translate(&path, game)?)},
|
GAME => s.push(backup_handler.game_translate(&path, game)?),
|
||||||
BASE => { s.push(backup_handler.base_translate(&path, game)?)},
|
BASE => s.push(backup_handler.base_translate(&path, game)?),
|
||||||
HOME => { s.push(backup_handler.home_translate(&path, game)?)},
|
HOME => s.push(backup_handler.home_translate(&path, game)?),
|
||||||
STORE_USER_ID => { s.push(backup_handler.store_user_id_translate(&path, game)?)},
|
STORE_USER_ID => s.push(backup_handler.store_user_id_translate(&path, game)?),
|
||||||
OS_USER_NAME => { s.push(backup_handler.os_user_name_translate(&path, game)?)},
|
OS_USER_NAME => s.push(backup_handler.os_user_name_translate(&path, game)?),
|
||||||
WIN_APP_DATA => { s.push(backup_handler.win_app_data_translate(&path, game)?)},
|
WIN_APP_DATA => s.push(backup_handler.win_app_data_translate(&path, game)?),
|
||||||
WIN_LOCAL_APP_DATA => { s.push(backup_handler.win_local_app_data_translate(&path, game)?)},
|
WIN_LOCAL_APP_DATA => s.push(backup_handler.win_local_app_data_translate(&path, game)?),
|
||||||
WIN_LOCAL_APP_DATA_LOW => { s.push(backup_handler.win_local_app_data_low_translate(&path, game)?)},
|
WIN_LOCAL_APP_DATA_LOW => {
|
||||||
WIN_DOCUMENTS => { s.push(backup_handler.win_documents_translate(&path, game)?)},
|
s.push(backup_handler.win_local_app_data_low_translate(&path, game)?)
|
||||||
WIN_PUBLIC => { s.push(backup_handler.win_public_translate(&path, game)?)},
|
}
|
||||||
WIN_PROGRAM_DATA => { s.push(backup_handler.win_program_data_translate(&path, game)?)},
|
WIN_DOCUMENTS => s.push(backup_handler.win_documents_translate(&path, game)?),
|
||||||
WIN_DIR => { s.push(backup_handler.win_dir_translate(&path, game)?)},
|
WIN_PUBLIC => s.push(backup_handler.win_public_translate(&path, game)?),
|
||||||
XDG_DATA => { s.push(backup_handler.xdg_data_translate(&path, game)?)},
|
WIN_PROGRAM_DATA => s.push(backup_handler.win_program_data_translate(&path, game)?),
|
||||||
XDG_CONFIG => { s.push(backup_handler.xdg_config_translate(&path, game)?)},
|
WIN_DIR => s.push(backup_handler.win_dir_translate(&path, game)?),
|
||||||
SKIP => { },
|
XDG_DATA => s.push(backup_handler.xdg_data_translate(&path, game)?),
|
||||||
_ => s.push(PathBuf::from(component.as_os_str()))
|
XDG_CONFIG => s.push(backup_handler.xdg_config_translate(&path, game)?),
|
||||||
|
SKIP => s.push(backup_handler.skip_translate(&path, game)?),
|
||||||
|
_ => s.push(PathBuf::from(component.as_os_str())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,13 +163,22 @@ pub fn parse_path(
|
|||||||
|
|
||||||
pub fn test() {
|
pub fn test() {
|
||||||
let mut meta = CloudSaveMetadata {
|
let mut meta = CloudSaveMetadata {
|
||||||
files: vec![GameFile {
|
files: vec![
|
||||||
path: String::from("<home>/favicon.png"),
|
GameFile {
|
||||||
id: None,
|
path: String::from("<home>/favicon.png"),
|
||||||
data_type: super::metadata::DataType::File,
|
id: None,
|
||||||
tags: Vec::new(),
|
data_type: super::metadata::DataType::File,
|
||||||
conditions: vec![Condition::Os(Platform::Linux)],
|
tags: Vec::new(),
|
||||||
}],
|
conditions: vec![Condition::Os(Platform::Linux)],
|
||||||
|
},
|
||||||
|
GameFile {
|
||||||
|
path: String::from("<home>/quexeky.jpg"),
|
||||||
|
id: None,
|
||||||
|
data_type: super::metadata::DataType::File,
|
||||||
|
tags: Vec::new(),
|
||||||
|
conditions: vec![Condition::Os(Platform::Linux)],
|
||||||
|
},
|
||||||
|
],
|
||||||
game_version: GameVersion {
|
game_version: GameVersion {
|
||||||
game_id: String::new(),
|
game_id: String::new(),
|
||||||
version_name: String::new(),
|
version_name: String::new(),
|
||||||
@ -121,5 +196,7 @@ pub fn test() {
|
|||||||
},
|
},
|
||||||
save_id: String::from("aaaaaaa"),
|
save_id: String::from("aaaaaaa"),
|
||||||
};
|
};
|
||||||
let file = resolve(&mut meta);
|
//resolve(&mut meta);
|
||||||
|
|
||||||
|
extract("save".into()).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user