mirror of
https://github.com/vladtrc/iw4L
synced 2026-10-26 02:11:39 -04:00
Discover Steam MW3 and Black Ops installations
This commit is contained in:
@@ -28,7 +28,7 @@ pub use progress::{
|
||||
StageOutcome, StageScope, StageSnapshot, WorkCount, peak_resident_bytes,
|
||||
process_resident_bytes,
|
||||
};
|
||||
pub use steam::{MW2_SHORTCUT, SteamCandidate, SteamProbe, link_steam_mw2};
|
||||
pub use steam::{MW2_SHORTCUT, SteamCandidate, SteamProbe, link_steam_games};
|
||||
pub use zone::{
|
||||
Iw4WireFormat, Iw5ZoneMemory, T5ZoneMemory, ZoneImage, ZoneMemory, ZoneOpenError, open_zone,
|
||||
open_zone_shared, parse_zone_image, xfile_arena_row, zone_share_counts,
|
||||
|
||||
@@ -4,16 +4,17 @@ use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::ZoneGame;
|
||||
use crate::discover::GamesRoot;
|
||||
#[cfg(windows)]
|
||||
use crate::discover::{IW4_ZONE_VERSION, peek_zone_version, search_roots};
|
||||
use crate::discover::{search_roots, zone_game_for_path};
|
||||
|
||||
pub const MW2_SHORTCUT: &str = "Modern Warfare 2.lnk";
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SteamProbe {
|
||||
pub steam_found: bool,
|
||||
pub tried: Vec<(PathBuf, SteamCandidate)>,
|
||||
pub tried: Vec<(ZoneGame, PathBuf, SteamCandidate)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@@ -26,47 +27,67 @@ pub enum SteamCandidate {
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn link_steam_mw2(root: &GamesRoot) -> SteamProbe {
|
||||
pub fn link_steam_games(root: &GamesRoot) -> SteamProbe {
|
||||
let mut probe = SteamProbe::default();
|
||||
let link = root.0.join(MW2_SHORTCUT);
|
||||
if link.exists() || has_mw2(&search_roots(&root.0)) {
|
||||
let roots = search_roots(&root.0);
|
||||
let titles = [
|
||||
(ZoneGame::Iw4, MW2_SHORTCUT, "Call of Duty Modern Warfare 2"),
|
||||
(
|
||||
ZoneGame::Iw5,
|
||||
"Modern Warfare 3.lnk",
|
||||
"Call of Duty Modern Warfare 3",
|
||||
),
|
||||
(ZoneGame::T5, "Black Ops.lnk", "Call of Duty Black Ops"),
|
||||
];
|
||||
let missing = titles
|
||||
.into_iter()
|
||||
.filter(|(game, shortcut, _)| !root.0.join(shortcut).exists() && !has_game(&roots, *game))
|
||||
.collect::<Vec<_>>();
|
||||
if missing.is_empty() {
|
||||
return probe;
|
||||
}
|
||||
let libraries = steam_libraries();
|
||||
probe.steam_found = !libraries.is_empty();
|
||||
let mut valid = Vec::new();
|
||||
for library in libraries {
|
||||
let path = library
|
||||
.join("steamapps")
|
||||
.join("common")
|
||||
.join("Call of Duty Modern Warfare 2");
|
||||
let candidate = if !path.is_dir() {
|
||||
SteamCandidate::Missing
|
||||
} else if has_mw2(std::slice::from_ref(&path)) {
|
||||
valid.push(probe.tried.len());
|
||||
SteamCandidate::Linked
|
||||
} else {
|
||||
SteamCandidate::NoMultiplayerData
|
||||
};
|
||||
probe.tried.push((path, candidate));
|
||||
}
|
||||
match valid[..] {
|
||||
[index] => {
|
||||
let (target, candidate) = &mut probe.tried[index];
|
||||
match create_shortcut(&link, target) {
|
||||
Ok(()) => diag::info!(
|
||||
Zone,
|
||||
"linked Steam MW2 {} as {}",
|
||||
target.display(),
|
||||
link.display()
|
||||
),
|
||||
Err(error) => *candidate = SteamCandidate::ShortcutFailed(error),
|
||||
}
|
||||
for (game, shortcut, folder) in missing {
|
||||
let link = root.0.join(shortcut);
|
||||
let mut valid = Vec::new();
|
||||
for library in &libraries {
|
||||
let path = library.join("steamapps").join("common").join(folder);
|
||||
let candidate = if !path.is_dir() {
|
||||
SteamCandidate::Missing
|
||||
} else if has_game(std::slice::from_ref(&path), game) {
|
||||
valid.push(probe.tried.len());
|
||||
SteamCandidate::Linked
|
||||
} else {
|
||||
SteamCandidate::NoMultiplayerData
|
||||
};
|
||||
probe.tried.push((game, path, candidate));
|
||||
}
|
||||
[] => {}
|
||||
_ => {
|
||||
for index in valid {
|
||||
probe.tried[index].1 = SteamCandidate::OneOfSeveral;
|
||||
match valid[..] {
|
||||
[index] => {
|
||||
let (_, target, candidate) = &mut probe.tried[index];
|
||||
match create_shortcut(&link, target) {
|
||||
Ok(()) => diag::info!(
|
||||
Zone,
|
||||
"linked Steam {} as {}",
|
||||
target.display(),
|
||||
link.display()
|
||||
),
|
||||
Err(error) => {
|
||||
diag::warn!(Zone, "{error}");
|
||||
*candidate = SteamCandidate::ShortcutFailed(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
[] => {}
|
||||
_ => {
|
||||
diag::warn!(
|
||||
Zone,
|
||||
"several Steam installs of {folder}; create {shortcut} to select one"
|
||||
);
|
||||
for index in valid {
|
||||
probe.tried[index].2 = SteamCandidate::OneOfSeveral;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,19 +95,20 @@ pub fn link_steam_mw2(root: &GamesRoot) -> SteamProbe {
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub fn link_steam_mw2(_root: &GamesRoot) -> SteamProbe {
|
||||
pub fn link_steam_games(_root: &GamesRoot) -> SteamProbe {
|
||||
SteamProbe::default()
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn has_mw2(roots: &[PathBuf]) -> bool {
|
||||
fn has_game(roots: &[PathBuf], game: ZoneGame) -> bool {
|
||||
roots.iter().any(|root| {
|
||||
std::iter::once(root.clone())
|
||||
.chain(subdirs(root))
|
||||
.flat_map(|tree| subdirs(&tree.join("zone")))
|
||||
.any(|language| {
|
||||
peek_zone_version(&language.join("common_mp.ff")) == Some(IW4_ZONE_VERSION)
|
||||
.flat_map(|tree| {
|
||||
let zone = tree.join("zone");
|
||||
std::iter::once(zone.clone()).chain(subdirs(&zone))
|
||||
})
|
||||
.any(|language| zone_game_for_path(&language.join("common_mp.ff")) == Some(game))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ pub fn launch(
|
||||
acceptance: Option<AcceptanceLaunch>,
|
||||
cheats: sim::HostCheats,
|
||||
) {
|
||||
let steam = asset_transport::link_steam_mw2(&games);
|
||||
let steam = asset_transport::link_steam_games(&games);
|
||||
diag::info!(Launch, "{}", asset_transport::games_root_report(&games));
|
||||
|
||||
if let Some(plan) = crate::frame_owner::prefer_performance_cores() {
|
||||
@@ -235,7 +235,11 @@ fn mw2_not_found(
|
||||
text.push_str("Steam was not found on this PC.\n");
|
||||
} else {
|
||||
text.push_str("Tried in Steam:\n");
|
||||
for (folder, candidate) in &steam.tried {
|
||||
for (_, folder, candidate) in steam
|
||||
.tried
|
||||
.iter()
|
||||
.filter(|(game, _, _)| *game == asset_core::ZoneGame::Iw4)
|
||||
{
|
||||
let outcome = match candidate {
|
||||
SteamCandidate::Missing => "not installed here".to_owned(),
|
||||
SteamCandidate::NoMultiplayerData => {
|
||||
|
||||
+8
-7
@@ -19,12 +19,13 @@ IW4L/
|
||||
`iw4l.exe licenses` prints the licence and notice texts compiled into the executable.
|
||||
|
||||
The runtime reads the installations those shortcuts point to. MW2 multiplayer data
|
||||
is required for the menu; BO1 and MW3 are optional. Without `Modern Warfare 2.lnk`
|
||||
and without MW2 in the folder or its shortcuts, `iw4l.exe` looks in the Steam
|
||||
libraries and, when exactly one has MW2 multiplayer data, creates
|
||||
`Modern Warfare 2.lnk` to it. Otherwise it shows the folders it tried and how to
|
||||
add the shortcut by hand: right-click inside the folder, New > Shortcut, paste the
|
||||
MW2 folder path.
|
||||
is required for the menu; BO1 and MW3 are optional. For each missing title,
|
||||
`iw4l.exe` checks the Steam libraries and creates its shortcut when exactly one
|
||||
install has supported multiplayer data. Existing shortcuts and titles already
|
||||
found in the configured folders are preserved. If several installs qualify,
|
||||
create a shortcut to the one you want. If MW2 is still missing, the launcher shows
|
||||
the folders it tried and how to add the shortcut: right-click inside the launcher
|
||||
folder, New > Shortcut, paste the game folder path.
|
||||
|
||||
Launch `iw4l.exe`. Before starting the game or contacting QUIC, it checks its
|
||||
community's HTTPS manifest. An unchanged executable starts normally. An update
|
||||
@@ -46,5 +47,5 @@ One adjacent `.iw4l-server` is selected automatically; with several, set
|
||||
`IW4L_COMMUNITY` to the chosen file path before launching. See [`MASTER.md`](MASTER.md).
|
||||
|
||||
On Windows the executable directory is the working directory. Game discovery
|
||||
uses `IW4L_GAMES` or shortcuts beside `iw4l.exe`, then Steam for MW2; local `.env` settings remain
|
||||
uses `IW4L_GAMES` or shortcuts beside `iw4l.exe`, then Steam for MW2, BO1 and MW3; local `.env` settings remain
|
||||
available for game configuration. Publishing: [`DEPLOY.md`](DEPLOY.md).
|
||||
|
||||
Reference in New Issue
Block a user