Not sure what I wanted to fix initially

but in the end I rewrote tons of code,
including total change of strings to PathBuf
also I fixed all the warnings generated by clippy
This commit is contained in:
Observer KRypt0n_ 2022-09-30 00:00:36 +02:00
parent aa6f08f0fd
commit eaa8379976
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
35 changed files with 286 additions and 283 deletions

7
Cargo.lock generated
View file

@ -31,7 +31,7 @@ dependencies = [
[[package]]
name = "anime-game-core"
version = "1.1.8"
version = "1.1.9"
dependencies = [
"anyhow",
"bzip2",
@ -56,6 +56,7 @@ version = "1.1.3"
dependencies = [
"anime-game-core",
"anyhow",
"cached",
"dirs",
"gtk4",
"lazy_static",
@ -2303,9 +2304,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "wincompatlib"
version = "0.1.0"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fb138cb8c6312731e7385e4500a2491be768ff4c8cfc04544dcc9227533f250"
checksum = "34cd4c8511402616780eeb46726ca69ad958c4ba123398389ce306552428b715"
dependencies = [
"regex",
]

View file

@ -21,7 +21,7 @@ adw = { package = "libadwaita", version = "0.2.0-alpha.3", features = ["v1_2"] }
rfd = { version = "0.10", features = ["xdg-portal"], default-features = false }
anime-game-core = { path = "anime-game-core", features = ["all", "static", "genshin"] }
wincompatlib = { version = "0.1.0", features = ["dxvk"] }
wincompatlib = { version = "0.1.2", features = ["dxvk"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
@ -32,3 +32,4 @@ regex = "1.6.0"
lazy_static = "1.4.0"
anyhow = "1.0"
md5 = "0.7"
cached = { version = "0.39", features = ["proc_macro"]}

@ -1 +1 @@
Subproject commit 6230dbf8ca26e3cd34cbe32468df18c823e89158
Subproject commit 66b0e5f9c44a7d3247010b9a830f386f715b5ac5

View file

@ -31,42 +31,40 @@ fn blp_process_dir(dir: String) {
let dist_dir = format!("assets/ui/.dist/{}", &dir).replace("//", "/");
if let Ok(entries) = read_dir(&source_dir) {
if let Err(_) = read_dir(&dist_dir) {
if read_dir(&dist_dir).is_err() {
create_dir_all(&dist_dir).expect("UI dist dir couldn't be created");
}
// println!("cargo:rerun-if-changed={}/*.blp", &source_dir);
for entry in entries {
if let Ok(entry) = entry {
if let Ok(metadata) = entry.metadata() {
let entry_path = entry.path().to_str().unwrap().to_string();
let entry_filename = entry.file_name().to_str().unwrap().to_string();
for entry in entries.flatten() {
if let Ok(metadata) = entry.metadata() {
let entry_path = entry.path().to_str().unwrap().to_string();
let entry_filename = entry.file_name().to_str().unwrap().to_string();
if metadata.is_file() {
let entry_dist_path = format!("{}/{}.ui", &dist_dir, &entry_filename[..entry_filename.len() - 4]);
if metadata.is_file() {
let entry_dist_path = format!("{}/{}.ui", &dist_dir, &entry_filename[..entry_filename.len() - 4]);
match compile_blueprint(&entry_path) {
Ok(xml) => {
let result = fs::write(entry_dist_path, xml);
match compile_blueprint(&entry_path) {
Ok(xml) => {
let result = fs::write(entry_dist_path, xml);
if let Err(err) = result {
println!("cargo:warning=Couldn't write compiled XML UI: {}", err);
}
},
Err(err) => {
if Path::new(&entry_dist_path).exists() {
fs::remove_file(entry_dist_path).expect("Couldn't remove broken file");
}
println!("cargo:warning=Couldn't compile {}: {}", entry_path, err);
if let Err(err) = result {
println!("cargo:warning=Couldn't write compiled XML UI: {}", err);
}
},
Err(err) => {
if Path::new(&entry_dist_path).exists() {
fs::remove_file(entry_dist_path).expect("Couldn't remove broken file");
}
println!("cargo:warning=Couldn't compile {}: {}", entry_path, err);
}
}
}
else if metadata.is_dir() && &entry_filename[0..1] != "." {
blp_process_dir(format!("{}/{}", &dir, &entry_filename));
}
else if metadata.is_dir() && &entry_filename[0..1] != "." {
blp_process_dir(format!("{}/{}", &dir, &entry_filename));
}
}
}
@ -76,7 +74,7 @@ fn blp_process_dir(dir: String) {
fn main() {
blp_process_dir(String::new());
if let Ok(_) = read_to_string("assets/resources.xml") {
if read_to_string("assets/resources.xml").is_ok() {
gtk::gio::compile_resources(
"assets",
"assets/resources.xml",

View file

@ -1,3 +1,5 @@
use std::path::PathBuf;
use serde::{Serialize, Deserialize};
use serde_json::Value as JsonValue;
@ -5,7 +7,7 @@ use crate::lib::consts::launcher_dir;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Dxvk {
pub builds: String
pub builds: PathBuf
}
impl Default for Dxvk {
@ -13,7 +15,7 @@ impl Default for Dxvk {
let launcher_dir = launcher_dir().expect("Failed to get launcher dir");
Self {
builds: format!("{launcher_dir}/dxvks")
builds: launcher_dir.join("dxvks")
}
}
}
@ -24,7 +26,10 @@ impl From<&JsonValue> for Dxvk {
Self {
builds: match value.get("builds") {
Some(value) => value.as_str().unwrap_or(&default.builds).to_string(),
Some(value) => match value.as_str() {
Some(value) => PathBuf::from(value),
None => default.builds
},
None => default.builds
}
}

View file

@ -1,3 +1,5 @@
use std::path::PathBuf;
use serde::{Serialize, Deserialize};
use serde_json::Value as JsonValue;
@ -15,7 +17,7 @@ use prelude::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FpsUnlocker {
pub path: String,
pub path: PathBuf,
pub enabled: bool,
pub config: Config
}
@ -25,7 +27,7 @@ impl Default for FpsUnlocker {
let launcher_dir = launcher_dir().expect("Failed to get launcher dir");
Self {
path: format!("{launcher_dir}/fps-unlocker"),
path: launcher_dir.join("fps-unlocker"),
enabled: false,
config: Config::default()
}
@ -38,7 +40,10 @@ impl From<&JsonValue> for FpsUnlocker {
Self {
path: match value.get("path") {
Some(value) => value.as_str().unwrap_or(&default.path).to_string(),
Some(value) => match value.as_str() {
Some(value) => PathBuf::from(value),
None => default.path
},
None => default.path
},

View file

@ -15,6 +15,6 @@ impl Default for WindowType {
impl From<&JsonValue> for WindowType {
fn from(value: &JsonValue) -> Self {
serde_json::from_value(value.clone()).unwrap_or(Self::default())
serde_json::from_value(value.clone()).unwrap_or_default()
}
}

View file

@ -20,7 +20,7 @@ impl Default for HUD {
impl From<&JsonValue> for HUD {
fn from(value: &JsonValue) -> Self {
serde_json::from_value(value.clone()).unwrap_or(Self::default())
serde_json::from_value(value.clone()).unwrap_or_default()
}
}
@ -37,6 +37,7 @@ impl TryFrom<u32> for HUD {
}
}
#[allow(clippy::from_over_into)]
impl Into<u32> for HUD {
fn into(self) -> u32 {
match self {

View file

@ -1,4 +1,5 @@
use std::collections::HashMap;
use std::path::PathBuf;
use serde::{Serialize, Deserialize};
use serde_json::Value as JsonValue;
@ -21,7 +22,7 @@ use prelude::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Game {
pub path: String,
pub path: PathBuf,
pub voices: Vec<String>,
pub wine: prelude::Wine,
pub dxvk: prelude::Dxvk,
@ -35,7 +36,7 @@ impl Default for Game {
let launcher_dir = launcher_dir().expect("Failed to get launcher dir");
Self {
path: format!("{launcher_dir}/game/drive_c/Program Files/Genshin Impact"),
path: launcher_dir.join("game/drive_c/Program Files/Genshin Impact"),
voices: vec![
String::from("en-us")
],
@ -54,7 +55,10 @@ impl From<&JsonValue> for Game {
Self {
path: match value.get("path") {
Some(value) => value.as_str().unwrap_or(&default.path).to_string(),
Some(value) => match value.as_str() {
Some(value) => PathBuf::from(value),
None => default.path
},
None => default.path
},

View file

@ -1,3 +1,5 @@
use std::path::PathBuf;
use serde::{Serialize, Deserialize};
use serde_json::Value as JsonValue;
@ -18,8 +20,8 @@ use prelude::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Wine {
pub prefix: String,
pub builds: String,
pub prefix: PathBuf,
pub builds: PathBuf,
pub selected: Option<String>,
pub sync: WineSync,
pub language: WineLang,
@ -32,8 +34,8 @@ impl Default for Wine {
let launcher_dir = launcher_dir().expect("Failed to get launcher dir");
Self {
prefix: format!("{launcher_dir}/game"),
builds: format!("{launcher_dir}/runners"),
prefix: launcher_dir.join("game"),
builds: launcher_dir.join("runners"),
selected: None,
sync: WineSync::default(),
language: WineLang::default(),
@ -49,12 +51,18 @@ impl From<&JsonValue> for Wine {
Self {
prefix: match value.get("prefix") {
Some(value) => value.as_str().unwrap_or(&default.prefix).to_string(),
Some(value) => match value.as_str() {
Some(value) => PathBuf::from(value),
None => default.prefix
},
None => default.prefix
},
builds: match value.get("builds") {
Some(value) => value.as_str().unwrap_or(&default.builds).to_string(),
Some(value) => match value.as_str() {
Some(value) => PathBuf::from(value),
None => default.builds
},
None => default.builds
},

View file

@ -26,10 +26,11 @@ impl Default for WineLang {
impl From<&JsonValue> for WineLang {
fn from(value: &JsonValue) -> Self {
serde_json::from_value(value.clone()).unwrap_or(Self::default())
serde_json::from_value(value.clone()).unwrap_or_default()
}
}
#[allow(clippy::from_over_into)]
impl Into<u32> for WineLang {
fn into(self) -> u32 {
for (i, lang) in Self::list().into_iter().enumerate() {
@ -69,10 +70,6 @@ impl WineLang {
model
}
pub fn to_string(&self) -> String {
format!("{:?}", self)
}
/// Get environment variables corresponding to used wine language
pub fn get_env_vars(&self) -> HashMap<&str, &str> {
HashMap::from([("LANG", match self {
@ -91,3 +88,9 @@ impl WineLang {
})])
}
}
impl std::fmt::Display for WineLang {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&format!("{:?}", self))
}
}

View file

@ -19,7 +19,7 @@ impl Default for WineSync {
impl From<&JsonValue> for WineSync {
fn from(value: &JsonValue) -> Self {
serde_json::from_value(value.clone()).unwrap_or(Self::default())
serde_json::from_value(value.clone()).unwrap_or_default()
}
}
@ -38,6 +38,7 @@ impl TryFrom<u32> for WineSync {
}
}
#[allow(clippy::from_over_into)]
impl Into<u32> for WineSync {
fn into(self) -> u32 {
match self {

View file

@ -1,3 +1,5 @@
use std::path::PathBuf;
use serde::{Serialize, Deserialize};
use serde_json::Value as JsonValue;
@ -41,11 +43,11 @@ impl Default for GameEdition {
}
}
impl Into<CoreGameEdition> for GameEdition {
fn into(self) -> CoreGameEdition {
match self {
Self::Global => CoreGameEdition::Global,
Self::China => CoreGameEdition::China
impl From<GameEdition> for CoreGameEdition {
fn from(edition: GameEdition) -> Self {
match edition {
GameEdition::Global => CoreGameEdition::Global,
GameEdition::China => CoreGameEdition::China
}
}
}
@ -62,7 +64,7 @@ impl From<CoreGameEdition> for GameEdition {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Launcher {
pub language: String,
pub temp: Option<String>,
pub temp: Option<PathBuf>,
pub speed_limit: u64,
pub repairer: Repairer,
pub edition: GameEdition
@ -96,7 +98,7 @@ impl From<&JsonValue> for Launcher {
None
} else {
match value.as_str() {
Some(value) => Some(value.to_string()),
Some(value) => Some(PathBuf::from(value)),
None => default.temp
}
}

View file

@ -1,11 +1,13 @@
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::io::Write;
use serde::{Serialize, Deserialize};
use serde_json::Value as JsonValue;
use wincompatlib::dxvk::Dxvk;
use crate::lib;
use super::consts::*;
use super::wine::{
@ -107,7 +109,7 @@ pub fn update_raw(config: Config) -> anyhow::Result<()> {
match serde_json::to_string_pretty(&config) {
Ok(json) => {
file.write_all(&mut json.as_bytes())?;
file.write_all(json.as_bytes())?;
Ok(())
},
@ -141,8 +143,7 @@ impl Config {
Some(selected) => {
WineList::get().iter()
.flat_map(|group| group.versions.clone())
.filter(|version| version.name.eq(selected))
.next()
.find(|version| version.name.eq(selected))
},
None => None
}
@ -153,12 +154,12 @@ impl Config {
/// Returns `Some("wine64")` if:
/// 1) `game.wine.selected = None`
/// 2) wine64 installed and available in system
pub fn try_get_wine_executable(&self) -> Option<String> {
pub fn try_get_wine_executable(&self) -> Option<PathBuf> {
match self.try_get_selected_wine_info() {
Some(selected) => Some(format!("{}/{}/{}", &self.game.wine.builds, selected.name, selected.files.wine64)),
Some(selected) => Some(self.game.wine.builds.join(selected.name).join(selected.files.wine64)),
None => {
if lib::is_available("wine64") {
Some(String::from("wine64"))
Some(PathBuf::from("wine64"))
} else {
None
}
@ -173,34 +174,14 @@ impl Config {
/// 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>> {
let (bytes, from, to) = match std::fs::read(format!("{}/drive_c/windows/system32/dxgi.dll", &self.game.wine.prefix)) {
Ok(bytes) => (bytes, 1600000, 1700000),
Err(_) => {
let bytes = std::fs::read(format!("{}/drive_c/windows/system32/d3d11.dll", &self.game.wine.prefix))?;
(bytes, 2400000, 2500000)
}
};
let bytes = if bytes.len() > to {
bytes[from..to].to_vec()
} else {
return Ok(None);
};
Ok({
DxvkList::get()
.iter()
.flat_map(|group| group.versions.clone())
.filter(|version| {
let version = format!("\0v{}\0", &version.version);
let version = version.as_bytes();
bytes.windows(version.len())
.position(|window| window == version)
.is_some()
})
.next()
Ok(match Dxvk::get_version(&self.game.wine.prefix)? {
Some(version) => {
DxvkList::get()
.iter()
.flat_map(|group| group.versions.clone())
.find(move |dxvk| dxvk.version == version)
},
None => None
})
}
}

View file

@ -1,4 +1,4 @@
use std::path::Path;
use std::path::{Path, PathBuf};
use serde::{Serialize, Deserialize};
use serde_json::Value as JsonValue;
@ -7,7 +7,7 @@ use crate::lib::consts::launcher_dir;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Patch {
pub path: String,
pub path: PathBuf,
pub servers: Vec<String>,
pub root: bool
}
@ -17,7 +17,7 @@ impl Default for Patch {
let launcher_dir = launcher_dir().expect("Failed to get launcher dir");
Self {
path: format!("{launcher_dir}/patch"),
path: launcher_dir.join("patch"),
servers: vec![
"https://notabug.org/Krock/dawn".to_string(),
"https://codespace.gay/Maroxy/dawnin".to_string()
@ -35,7 +35,10 @@ impl From<&JsonValue> for Patch {
Self {
path: match value.get("path") {
Some(value) => value.as_str().unwrap_or(&default.path).to_string(),
Some(value) => match value.as_str() {
Some(value) => PathBuf::from(value),
None => default.path
},
None => default.path
},

View file

@ -64,10 +64,12 @@ impl Resolution {
Self::Custom(w, h) => (*w, *h)
}
}
}
pub fn to_string(&self) -> String {
impl std::fmt::Display for Resolution {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (w, h) = self.get_pair();
format!("{w}x{h}")
f.write_str(&format!("{w}x{h}"))
}
}

View file

@ -1,7 +1,7 @@
use std::path::PathBuf;
use std::time::Duration;
static mut LAUNCHER_DIR: Option<Option<String>> = None;
static mut CONFIG_FILE: Option<Option<String>> = None;
use cached::proc_macro::cached;
/// Timeout used by `anime_game_core::telemetry::is_disabled` to check acessibility of telemetry servers
pub const TELEMETRY_CHECK_TIMEOUT: Option<Duration> = Some(Duration::from_secs(3));
@ -9,38 +9,12 @@ pub const TELEMETRY_CHECK_TIMEOUT: Option<Duration> = Some(Duration::from_secs(3
/// Timeout used by `anime_game_core::linux_patch::Patch::try_fetch` to fetch patch info
pub const PATCH_FETCHING_TIMEOUT: Option<Duration> = Some(Duration::from_secs(5));
pub fn launcher_dir() -> Option<String> {
unsafe {
match &LAUNCHER_DIR {
Some(value) => value.clone(),
None => {
let value = match dirs::data_dir() {
Some(dir) => Some(format!("{}/anime-game-launcher", dir.to_string_lossy())),
None => None
};
LAUNCHER_DIR = Some(value.clone());
value
}
}
}
#[cached]
pub fn launcher_dir() -> Option<PathBuf> {
dirs::data_dir().map(|dir| dir.join("anime-game-launcher"))
}
pub fn config_file() -> Option<String> {
unsafe {
match &CONFIG_FILE {
Some(value) => value.clone(),
None => {
let value = match launcher_dir() {
Some(dir) => Some(format!("{}/config.json", dir)),
None => None
};
CONFIG_FILE = Some(value.clone());
value
}
}
}
#[cached]
pub fn config_file() -> Option<PathBuf> {
launcher_dir().map(|dir| dir.join("config.json"))
}

View file

@ -33,12 +33,12 @@ impl List {
}
/// List only downloaded DXVK versions in some specific folder
pub fn list_downloaded<T: ToString>(folder: T) -> std::io::Result<Vec<Version>> {
pub fn list_downloaded<T: Into<PathBuf>>(folder: T) -> std::io::Result<Vec<Version>> {
let mut downloaded = Vec::new();
let list = Self::get();
for entry in std::fs::read_dir(folder.to_string())? {
for entry in std::fs::read_dir(folder.into())? {
let name = entry?.file_name();
for group in &list {
@ -76,32 +76,34 @@ impl Version {
Ok(List::get()[0].versions[0].clone())
}
pub fn is_downloaded_in<T: ToString>(&self, folder: T) -> bool {
std::path::Path::new(&format!("{}/{}", folder.to_string(), self.name)).exists()
pub fn is_downloaded_in<T: Into<PathBuf>>(&self, folder: T) -> bool {
folder.into().join(&self.name).exists()
}
pub fn apply<T: ToString>(&self, dxvks_folder: T, prefix_path: T) -> anyhow::Result<Output> {
let apply_path = format!("{}/{}/setup_dxvk.sh", dxvks_folder.to_string(), self.name);
pub fn apply<T: Into<PathBuf>>(&self, dxvks_folder: T, prefix_path: T) -> anyhow::Result<Output> {
let apply_path = dxvks_folder.into().join(&self.name).join("setup_dxvk.sh");
let config = config::get()?;
let (wine_path, wineserver_path, wineboot_path) = match config.try_get_selected_wine_info() {
Some(wine) => {
let wine_path = format!("{}/{}/{}", &config.game.wine.builds, wine.name, wine.files.wine64);
let wineserver_path = format!("{}/{}/{}", &config.game.wine.builds, wine.name, wine.files.wineserver);
let wineboot_path = format!("{}/{}/{}", &config.game.wine.builds, wine.name, wine.files.wineboot);
let wine_folder = config.game.wine.builds.join(wine.name);
let wine_path = wine_folder.join(wine.files.wine64);
let wineserver_path = wine_folder.join(wine.files.wineserver);
let wineboot_path = wine_folder.join(wine.files.wineboot);
(wine_path, wineserver_path, wineboot_path)
},
None => (String::from("wine64"), String::from("wineserver"), String::from("wineboot"))
None => (PathBuf::from("wine64"), PathBuf::from("wineserver"), PathBuf::from("wineboot"))
};
let result = Dxvk::install(
PathBuf::from(apply_path),
PathBuf::from(prefix_path.to_string()),
PathBuf::from(&wine_path),
PathBuf::from(wine_path),
PathBuf::from(wineboot_path),
PathBuf::from(wineserver_path)
apply_path,
prefix_path.into(),
wine_path.clone(),
wine_path,
wineboot_path,
wineserver_path
);
match result {

View file

@ -1,3 +1,5 @@
use std::path::PathBuf;
use anime_game_core::installer::downloader::Downloader;
use crate::lib::config::game::enhancements::fps_unlocker::config::Config as FpsUnlockerConfig;
@ -11,7 +13,7 @@ const LATEST_INFO: (&str, &str) = (
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FpsUnlocker {
dir: String
dir: PathBuf
}
impl FpsUnlocker {
@ -21,28 +23,32 @@ impl FpsUnlocker {
/// - `Err(..)` if failed to read `unlocker.exe` file
/// - `Ok(None)` if version is not latest
/// - `Ok(..)` if version is latest
pub fn from_dir<T: ToString>(dir: T) -> anyhow::Result<Option<Self>> {
let hash = format!("{:x}", md5::compute(std::fs::read(format!("{}/unlocker.exe", dir.to_string()))?));
pub fn from_dir<T: Into<PathBuf>>(dir: T) -> anyhow::Result<Option<Self>> {
let dir = dir.into();
if hash == LATEST_INFO.0 {
Ok(Some(Self { dir: dir.to_string() }))
let hash = format!("{:x}", md5::compute(std::fs::read(dir.join("unlocker.exe"))?));
Ok(if hash == LATEST_INFO.0 {
Some(Self { dir })
} else {
Ok(None)
}
None
})
}
/// Download FPS unlocker to specified directory
pub fn download<T: ToString>(dir: T) -> anyhow::Result<Self> {
pub fn download<T: Into<PathBuf>>(dir: T) -> anyhow::Result<Self> {
let mut downloader = Downloader::new(LATEST_INFO.1)?;
let dir = dir.into();
// Create FPS unlocker folder if needed
if !std::path::Path::new(&dir.to_string()).exists() {
std::fs::create_dir_all(dir.to_string())?;
if !dir.exists() {
std::fs::create_dir_all(&dir)?;
}
match downloader.download_to(format!("{}/unlocker.exe", dir.to_string()), |_, _| {}) {
match downloader.download_to(dir.join("unlocker.exe"), |_, _| {}) {
Ok(_) => Ok(Self {
dir: dir.to_string()
dir
}),
Err(err) => {
let err: std::io::Error = err.into();
@ -52,16 +58,16 @@ impl FpsUnlocker {
}
}
pub fn get_binary(&self) -> String {
pub fn get_binary(&self) -> PathBuf {
Self::get_binary_in(&self.dir)
}
pub fn get_binary_in<T: ToString>(dir: T) -> String {
format!("{}/unlocker.exe", dir.to_string())
pub fn get_binary_in<T: Into<PathBuf>>(dir: T) -> PathBuf {
dir.into().join("unlocker.exe")
}
pub fn dir(&self) -> &str {
self.dir.as_str()
pub fn dir(&self) -> &PathBuf {
&self.dir
}
/// Generate and save FPS unlocker config file to the game's directory
@ -69,7 +75,7 @@ impl FpsUnlocker {
let config = config_schema::ConfigSchema::from_config(config);
Ok(std::fs::write(
format!("{}/fps_config.json", self.dir),
self.dir.join("fps_config.json"),
config.json()?
)?)
}

View file

@ -110,13 +110,13 @@ pub fn run() -> anyhow::Result<()> {
return Err(anyhow::anyhow!("Failed to update FPS unlocker config: {err}"));
}
let bat_path = format!("{}/fpsunlocker.bat", config.game.path);
let original_bat_path = format!("{}/launcher.bat", config.game.path);
let bat_path = config.game.path.join("fpsunlocker.bat");
let original_bat_path = config.game.path.join("launcher.bat");
// Generate fpsunlocker.bat from launcher.bat
std::fs::write(bat_path, std::fs::read_to_string(original_bat_path)?
.replace("start GenshinImpact.exe %*", &format!("start GenshinImpact.exe %*\n\nZ:\ncd \"{}\"\nstart unlocker.exe", unlocker.dir()))
.replace("start YuanShen.exe %*", &format!("start YuanShen.exe %*\n\nZ:\ncd \"{}\"\nstart unlocker.exe", unlocker.dir())))?;
.replace("start GenshinImpact.exe %*", &format!("start GenshinImpact.exe %*\n\nZ:\ncd \"{}\"\nstart unlocker.exe", unlocker.dir().to_string_lossy()))
.replace("start YuanShen.exe %*", &format!("start YuanShen.exe %*\n\nZ:\ncd \"{}\"\nstart unlocker.exe", unlocker.dir().to_string_lossy())))?;
}
// Prepare bash -c '<command>'
@ -127,7 +127,7 @@ pub fn run() -> anyhow::Result<()> {
bash_chain += "gamemoderun ";
}
bash_chain += &format!("'{wine_executable}' ");
bash_chain += &format!("'{}' ", wine_executable.to_string_lossy());
if let Some(virtual_desktop) = config.game.wine.virtual_desktop.get_command() {
bash_chain += &format!("{virtual_desktop} ");

View file

@ -1,5 +1,3 @@
use std::path::PathBuf;
use anime_game_core::prelude::*;
use anime_game_core::genshin::prelude::*;
@ -51,12 +49,12 @@ impl LauncherState {
let config = config::get()?;
// Check wine existence
if let None = config.try_get_wine_executable() {
if config.try_get_wine_executable().is_none() {
return Ok(Self::WineNotInstalled);
}
// Check prefix existence
if !PathBuf::from(&config.game.wine.prefix).join("drive_c").exists() {
if !config.game.wine.prefix.join("drive_c").exists() {
return Ok(Self::PrefixNotExists);
}

View file

@ -37,12 +37,12 @@ impl List {
}
/// List only downloaded wine versions in some specific folder
pub fn list_downloaded<T: ToString>(folder: T) -> std::io::Result<Vec<Version>> {
pub fn list_downloaded<T: Into<PathBuf>>(folder: T) -> std::io::Result<Vec<Version>> {
let mut downloaded = Vec::new();
let list = Self::get();
for entry in std::fs::read_dir(folder.to_string())? {
for entry in std::fs::read_dir(folder.into())? {
let name = entry?.file_name();
for group in &list {

View file

@ -69,9 +69,9 @@ fn main() {
// Create default launcher folder if needed
let launcher_dir = lib::consts::launcher_dir().expect("Failed to get launcher dir");
if !Path::new(&launcher_dir).exists() || Path::new(&format!("{}/.first-run", launcher_dir)).exists() {
if !launcher_dir.exists() || launcher_dir.join(".first-run").exists() {
fs::create_dir_all(&launcher_dir).expect("Failed to create default launcher dir");
fs::write(format!("{}/.first-run", launcher_dir), "").expect("Failed to create .first-run file");
fs::write(launcher_dir.join(".first-run"), "").expect("Failed to create .first-run file");
let first_run = FirstRunApp::new(app).expect("Failed to init FirstRunApp");

View file

@ -1,5 +1,7 @@
use adw::prelude::*;
use std::path::PathBuf;
use crate::lib::dxvk::Group;
use super::dxvk_row::DxvkRow;
@ -35,9 +37,11 @@ impl DxvkGroup {
}
}
pub fn update_states<T: ToString>(&self, runners_folder: T) {
pub fn update_states<T: Into<PathBuf>>(&self, runners_folder: T) {
let runners_folder = runners_folder.into();
for component in &self.version_components {
component.update_state(runners_folder.to_string());
component.update_state(&runners_folder);
}
}
}

View file

@ -1,6 +1,8 @@
use gtk::prelude::*;
use adw::prelude::*;
use std::path::PathBuf;
use crate::lib::dxvk::Version;
use crate::ui::traits::download_component::*;
@ -57,7 +59,7 @@ impl DxvkRow {
}
}
pub fn update_state<T: ToString>(&self, dxvks_folder: T) {
pub fn update_state<T: Into<PathBuf>>(&self, dxvks_folder: T) {
if self.is_downloaded(dxvks_folder) {
self.button.set_icon_name("user-trash-symbolic");
@ -71,7 +73,7 @@ impl DxvkRow {
}
}
pub fn apply<T: ToString>(&self, dxvks_folder: T, prefix_path: T) -> anyhow::Result<std::process::Output> {
pub fn apply<T: Into<PathBuf>>(&self, dxvks_folder: T, prefix_path: T) -> anyhow::Result<std::process::Output> {
self.button.set_sensitive(false);
self.apply_button.set_sensitive(false);
@ -85,8 +87,8 @@ impl DxvkRow {
}
impl DownloadComponent for DxvkRow {
fn get_component_path<T: ToString>(&self, installation_path: T) -> String {
format!("{}/{}", installation_path.to_string(), self.version.name)
fn get_component_path<T: Into<PathBuf>>(&self, installation_path: T) -> PathBuf {
installation_path.into().join(&self.version.name)
}
fn get_downloading_widgets(&self) -> (gtk::ProgressBar, gtk::Button) {

View file

@ -1,5 +1,7 @@
use adw::prelude::*;
use std::path::PathBuf;
use crate::lib::wine::Group;
use super::wine_row::WineRow;
@ -35,9 +37,11 @@ impl WineGroup {
}
}
pub fn update_states<T: ToString>(&self, runners_folder: T) {
pub fn update_states<T: Into<PathBuf>>(&self, runners_folder: T) {
let runners_folder = runners_folder.into();
for component in &self.version_components {
component.update_state(runners_folder.to_string());
component.update_state(&runners_folder);
}
}
}

View file

@ -1,6 +1,8 @@
use gtk::prelude::*;
use adw::prelude::*;
use std::path::PathBuf;
use crate::lib::wine::Version;
use crate::ui::traits::download_component::*;
@ -46,7 +48,7 @@ impl WineRow {
}
}
pub fn update_state<T: ToString>(&self, runners_folder: T) {
pub fn update_state<T: Into<PathBuf>>(&self, runners_folder: T) {
if self.is_downloaded(runners_folder) {
self.button.set_icon_name("user-trash-symbolic");
}
@ -58,8 +60,8 @@ impl WineRow {
}
impl DownloadComponent for WineRow {
fn get_component_path<T: ToString>(&self, installation_path: T) -> String {
format!("{}/{}", installation_path.to_string(), self.version.name)
fn get_component_path<T: Into<PathBuf>>(&self, installation_path: T) -> PathBuf {
installation_path.into().join(&self.version.name)
}
fn get_downloading_widgets(&self) -> (gtk::ProgressBar, gtk::Button) {

View file

@ -3,6 +3,8 @@ use adw::prelude::*;
use gtk::glib;
use gtk::glib::clone;
use std::path::PathBuf;
use wait_not_await::Await;
use crate::lib::config;
@ -65,13 +67,13 @@ impl Page {
let config = config::get()?;
// Add paths to subtitles
result.runners_folder.set_subtitle(&config.game.wine.builds);
result.dxvk_folder.set_subtitle(&config.game.dxvk.builds);
result.prefix_folder.set_subtitle(&config.game.wine.prefix);
result.game_folder.set_subtitle(&config.game.path);
result.patch_folder.set_subtitle(&config.patch.path);
result.runners_folder.set_subtitle(config.game.wine.builds.to_str().unwrap());
result.dxvk_folder.set_subtitle(config.game.dxvk.builds.to_str().unwrap());
result.prefix_folder.set_subtitle(config.game.wine.prefix.to_str().unwrap());
result.game_folder.set_subtitle(config.game.path.to_str().unwrap());
result.patch_folder.set_subtitle(config.patch.path.to_str().unwrap());
result.temp_folder.set_subtitle(&match config.launcher.temp {
Some(temp) => temp,
Some(temp) => temp.to_string_lossy().to_string(),
None => String::from("/tmp")
});
@ -107,12 +109,12 @@ impl Page {
}
pub fn update_config(&self, mut config: config::Config) -> config::Config {
config.game.wine.builds = self.runners_folder.subtitle().unwrap().to_string();
config.game.dxvk.builds = self.dxvk_folder.subtitle().unwrap().to_string();
config.game.wine.prefix = self.prefix_folder.subtitle().unwrap().to_string();
config.game.path = self.game_folder.subtitle().unwrap().to_string();
config.patch.path = self.patch_folder.subtitle().unwrap().to_string();
config.launcher.temp = Some(self.temp_folder.subtitle().unwrap().to_string());
config.game.wine.builds = PathBuf::from(self.runners_folder.subtitle().unwrap().to_string());
config.game.dxvk.builds = PathBuf::from(self.dxvk_folder.subtitle().unwrap().to_string());
config.game.wine.prefix = PathBuf::from(self.prefix_folder.subtitle().unwrap().to_string());
config.game.path = PathBuf::from(self.game_folder.subtitle().unwrap().to_string());
config.patch.path = PathBuf::from(self.patch_folder.subtitle().unwrap().to_string());
config.launcher.temp = Some(PathBuf::from(self.temp_folder.subtitle().unwrap().to_string()));
config
}

View file

@ -6,7 +6,6 @@ use gtk::glib::clone;
use std::rc::Rc;
use std::cell::Cell;
use std::process::Command;
use std::path::PathBuf;
use anime_game_core::prelude::*;
@ -248,7 +247,7 @@ impl App {
let progress_bar = this.widgets.download_components.progress_bar.clone();
let wine_version = this.widgets.download_components.get_wine_version().clone();
let wine_version = this.widgets.download_components.get_wine_version();
let dxvk_version = this.widgets.download_components.get_dxvk_version().clone();
// Prepare wine downloader
@ -262,7 +261,7 @@ impl App {
match Installer::new(&wine_version_copy.uri) {
Ok(mut installer) => {
if let Some(temp_folder) = config.launcher.temp {
installer.temp_folder = PathBuf::from(temp_folder);
installer.temp_folder = temp_folder;
}
installer.downloader
@ -333,7 +332,7 @@ impl App {
match Installer::new(&dxvk_version.uri) {
Ok(mut installer) => {
if let Some(temp_folder) = config.launcher.temp {
installer.temp_folder = PathBuf::from(temp_folder);
installer.temp_folder = temp_folder;
}
installer.downloader
@ -393,9 +392,9 @@ impl App {
// Remove .first-run file
let launcher_dir = crate::lib::consts::launcher_dir().unwrap();
std::fs::remove_file(format!("{}/.first-run", launcher_dir)).unwrap();
std::fs::remove_file(launcher_dir.join(".first-run")).unwrap();
// Show next page
this.update(Actions::DownloadComponentsContinue).unwrap();
},

View file

@ -7,7 +7,7 @@ use std::rc::Rc;
use std::cell::Cell;
use std::io::Error;
use std::process::{Command, Stdio};
use std::path::PathBuf;
use std::path::Path;
use wait_not_await::Await;
@ -68,7 +68,7 @@ impl AppWidgets {
let result = Self {
window: window.clone(),
toast_overlay: toast_overlay.clone(),
toast_overlay,
menu: get_object(&builder, "menu")?,
about: get_object(&builder, "about")?,
@ -127,6 +127,7 @@ impl AppWidgets {
let curl_info = anime_game_core::curl_sys::Version::get();
#[allow(clippy::or_fun_call)]
result.about.set_debug_info(&[
format!("Anime Game core library version: {}", anime_game_core::VERSION),
format!("Curl version: {}", curl_info.version()),
@ -162,6 +163,7 @@ pub enum Actions {
}
impl Actions {
#[allow(clippy::expect_fun_call, clippy::wrong_self_convention)]
pub fn into_fn<T: gtk::glib::IsA<gtk::Widget>>(&self, app: &App) -> Box<dyn Fn(&T)> {
Box::new(clone!(@strong self as action, @weak app => move |_| {
app.update(action.clone()).expect(&format!("Failed to execute action {:?}", &action));
@ -443,7 +445,7 @@ impl App {
match Installer::new(wine.uri) {
Ok(mut installer) => {
if let Some(temp_folder) = config.launcher.temp {
installer.temp_folder = PathBuf::from(temp_folder);
installer.temp_folder = temp_folder;
}
installer.downloader
@ -750,7 +752,7 @@ impl App {
}).unwrap();
}
if broken.len() > 0 {
if !broken.is_empty() {
this.update(Actions::UpdateProgress {
fraction: Rc::new(0.0),
title: Rc::new(String::from("Repairing files: 0%"))
@ -771,7 +773,7 @@ impl App {
println!("Patch status: {}", is_patch_applied);
fn should_ignore(path: &PathBuf) -> bool {
fn should_ignore(path: &Path) -> bool {
for part in ["UnityPlayer.dll", "xlua.dll", "crashreport.exe", "upload_crash.exe", "vulkan-1.dll"] {
if path.ends_with(part) {
return true;
@ -890,7 +892,7 @@ impl App {
// Calculate size of the update
let size =
game.size().unwrap_or((0, 0)).0 +
voices.into_iter().fold(0, |acc, voice| acc + voice.size().unwrap_or((0, 0)).0);
voices.iter().fold(0, |acc, voice| acc + voice.size().unwrap_or((0, 0)).0);
// Update tooltip
self.widgets.predownload_game.set_tooltip_text(Some(&format!("Pre-download {} update ({})", game.latest(), prettify_bytes(size))));
@ -902,12 +904,10 @@ impl App {
if let Ok(config) = config::get() {
if let Some(temp) = config.launcher.temp {
let tmp = PathBuf::from(temp);
// If all the files were downloaded
let downloaded =
tmp.join(game.file_name().unwrap()).exists() &&
voices.into_iter().fold(true, move |acc, voice| acc && tmp.join(voice.file_name().unwrap()).exists());
temp.join(game.file_name().unwrap()).exists() &&
voices.iter().all(|voice| temp.join(voice.file_name().unwrap()).exists());
if downloaded {
self.widgets.predownload_game.remove_css_class("warning");

View file

@ -146,34 +146,32 @@ impl App {
Actions::Add(strs) => {
let (name, value) = &*strs;
if !name.is_empty() && !value.is_empty() {
if !values.rows.contains_key(name) {
config.game.environment.insert(name.clone(), value.clone());
if !name.is_empty() && !value.is_empty() && !values.rows.contains_key(name) {
config.game.environment.insert(name.clone(), value.clone());
let row = adw::ActionRow::new();
let row = adw::ActionRow::new();
row.set_title(name);
row.set_subtitle(value);
row.set_title(name);
row.set_subtitle(value);
let button = gtk::Button::new();
let button = gtk::Button::new();
button.set_icon_name("user-trash-symbolic");
button.set_valign(gtk::Align::Center);
button.add_css_class("flat");
button.set_icon_name("user-trash-symbolic");
button.set_valign(gtk::Align::Center);
button.add_css_class("flat");
button.connect_clicked(clone!(@weak this, @strong name => move |_| {
this.update(Actions::Delete(Rc::new(name.clone()))).unwrap();
}));
button.connect_clicked(clone!(@weak this, @strong name => move |_| {
this.update(Actions::Delete(Rc::new(name.clone()))).unwrap();
}));
row.add_suffix(&button);
row.add_suffix(&button);
this.widgets.variables.add(&row);
this.widgets.variables.add(&row);
values.rows.insert(name.clone(), row);
values.rows.insert(name.clone(), row);
this.widgets.name.set_text("");
this.widgets.value.set_text("");
}
this.widgets.name.set_text("");
this.widgets.value.set_text("");
}
}
@ -230,7 +228,7 @@ impl App {
status_page.set_description(Some("Loading environment..."));
// Set game command
self.widgets.command.set_text(&config.game.command.unwrap_or(String::new()));
self.widgets.command.set_text(&config.game.command.unwrap_or_default());
// Add environment variables
for (name, value) in config.game.environment {

View file

@ -152,6 +152,7 @@ pub enum Actions {
}
impl Actions {
#[allow(clippy::expect_fun_call, clippy::wrong_self_convention)]
pub fn into_fn<T: gtk::glib::IsA<gtk::Widget>>(&self, app: &App) -> Box<dyn Fn(&T)> {
Box::new(clone!(@strong self as action, @weak app => move |_| {
app.update(action.clone()).expect(&format!("Failed to execute action {:?}", &action));
@ -211,7 +212,7 @@ impl App {
self.widgets.repair_game.connect_clicked(Actions::RepairGame.into_fn(&self));
// Voiceover download/delete button event
for (i, row) in (&*self.widgets.voieover_components).into_iter().enumerate() {
for (i, row) in (*self.widgets.voieover_components).iter().enumerate() {
row.button.connect_clicked(clone!(@weak self as this => move |_| {
this.update(Actions::VoiceoverPerformAction(Rc::new(i))).unwrap();
}));
@ -251,8 +252,8 @@ impl App {
// Wine install/remove buttons
let components = &*self.widgets.wine_components;
for (i, group) in components.into_iter().enumerate() {
for (j, component) in (&group.version_components).into_iter().enumerate() {
for (i, group) in components.iter().enumerate() {
for (j, component) in group.version_components.iter().enumerate() {
component.button.connect_clicked(Actions::WinePerformAction(Rc::new((i, j))).into_fn(&self));
}
}
@ -273,8 +274,8 @@ impl App {
// DXVK install/remove/apply buttons
let components = &*self.widgets.dxvk_components;
for (i, group) in components.into_iter().enumerate() {
for (j, component) in (&group.version_components).into_iter().enumerate() {
for (i, group) in components.iter().enumerate() {
for (j, component) in group.version_components.iter().enumerate() {
component.button.connect_clicked(Actions::DxvkPerformAction(Rc::new((i, j))).into_fn(&self));
component.apply_button.connect_clicked(clone!(@strong component, @weak self as this => move |_| {
@ -314,7 +315,7 @@ impl App {
match action {
Actions::RepairGame => {
let option = (&*this.app).take();
let option = (*this.app).take();
this.app.set(option.clone());
let app = option.unwrap();
@ -345,7 +346,7 @@ impl App {
}
else {
let option = (&*this.app).take();
let option = (*this.app).take();
this.app.set(option.clone());
let app = option.unwrap();
@ -382,23 +383,21 @@ impl App {
this.update(Actions::UpdateDxvkComboRow).unwrap();
}
else {
if let Ok(awaiter) = component.download(&config.game.dxvk.builds) {
awaiter.then(clone!(@strong this => move |_| {
match component.apply(&config.game.dxvk.builds, &config.game.wine.prefix) {
Ok(output) => println!("{}", String::from_utf8_lossy(&output.stdout)),
Err(err) => {
this.update(Actions::Toast(Rc::new((
String::from("Failed to apply DXVK"), err.to_string()
)))).unwrap();
}
else if let Ok(awaiter) = component.download(&config.game.dxvk.builds) {
awaiter.then(clone!(@strong this => move |_| {
match component.apply(&config.game.dxvk.builds, &config.game.wine.prefix) {
Ok(output) => println!("{}", String::from_utf8_lossy(&output.stdout)),
Err(err) => {
this.update(Actions::Toast(Rc::new((
String::from("Failed to apply DXVK"), err.to_string()
)))).unwrap();
}
}
component.update_state(&config.game.dxvk.builds);
component.update_state(&config.game.dxvk.builds);
this.update(Actions::UpdateDxvkComboRow).unwrap();
}));
}
this.update(Actions::UpdateDxvkComboRow).unwrap();
}));
}
}
@ -419,14 +418,12 @@ impl App {
this.update(Actions::UpdateWineComboRow).unwrap();
}
else {
if let Ok(awaiter) = component.download(&config.game.wine.builds) {
awaiter.then(clone!(@strong this => move |_| {
component.update_state(&config.game.wine.builds);
else if let Ok(awaiter) = component.download(&config.game.wine.builds) {
awaiter.then(clone!(@strong this => move |_| {
component.update_state(&config.game.wine.builds);
this.update(Actions::UpdateWineComboRow).unwrap();
}));
}
this.update(Actions::UpdateWineComboRow).unwrap();
}));
}
}
@ -518,7 +515,7 @@ impl App {
let mut selected = 0;
for (i, version) in (&list).into_iter().enumerate() {
for (i, version) in list.iter().enumerate() {
model.append(version.title.as_str());
if let Some(curr) = &config.game.wine.selected {
@ -708,7 +705,7 @@ impl App {
impl Toast for App {
fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) {
let app = (&*self.app).take();
let app = (*self.app).take();
self.app.set(app.clone());
app.unwrap().get_toast_widgets()

View file

@ -91,7 +91,7 @@ impl PreferencesStack {
impl Toast for PreferencesStack {
fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) {
let app = (&*self.app).take();
let app = (*self.app).take();
self.app.set(app.clone());
app.unwrap().get_toast_widgets()

View file

@ -17,15 +17,15 @@ pub enum DownloadingResult {
}
pub trait DownloadComponent {
fn get_component_path<T: ToString>(&self, installation_path: T) -> String;
fn get_component_path<T: Into<PathBuf>>(&self, installation_path: T) -> PathBuf;
fn get_downloading_widgets(&self) -> (gtk::ProgressBar, gtk::Button);
fn get_download_uri(&self) -> String;
fn is_downloaded<T: ToString>(&self, installation_path: T) -> bool {
fn is_downloaded<T: Into<PathBuf>>(&self, installation_path: T) -> bool {
Path::new(&self.get_component_path(installation_path)).exists()
}
fn download<T: ToString>(&self, installation_path: T) -> anyhow::Result<Await<DownloadingResult>> {
fn download<T: Into<PathBuf>>(&self, installation_path: T) -> anyhow::Result<Await<DownloadingResult>> {
let (sender, receiver) = glib::MainContext::channel::<InstallerUpdate>(glib::PRIORITY_DEFAULT);
let (progress_bar, button) = self.get_downloading_widgets();
@ -66,11 +66,11 @@ pub trait DownloadComponent {
}
InstallerUpdate::DownloadingError(err) => {
downl_send.send(DownloadingResult::DownloadingError(err.into())).unwrap();
downl_send.send(DownloadingResult::DownloadingError(err)).unwrap();
}
InstallerUpdate::UnpackingError(err) => {
downl_send.send(DownloadingResult::UnpackingError(err.to_string())).unwrap();
downl_send.send(DownloadingResult::UnpackingError(err)).unwrap();
}
}
@ -83,17 +83,17 @@ pub trait DownloadComponent {
let mut installer = Installer::new(self.get_download_uri())?;
if let Some(temp_folder) = config.launcher.temp {
installer.temp_folder = PathBuf::from(temp_folder);
installer.temp_folder = temp_folder;
}
installer.downloader
.set_downloading_speed(config.launcher.speed_limit)
.expect("Failed to set downloading speed limit");
let installation_path = installation_path.to_string();
send.send(installer).unwrap();
let installation_path = installation_path.into();
std::thread::spawn(move || {
let mut installer = recv.recv().unwrap();
@ -107,7 +107,7 @@ pub trait DownloadComponent {
}))
}
fn delete<T: ToString>(&self, installation_path: T) -> std::io::Result<()> {
fn delete<T: Into<PathBuf>>(&self, installation_path: T) -> std::io::Result<()> {
std::fs::remove_dir_all(self.get_component_path(installation_path))
}
}

View file

@ -16,7 +16,7 @@ pub trait Toast {
let message = format!("{}", message);
if message.len() > 0 {
if !message.is_empty() {
toast.set_button_label(Some("See message"));
toast.set_action_name(Some("see-message.see-message"));