Skip to content

Commit

Permalink
0.5.5 (#150)
Browse files Browse the repository at this point in the history
Fixed some duplicates being allowed in the game list.
Fixed some manually-added games becoming undeletable.
Make some changes that makes antivirus software hopefully less
suspicious.
The dialog messages that show up when WebView2 is missing or corrupt are
now less helpful. Seems like some antivirus software really didn't like
that I was trying to open webpages and trying to repair WebView2 myself,
so now I'm just telling the users how to do it themselves, which sucks.
I'm not even sure if these changes will actually help with the antivirus
problems, but we'll see.
  • Loading branch information
Raicuparta authored Jan 6, 2024
2 parents 66ad7cf + 315b225 commit 43264a2
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 139 deletions.
25 changes: 24 additions & 1 deletion backend/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions backend/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rai-pal"
version = "0.5.4"
version = "0.5.5"
authors = ["Raicuparta"]
license = "GPL-3.0-or-later"
repository = "https://github.com/Raicuparta/rai-pal"
Expand Down Expand Up @@ -45,7 +45,7 @@ lazy_static = "1.4.0"
uuid = "1.6.1"
rand = "0.8.5"
winreg = "0.52.0"
tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1", features = ["colored"] }
log = "0.4.20"
tauri-runtime = "0.14.1"

Expand Down
4 changes: 3 additions & 1 deletion backend/src/installed_game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@ impl InstalledGame {

let game_mode = steam_launch.map_or(GameMode::Flat, SteamLaunchOption::get_game_mode);

let executable = GameExecutable::new(path)?;

Some(Self {
id: hash_path(path),
id: hash_path(&executable.path),
name: name.to_string(),
provider_id,
discriminator,
Expand Down
59 changes: 37 additions & 22 deletions backend/src/steam/id_lists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ use std::{
future,
};

use log::error;

use crate::{
game_engines::game_engine::GameEngineBrand,
serializable_enum,
serializable_struct,
Result,
};

const STEAM_APP_IDS_URL_BASE: &str =
"https://raw.githubusercontent.com/Raicuparta/steam-app-ids-by-engine/main";
const STEAM_APP_IDS_URL_BASE: &str = "https://raicuparta.github.io/rai-pal-db/steam-ids";

serializable_enum!(UevrScore { A, B, C, D, E });

Expand All @@ -24,28 +25,42 @@ serializable_struct!(SteamGame {
pub uevr_score: Option<UevrScore>,
});

async fn get_list(list_name: &str) -> Result<HashSet<String>> {
Ok(
reqwest::get(format!("{STEAM_APP_IDS_URL_BASE}/{list_name}"))
.await?
.text()
.await?
.split('\n')
.map(std::string::ToString::to_string)
.collect(),
)
async fn get_list(list_name: &str) -> HashSet<String> {
match reqwest::get(format!("{STEAM_APP_IDS_URL_BASE}/{list_name}")).await {
Ok(response) => match response.text().await {
Ok(text) => text
.split('\n')
.map(|line| line.trim().to_string())
.collect(),
Err(err) => {
error!("Failed to parse ids list {list_name}: {err}");
HashSet::default()
}
},
Err(err) => {
error!("Failed to download ids list {list_name}: {err}");
HashSet::default()
}
}
}

// TODO this should be a more generic thing where you can load arbitrary json databases,
// and show them as columns in Rai Pal.
// For now it's just a hardcoded uevr db.
async fn get_uevr_scores() -> Result<HashMap<String, UevrScore>> {
Ok(
reqwest::get(format!("{STEAM_APP_IDS_URL_BASE}/uevr-scores.json"))
.await?
async fn get_uevr_scores() -> HashMap<String, UevrScore> {
match reqwest::get(format!("{STEAM_APP_IDS_URL_BASE}/uevr-scores.json")).await {
Ok(response) => response
.json::<HashMap<String, UevrScore>>()
.await?,
)
.await
.unwrap_or_else(|err| {
error!("Failed to parse uevr ids list: {err}");
HashMap::default()
}),
Err(err) => {
error!("Failed to download uevr ids list: {err}");
HashMap::default()
}
}
}

fn get_ids_data_list(ids: &HashSet<String>, engine: GameEngineBrand) -> Vec<SteamGame> {
Expand All @@ -62,12 +77,12 @@ pub async fn get() -> Result<HashMap<String, SteamGame>> {
let (unity, unreal, godot) =
future::join!(get_list("Unity"), get_list("Unreal"), get_list("Godot"),).await;

let uevr_scores = get_uevr_scores().await.unwrap_or_default();
let uevr_scores = get_uevr_scores().await;

let mut games = [
get_ids_data_list(&unity.unwrap_or_default(), GameEngineBrand::Unity),
get_ids_data_list(&unreal.unwrap_or_default(), GameEngineBrand::Unreal),
get_ids_data_list(&godot.unwrap_or_default(), GameEngineBrand::Godot),
get_ids_data_list(&unity, GameEngineBrand::Unity),
get_ids_data_list(&unreal, GameEngineBrand::Unreal),
get_ids_data_list(&godot, GameEngineBrand::Godot),
]
.concat();

Expand Down
118 changes: 5 additions & 113 deletions backend/src/windows.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,9 @@
use std::{
ffi::OsStr,
io,
os::windows::ffi::OsStrExt,
path::Path,
};

use winapi::um::{
shellapi::ShellExecuteW,
winuser::SW_SHOW,
};

use crate::{
paths,
Result,
};

pub fn run_as_admin(exe_path: &Path, parameters: &str) -> Result {
let directory = path_to_wide(paths::path_parent(exe_path)?);

let result = unsafe {
ShellExecuteW(
ptr::null_mut(),
str_to_wide("runas").as_ptr(),
path_to_wide(exe_path).as_ptr(),
str_to_wide(parameters).as_ptr(),
directory.as_ptr(),
SW_SHOW,
)
};

#[allow(clippy::as_conversions)]
if result as c_int > 32 {
Ok(())
} else {
Err(io::Error::last_os_error())?
}
}

use std::{
path::PathBuf,
ptr,
};

use lazy_regex::regex_captures;
use log::error;
use winapi::{
ctypes::{
Expand All @@ -58,10 +19,8 @@ use winapi::{
MB_YESNO,
},
};
use winreg::{
enums::HKEY_LOCAL_MACHINE,
RegKey,
};

use crate::paths;

fn os_str_to_wide(os_str: &OsStr) -> Vec<u16> {
os_str.encode_wide().chain(std::iter::once(0)).collect()
Expand All @@ -71,10 +30,6 @@ fn str_to_wide(text: &str) -> Vec<u16> {
os_str_to_wide(OsStr::new(text))
}

fn path_to_wide(path: &Path) -> Vec<u16> {
os_str_to_wide(path.as_os_str())
}

fn base_error_dialog(error_text: &str, flags: c_uint) -> c_int {
error!("{error_text}");

Expand All @@ -98,24 +53,11 @@ pub fn error_question_dialog(error_text: &str) -> bool {

const WEBVIEW_ERROR_MESSAGE: &str = r#"Webview error. This usually means something is wrong with your Webview2 installation.
Would you like to attempt to repair it?"#;

const WEBVIEW_REPAIR_FAILED_MESSAGE: &str = r#"Ok, that didn't work either.
You can try to repair it yourself by going to Windows "Installed Apps", then selecting WebView2 and picking "Repair".
If that does't work, you can try downloading it from Microsoft. Would you like to open the website to download WebView2?"#;
You can try to repair it by going to Windows Settings > Apps > Installed Apps, then finding "Microsoft Edge WebView2 Runtime" and picking "Modify" from the three dots menu.
const WEBVIEW_WEBSITE_URL: &str =
"https://developer.microsoft.com/microsoft-edge/webview2#download";
If that doesn't work, search online for "Microsoft Edge WebView2", and download the installer from Microsoft's website.
const DEFAULT_WEBVIEW2_REPAIR_PATH: &str =
r"C:\Program Files (x86)\Microsoft\EdgeUpdate\MicrosoftEdgeUpdate.exe";
const DEFAULT_WEBVIEW2_REPAIR_ARGS: &str =
"/install appguid={F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}&appname=Microsoft%20Edge%20WebView&needsadmin=true&repairtype=windowsonlinerepair /installsource otherinstallcmd";

const WEBVIEW_REGISTRY_KEY: &str =
r"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft EdgeWebView";
Would you like to open the logs folder?"#;

fn try_open_logs_folder() {
if let Err(error) = paths::open_logs_folder() {
Expand All @@ -129,56 +71,6 @@ fn try_open_logs_folder() {
pub fn webview_error_dialog(error_text: &str) {
error!("{error_text}");
if error_question_dialog(WEBVIEW_ERROR_MESSAGE) {
let (path, command) = get_webview2_repair();

run_as_admin(&path, &command).unwrap_or_else(|repair_error| {
error!("{repair_error}");
if error_question_dialog(WEBVIEW_REPAIR_FAILED_MESSAGE) {
open::that_detached(WEBVIEW_WEBSITE_URL).unwrap_or_else(|open_website_error| {
error!("{open_website_error}");
// If we reached here then everything failed, just give up please.
error_dialog(&format!(
"Somehow even that failed.\n\nPlease report this error, and include the logs file."
));
try_open_logs_folder();
});
} else {
try_open_logs_folder();
}
});
} else {
try_open_logs_folder();
}
}

// Seems to be common for Webview2 to be broken, but with Edge still present,
// so the Edge installer can be used to repair Webview2.
fn get_webview2_repair() -> (PathBuf, String) {
// We crawl through the windows registry sewage to find the command for repairing Webview2.
RegKey::predef(HKEY_LOCAL_MACHINE)
.open_subkey(WEBVIEW_REGISTRY_KEY)
.and_then(|key| key.get_value::<String, _>("ModifyPath"))
.map_or_else(
|error| get_fallback_webview2_repair(&error.to_string()),
|path_with_args| {
// The command is store in the registry in a format like "C:/Some/Path/To/the.exe \and some=args".
if let Some((_, path, args)) = regex_captures!(r#""(.+)" ?(.*)"#, &path_with_args) {
return (PathBuf::from(path), args.to_string());
}
get_fallback_webview2_repair(&format!(
"Failed to parse registry item: {path_with_args}"
))
},
)
}

// If we aren't able to figure out the Webview2 repair command from the registry,
// we just fall back to a hardcoded one, which should be the case for the majority of users.
// If we reach here it's unlikely that the repair exe will be present anyway, but worth a shot I guess.
fn get_fallback_webview2_repair(message: &str) -> (PathBuf, String) {
error!("Failed to get WebView2 repair exe path from registry. Attempting with default path. {message}");
(
PathBuf::from(DEFAULT_WEBVIEW2_REPAIR_PATH),
DEFAULT_WEBVIEW2_REPAIR_ARGS.to_string(),
)
}

0 comments on commit 43264a2

Please sign in to comment.