use crate::Settings;
use anyhow::Result;
use larian_formats::lspk::{self, DecompressedLspk};
use std::{
fs::{File, OpenOptions},
io::Write,
path::Path,
};
impl Settings {
pub(crate) fn install_all_inner(
&self,
verbose: bool,
dry_run: bool,
refresh: bool,
mod_file_paths: Vec<impl AsRef<Path>>,
) -> Result<()> {
mod_file_paths.into_iter().try_for_each(|mod_file_path| {
let mod_file_path = mod_file_path.as_ref().display().to_string();
println!("Installing {mod_file_path}");
self.install(verbose, dry_run, refresh, mod_file_path)
})
}
fn install(
&self,
verbose: bool,
dry_run: bool,
refresh: bool,
mut mod_file_path: String,
) -> Result<()> {
if mod_file_path.starts_with('~') {
let home_dir = std::env::var("HOME")?;
mod_file_path = mod_file_path.replacen('~', &home_dir, 1);
}
let mod_file = File::open(&mod_file_path)?;
let decompressed_lspk = read_mod(&mod_file_path, mod_file)?;
let module_info = decompressed_lspk
.clone()
.extract_meta_lsx()?
.deserialize_as_mod_pak()?
.module_info;
let mut parsed_settings = self.read_current_installed_mods()?;
let mods = parsed_settings.find_node_mut_by_id("Mods")?;
let base_id = mods
.get_uuid_where_name("GustavDev")
.map(ToString::to_string);
let existing_version_node = mods.get_or_insert_child_mut_where_id(
&module_info.uuid,
&module_info.name,
&module_info.folder,
);
match existing_version_node.get_version() {
Some(existing) if existing > module_info.version => {
println!(" {} version {existing} is already installed", module_info.name);
println!(
" The mod file you're trying to install is version {}, which is older.",
module_info.version
);
println!(" \nNo installation needed!\n");
return Ok(());
}
Some(existing) if existing == module_info.version && !refresh => {
println!(" {} version {existing} is already installed", module_info.name);
println!(" The mod file you're trying to install is the same version");
println!("\n No installation needed!\n");
return Ok(());
}
Some(existing) => {
if verbose {
println!(
" updating {} from {existing} to {}",
module_info.name, module_info.version
);
}
}
None => {
if verbose {
println!(" installing {} version {}", module_info.name, module_info.version);
}
}
}
let lspk_installation_path = self.get_mod_file_path(format!("{}.pak", module_info.folder));
if !dry_run {
let mut file = OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(&lspk_installation_path)?;
file.write_all(&decompressed_lspk.original_bytes)?;
}
existing_version_node.set_attribute_where_id(
module_info.version.to_string(),
"int64",
|id| id == "Version" || id == "Version64",
);
let mod_order = parsed_settings.find_node_mut_by_id("ModOrder")?;
if let Some(base_id) = base_id {
if dry_run && verbose {
println!(
" non-dry run would ensure that GustavDev ({base_id} is first is mod order"
);
} else {
if verbose {
println!(" ensuring that GustavDev ({base_id} is first is mod order");
}
mod_order.prepend_base(base_id);
}
}
mod_order.ensure_node_exists(&module_info.uuid);
if dry_run {
print!(" non-dry run would have ");
} else {
self.write_settings_file(&parsed_settings)?;
}
println!(" installed {}\n", lspk_installation_path.display());
Ok(())
}
}
fn read_mod(mod_file_path: &str, mod_file: File) -> Result<DecompressedLspk> {
let is_zipfile = Path::new(mod_file_path)
.extension()
.map_or(false, |e| e.eq_ignore_ascii_case("zip"));
let named_file = if is_zipfile {
lspk::entry_from_zipfile(&mod_file)?.1
} else {
lspk::Reader::new(mod_file)?.read()?
};
Ok(named_file)
}