Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
757c1acf1b | |||
b15d0eb7d7 | |||
eb508f2b48 | |||
6ae9bcabad | |||
d25e7cf3cd | |||
d587553845 | |||
4a488abb8e |
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -4,7 +4,7 @@ version = 3
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alterware-launcher"
|
name = "alterware-launcher"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"http_req",
|
"http_req",
|
||||||
"rand",
|
"rand",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "alterware-launcher"
|
name = "alterware-launcher"
|
||||||
version = "0.2.1"
|
version = "0.2.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
build = "res/build.rs"
|
build = "res/build.rs"
|
||||||
|
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
# alterware-launcher
|
# alterware-launcher
|
||||||
|
|
||||||
1. Download [latest release](https://github.com/mxve/alterware-launcher/releases/latest/download/alterware-launcher-x86_64-pc-windows-msvc.zip)
|
1. Download [latest release](https://github.com/mxve/alterware-launcher/releases/latest/download/alterware-launcher-x86_64-pc-windows-msvc.zip)
|
||||||
2. Unpack the archive and place alterware-launcher.exe in MW2/MW3 game directory
|
2. Unpack the archive and place alterware-launcher.exe in MW2/MW3/Ghosts game directory
|
||||||
3. Run alterware-launcher.exe
|
3. Run alterware-launcher.exe
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- Passing ```iw4-sp```, ```iw5-mod``` or ```iw6-mod``` as the first argument will skip automatic game detection
|
||||||
|
- Passing ```update``` will stop the launcher from launching the game
|
129
src/main.rs
129
src/main.rs
@ -8,25 +8,14 @@ struct CdnFile {
|
|||||||
hash: String,
|
hash: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
struct Game<'a> {
|
struct Game<'a> {
|
||||||
engine: &'a str,
|
engine: &'a str,
|
||||||
client: &'a str,
|
client: &'a str,
|
||||||
references: &'a [&'a str],
|
references: Vec<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MASTER: &str = "https://master.alterware.dev";
|
const MASTER: &str = "https://master.alterware.dev";
|
||||||
const GAMES: [Game; 2] = [
|
|
||||||
Game {
|
|
||||||
engine: "iw4",
|
|
||||||
client: "iw4-sp",
|
|
||||||
references: &["iw4sp.exe", "iw4mp.exe"],
|
|
||||||
},
|
|
||||||
Game {
|
|
||||||
engine: "iw5",
|
|
||||||
client: "iw5-mod",
|
|
||||||
references: &["iw5sp.exe", "iw5mp.exe", "iw5mp_server.exe"],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
fn file_get_sha1(path: &PathBuf) -> String {
|
fn file_get_sha1(path: &PathBuf) -> String {
|
||||||
let mut sha1 = sha1_smol::Sha1::new();
|
let mut sha1 = sha1_smol::Sha1::new();
|
||||||
@ -34,27 +23,67 @@ fn file_get_sha1(path: &PathBuf) -> String {
|
|||||||
sha1.digest().to_string()
|
sha1.digest().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn download_and_launch(url: &str, file_path: &PathBuf, hash: Option<String>) {
|
fn update(game: &Game) {
|
||||||
|
let cdn_info: Vec<CdnFile> = serde_json::from_str(&http::get_body_string(
|
||||||
|
format!(
|
||||||
|
"{}/files.json?{}",
|
||||||
|
MASTER,
|
||||||
|
rand::Rng::gen_range(&mut rand::thread_rng(), 0..1000)
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut files_to_update: Vec<CdnFile> = Vec::new();
|
||||||
|
for file in cdn_info {
|
||||||
|
if file.name.starts_with(game.engine) {
|
||||||
|
files_to_update.push(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for file in files_to_update {
|
||||||
|
let file_path = PathBuf::from(&file.name.replace(&format!("{}/", game.engine), ""));
|
||||||
|
if file_path.exists() {
|
||||||
|
let sha1_local = file_get_sha1(&file_path).to_lowercase();
|
||||||
|
let sha1_remote = file.hash.to_lowercase();
|
||||||
|
if sha1_local != sha1_remote {
|
||||||
|
println!(
|
||||||
|
"Updating {}...\nLocal hash: {}\nRemote hash: {}",
|
||||||
|
file_path.display(),
|
||||||
|
sha1_local,
|
||||||
|
sha1_remote
|
||||||
|
);
|
||||||
|
http::download_file(
|
||||||
|
&format!(
|
||||||
|
"{}/{}?{}",
|
||||||
|
MASTER,
|
||||||
|
file.name,
|
||||||
|
rand::Rng::gen_range(&mut rand::thread_rng(), 0..1000)
|
||||||
|
),
|
||||||
|
&file_path,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Downloading {}...", file_path.display());
|
||||||
if let Some(parent) = file_path.parent() {
|
if let Some(parent) = file_path.parent() {
|
||||||
if !parent.exists() {
|
if !parent.exists() {
|
||||||
fs::create_dir_all(parent).unwrap();
|
fs::create_dir_all(parent).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
http::download_file(
|
||||||
if file_path.exists() && hash.is_some() {
|
&format!(
|
||||||
let sha1_local = file_get_sha1(file_path).to_lowercase();
|
"{}/{}?{}",
|
||||||
let sha1_remote = hash.unwrap().to_lowercase();
|
MASTER,
|
||||||
if sha1_local != sha1_remote {
|
file.name,
|
||||||
println!("Local hash: {}", sha1_local);
|
rand::Rng::gen_range(&mut rand::thread_rng(), 0..1000)
|
||||||
println!("Remote hash: {}", sha1_remote);
|
),
|
||||||
println!("Updating {}...", file_path.display());
|
&file_path,
|
||||||
http::download_file(url, file_path);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
println!("Downloading {}...", file_path.display());
|
|
||||||
http::download_file(url, file_path);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn launch(file_path: &PathBuf) {
|
||||||
println!("Launching {}...", file_path.display());
|
println!("Launching {}...", file_path.display());
|
||||||
std::process::Command::new(file_path)
|
std::process::Command::new(file_path)
|
||||||
.spawn()
|
.spawn()
|
||||||
@ -63,28 +92,25 @@ fn download_and_launch(url: &str, file_path: &PathBuf, hash: Option<String>) {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_hash(game: &Game) -> Option<String> {
|
|
||||||
let cdn_info: Vec<CdnFile> = serde_json::from_str(&http::get_body_string(
|
|
||||||
format!("{}/files.json?{}", MASTER, rand::Rng::gen_range(&mut rand::thread_rng(), 0..1000)).as_str(),
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
for file in cdn_info {
|
|
||||||
if file.name == format!("{}/{}.exe", game.engine, game.client) {
|
|
||||||
return Some(file.hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let mut args: Vec<String> = std::env::args().collect();
|
||||||
|
|
||||||
|
let games_json = http::get_body_string(format!("{}/games.json", MASTER).as_str());
|
||||||
|
let games: Vec<Game> = serde_json::from_str(&games_json).unwrap();
|
||||||
|
|
||||||
|
let mut update_only = false;
|
||||||
|
if args.contains(&String::from("update")) {
|
||||||
|
update_only = true;
|
||||||
|
args.iter()
|
||||||
|
.position(|r| r == "update")
|
||||||
|
.map(|e| args.remove(e));
|
||||||
|
}
|
||||||
|
|
||||||
let mut game: String = String::new();
|
let mut game: String = String::new();
|
||||||
if args.len() > 1 {
|
if args.len() > 1 {
|
||||||
game = String::from(&args[1]);
|
game = String::from(&args[1]);
|
||||||
} else {
|
} else {
|
||||||
'main: for g in GAMES.iter() {
|
'main: for g in games.iter() {
|
||||||
for r in g.references.iter() {
|
for r in g.references.iter() {
|
||||||
if std::path::Path::new(r).exists() {
|
if std::path::Path::new(r).exists() {
|
||||||
game = String::from(g.client);
|
game = String::from(g.client);
|
||||||
@ -94,14 +120,19 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for g in GAMES.iter() {
|
for g in games.iter() {
|
||||||
if g.client == game {
|
if g.client == game {
|
||||||
download_and_launch(
|
update(g);
|
||||||
&format!("{}/{}/{}.exe?{}", MASTER, g.engine, g.client, rand::Rng::gen_range(&mut rand::thread_rng(), 0..1000)),
|
if update_only {
|
||||||
&PathBuf::from(format!("{}.exe", g.client)),
|
return;
|
||||||
get_hash(g)
|
}
|
||||||
);
|
launch(&PathBuf::from(format!("{}.exe", g.client)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("Game not found!");
|
||||||
|
println!("Place the launcher in the game folder, if that doesn't work specify the client on the command line (ex. alterware-launcher.exe iw4-sp)");
|
||||||
|
println!("Press enter to exit...");
|
||||||
|
std::io::stdin().read_line(&mut String::new()).unwrap();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user