Skip to content

Commit

Permalink
Implmented some feedback, preparing for Heroic 2.9
Browse files Browse the repository at this point in the history
  • Loading branch information
sluedecke committed Jul 30, 2023
1 parent 5e5db57 commit eccde7e
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 90 deletions.
23 changes: 2 additions & 21 deletions src/wrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,11 @@ use crate::{prelude::Error, resource::config::RootsConfig};
mod gogdl;
mod legendary;

/// Trait for command line argument parsers to determine the actual game name,
/// for implementations check the submodules.
trait LaunchParser {
/// Determine game name from `commands`, return `None` if it fails.
fn parse(&self, roots: &[RootsConfig], commands: &[String]) -> Option<String>;
}

pub fn get_game_name_from_heroic_launch_commands(roots: &[RootsConfig], commands: &[String]) -> Result<String, Error> {
// I'd love to write let d = vec![Heroic{}, Legendary{}];
//
// Coming from OOP the code below seems a bit much of syntactical noise, but
// it handles the fact that a trait is a compile time structure with unknown
// size, so I "Box" it to put the actual objects on the heap.
//
// Taken from https://doc.rust-lang.org/book/ch17-02-trait-objects.html
//
// Also support for Amazon Prime is in the works for heroic:
// https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher/pull/2831
let detectors: Vec<Box<dyn LaunchParser>> =
vec![Box::new(gogdl::HeroicGogdl {}), Box::new(legendary::Legendary {})];

// TODO.2023-07-19 check only applicable roots

match detectors.iter().find_map(|parser| parser.parse(roots, commands)) {
let parsers = vec![gogdl::parse_heroic_2_8, legendary::parse_heroic_2_8];
match parsers.iter().find_map(|parser| parser(roots, commands)) {
Some(game_name) => Ok(game_name),
None => Err(Error::WrapCommandNotRecognized {
msg: "get_game_name_from_heroic_launch_commands: could not detect any known launcher.".to_string(),
Expand Down
79 changes: 46 additions & 33 deletions src/wrap/gogdl.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,55 @@
use itertools::Itertools;

use super::LaunchParser;
use crate::{resource::config::RootsConfig, scan::launchers::heroic::get_gog_games_library};

pub struct HeroicGogdl;
impl LaunchParser for HeroicGogdl {
fn parse(&self, roots: &[RootsConfig], commands: &[String]) -> Option<String> {
let mut iter = commands.iter();
// TODO.2023-07-30 - Linux Native: heroic 2.9.x changed invocation for gogdl
// based games and we no longer get the GAME_DIR ang GAME_ID information from
// the command line parameters.
//
// Instead we need to rely on GAMEDIR/goggame-GAME_ID.id (wine/proton) or
// GAMEDIR/game/__game/goggame-GAME_ID.id (Linux native) to get a game id
//
// Linux Native:
//
// commands is GAME_DIR/start.sh
//
// Wine/Proton:
//
// check environment variable
// STEAM_COMPAT_INSTALL_PATH='/home/saschal/Games/The Riftbreaker'

if iter.find_position(|p| p.ends_with("gogdl")).is_none() {
log::debug!("HeroicGogdl::parse: gogdl not found");
return None;
}
log::debug!("HeroicGogdl::parse: gogdl found");
/// Parsing of command line for Heroic 2.8.x (and probably earlier versions)
pub fn parse_heroic_2_8(roots: &[RootsConfig], commands: &[String]) -> Option<String> {
let mut iter = commands.iter();

if iter.find_position(|p| p.ends_with("launch")).is_none() {
log::debug!("HeroicGogdl::parse: launch not found");
return None;
}
// TODO.2023-07-19 fails if user selects different game exe in heroic
let game_dir = iter.next().unwrap();
let game_id = iter.next().unwrap();
log::debug!(
"HeroicGogdl::parse: gogdl launch found: dir = {}, id = {}",
game_dir,
game_id
);
if iter.find_position(|p| p.ends_with("gogdl")).is_none() {
log::debug!("HeroicGogdl::parse: gogdl not found");
return None;
}
log::debug!("HeroicGogdl::parse: gogdl found");

// TODO.2023-07-14 filter for root.type = Heroic
roots.iter().find_map(|root| {
log::debug!("HeroicGogdl::parse: checking root {:?}", root);
match get_gog_games_library(root) {
Some(gog_games) => gog_games.iter().find_map(|g| match g.app_name == *game_id {
true => Some(g.title.clone()),
false => None,
}),
None => None,
}
})
if iter.find_position(|p| p.ends_with("launch")).is_none() {
log::debug!("HeroicGogdl::parse: launch not found");
return None;
}
// TODO.2023-07-19 fails if user selects different game exe in heroic
let game_dir = iter.next().unwrap();
let game_id = iter.next().unwrap();
log::debug!(
"HeroicGogdl::parse: gogdl launch found: dir = {}, id = {}",
game_dir,
game_id
);

// TODO.2023-07-14 filter for root.type = Heroic
roots.iter().find_map(|root| {
log::debug!("HeroicGogdl::parse: checking root {:?}", root);
match get_gog_games_library(root) {
Some(gog_games) => gog_games.iter().find_map(|g| match g.app_name == *game_id {
true => Some(g.title.clone()),
false => None,
}),
None => None,
}
})
}
65 changes: 29 additions & 36 deletions src/wrap/legendary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,39 @@ use itertools::Itertools;

use crate::{resource::config::RootsConfig, scan::heroic::get_legendary_installed_games};

use super::LaunchParser;
// Heroic 2.9:
//
// GAME_ID found in command line parameter
// "-epicapp=d8a4c98b5020483881eb7f0c3fc4cea3",

/// Deserialization of legendary game metadata from calling 'legendary list-installed --json'
#[derive(Debug, serde::Deserialize)]
pub struct LegendaryGameInfo {
pub app_name: String,
pub title: String,
// ignore everything else
}

pub struct Legendary;
impl LaunchParser for Legendary {
fn parse(&self, roots: &[RootsConfig], commands: &[String]) -> Option<String> {
let mut iter = commands.iter();
/// Parsing of command line for Heroic 2.8.x (and probably earlier versions)
pub fn parse_heroic_2_8(roots: &[RootsConfig], commands: &[String]) -> Option<String> {
let mut iter = commands.iter();

let legendary_command = match iter.find_position(|p| p.ends_with("legendary")) {
None => {
log::debug!("Legendary::parse: legendary not found");
return None;
}
Some(cmd) => cmd.1,
};
log::debug!("Legendary::parse: legendary found: {}", legendary_command);

if iter.find_position(|p| p.ends_with("launch")).is_none() {
log::debug!("Legendary::parse: launch not found");
let legendary_command = match iter.find_position(|p| p.ends_with("legendary")) {
None => {
log::debug!("Legendary::parse: legendary not found");
return None;
}
let game_id = iter.next().unwrap();
log::debug!("Legendary::parse: legendary launch found: id = {}", game_id);
Some(cmd) => cmd.1,
};
log::debug!("Legendary::parse: legendary found: {}", legendary_command);

// TODO.2023-07-14 filter for root.type?
roots.iter().find_map(|root| {
// TODO.2023-07-19 use some valid value for legendary parameter instead of None
get_legendary_installed_games(root, None)
.iter()
.find_map(|legendary_game| match legendary_game.app_name == *game_id {
true => Some(legendary_game.title.clone()),
false => None,
})
})
if iter.find_position(|p| p.ends_with("launch")).is_none() {
log::debug!("Legendary::parse: launch not found");
return None;
}
let game_id = iter.next().unwrap();
log::debug!("Legendary::parse: legendary launch found: id = {}", game_id);

// TODO.2023-07-14 filter for root.type?
roots.iter().find_map(|root| {
// TODO.2023-07-19 use some valid value for legendary parameter instead of None
get_legendary_installed_games(root, None)
.iter()
.find_map(|legendary_game| match legendary_game.app_name == *game_id {
true => Some(legendary_game.title.clone()),
false => None,
})
})
}

0 comments on commit eccde7e

Please sign in to comment.