mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
140 lines
5.6 KiB
Rust
140 lines
5.6 KiB
Rust
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
|
#![allow(dead_code)]
|
|
|
|
#[macro_use]
|
|
extern crate log;
|
|
|
|
use anyhow::{bail, Result};
|
|
use clap::{arg, value_parser, Command};
|
|
use memmap2::Mmap;
|
|
use std::ffi::OsString;
|
|
use std::fs::File;
|
|
use std::{env, path::PathBuf};
|
|
use velopack_bins::*;
|
|
|
|
#[used]
|
|
#[no_mangle]
|
|
static BUNDLE_PLACEHOLDER: [u8; 48] = [
|
|
0, 0, 0, 0, 0, 0, 0, 0, // 8 bytes for package offset
|
|
0, 0, 0, 0, 0, 0, 0, 0, // 8 bytes for package length
|
|
0x94, 0xf0, 0xb1, 0x7b, 0x68, 0x93, 0xe0, 0x29, // 32 bytes for bundle signature
|
|
0x37, 0xeb, 0x34, 0xef, 0x53, 0xaa, 0xe7, 0xd4, //
|
|
0x2b, 0x54, 0xf5, 0x70, 0x7e, 0xf5, 0xd6, 0xf5, //
|
|
0x78, 0x54, 0x98, 0x3e, 0x5e, 0x94, 0xed, 0x7d, //
|
|
];
|
|
|
|
#[inline(never)]
|
|
pub fn header_offset_and_length() -> (i64, i64) {
|
|
use core::ptr;
|
|
// Perform volatile reads to avoid optimization issues
|
|
// TODO: refactor to use little-endian, also need to update the writer in dotnet
|
|
unsafe {
|
|
let offset = i64::from_ne_bytes([
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[0]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[1]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[2]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[3]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[4]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[5]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[6]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[7]),
|
|
]);
|
|
let length = i64::from_ne_bytes([
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[8]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[9]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[10]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[11]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[12]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[13]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[14]),
|
|
ptr::read_volatile(&BUNDLE_PLACEHOLDER[15]),
|
|
]);
|
|
(offset, length)
|
|
}
|
|
}
|
|
|
|
fn main() -> Result<()> {
|
|
windows::mitigate::pre_main_sideload_mitigation();
|
|
shared::cli_host::clap_run_main("Setup", main_inner)
|
|
}
|
|
|
|
fn main_inner() -> Result<()> {
|
|
#[rustfmt::skip]
|
|
let mut arg_config = Command::new("Setup")
|
|
.about(format!("Velopack Setup ({}) installs applications.\nhttps://velopack.io", env!("NGBV_VERSION")))
|
|
.arg(arg!(-s --silent "Hides all dialogs and answers 'yes' to all prompts"))
|
|
.arg(arg!(-v --verbose "Print debug messages to console"))
|
|
.arg(arg!(-l --log <FILE> "Enable file logging and set location").required(false).value_parser(value_parser!(PathBuf)))
|
|
.arg(arg!(-t --installto <DIR> "Installation directory to install the application").required(false).value_parser(value_parser!(PathBuf)))
|
|
.arg(arg!([EXE_ARGS] "Arguments to pass to the started executable. Must be preceded by '--'.").required(false).last(true).num_args(0..));
|
|
|
|
if cfg!(debug_assertions) {
|
|
arg_config = arg_config
|
|
.arg(arg!(-d --debug <FILE> "Debug mode, install from a nupkg file").required(false).value_parser(value_parser!(PathBuf)));
|
|
}
|
|
|
|
let matches = arg_config.try_get_matches()?;
|
|
|
|
let silent = matches.get_flag("silent");
|
|
dialogs::set_silent(silent);
|
|
|
|
let verbose = matches.get_flag("verbose");
|
|
let logfile = matches.get_one::<PathBuf>("log");
|
|
velopack::logging::init_logging("setup", logfile, true, verbose, None);
|
|
|
|
let debug = matches.get_one::<PathBuf>("debug");
|
|
let install_to = matches.get_one::<PathBuf>("installto");
|
|
let exe_args = matches.get_many::<OsString>("EXE_ARGS").map(|v| v.map(|f| f.to_os_string()).collect());
|
|
|
|
info!("Starting Velopack Setup ({})", env!("NGBV_VERSION"));
|
|
info!(" Location: {:?}", env::current_exe()?);
|
|
info!(" Silent: {}", silent);
|
|
info!(" Verbose: {}", verbose);
|
|
info!(" Log: {:?}", logfile);
|
|
info!(" Install To: {:?}", install_to);
|
|
if cfg!(debug_assertions) {
|
|
info!(" Debug: {:?}", debug);
|
|
}
|
|
|
|
// change working directory to the containing directory of the exe
|
|
let mut containing_dir = env::current_exe()?;
|
|
containing_dir.pop();
|
|
env::set_current_dir(containing_dir)?;
|
|
|
|
// load the bundle which is embedded or if missing from the debug nupkg path
|
|
let osinfo = os_info::get();
|
|
let osarch = shared::runtime_arch::RuntimeArch::from_current_system();
|
|
info!("OS: {osinfo}, Arch={osarch:#?}");
|
|
|
|
if !windows::is_windows_7_sp1_or_greater() {
|
|
bail!("This installer requires Windows 7 SPA1 or later and cannot run.");
|
|
}
|
|
|
|
// in debug mode only, allow a nupkg to be passed in as the first argument
|
|
if cfg!(debug_assertions) {
|
|
if let Some(pkg) = debug {
|
|
info!("Loading bundle from DEBUG nupkg file {:?}...", pkg);
|
|
let mut bundle = velopack::bundle::load_bundle_from_file(pkg)?;
|
|
commands::install(&mut bundle, install_to, exe_args)?;
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
info!("Reading bundle header...");
|
|
let (offset, length) = header_offset_and_length();
|
|
info!("Bundle offset = {}, length = {}", offset, length);
|
|
|
|
// try to load the bundle from embedded zip
|
|
if offset > 0 && length > 0 {
|
|
info!("Loading bundle from embedded zip...");
|
|
let file = File::open(env::current_exe()?)?;
|
|
let mmap = unsafe { Mmap::map(&file)? };
|
|
let zip_range: &[u8] = &mmap[offset as usize..(offset + length) as usize];
|
|
let mut bundle = velopack::bundle::load_bundle_from_memory(&zip_range)?;
|
|
commands::install(&mut bundle, install_to, exe_args)?;
|
|
return Ok(());
|
|
}
|
|
|
|
bail!("Could not find embedded zip file. Please contact the application author.");
|
|
}
|