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;
6
7use crate::patch::{PatchError, ZiPatch};
8
9/// Represents the boot data for FFXIV, which is located under the "boot" directory.
10pub struct BootData {
11    path: String,
12
13    /// The current version of the boot data, e.g. "2012.01.01.0000.0000".
14    pub version: String,
15}
16
17impl BootData {
18    /// Reads from an existing boot data location.
19    ///
20    /// This will return a BootData even if the game directory is technically
21    /// invalid, but it won't have a valid version.
22    ///
23    /// # Example
24    ///
25    /// ```
26    /// # use physis::bootdata::BootData;
27    /// let boot = BootData::from_existing("SquareEnix/Final Fantasy XIV - A Realm Reborn/boot");
28    /// ```
29    pub fn from_existing(directory: &str) -> BootData {
30        match Self::is_valid(directory) {
31            true => BootData {
32                path: directory.parse().ok().unwrap(),
33                version: fs::read_to_string(format!("{directory}/ffxivboot.ver"))
34                    .ok()
35                    .unwrap(),
36            },
37            false => {
38                // Boot data is not valid! Returning one anyway, but without a version.
39                BootData {
40                    path: directory.parse().ok().unwrap(),
41                    version: String::default(),
42                }
43            }
44        }
45    }
46
47    /// Applies the patch to boot data and returns any errors it encounters. This function will not update the version in the BootData struct.
48    pub fn apply_patch(&self, patch_path: &str) -> Result<(), PatchError> {
49        ZiPatch::apply(&self.path, patch_path)
50    }
51
52    fn is_valid(path: &str) -> bool {
53        let mut d = PathBuf::from(path);
54
55        // Check if directory exists
56        if fs::metadata(d.as_path()).is_err() {
57            return false;
58        }
59
60        // Check if it has a version file
61        d.push("ffxivboot.ver");
62        if fs::metadata(d.as_path()).is_err() {
63            return false;
64        }
65
66        true
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn test_valid_boot_dir() {
76        let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
77        d.push("resources/tests");
78        d.push("valid_boot");
79
80        let boot_data = BootData::from_existing(d.as_path().to_str().unwrap());
81        assert_eq!(boot_data.version, "2012.01.01.0000.0000");
82    }
83
84    #[test]
85    fn test_invalid_boot_dir() {
86        let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
87        d.push("resources/tests");
88        d.push("invalid_boot"); // intentionally missing so it doesn't have a .ver
89
90        let boot_data = BootData::from_existing(d.as_path().to_str().unwrap());
91        assert_eq!(boot_data.version, "");
92    }
93}