1
Fork 0
mirror of https://github.com/redstrate/Physis.git synced 2025-04-21 04:07:46 +00:00

Allow naive repairing of version files

Currently, we not handle repairing version files well, so I
sought to seek a solution to the problem. This is one of part of the
puzzle, by implementing a simple "game repair". This is not
IndexedZiPatch support, but simply checking if we can restore version
files if possible, or otherwise wipe the directory.
This commit is contained in:
Joshua Goins 2022-10-25 11:03:05 -04:00
parent daa7384d89
commit d17f860f07
3 changed files with 109 additions and 11 deletions

View file

@ -1,3 +1,5 @@
use std::fs;
use std::path::Path;
use binrw::binrw;
#[binrw]
@ -43,3 +45,8 @@ pub fn get_language_code(lang: &Language) -> &'static str {
pub enum Region {
Global = -1, // TODO: find patch codes for other regions :-)
}
/// Reads a version file.
pub fn read_version(p: &Path) -> Option<String> {
fs::read_to_string(p).ok()
}

View file

@ -1,4 +1,4 @@
use crate::common::Language;
use crate::common::{Language, read_version};
use crate::dat::DatFile;
use crate::exd::EXD;
use crate::exh::EXH;
@ -31,6 +31,16 @@ fn is_valid(path: &str) -> bool {
true
}
#[derive(Debug)]
pub enum RepairAction {
VersionFileMissing,
VersionFileCanRestore
}
pub enum RepairError<'a> {
FailedRepair(&'a Repository)
}
pub type MemoryBuffer = Vec<u8>;
impl GameData {
@ -229,6 +239,94 @@ impl GameData {
pub fn apply_patch(&self, patch_path: &str) -> Result<(), PatchError> {
apply_patch(&self.game_directory, patch_path)
}
/// Detects whether or not the game files need a repair, right now it only checks for invalid
/// version files.
/// If the repair is needed, a list of invalid repositories is given.
pub fn needs_repair(&self) -> Option<Vec<(&Repository, RepairAction)>> {
let mut repositories : Vec<(&Repository, RepairAction)> = Vec::new();
for repository in &self.repositories {
if repository.version.is_none() {
// Check to see if a .bck file is created, as we might be able to use that
let ver_bak_path: PathBuf = [
self.game_directory.clone(),
"sqpack".to_string(),
repository.name.clone(),
format!("{}.bck", repository.name),
]
.iter()
.collect();
let repair_action = if read_version(&ver_bak_path).is_some() {
RepairAction::VersionFileCanRestore
} else {
RepairAction::VersionFileMissing
};
repositories.push((repository, repair_action));
}
}
if repositories.is_empty() {
None
} else {
Some(repositories)
}
}
/// Performs the repair, assuming any damaging effects it may have
/// Returns true only if all actions were taken are successful.
/// NOTE: This is a destructive operation, especially for InvalidVersion errors.
pub fn perform_repair<'a>(&self, repositories: &Vec<(&'a Repository, RepairAction)>) -> Result<(), RepairError<'a>> {
for (repository, action) in repositories {
let ver_path: PathBuf = [
self.game_directory.clone(),
"sqpack".to_string(),
repository.name.clone(),
format!("{}.ver", repository.name),
]
.iter()
.collect();
let new_version : String = match action {
RepairAction::VersionFileMissing => {
let repo_path: PathBuf = [
self.game_directory.clone(),
"sqpack".to_string(),
repository.name.clone()
]
.iter()
.collect();
std::fs::remove_dir_all(&repo_path).ok()
.ok_or(RepairError::FailedRepair(repository))?;
std::fs::create_dir_all(&repo_path).ok()
.ok_or(RepairError::FailedRepair(repository))?;
"2012.01.01.0000.0000".to_string() // TODO: is this correct for expansions?
}
RepairAction::VersionFileCanRestore => {
let ver_bak_path: PathBuf = [
self.game_directory.clone(),
"sqpack".to_string(),
repository.name.clone(),
format!("{}.bck", repository.name),
]
.iter()
.collect();
read_version(&ver_bak_path)
.ok_or(RepairError::FailedRepair(repository))?
}
};
std::fs::write(&ver_path, new_version).ok()
.ok_or(RepairError::FailedRepair(repository))?;
}
Ok(())
}
}
#[cfg(test)]

View file

@ -3,6 +3,7 @@ use std::cmp::Ordering;
use std::cmp::Ordering::{Greater, Less};
use std::fs;
use std::path::{Path, PathBuf};
use crate::common::read_version;
/// The type of repository, discerning game data from expansion data.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
@ -26,7 +27,7 @@ pub struct Repository {
/// The type of repository, such as "base game" or "expansion".
pub repo_type: RepositoryType,
/// The version of the game data.
pub version: String,
pub version: Option<String>,
}
impl Eq for Repository {}
@ -59,10 +60,6 @@ impl PartialOrd for Repository {
}
}
fn read_version(p: &Path) -> Option<String> {
fs::read_to_string(p).ok()
}
/// This refers to the specific root directory a file is located in.
/// This is a fixed list of directories, and all of them are known.
#[derive(Debug, PartialEq, Eq)]
@ -155,14 +152,10 @@ impl Repository {
read_version(d.as_path())
};
if version == None {
return None;
}
Some(Repository {
name,
repo_type,
version: version.unwrap(),
version,
})
}