diff --git a/.gitmodules b/.gitmodules index 183769b..3a3be25 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "anime-game-core"] path = anime-game-core url = https://github.com/an-anime-team/anime-game-core -[submodule "components"] - path = components - url = https://github.com/an-anime-team/components diff --git a/Cargo.toml b/Cargo.toml index 101e9f5..10d9d3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "anime-launcher-sdk" -version = "0.2.5" +version = "0.3.0" authors = ["Nikita Podvirnyy "] license = "GPL-3.0" readme = "README.md" diff --git a/anime-game-core b/anime-game-core index a90aae5..33dce6a 160000 --- a/anime-game-core +++ b/anime-game-core @@ -1 +1 @@ -Subproject commit a90aae53797802b458670bd99428cd258ac1d902 +Subproject commit 33dce6a6d0202e1f8b17a8193603cc05c4d6a5ef diff --git a/components b/components deleted file mode 160000 index 50c9322..0000000 --- a/components +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 50c93220b16ef7609f61fc0785a1e435683d4a0c diff --git a/src/components/dxvk.rs b/src/components/dxvk.rs index c146344..5b1555b 100644 --- a/src/components/dxvk.rs +++ b/src/components/dxvk.rs @@ -3,22 +3,12 @@ use std::path::PathBuf; use serde::{Serialize, Deserialize}; use wincompatlib::prelude::*; -lazy_static::lazy_static! { - static ref GROUPS: Vec = vec![ - Group { - name: String::from("Vanilla"), - versions: serde_json::from_str::>(include_str!("../../components/dxvk/vanilla.json")).unwrap().into_iter().take(12).collect() - }, - Group { - name: String::from("Async"), - versions: serde_json::from_str::>(include_str!("../../components/dxvk/async.json")).unwrap().into_iter().take(12).collect() - } - ]; -} +use super::loader::ComponentsLoader; #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Group { pub name: String, + pub title: String, pub versions: Vec } @@ -26,25 +16,23 @@ pub struct Group { pub struct Version { pub name: String, pub version: String, - pub uri: String, - pub recommended: bool + pub uri: String } impl Version { /// Get latest recommended dxvk version - #[inline] - pub fn latest() -> Self { - get_groups()[0].versions[0].clone() + pub fn latest>(components: T) -> anyhow::Result { + Ok(get_groups(components)?[0].versions[0].clone()) } /// Check is current dxvk downloaded in specified folder #[inline] - pub fn is_downloaded_in + std::fmt::Debug>(&self, folder: T) -> bool { + pub fn is_downloaded_in>(&self, folder: T) -> bool { folder.into().join(&self.name).exists() } /// Install current dxvk - #[tracing::instrument(level = "debug")] + #[tracing::instrument(level = "debug", ret)] #[inline] pub fn install + std::fmt::Debug>(&self, dxvks_folder: T, wine: &Wine, params: InstallParams) -> std::io::Result<()> { tracing::debug!("Installing DXVK"); @@ -57,7 +45,7 @@ impl Version { } /// Uninstall current dxvk - #[tracing::instrument(level = "debug")] + #[tracing::instrument(level = "debug", ret)] #[inline] pub fn uninstall(&self, wine: &Wine, params: InstallParams) -> std::io::Result<()> { tracing::debug!("Uninstalling DXVK"); @@ -69,17 +57,15 @@ impl Version { } } -/// Get dxvk groups -#[inline] -pub fn get_groups() -> Vec { - GROUPS.clone() +pub fn get_groups>(components: T) -> anyhow::Result> { + ComponentsLoader::new(components).get_dxvk_versions() } /// List downloaded dxvk versions in some specific folder -pub fn get_downloaded>(folder: T) -> std::io::Result> { +pub fn get_downloaded>(components: T, folder: T) -> anyhow::Result> { let mut downloaded = Vec::new(); - let list = get_groups() + let list = get_groups(components)? .into_iter() .flat_map(|group| group.versions) .collect::>(); diff --git a/src/components/loader.rs b/src/components/loader.rs index 70b786d..7e06312 100644 --- a/src/components/loader.rs +++ b/src/components/loader.rs @@ -1 +1,148 @@ -// TODO +use std::path::{Path, PathBuf}; + +use crate::anime_game_core::traits::git_sync::RemoteGitSync; +use super::wine; +use super::dxvk; + +#[derive(Debug)] +pub struct ComponentsLoader { + folder: PathBuf +} + +impl RemoteGitSync for ComponentsLoader { + fn folder(&self) -> &Path { + self.folder.as_path() + } +} + +impl ComponentsLoader { + pub fn new>(folder: T) -> Self { + Self { + folder: folder.into() + } + } + + /// Try to get wine versions from components index + #[tracing::instrument(level = "debug", ret)] + pub fn get_wine_versions(&self) -> anyhow::Result> { + tracing::debug!("Getting wine versions"); + + let components = serde_json::from_str::(&std::fs::read_to_string(self.folder.join("components.json"))?)?; + + match components.get("wine") { + Some(wine) => match wine.as_array() { + Some(groups) => { + let mut wine_groups = Vec::with_capacity(groups.len()); + + for group in groups { + let name = match group.get("name") { + Some(name) => match name.as_str() { + Some(name) => name.to_string(), + None => anyhow::bail!("Wrong components index structure: wine group's name entry must be a string") + } + + None => anyhow::bail!("Wrong components index structure: wine group's name not found") + }; + + let title = match group.get("title") { + Some(title) => match title.as_str() { + Some(title) => title.to_string(), + None => anyhow::bail!("Wrong components index structure: wine group's title entry must be a string") + } + + None => anyhow::bail!("Wrong components index structure: wine group's title not found") + }; + + let versions = serde_json::from_str::(&std::fs::read_to_string(self.folder.join("wine").join(format!("{name}.json")))?)?; + + let mut wine_versions = Vec::new(); + + match versions.as_array() { + Some(versions) => { + for version in versions { + wine_versions.push(serde_json::from_value::(version.to_owned())?); + } + } + + None => anyhow::bail!("Wrong components index structure: wine versions must be a list") + } + + wine_groups.push(wine::Group { + name, + title, + versions: wine_versions + }); + } + + Ok(wine_groups) + } + + None => anyhow::bail!("Wrong components index structure: wine entry must be a list") + } + + None => anyhow::bail!("Wrong components index structure: wine entry not found") + } + } + + /// Try to get dxvk versions from components index + #[tracing::instrument(level = "debug", ret)] + pub fn get_dxvk_versions(&self) -> anyhow::Result> { + tracing::debug!("Getting dxvk versions"); + + let components = serde_json::from_str::(&std::fs::read_to_string(self.folder.join("components.json"))?)?; + + match components.get("dxvk") { + Some(dxvk) => match dxvk.as_array() { + Some(groups) => { + let mut dxvk_groups = Vec::with_capacity(groups.len()); + + for group in groups { + let name = match group.get("name") { + Some(name) => match name.as_str() { + Some(name) => name.to_string(), + None => anyhow::bail!("Wrong components index structure: dxvk group's name entry must be a string") + } + + None => anyhow::bail!("Wrong components index structure: dxvk group's name not found") + }; + + let title = match group.get("title") { + Some(title) => match title.as_str() { + Some(title) => title.to_string(), + None => anyhow::bail!("Wrong components index structure: dxvk group's title entry must be a string") + } + + None => anyhow::bail!("Wrong components index structure: dxvk group's title not found") + }; + + let versions = serde_json::from_str::(&std::fs::read_to_string(self.folder.join("dxvk").join(format!("{name}.json")))?)?; + + let mut dxvk_versions = Vec::new(); + + match versions.as_array() { + Some(versions) => { + for version in versions { + dxvk_versions.push(serde_json::from_value::(version.to_owned())?); + } + } + + None => anyhow::bail!("Wrong components index structure: wine versions must be a list") + } + + dxvk_groups.push(dxvk::Group { + name, + title, + versions: dxvk_versions + }); + } + + Ok(dxvk_groups) + } + + None => anyhow::bail!("Wrong components index structure: wine entry must be a list") + } + + None => anyhow::bail!("Wrong components index structure: wine entry not found") + } + } +} diff --git a/src/components/wine.rs b/src/components/wine.rs index 7644e89..07af717 100644 --- a/src/components/wine.rs +++ b/src/components/wine.rs @@ -3,30 +3,12 @@ use std::path::PathBuf; use serde::{Serialize, Deserialize}; use wincompatlib::prelude::*; -lazy_static::lazy_static! { - static ref GROUPS: Vec = vec![ - Group { - name: String::from("Wine-GE-Proton"), - versions: serde_json::from_str::>(include_str!("../../components/wine/wine-ge-proton.json")).unwrap().into_iter().take(12).collect() - }, - Group { - name: String::from("GE-Proton"), - versions: serde_json::from_str::>(include_str!("../../components/wine/ge-proton.json")).unwrap().into_iter().take(12).collect() - }, - Group { - name: String::from("Soda"), - versions: serde_json::from_str::>(include_str!("../../components/wine/soda.json")).unwrap().into_iter().take(12).collect() - }, - Group { - name: String::from("Lutris"), - versions: serde_json::from_str::>(include_str!("../../components/wine/lutris.json")).unwrap().into_iter().take(12).collect() - } - ]; -} +use super::loader::ComponentsLoader; #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Group { pub name: String, + pub title: String, pub versions: Vec } @@ -35,20 +17,27 @@ pub struct Version { pub name: String, pub title: String, pub uri: String, - pub files: Files, - pub recommended: bool + pub files: Files +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Files { + pub wine: String, + pub wine64: String, + pub wineserver: String, + pub wineboot: String, + pub winecfg: String } impl Version { /// Get latest recommended wine version - #[inline] - pub fn latest() -> Self { - get_groups()[0].versions[0].clone() + pub fn latest>(components: T) -> anyhow::Result { + Ok(get_groups(components)?[0].versions[0].clone()) } /// Check is current wine downloaded in specified folder #[inline] - pub fn is_downloaded_in + std::fmt::Debug>(&self, folder: T) -> bool { + pub fn is_downloaded_in>(&self, folder: T) -> bool { folder.into().join(&self.name).exists() } @@ -70,26 +59,15 @@ impl Version { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct Files { - pub wine: String, - pub wine64: String, - pub wineserver: String, - pub wineboot: String, - pub winecfg: String -} - -/// Get wine groups -#[inline] -pub fn get_groups() -> Vec { - GROUPS.clone() +pub fn get_groups>(components: T) -> anyhow::Result> { + ComponentsLoader::new(components).get_wine_versions() } /// List downloaded wine versions in some specific folder -pub fn get_downloaded + std::fmt::Debug>(folder: T) -> std::io::Result> { +pub fn get_downloaded>(components: T, folder: T) -> anyhow::Result> { let mut downloaded = Vec::new(); - let list = get_groups() + let list = get_groups(components)? .into_iter() .flat_map(|group| group.versions) .collect::>(); diff --git a/src/config/mod.rs b/src/config/mod.rs index f3d5358..8b76531 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,7 +1,6 @@ use std::fs::File; -use std::io::Read; -use std::path::{Path, PathBuf}; -use std::io::Write; +use std::io::{Read, Write}; +use std::path::Path; use serde::{Serialize, Deserialize}; use serde_json::Value as JsonValue; @@ -198,33 +197,16 @@ use crate::components::dxvk::{self, Version as DxvkVersion}; #[cfg(feature = "components")] impl Config { - pub fn try_get_selected_wine_info(&self) -> Option { + pub fn try_get_selected_wine_info(&self) -> anyhow::Result> { match &self.game.wine.selected { Some(selected) => { - wine::get_groups() + Ok(wine::get_groups(&self.components.path)? .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 { - 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 - } + .find(|version| version.name.eq(selected))) } + + None => Ok(None) } } @@ -234,16 +216,17 @@ impl Config { /// 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> { - Ok(match wincompatlib::dxvk::Dxvk::get_version(&self.game.wine.prefix)? { + pub fn try_get_selected_dxvk_info(&self) -> anyhow::Result> { + match wincompatlib::dxvk::Dxvk::get_version(&self.game.wine.prefix)? { Some(version) => { - dxvk::get_groups() + Ok(dxvk::get_groups(&self.components.path)? .iter() .flat_map(|group| group.versions.clone()) - .find(move |dxvk| dxvk.version == version) - }, - None => None - }) + .find(move |dxvk| dxvk.version == version)) + } + + None => Ok(None) + } } } diff --git a/src/game.rs b/src/game.rs index 92d7961..65a1ea9 100644 --- a/src/game.rs +++ b/src/game.rs @@ -24,9 +24,8 @@ pub fn run() -> anyhow::Result<()> { return Err(anyhow::anyhow!("Game is not installed")); } - let wine_executable = match config.try_get_wine_executable() { - Some(path) => path, - None => return Err(anyhow::anyhow!("Couldn't find wine executable")) + let Some(wine) = config.try_get_selected_wine_info()? else { + anyhow::bail!("Couldn't find wine executable"); }; // Check telemetry servers @@ -87,7 +86,7 @@ pub fn run() -> anyhow::Result<()> { bash_chain += "gamemoderun "; } - bash_chain += &format!("'{}' ", wine_executable.to_string_lossy()); + bash_chain += &format!("'{}' ", wine.files.wine64); if let Some(virtual_desktop) = config.game.wine.virtual_desktop.get_command() { bash_chain += &format!("{virtual_desktop} "); diff --git a/src/states/mod.rs b/src/states/mod.rs index 2219bbc..4827e75 100644 --- a/src/states/mod.rs +++ b/src/states/mod.rs @@ -148,7 +148,7 @@ impl LauncherState { // Check wine existence #[cfg(feature = "components")] { - if config.try_get_wine_executable().is_none() { + if config.try_get_selected_wine_info()?.is_none() { return Ok(Self::WineNotInstalled); } }