From fbf1b9241be71266653c892fac295ec304fa7e5a Mon Sep 17 00:00:00 2001 From: mojavemf Date: Tue, 9 Jan 2024 18:19:02 +0000 Subject: [PATCH] Added build config --- Cargo.lock | 73 +++++-- Cargo.toml | 8 +- assets/ascii.txt | 9 + build.rs | 175 ++++++++++++++--- build.yaml | 24 +++ src/log/mod.rs | 92 +++++++++ src/main.rs | 494 ++++++----------------------------------------- 7 files changed, 395 insertions(+), 480 deletions(-) create mode 100644 assets/ascii.txt create mode 100644 build.yaml create mode 100644 src/log/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 1d33c26..1d78383 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -313,6 +313,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.2" @@ -508,7 +514,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -521,6 +527,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "hermit-abi" version = "0.3.2" @@ -647,7 +659,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", ] [[package]] @@ -980,18 +1002,18 @@ checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.32" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1135,18 +1157,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.186" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f5db24220c009de9bd45e69fb2938f4b6d2df856aa9304ce377b3180f83b7c1" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.186" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad697f7e0b65af4983a4ce8f56ed5b357e8d3c36651bf6a7e13639c17b8e670" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", @@ -1176,6 +1198,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.9.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38" +dependencies = [ + "indexmap 2.1.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha1" version = "0.10.5" @@ -1250,9 +1285,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.28" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -1271,9 +1306,11 @@ dependencies = [ "indicatif", "md5", "reqwest", + "serde", + "serde_yaml", "term_size", "tokio", - "winreg 0.51.0", + "winreg 0.52.0", "winres", "zip-extract", ] @@ -1492,6 +1529,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "unsafe-libyaml" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" + [[package]] name = "url" version = "2.4.0" @@ -1800,9 +1843,9 @@ dependencies = [ [[package]] name = "winreg" -version = "0.51.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" dependencies = [ "cfg-if", "windows-sys 0.48.0", diff --git a/Cargo.toml b/Cargo.toml index e4f57f9..edc8e3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,19 +11,21 @@ colored = "2.0.4" chrono = "0.4.26" dirs = "5.0.1" indicatif = "0.17.6" -reqwest = { version = "0.11.20", features = ["stream"]} +reqwest = { version = "0.11.20", features = ["stream"] } futures = "0.3.28" -tokio = { version = "1.32.0", features=["full"]} +tokio = { version = "1.32.0", features = ["full"] } futures-util = "0.3.28" md5 = "0.7.0" zip-extract = "0.1.2" [target.'cfg(windows)'.dependencies] -winreg = "0.51.0" +winreg = "0.52.0" [build-dependencies] chrono = "0.4.26" winres = "0.1.12" +serde = { version = "1.0.195", features = ["derive"] } +serde_yaml = "0.9.30" [profile.release] strip = true diff --git a/assets/ascii.txt b/assets/ascii.txt new file mode 100644 index 0000000..1633543 --- /dev/null +++ b/assets/ascii.txt @@ -0,0 +1,9 @@ +.d8888b. Y88b d88P 888b 888 88888888888 d8888 Y88b d88P +d88P Y88b Y88b d88P 8888b 888 888 d88888 Y88b d88P +Y88b. Y88o88P 88888b 888 888 d88P888 Y88o88P + \"Y888b. Y888P 888Y88b 888 888 d88P 888 Y888P + \"Y88b. 888 888 Y88b888 888 d88P 888 d888b + \"888 888 888 Y88888 888 d88P 888 d88888b +Y88b d88P 888 888 Y8888 888 d8888888888 d88P Y88b +\"Y8888P\" 888 888 Y888 888 d88P 888 d88P Y88b + \ No newline at end of file diff --git a/build.rs b/build.rs index 24664fb..ebc4a17 100644 --- a/build.rs +++ b/build.rs @@ -1,30 +1,161 @@ +use std::collections::HashMap; use std::fs; use std::env; +use std::path::PathBuf; use winres::WindowsResource; -fn main() { - // Get the current build date and time - let build_date = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC").to_string(); +use serde::Deserialize; - // Write the build date to a file +/// CHANGES +/// [+] Use rust CFG for build (not sure) +/// [+] A build config +/// [+] Ascii is now premade due to the result always being static anyways +/// [+] Cut down into functions + +const ENGLISH_LANGUAGE: u16 = 0x0409; + +#[derive(Deserialize)] +struct BuildConfig { + #[serde(rename = "Ascii")] + pub ascii: AsciiConfig, + #[serde(rename = "Runtime")] + pub runtime: RuntimeConfig, + #[serde(rename = "Windows")] + pub windows: WindowsConfig, +} + +#[derive(Deserialize)] +struct AsciiConfig { + #[serde(rename = "Location")] + pub location: String, + #[serde(rename = "Padding")] + pub padding: usize, + #[serde(rename = "FallbackText")] + pub fallback_text: String, +} + +#[derive(Deserialize)] +struct RuntimeConfig { + #[serde(rename = "SetupUrl")] + pub setup_url: String, + #[serde(rename = "BaseUrl")] + pub base_url: String, + #[serde(rename = "ThreadCount")] + pub thread_count: usize, +} + +#[derive(Deserialize)] +struct WindowsConfig { + #[serde(rename = "Icon")] + pub icon: String, + #[serde(rename = "Resources")] + pub resources: HashMap, +} + +fn get_padding(size: usize) -> String { + " ".repeat(size) +} + +fn out_dir() -> PathBuf { let out_dir = env::var("OUT_DIR").unwrap(); - let dest_path = format!("{}/build_date.txt", out_dir); - fs::write(&dest_path, &build_date).unwrap(); + return PathBuf::from(out_dir); +} - if env::var_os("CARGO_CFG_WINDOWS").is_some() { - // Add a version resource to the executable - let mut res = WindowsResource::new(); - res.set_icon("assets/Bootstrapper.ico"); - res.set_language(0x0409); // US English - res.set("FileVersion", env!("CARGO_PKG_VERSION")); - res.set("FileDescription", "SYNTAX Windows Bootstrapper"); - res.set("ProductName", "SYNTAX Bootstrapper"); - res.set("ProductVersion", env!("CARGO_PKG_VERSION")); - res.set("InternalName", "SYNTAX Bootstrapper"); - res.set("OriginalFilename", "SyntaxPlayerLauncher.exe"); - res.set("CompanyName", "SYNTAX Corporation"); - res.set("LegalCopyright", "Copyright (c) 2023"); - res.compile().unwrap(); +/* Builds ascii that is displayed at startup */ +fn build_large_ascii(config: &BuildConfig) { + println!("cargo:rerun-if-changed={}", config.ascii.location); + + let build_date = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC").to_string(); + let ascii_b = fs::read(&config.ascii.location).unwrap(); + let padding = get_padding(config.ascii.padding); + let mut ascii = String::from_utf8(ascii_b).unwrap(); + + ascii += "\n"; + ascii += &format!( + "{} | Build Date: {} | Version: {}", + config.runtime.base_url, + build_date, + env!("CARGO_PKG_VERSION") + ); + + let mut new_asci = String::new(); + for line in ascii.lines() { + new_asci += &format!("{}{}\n", padding.clone(), line); + } + fs::write(out_dir().join("./ascii.txt"), new_asci).unwrap(); +} + +fn build_small_ascii(config: &BuildConfig) { + let build_date = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC").to_string(); + fs::write( + out_dir().join("./ascii_small.txt"), + format!( + "{} | {} | Build Date: {} | Version: {}", + config.ascii.fallback_text, + config.runtime.base_url, + build_date, + env!("CARGO_PKG_VERSION") + ) + ).unwrap() +} + +fn build_ascii(config: &BuildConfig) { + build_large_ascii(config); + build_small_ascii(config); +} + +fn build_windows(cfg: &WindowsConfig) { + println!("cargo:rerun-if-changed={}", cfg.icon); + + let mut res = WindowsResource::new(); + res.set_language(ENGLISH_LANGUAGE); // US English + res.set_icon(&cfg.icon); + + for (key, value) in &cfg.resources { + if value.starts_with("env:") { + let (_, env) = value.split_at(4); + let value = env::var(env).unwrap(); + res.set(key, &value); + println!("cargo:rerun-if-env-changed={}", env); + } else { + res.set(key, value); + } } - println!("cargo:rerun-if-changed=build.rs"); -} \ No newline at end of file + res.compile().unwrap() +} + +fn generate_const>(name: &str, c_type: &str, value: T) -> String { + let value = match c_type { + "&str" => format!(r#""{}""#, value.as_ref()), + _ => value.as_ref().into(), + }; + + format!("const {name}: {c_type} = {value};\n") +} + +/* Passes values */ +fn build_runtime(config: &RuntimeConfig) { + let mut output = String::new(); + output += &generate_const("SETUP_URL", "&str", &config.setup_url); + output += &generate_const("BASE_URL", "&str", &config.base_url); + output += &generate_const("THREAD_COUNT", "usize", format!("{}", config.thread_count)); + + fs::write(out_dir().join("./codegen.rs"), output).unwrap(); +} + +fn load_config() -> BuildConfig { + println!("cargo:rerun-if-changed=build.yaml"); + let bytes = fs::read("./build.yaml").unwrap(); + serde_yaml::from_slice(&bytes).unwrap() +} + +fn main() { + let config = load_config(); + + build_ascii(&config); + build_runtime(&config.runtime); + + if let Some(_) = env::var_os("CARGO_CFG_WINDOWS") { + build_windows(&config.windows) + } +} diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000..0f3912d --- /dev/null +++ b/build.yaml @@ -0,0 +1,24 @@ +# Information about the startup ascii +Ascii: + Location: assets/ascii.txt + Padding: 4 + FallbackText: SYNTAX Bootstrapper + +# Information to be used at runtime +Runtime: + SetupUrl: setup.syntax.eco + BaseUrl: syntax.eco + ThreadCount: 2 + +# Information about the exe +Windows: + Icon: assets/Bootstrapper.ico + Resources: + FileVersion: env:CARGO_PKG_VERSION + FileDescription: SYNTAX Windows Bootstrapper + ProductName: SYNTAX Bootstrapper + ProductVersion: env:CARGO_PKG_VERSION + InternalName: SYNTAX Bootstrapper + OriginalFilename: SyntaxPlayerLauncher.exe + CompanyName: SYNTAX Corporation + LegalCopyright: Copyright (c) 2024 \ No newline at end of file diff --git a/src/log/mod.rs b/src/log/mod.rs new file mode 100644 index 0000000..e93f044 --- /dev/null +++ b/src/log/mod.rs @@ -0,0 +1,92 @@ +use std::{ result, error::Error }; + +use colored::*; + +type Result = result::Result>; + +pub fn get_time() -> String { + chrono::Local::now().format("%H:%M:%S").to_string() +} + +fn get_progress_style>(target: T) -> Result { + let progress_style = indicatif::ProgressStyle + ::default_bar() + .template( + format!( + "{}\n{}", + format!( + "[{}] [{}] {}", + get_time().bold().blue(), + "INFO".bold().green(), + format!("Downloading {}", target.as_ref()) + ), + " ".repeat(16) + + "{spinner:.green} [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})" + ).as_str() + )? + .progress_chars("#>-"); + + Ok(progress_style) +} + +pub fn create_progress() {} + +#[macro_export] +macro_rules! info { + ($($t:tt)*) => { + { + use ::colored::*; + use crate::log::get_time; + println!( "[{}] [{}] {}",get_time().bold().blue(),"INFO".bold().green(),format!($($t)*)); + } + }; +} + +#[macro_export] +macro_rules! error { + ($($t:tt)*) => { + { + use ::colored::*; + use crate::log::get_time; + println!( "[{}] [{}] {}",get_time().bold().blue(),"ERROR".bold().red(),format!($($t)*)); + } + }; +} + +#[cfg(debug_assertions)] +#[macro_export] +macro_rules! debug { + ($($t:tt)*) => { + { + use ::colored::*; + use crate::log::get_time; + println!( "[{}] [{}] {}",get_time().bold().blue(),"DEBUG".bold().yellow(),format!($($t)*)); + } + }; +} + +#[cfg(not(debug_assertions))] +#[macro_export] +macro_rules! debug { + ($($t:tt)*) => { + { + + } + }; +} +#[macro_export] +macro_rules! fatal { + ($($t:tt)*) => { + { + use ::colored::*; + use crate::log::get_time; + panic!( "[{}] [{}] {}",get_time().bold().blue(),"FATAL".bold().red().underline(),format!($($t)*)); + } + }; +} + +use indicatif::ProgressStyle; +pub(crate) use info; +pub(crate) use error; +pub(crate) use debug; +pub(crate) use fatal; diff --git a/src/main.rs b/src/main.rs index c6b0126..8207417 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ use chrono::format; use colored::*; +use std::io::Write; use std::path::PathBuf; use reqwest::Client; use dirs::data_local_dir; @@ -18,96 +19,66 @@ use std::io::prelude::*; #[cfg(not(target_os = "windows"))] use std::os::unix::fs::FileExt; -fn info( message : &str ) { - let time = chrono::Local::now().format("%H:%M:%S").to_string(); - println!("[{}] [{}] {}", time.bold().blue(), "INFO".bold().green(), message); -} +pub mod log; -fn error( message : &str ) { - let time = chrono::Local::now().format("%H:%M:%S").to_string(); - println!("[{}] [{}] {}", time.bold().blue(), "ERROR".bold().red(), message); -} +include!(concat!(env!("OUT_DIR"), "/codegen.rs")); -#[cfg(debug_assertions)] -fn debug( message : &str ) { - let time = chrono::Local::now().format("%H:%M:%S").to_string(); - println!("[{}] [{}] {}", time.bold().blue(), "DEBUG".bold().yellow(), message); -} +const ASCII_ART: &str = include_str!(concat!(env!("OUT_DIR"), "./ascii.txt")); +const SMALL_ACII: &str = include_str!(concat!(env!("OUT_DIR"), "./ascii_small.txt")); -#[cfg(not(debug_assertions))] -fn debug( message : &str ) {} - -pub async fn http_get( client: &Client ,url: &str ) -> Result { - debug(&format!("{} {}", "GET".green(), url.bright_blue())); +pub async fn http_get(client: &Client, url: &str) -> Result { + debug!("{} {}", "GET".green(), url.bright_blue()); let response = client.get(url).send().await; - if (response.is_err()) { - debug(&format!("Failed to fetch {}", url.bright_blue())); + if response.is_err() { + debug!("Failed to fetch {}", url.bright_blue()); return Err(response.err().unwrap()); } let response_body = response.unwrap().text().await.unwrap(); Ok(response_body) } -pub async fn download_file( client: &Client, url: &str, path: &PathBuf ) { - debug(&format!("{} {}", "GET".green(), url.bright_blue())); +pub async fn download_file(client: &Client, url: &str, path: &PathBuf) { + debug!("{} {}", "GET".green(), url.bright_blue()); let response = client.get(url).send().await.unwrap(); let content_length = response.content_length().unwrap(); - debug(&format!("Content Length: {}", content_length)); + debug!("Content Length: {}", content_length); let time = chrono::Local::now().format("%H:%M:%S").to_string(); - let pg_bar_str = " {spinner:.green} [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})"; + let pg_bar_str = " "; let progress_bar = indicatif::ProgressBar::new(content_length); - let progress_style = indicatif::ProgressStyle::default_bar() - .template( - format!("{}\n{}", - format!( - "[{}] [{}] {}", - time.bold().blue(), - "INFO".bold().green(), - &format!("Downloading {}", &url.bright_blue()) - ), - pg_bar_str).as_str() - ) - .unwrap().progress_chars("#>-"); - progress_bar.set_style(progress_style); + + // progress_bar.set_style(progress_style); progress_bar.set_message("Downloading File"); - let file = std::fs::File::create(path).unwrap(); + let mut file = std::fs::File::create(path).unwrap(); let mut downloaded: u64 = 0; let mut stream = response.bytes_stream(); while let Some(item) = stream.next().await { let chunk = item.or(Err(format!("Error while downloading file"))).unwrap(); - #[cfg(target_os = "windows")] - { - file.seek_write(chunk.as_ref(), downloaded).unwrap(); - } - #[cfg(not(target_os = "windows"))] - { - file.write_at(chunk.as_ref(), downloaded).unwrap(); - } + file.write_all(&chunk); let new = std::cmp::min(downloaded + (chunk.len() as u64), content_length); downloaded = new; progress_bar.set_position(new); } progress_bar.finish(); - info(format!("Finished downloading {}", url.green()).as_str()); + info!("Finished downloading {}", url.green()); } -pub async fn download_file_prefix( client: &Client, url: &str, path_prefix : &PathBuf ) -> PathBuf { +pub async fn download_file_prefix(client: &Client, url: &str, path_prefix: &PathBuf) -> PathBuf { let path = path_prefix.join(generate_md5(url).await); download_file(client, url, &path).await; return path; } -pub async fn generate_md5( input : &str ) -> String { +pub async fn generate_md5(input: &str) -> String { let hashed_input = md5::compute(input.as_bytes()); return format!("{:x}", hashed_input); } -pub async fn create_folder_if_not_exists( path: &PathBuf ) { +pub async fn create_folder_if_not_exists(path: &PathBuf) { if !path.exists() { - info(&format!("Creating folder {}", path.to_str().unwrap().bright_blue())); + info!("Creating folder {}", path.to_str().unwrap().bright_blue()); std::fs::create_dir_all(path).unwrap(); } } @@ -116,403 +87,46 @@ fn get_installation_directory() -> PathBuf { return PathBuf::from(data_local_dir().unwrap().to_str().unwrap()).join("Syntax"); } -#[tokio::main] -async fn main() { +fn format_info_line>(data: T) -> ColoredString { + let data = data.as_ref(); - // Clear the terminal before printing the startup text - #[cfg(target_os = "windows")] - { - std::process::Command::new("cmd") - .args(&["/c", "cls"]) - .spawn() - .expect("cls command failed to start") - .wait() - .expect("failed to wait"); - } - #[cfg(not(target_os = "windows"))] - { - std::process::Command::new("clear").spawn().unwrap(); - } + data.magenta().cyan().italic().on_black() +} - let args: Vec = std::env::args().collect(); - let base_url : &str = "www.syntax.eco"; - let mut setup_url : &str = "setup.syntax.eco"; - let fallback_setup_url : &str = "d2f3pa9j0u8v6f.cloudfront.net"; - let mut bootstrapper_filename :&str = "SyntaxPlayerLauncher.exe"; - #[cfg(not(target_os = "windows"))] - { - bootstrapper_filename = "SyntaxPlayerLinuxLauncher"; - } - let build_date = include_str!(concat!(env!("OUT_DIR"), "/build_date.txt")); - let startup_text = format!(" - .d8888b. Y88b d88P 888b 888 88888888888 d8888 Y88b d88P - d88P Y88b Y88b d88P 8888b 888 888 d88888 Y88b d88P - Y88b. Y88o88P 88888b 888 888 d88P888 Y88o88P - \"Y888b. Y888P 888Y88b 888 888 d88P 888 Y888P - \"Y88b. 888 888 Y88b888 888 d88P 888 d888b - \"888 888 888 Y88888 888 d88P 888 d88888b - Y88b d88P 888 888 Y8888 888 d8888888888 d88P Y88b - \"Y8888P\" 888 888 Y888 888 d88P 888 d88P Y88b - - {} | Build Date: {} | Version: {}", base_url ,build_date, env!("CARGO_PKG_VERSION")); - - // Format the startup text to be centered - let mut terminal_width = 80; - if let Some((w, _h)) = term_size::dimensions() { - terminal_width = w; - } - if terminal_width < 80 { - print!("{}\n", format!("SYNTAX Bootstrapper | {} | Build Date: {} | Version: {}", base_url, build_date, env!("CARGO_PKG_VERSION")).to_string().magenta().cyan().italic().on_black()); // Fallback message +fn is_large() -> bool { + if let Some((terminal_width, _)) = term_size::dimensions() { + return terminal_width > 80; } else { - let startup_text_lines = startup_text.lines().collect::>(); - //println!("{}", startup_text.bold().blue().on_black()); - - // print all lines except the last one - for line in &startup_text_lines[0..startup_text_lines.len() - 1] { - let spaces = (terminal_width - line.len()) / 2; - let formatted_line = format!("{}{}", " ".repeat(spaces), line); - println!("{}", formatted_line.bright_magenta().italic().on_black()); - } + return false; + }; +} - // print last line as a different color - let last_line = startup_text_lines[startup_text_lines.len() - 1]; - let spaces = (terminal_width - last_line.len()) / 2; - let last_line = format!("{}{}", " ".repeat(spaces), last_line); - println!("{}\n", last_line.magenta().cyan().italic().on_black()); +fn startup_info() { + /* Clear screen */ + print!("\x1b[2J\x1b[H"); + + if !is_large() { + return println!("{}", format_info_line(SMALL_ACII)); } - let http_client: Client = reqwest::Client::builder() - .no_gzip() - .build() - .unwrap(); - debug(format!("Setup Server: {} | Base Server: {}", setup_url.bright_blue(), base_url.bright_blue()).as_str()); - debug("Fetching latest client version from setup server"); - - let latest_client_version : String; - let latest_client_version_response = http_get(&http_client ,&format!("https://{}/version", setup_url)).await; - match latest_client_version_response { - Ok(latest_client_version_result) => { - debug(&format!("Latest Client Version: {}", latest_client_version_result.bright_blue())); - latest_client_version = latest_client_version_result; - }, - Err(e) => { - error(&format!("Failed to fetch latest client version from setup server: [{}], attempting to fallback to {}", e.to_string().bright_red(), fallback_setup_url.bright_blue())); - let fallback_client_version_response = http_get(&http_client ,&format!("https://{}/version", fallback_setup_url)).await; - match fallback_client_version_response { - Ok(fallback_client_version_result) => { - info(&format!("Successfully fetched latest client version from fallback setup server: {}", fallback_setup_url.bright_blue())); - debug(&format!("Latest Client Version: {}", fallback_client_version_result.bright_blue())); - latest_client_version = fallback_client_version_result; - setup_url = fallback_setup_url; - }, - Err(e) => { - error(&format!("Failed to fetch latest client version from fallback setup server: {}, are you connected to the internet?", e)); - std::thread::sleep(std::time::Duration::from_secs(10)); - std::process::exit(0); - } - } - } + let mut lines: Vec<&str> = ASCII_ART.lines().collect(); + let last = lines.pop(); + + for value in lines { + println!("{}", value.bright_magenta().italic().on_black()); } - - // Wait for the latest client version to be fetched - info(&format!("Latest Client Version: {}", latest_client_version.cyan().underline())); - debug(&format!("Setup Server: {}", setup_url.cyan().underline())); - - let installation_directory = get_installation_directory(); - debug(&format!("Installation Directory: {}", installation_directory.to_str().unwrap().bright_blue())); - create_folder_if_not_exists(&installation_directory).await; - - let versions_directory = installation_directory.join("Versions"); - debug(&format!("Versions Directory: {}", versions_directory.to_str().unwrap().bright_blue())); - create_folder_if_not_exists(&versions_directory).await; - - let temp_downloads_directory = installation_directory.join("Downloads"); - debug(&format!("Temp Downloads Directory: {}", temp_downloads_directory.to_str().unwrap().bright_blue())); - create_folder_if_not_exists(&temp_downloads_directory).await; - - let current_version_directory = versions_directory.join(format!("{}", latest_client_version)); - debug(&format!("Current Version Directory: {}", current_version_directory.to_str().unwrap().bright_blue())); - create_folder_if_not_exists(¤t_version_directory).await; - - let latest_bootstrapper_path = current_version_directory.join(bootstrapper_filename); - // Is the program currently running from the latest version directory? - let current_exe_path = std::env::current_exe().unwrap(); - // If the current exe path is not in the current version directory, then we need to run the latest bootstrapper ( download if needed ) - if !current_exe_path.starts_with(¤t_version_directory) { - // Check if the latest bootstrapper is downloaded - if !latest_bootstrapper_path.exists() { - info("Downloading the latest bootstrapper and restarting"); - // Download the latest bootstrapper - download_file(&http_client, &format!("https://{}/{}-{}", setup_url, latest_client_version, bootstrapper_filename), &latest_bootstrapper_path).await; - } - // Run the latest bootstrapper ( with the same arguments passed to us ) and exit - #[cfg(target_os = "windows")] - { - let mut command = std::process::Command::new(latest_bootstrapper_path.clone()); - command.args(&args[1..]); - match command.spawn() { - Ok(_) => {}, - Err(e) => { - debug(&format!("Bootstrapper errored with error {}", e)); - info("Found bootstrapper was corrupted! Downloading..."); - std::fs::remove_file(latest_bootstrapper_path.clone()).unwrap(); - download_file(&http_client, &format!("https://{}/{}-{}", setup_url, latest_client_version, bootstrapper_filename), &latest_bootstrapper_path).await; - command.spawn().expect("Bootstrapper is still corrupted."); - std::thread::sleep(std::time::Duration::from_secs(20)); - } - } - } - #[cfg(not(target_os = "windows"))] - { - // Make sure the latest bootstrapper is executable - std::process::Command::new("chmod").arg("+x").arg(latest_bootstrapper_path.to_str().unwrap()).spawn().unwrap(); - - info("We need permission to run the latest bootstrapper"); - let mut command = std::process::Command::new(latest_bootstrapper_path); - command.args(&args[1..]); - command.spawn().unwrap(); - } - std::process::exit(0); + if let Some(last_val) = last { + println!("{}", format_info_line(last_val)); } +} - // Looks like we are running from the latest version directory, so we can continue with the update process - // Check for "AppSettings.xml" in the current version directory - // If it doesent exist, then we got either a fresh directory or a corrupted installation - // So delete the every file in the current version directory except for the Bootstrapper itself - let app_settings_path = current_version_directory.join("AppSettings.xml"); - let client_executable_path = current_version_directory.join("SyntaxPlayerBeta.exe"); - if !app_settings_path.exists() { //|| !client_executable_path.exists() { - info("Downloading the latest client files, this may take a while."); - for entry in std::fs::read_dir(¤t_version_directory).unwrap() { - let entry = entry.unwrap(); - let path = entry.path(); - if path.is_file() { - if path != current_exe_path { - std::fs::remove_file(path).unwrap(); - } - } else { - std::fs::remove_dir_all(path).unwrap(); - } - } +fn main() { + /* Ansi character to clear line */ - let VersionURLPrefix = format!("https://{}/{}-", setup_url, latest_client_version); + startup_info(); - let Client2018Zip : PathBuf = download_file_prefix(&http_client, format!("{}2018client.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await; - let Client2020Zip : PathBuf = download_file_prefix(&http_client, format!("{}2020client.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await; - let Client2014Zip : PathBuf = download_file_prefix(&http_client, format!("{}2014client.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await; - let Client2016Zip : PathBuf = download_file_prefix(&http_client, format!("{}2016client.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await; - info("Download finished, extracting files."); - - fn extract_to_dir( zip_file : &PathBuf, target_dir : &PathBuf ) { - info(format!("Extracting {} to {}", zip_file.to_str().unwrap().bright_blue(), target_dir.to_str().unwrap().bright_blue()).as_str()); - let zip_file_cursor = std::fs::File::open(zip_file).unwrap(); - zip_extract::extract(zip_file_cursor, target_dir, false).unwrap(); - } - - let client_2018_directory = current_version_directory.join("Client2018"); - create_folder_if_not_exists(&client_2018_directory).await; - extract_to_dir(&Client2018Zip, &client_2018_directory); - - let client_2020_directory = current_version_directory.join("Client2020"); - create_folder_if_not_exists(&client_2020_directory).await; - extract_to_dir(&Client2020Zip, &client_2020_directory); - - let client_2014_directory = current_version_directory.join("Client2014"); - create_folder_if_not_exists(&client_2014_directory).await; - extract_to_dir(&Client2014Zip, &client_2014_directory); - - let client_2016_directory = current_version_directory.join("Client2016"); - create_folder_if_not_exists(&client_2016_directory).await; - extract_to_dir(&Client2016Zip, &client_2016_directory); - - info("Finished extracting files, cleaning up."); - std::fs::remove_dir_all(&temp_downloads_directory).unwrap(); - - // Install the syntax-player scheme in the registry - info("Installing syntax-player scheme"); - #[cfg(target_os = "windows")] - { - let hkey_current_user = RegKey::predef(HKEY_CURRENT_USER); - let hkey_classes_root : RegKey = hkey_current_user.open_subkey("Software\\Classes").unwrap(); - let hkey_syntax_player = hkey_classes_root.create_subkey("syntax-player").unwrap().0; - let hkey_syntax_player_shell = hkey_syntax_player.create_subkey("shell").unwrap().0; - let hkey_syntax_player_shell_open = hkey_syntax_player_shell.create_subkey("open").unwrap().0; - let hkey_syntax_player_shell_open_command = hkey_syntax_player_shell_open.create_subkey("command").unwrap().0; - let defaulticon = hkey_syntax_player.create_subkey("DefaultIcon").unwrap().0; - hkey_syntax_player_shell_open_command.set_value("", &format!("\"{}\" \"%1\"", current_exe_path.to_str().unwrap())).unwrap(); - defaulticon.set_value("", &format!("\"{}\",0", current_exe_path.to_str().unwrap())).unwrap(); - hkey_syntax_player.set_value("", &format!("URL: Syntax Protocol")).unwrap(); - hkey_syntax_player.set_value("URL Protocol", &"").unwrap(); - } - #[cfg(not(target_os = "windows"))] - { - // Linux support - // We have to write a .desktop file to ~/.local/share/applications - let desktop_file_path = dirs::data_local_dir().unwrap().join("applications").join("syntax-player.desktop"); - let desktop_file = format!( -"[Desktop Entry] -Name=Syntax Launcher -Exec={} %u -Terminal=true -Type=Application -MimeType=x-scheme-handler/syntax-player; -Icon={} -StartupWMClass=SyntaxLauncher -Categories=Game; -Comment=Syntax Launcher -", current_exe_path.to_str().unwrap(), current_exe_path.to_str().unwrap()); - std::fs::write(desktop_file_path, desktop_file).unwrap(); - // We also have to write a mimeapps.list file to ~/.config - let mimeapps_list_path = dirs::config_dir().unwrap().join("mimeapps.list"); - let mimeapps_list = format!( -"[Default Applications] -x-scheme-handler/syntax-player=syntax-player.desktop -"); - std::fs::write(mimeapps_list_path, mimeapps_list).unwrap(); - // We also have to write a mimeapps.list file to ~/.local/share - let mimeapps_list_path = dirs::data_local_dir().unwrap().join("mimeapps.list"); - let mimeapps_list = format!( -"[Default Applications] -x-scheme-handler/syntax-player=syntax-player.desktop -"); - std::fs::write(mimeapps_list_path, mimeapps_list).unwrap(); - } - - // Write the AppSettings.xml file - let app_settings_xml = format!( -" - - content - http://{} -", base_url - ); - std::fs::write(app_settings_path, app_settings_xml).unwrap(); - - // Check for any other version directories and deletes them - for entry in std::fs::read_dir(&versions_directory).unwrap() { - let entry = entry.unwrap(); - let path = entry.path(); - if path.is_dir() { - if path != current_version_directory { - std::fs::remove_dir_all(path).unwrap(); - } - } - } - } - - // Parse the arguments passed to the bootstrapper - // Looks something like "syntax-player://1+launchmode:play+gameinfo:TICKET+placelauncherurl:https://www.syntax.eco/Game/placelauncher.ashx?placeId=660&t=TICKET+k:l" - debug(&format!("Arguments Passed: {}", args.join(" ").bright_blue())); - if args.len() == 1 { - // Just open the website - #[cfg(target_os = "windows")] - { - std::process::Command::new("cmd").arg("/c").arg("start").arg("https://www.syntax.eco/games").spawn().unwrap(); - std::process::exit(0); - } - #[cfg(not(target_os = "windows"))] - { - std::process::Command::new("xdg-open").arg("https://www.syntax.eco/games").spawn().unwrap(); - std::process::exit(0); - } - } - - let main_args = &args[1]; - let main_args = main_args.replace("syntax-player://", ""); - let main_args = main_args.split("+").collect::>(); - - let mut launch_mode = String::new(); - let mut authentication_ticket = String::new(); - let mut join_script = String::new(); - let mut client_year = String::new(); - - for arg in main_args { - let mut arg_split = arg.split(":"); - let key = arg_split.next().unwrap(); - let value = - if arg_split.clone().count() > 0 { - arg_split.collect::>().join(":") - } else { - String::new() - }; - debug(&format!("{}: {}", key.bright_blue(), value.bright_blue())); - match key { - "launchmode" => { - launch_mode = value.to_string(); - }, - "gameinfo" => { - authentication_ticket = value.to_string(); - }, - "placelauncherurl" => { - join_script = value.to_string(); - }, - "clientyear" => { - client_year = value.to_string(); - }, - _ => {} - } - } - - let custom_wine = "wine"; - #[cfg(not(target_os = "windows"))] - { - // We allow user to specify the wine binary path in installation_directory/winepath.txt - let wine_path_file = installation_directory.join("winepath.txt"); - if wine_path_file.exists() { - let custom_wine = std::fs::read_to_string(wine_path_file).unwrap(); - info(&format!("Using custom wine binary: {}", custom_wine.bright_blue())); - } else { - info("No custom wine binary specified, using default wine command"); - info(format!("If you want to use a custom wine binary, please create a file at {} with the path to the wine binary", wine_path_file.to_str().unwrap()).as_str()); - } - } - let client_executable_path : PathBuf; - debug(&client_year.to_string()); - if client_year == "2018" { - client_executable_path = current_version_directory.join("Client2018").join("SyntaxPlayerBeta.exe"); - } else if client_year == "2020" { - client_executable_path = current_version_directory.join("Client2020").join("SyntaxPlayerBeta.exe"); - } else if client_year == "2014" { - client_executable_path = current_version_directory.join("Client2014").join("SyntaxPlayerBeta.exe"); - } else { - client_executable_path = current_version_directory.join("Client2016").join("SyntaxPlayerBeta.exe"); - } - if !client_executable_path.exists() { - // Delete AppSettings.xml so the bootstrapper will download the client again - let app_settings_path = current_version_directory.join("AppSettings.xml"); - std::fs::remove_file(app_settings_path).unwrap(); - - error("Failed to run SyntaxPlayerBeta.exe, is your antivirus removing it? The bootstrapper will attempt to redownload the client on next launch."); - std::thread::sleep(std::time::Duration::from_secs(20)); - std::process::exit(0); - } - match launch_mode.as_str() { - "play" => { - info("Launching SYNTAX"); - #[cfg(target_os = "windows")] - { - let mut command = std::process::Command::new(client_executable_path); - command.args(&["--play","--authenticationUrl", format!("https://{}/Login/Negotiate.ashx", base_url).as_str(), "--authenticationTicket", authentication_ticket.as_str(), "--joinScriptUrl", format!("{}",join_script.as_str()).as_str()]); - command.spawn().unwrap(); - std::thread::sleep(std::time::Duration::from_secs(5)); - std::process::exit(0); - } - #[cfg(not(target_os = "windows"))] - { - // We have to launch the game through wine - let mut command = std::process::Command::new(custom_wine); - command.args(&[client_executable_path.to_str().unwrap(), "--play","--authenticationUrl", format!("https://{}/Login/Negotiate.ashx", base_url).as_str(), "--authenticationTicket", authentication_ticket.as_str(), "--joinScriptUrl", format!("{}",join_script.as_str()).as_str()]); - // We must wait for the game to exit before exiting the bootstrapper - let mut child = command.spawn().unwrap(); - child.wait().unwrap(); - std::thread::sleep(std::time::Duration::from_secs(1)); - std::process::exit(0); - } - }, - _ => { - error("Unknown launch mode, exiting."); - std::thread::sleep(std::time::Duration::from_secs(10)); - std::process::exit(0); - } - } -} \ No newline at end of file + log::info!("{}", SETUP_URL); + log::debug!("Hello {}", "World"); + log::error!("Oh no there was an issue"); + log::fatal!("Oh shit") +} -- 2.40.1