2022-11-11 16:29:16 +00:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::Read;
|
2022-11-11 21:31:36 +00:00
|
|
|
use std::path::{Path, PathBuf};
|
2022-11-11 16:29:16 +00:00
|
|
|
use std::io::Write;
|
|
|
|
|
|
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
use serde_json::Value as JsonValue;
|
|
|
|
|
2022-11-11 21:31:36 +00:00
|
|
|
use crate::consts::config_file;
|
2022-11-11 16:29:16 +00:00
|
|
|
|
|
|
|
pub mod launcher;
|
|
|
|
pub mod game;
|
|
|
|
pub mod patch;
|
|
|
|
pub mod resolution;
|
|
|
|
|
|
|
|
pub mod prelude {
|
|
|
|
pub use super::launcher::prelude::*;
|
|
|
|
pub use super::game::prelude::*;
|
|
|
|
|
|
|
|
pub use super::patch::Patch;
|
|
|
|
pub use super::resolution::Resolution;
|
|
|
|
}
|
|
|
|
|
|
|
|
use prelude::*;
|
|
|
|
|
|
|
|
static mut CONFIG: Option<Config> = None;
|
|
|
|
|
|
|
|
/// Get config data
|
|
|
|
///
|
|
|
|
/// This method will load config from file once and store it into the memory.
|
|
|
|
/// If you know that the config file was updated - you should run `get_raw` method
|
|
|
|
/// that always loads config directly from the file. This will also update in-memory config
|
2023-02-22 23:01:16 +00:00
|
|
|
#[tracing::instrument(level = "trace")]
|
2022-11-11 16:29:16 +00:00
|
|
|
pub fn get() -> anyhow::Result<Config> {
|
|
|
|
unsafe {
|
|
|
|
match &CONFIG {
|
|
|
|
Some(config) => Ok(config.clone()),
|
|
|
|
None => get_raw()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get config data
|
|
|
|
///
|
|
|
|
/// This method will always load data directly from the file and update in-memory config
|
2023-02-23 11:01:53 +00:00
|
|
|
#[tracing::instrument(level = "debug", ret)]
|
2022-11-11 16:29:16 +00:00
|
|
|
pub fn get_raw() -> anyhow::Result<Config> {
|
2023-02-23 11:01:53 +00:00
|
|
|
tracing::debug!("Reading config data from file");
|
|
|
|
|
2022-11-11 16:29:16 +00:00
|
|
|
match config_file() {
|
|
|
|
Some(path) => {
|
|
|
|
// Try to read config if the file exists
|
|
|
|
if Path::new(&path).exists() {
|
|
|
|
let mut file = File::open(path)?;
|
|
|
|
let mut json = String::new();
|
|
|
|
|
|
|
|
file.read_to_string(&mut json)?;
|
|
|
|
|
|
|
|
match serde_json::from_str(&json) {
|
|
|
|
Ok(config) => {
|
|
|
|
let config = Config::from(&config);
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
CONFIG = Some(config.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(config)
|
|
|
|
},
|
2023-02-23 11:01:53 +00:00
|
|
|
Err(err) => {
|
|
|
|
tracing::error!("Failed to decode config data from json format: {}", err.to_string());
|
|
|
|
|
|
|
|
Err(anyhow::anyhow!("Failed to decode config data from json format: {}", err.to_string()))
|
|
|
|
}
|
2022-11-11 16:29:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise create default config file
|
|
|
|
else {
|
|
|
|
update_raw(Config::default())?;
|
|
|
|
|
|
|
|
Ok(Config::default())
|
|
|
|
}
|
|
|
|
},
|
2023-02-23 11:01:53 +00:00
|
|
|
None => {
|
|
|
|
tracing::error!("Failed to get config file path");
|
|
|
|
|
|
|
|
Err(anyhow::anyhow!("Failed to get config file path"))
|
|
|
|
}
|
2022-11-11 16:29:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Update in-memory config data
|
|
|
|
///
|
|
|
|
/// Use `update_raw` if you want to update config file itself
|
2023-02-22 23:01:16 +00:00
|
|
|
#[tracing::instrument(level = "trace")]
|
2022-11-11 16:29:16 +00:00
|
|
|
pub fn update(config: Config) {
|
2023-02-23 11:01:53 +00:00
|
|
|
tracing::trace!("Updating hot config record");
|
|
|
|
|
2022-11-11 16:29:16 +00:00
|
|
|
unsafe {
|
|
|
|
CONFIG = Some(config);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Update config file
|
|
|
|
///
|
|
|
|
/// This method will also update in-memory config data
|
2023-02-23 11:01:53 +00:00
|
|
|
#[tracing::instrument(level = "debug", ret)]
|
2022-11-11 16:29:16 +00:00
|
|
|
pub fn update_raw(config: Config) -> anyhow::Result<()> {
|
2023-02-23 11:01:53 +00:00
|
|
|
tracing::debug!("Updating config data");
|
|
|
|
|
2022-11-11 16:29:16 +00:00
|
|
|
update(config.clone());
|
|
|
|
|
|
|
|
match config_file() {
|
|
|
|
Some(path) => {
|
|
|
|
let mut file = File::create(&path)?;
|
|
|
|
|
|
|
|
match serde_json::to_string_pretty(&config) {
|
|
|
|
Ok(json) => {
|
|
|
|
file.write_all(json.as_bytes())?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
},
|
2023-02-23 11:01:53 +00:00
|
|
|
Err(err) => {
|
|
|
|
tracing::error!("Failed to encode config data into json format: {}", err.to_string());
|
|
|
|
|
|
|
|
Err(anyhow::anyhow!("Failed to encode config data into json format: {}", err.to_string()))
|
|
|
|
}
|
2022-11-11 16:29:16 +00:00
|
|
|
}
|
|
|
|
},
|
2023-02-23 11:01:53 +00:00
|
|
|
None => {
|
|
|
|
tracing::error!("Failed to get config file path");
|
|
|
|
|
|
|
|
Err(anyhow::anyhow!("Failed to get config file path"))
|
|
|
|
}
|
2022-11-11 16:29:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Update config file from the in-memory saved config
|
2023-02-23 11:01:53 +00:00
|
|
|
#[tracing::instrument(level = "debug", ret)]
|
2022-11-11 16:29:16 +00:00
|
|
|
pub fn flush() -> anyhow::Result<()> {
|
2023-02-23 11:01:53 +00:00
|
|
|
tracing::debug!("Flushing config data");
|
|
|
|
|
2022-11-11 16:29:16 +00:00
|
|
|
unsafe {
|
|
|
|
match &CONFIG {
|
|
|
|
Some(config) => update_raw(config.clone()),
|
2023-02-23 11:01:53 +00:00
|
|
|
None => {
|
|
|
|
tracing::error!("Config wasn't loaded into the memory");
|
|
|
|
|
|
|
|
Err(anyhow::anyhow!("Config wasn't loaded into the memory"))
|
|
|
|
}
|
2022-11-11 16:29:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 12:57:43 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
2022-11-11 16:29:16 +00:00
|
|
|
pub struct Config {
|
|
|
|
pub launcher: Launcher,
|
|
|
|
pub game: Game,
|
|
|
|
pub patch: Patch
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<&JsonValue> for Config {
|
|
|
|
fn from(value: &JsonValue) -> Self {
|
|
|
|
let default = Self::default();
|
|
|
|
|
|
|
|
Self {
|
|
|
|
launcher: match value.get("launcher") {
|
|
|
|
Some(value) => Launcher::from(value),
|
|
|
|
None => default.launcher
|
|
|
|
},
|
|
|
|
|
|
|
|
game: match value.get("game") {
|
|
|
|
Some(value) => Game::from(value),
|
|
|
|
None => default.game
|
|
|
|
},
|
|
|
|
|
|
|
|
patch: match value.get("patch") {
|
|
|
|
Some(value) => Patch::from(value),
|
|
|
|
None => default.patch
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-11-11 21:31:36 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "components")]
|
|
|
|
use crate::components::wine::{self, Version as WineVersion};
|
|
|
|
|
|
|
|
#[cfg(feature = "components")]
|
|
|
|
use crate::components::dxvk::{self, Version as DxvkVersion};
|
|
|
|
|
|
|
|
#[cfg(feature = "components")]
|
|
|
|
impl Config {
|
|
|
|
pub fn try_get_selected_wine_info(&self) -> Option<WineVersion> {
|
|
|
|
match &self.game.wine.selected {
|
|
|
|
Some(selected) => {
|
|
|
|
wine::get_groups()
|
|
|
|
.iter()
|
|
|
|
.flat_map(|group| group.versions.clone())
|
|
|
|
.find(|version| version.name.eq(selected))
|
|
|
|
},
|
|
|
|
None => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Try to get a path to the wine64 executable based on `game.wine.builds` and `game.wine.selected`
|
|
|
|
///
|
|
|
|
/// Returns `Some("wine64")` if:
|
|
|
|
/// 1) `game.wine.selected = None`
|
|
|
|
/// 2) wine64 installed and available in system
|
|
|
|
pub fn try_get_wine_executable(&self) -> Option<PathBuf> {
|
|
|
|
match self.try_get_selected_wine_info() {
|
|
|
|
Some(selected) => Some(self.game.wine.builds.join(selected.name).join(selected.files.wine64)),
|
|
|
|
None => {
|
|
|
|
if crate::is_available("wine64") {
|
|
|
|
Some(PathBuf::from("wine64"))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Try to get DXVK version applied to wine prefix
|
|
|
|
///
|
|
|
|
/// Returns:
|
|
|
|
/// 1) `Ok(Some(..))` if version was found
|
|
|
|
/// 2) `Ok(None)` if version wasn't found, so too old or dxvk is not applied
|
|
|
|
/// 3) `Err(..)` if failed to get applied dxvk version, likely because wrong prefix path specified
|
|
|
|
pub fn try_get_selected_dxvk_info(&self) -> std::io::Result<Option<DxvkVersion>> {
|
|
|
|
Ok(match wincompatlib::dxvk::Dxvk::get_version(&self.game.wine.prefix)? {
|
|
|
|
Some(version) => {
|
|
|
|
dxvk::get_groups()
|
|
|
|
.iter()
|
|
|
|
.flat_map(|group| group.versions.clone())
|
|
|
|
.find(move |dxvk| dxvk.version == version)
|
|
|
|
},
|
|
|
|
None => None
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2023-02-23 11:01:53 +00:00
|
|
|
|