physis/
bootdata.rs

1// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::fs;
5use std::path::PathBuf;
6use tracing::warn;
7
8use crate::patch::{PatchError, ZiPatch};
9
10/// Represents the boot data for FFXIV, which is located under the "boot" directory.
11pub struct BootData {
12    path: String,
13
14    /// The current version of the boot data, e.g. "2012.01.01.0000.0000".
15    pub version: String,
16}
17
18impl BootData {
19    /// Reads from an existing boot data location.
20    ///
21    /// This will return _None_ if the boot directory is not valid, but it does not check the validity
22    /// of each individual file.
23    ///
24    /// # Example
25    ///
26    /// ```
27    /// # use physis::bootdata::BootData;
28    /// let boot = BootData::from_existing("SquareEnix/Final Fantasy XIV - A Realm Reborn/boot");
29    /// # assert!(boot.is_none())
30    /// ```
31    pub fn from_existing(directory: &str) -> Option<BootData> {
32        match Self::is_valid(directory) {
33            true => Some(BootData {
34                path: directory.parse().ok()?,
35                version: fs::read_to_string(format!("{directory}/ffxivboot.ver")).ok()?,
36            }),
37            false => {
38                warn!("Boot data is not valid!");
39                None
40            }
41        }
42    }
43
44    /// Applies the patch to boot data and returns any errors it encounters. This function will not update the version in the BootData struct.
45    pub fn apply_patch(&self, patch_path: &str) -> Result<(), PatchError> {
46        ZiPatch::apply(&self.path, patch_path)
47    }
48
49    fn is_valid(path: &str) -> bool {
50        let d = PathBuf::from(path);
51
52        if fs::metadata(d.as_path()).is_err() {
53            return false;
54        }
55
56        true
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_valid_boot_dir() {
66        let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
67        d.push("resources/tests");
68        d.push("valid_boot");
69
70        assert!(BootData::from_existing(d.as_path().to_str().unwrap()).is_some());
71    }
72
73    #[test]
74    fn test_invalid_boot_dir() {
75        let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
76        d.push("resources/tests");
77        d.push("invalid_boot"); // intentionally missing so it doesn't have a .ver
78
79        assert!(BootData::from_existing(d.as_path().to_str().unwrap()).is_none());
80    }
81}