Skip to content

Commit

Permalink
Bash09/steamlocate (#47)
Browse files Browse the repository at this point in the history
* Refactor to use steamlocate and improve error handling when loading settings.

---------

Co-authored-by: Bash <>
  • Loading branch information
Bash-09 authored Aug 26, 2023
1 parent c8607e9 commit 95538ad
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 177 deletions.
70 changes: 69 additions & 1 deletion Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ keyvalues-serde = "0.1.0"
substring = "1.4.5"
tower-http = { version = "0.4.3", features = ["cors"] }
include_dir = "0.7.3"
steamlocate = { version = "1.2.1", features = ["steamid_ng"] }
135 changes: 26 additions & 109 deletions src/gamefinder.rs
Original file line number Diff line number Diff line change
@@ -1,128 +1,45 @@
use std::{
fs,
path::{Path, PathBuf},
};
use std::path::PathBuf;

use anyhow::{anyhow, Result};
use steamid_ng::SteamID;
use steamlocate::SteamDir;

pub const TF2_GAME_ID: &str = "440";
pub const TF2_GAME_ID: u32 = 440;

pub fn locate_steam_logged_in_users() -> Option<PathBuf> {
pub fn locate_steam_logged_in_users() -> Result<PathBuf> {
tracing::debug!("Fetching Steam loginusers.vdf");
let mut base_folder: PathBuf = find_base_lib();
let mut base_folder: PathBuf = SteamDir::locate()
.ok_or(anyhow!("Failed to locate Steam directory"))?
.path;
base_folder.push::<PathBuf>("config/loginusers.vdf".into());
if base_folder.as_path().exists() {
tracing::info!("Located current Steam user data.");
Some(base_folder)
Ok(base_folder)
} else {
tracing::error!("Could not locate loginusers.vdf in the Steam dir");
None
Err(anyhow!("Could not locate loginusers.vdf in the Steam dir"))
}
}

pub fn locate_steam_launch_configs(steam_user: SteamID) -> Option<PathBuf> {
tracing::debug!("Fetching Steam userdata/<player>/config/localconfig.vdf");
pub fn locate_steam_launch_configs(steam_user: SteamID) -> Result<PathBuf> {
let a_id = steam_user.account_id();
let mut base_folder: PathBuf = find_base_lib();
base_folder.push::<PathBuf>(format!("userdata/{}/config/localconfig.vdf", a_id,).into());
let local_config_path = format!("userdata/{}/config/localconfig.vdf", a_id);
tracing::debug!("Fetching Steam {}", local_config_path);

let steam = SteamDir::locate().ok_or(anyhow!("Failed to locate Steam directory."))?;
let mut base_folder: PathBuf = steam.path;
base_folder.push(local_config_path);
if base_folder.as_path().exists() {
tracing::info!("Located local launch configs.");
Some(base_folder)
Ok(base_folder)
} else {
tracing::error!("Could not find local configs (player not found).");
None
Err(anyhow!("Could not find local configs (player not found)."))
}
}

/// Attempts to open the TF2 directory or locate it if it's not in the expected place
pub fn locate_tf2_folder() -> Option<PathBuf> {
tracing::debug!("Fetching TF2 Folder");
let libs: Vec<PathBuf> = fetch_libraryfolders();

for lib in libs {
let mut path = lib.to_path_buf();
path.push::<PathBuf>(get_rel_tf2_path().into());
tracing::debug!("Found TF2 Folder: {:?}", path);

if path.exists() && verify_tf_location(&path) {
tracing::info!("Using TF2 directory: {}", path.to_string_lossy());
return Some(path);
}
}
None
}

fn fetch_libraryfolders() -> Vec<PathBuf> {
tracing::debug!("Attempting to open libraryfolders.vdf");
const LIBFILE: &str = "libraryfolders.vdf";
let libraryfolders = fs::read_to_string(Path::join(&find_default_lib(), LIBFILE));

match libraryfolders {
Ok(content) => {
let mut paths = Vec::new();
let lines = content.lines();

for line in lines {
if line.contains("path") {
if let Some(path_str) = line.split('"').nth(3) {
paths.push(PathBuf::from(path_str));
}
}
}

tracing::debug!("Successfully read libraryfolders");
paths
}
Err(err) => {
tracing::error!("Failed to read libraryfolders.vdf: {:?}", err);
Vec::new()
}
}
}

fn find_default_lib() -> PathBuf {
let mut path: PathBuf = find_base_lib();
path.push::<PathBuf>("steamapps/".into());

path
}

fn find_base_lib() -> PathBuf {
#[cfg(target_os = "windows")]
let default_dir = r"C:\Program Files (x86)\Steam\".into();

#[cfg(not(target_os = "windows"))]
let default_dir = {
use std::env::var_os;
var_os("HOME")
.map(PathBuf::from)
.unwrap_or("~".into())
.join(".steam/steam/")
};

default_dir
}

fn verify_tf_location(lib: &Path) -> bool {
tracing::debug!("Start TF2 Verification of {:?}", lib.to_string_lossy());
let gameinfo = "tf/gameinfo.txt";
let mut path = lib.to_path_buf();
path.push(gameinfo);

if path.exists() {
tracing::debug!("Passed Verification Check");
return true;
}
tracing::debug!("Failed Verification Check");
false
}

#[cfg(target_os = "windows")]
fn get_rel_tf2_path() -> String {
r"steamapps\common\Team Fortress 2".to_string()
}

#[cfg(not(target_os = "windows"))]
fn get_rel_tf2_path() -> String {
r"steamapps/common/Team Fortress 2".to_string()
pub fn locate_tf2_folder() -> Result<PathBuf> {
Ok(SteamDir::locate()
.ok_or(anyhow!("Failed to locate Steam directory"))?
.app(&TF2_GAME_ID)
.ok_or(anyhow!("Failed to locate TF2 installation."))?
.path
.clone())
}
19 changes: 11 additions & 8 deletions src/launchoptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ pub const TF2_REQUIRED_OPTS: [&str; 4] = ["-condebug", "-conclearlog", "-usercon
/// ID.
/// Handles referencing the VDF store of a Steam app's launch options and provides an interface to read
/// and write launch options based on a set of required options.
pub struct LaunchOptionsV2 {
pub struct LaunchOptions {
local_config: PathBuf,
launch_args_regex: Regex,
app_data: Option<String>,
new_app_data: Option<String>,
}

impl LaunchOptionsV2 {
impl LaunchOptions {
/// Get the current configured launch options for the target app under the current logged in steam user.
///
/// # Errors
Expand All @@ -39,7 +39,7 @@ impl LaunchOptionsV2 {
/// - Could not read the `localconfig.vdf` file. (because of any non-`ErrorKind::Interrupted` during read)
/// - Failed to parse the `localconfig.vdf` file. (File is corrupted/broken/incomplete)
/// - Target app ID does not exist in `localconfig.vdf` file or the object is corrupted.
pub fn new(user: SteamID, target_app: String) -> Result<LaunchOptionsV2, anyhow::Error> {
pub fn new(user: SteamID, target_app: String) -> Result<LaunchOptions, anyhow::Error> {
let span = tracing::span!(Level::INFO, "LaunchOptions");
let _enter = span.enter();

Expand Down Expand Up @@ -84,7 +84,7 @@ impl LaunchOptionsV2 {
let launch_options_regex = Regex::new(r#"\t{6}"LaunchOptions"\t{2}"([(\-\w)\s]*)""#)
.expect("Constructing LaunchOptions regex");

Ok(LaunchOptionsV2 {
Ok(LaunchOptions {
local_config: config_path,
launch_args_regex: launch_options_regex,
app_data: matched_app_block,
Expand All @@ -109,10 +109,13 @@ impl LaunchOptionsV2 {
None => &self.app_data,
};
let app_data = data_ref.clone().context("No data currently stored.")?;
let current_args = self
.launch_args_regex
.find(&app_data)
.context("Failed to find launch args object.")?;
let current_args = match self.launch_args_regex.find(&app_data) {
Some(current_args) => current_args,
None => {
missing_args.extend(TF2_REQUIRED_OPTS.iter());
return Ok(missing_args);
}
};

let mat_str = current_args.as_str();
TF2_REQUIRED_OPTS.iter().for_each(|opt| {
Expand Down
Loading

0 comments on commit 95538ad

Please sign in to comment.