use crate::Settings; use anyhow::{anyhow, Result}; use larian_formats::bg3::{ raw::{ModuleInfoNode, ModulesChildren}, ModuleInfo, }; use std::{collections::HashMap, path::PathBuf}; impl Settings { pub(crate) fn uninstall_all_helper( &self, dry_run: bool, mut ordered_indexes: Vec, ) -> Result<()> { let mut save = self.read_current_installed_mods()?; let Some(order_node) = save.find_node_by_id("ModOrder")?.children.first() else { return Ok(()); }; let mods_node = save.find_node_by_id("Mods")?; let unordered_entries: Result> = ordered_indexes .iter() .copied() .filter_map(|ordered_index| { get_unordered_index(order_node, ordered_index, mods_node) .map(|possible_info| { possible_info.map(|(info, index)| (info.uuid.clone(), (info, index))) }) .transpose() }) .collect(); let unordered_entries = unordered_entries?; let mod_files_to_delete: Result> = self .read_mod_files_dir()? .map(|e| { let entry = e?; if !entry.file_type()?.is_file() { return Ok(None); } let file_name = PathBuf::from(entry.file_name()); let extension = file_name.extension(); if !extension.map_or(false, |e| e.eq_ignore_ascii_case("pak")) { return Ok(None); } let pak = self .read_mod_file(&file_name)? .read()? .extract_meta_lsx()? .deserialize_as_mod_pak()?; Ok(unordered_entries .get(pak.module_info.uuid.as_str()) .map(|(info, index)| (info, index, file_name))) }) .collect(); let mod_files_to_delete = mod_files_to_delete?; let mut indexes_to_delete = Vec::with_capacity(mod_files_to_delete.len()); for mod_file_info in mod_files_to_delete { let Some((info, index, file_name)) = mod_file_info else { continue; }; if dry_run { println!("{} would be uninstalled with a non-dry run", info.name); } else { println!("uninstalling {}", info.name); let mod_file = self.get_mod_file_path(file_name); std::fs::remove_file(mod_file)?; } indexes_to_delete.push(*index); } if !dry_run { indexes_to_delete.sort_unstable_by_key(|i| usize::MAX - i); let mods_node = save.get_or_insert_node_mut_by_id("Mods"); for i in indexes_to_delete { mods_node.children[0].node.remove(i); } let order_node = save.get_or_insert_node_mut_by_id("ModOrder"); ordered_indexes.sort_unstable_by_key(|i| usize::MAX - i); for i in ordered_indexes { order_node.children[0].node.remove(i); } self.write_settings_file(&save)?; } Ok(()) } } fn get_unordered_index<'a>( ordered: &'a ModulesChildren, mod_index: usize, mods: &'a ModuleInfoNode, ) -> Result> { let Some(order_entry) = ordered.node.get(mod_index) else { return Ok(None); }; let uuid = order_entry .get_attribute_value_where_id(|id| id == "UUID") .ok_or_else(|| anyhow!("missing UUID for mod order index {mod_index}"))? .to_string(); let Some(index) = mods.find_index_where_child_id(&uuid) else { return Ok(None); }; let pak = mods.children[0].node[index].clone().try_into()?; Ok(Some((pak, index))) }