use std::{ io, path::{Path, PathBuf}, }; use libloot::{ Game, GameType, error::{GameHandleCreationError, LoadPluginsError, SortPluginsError}, }; use log::trace; use thiserror::Error; use crate::{ basic_types::{ModdedInstance, RootConfig}, utils::walk_files_recursive, }; pub struct LoadOrder { game: libloot::Game, target: PathBuf, } impl LoadOrder { pub fn new(install_dir: impl AsRef, game_type: GameType) -> Result { Ok(Self { game: Game::with_local_path( game_type, install_dir.as_ref(), &install_dir.as_ref().join("appdata"), )?, target: install_dir.as_ref().to_owned(), }) } pub fn add_plugins_from_instance( &mut self, root_config: &RootConfig, instance: &ModdedInstance, ) -> Result<(), LoadOrderError> { for installed_mod in &instance.mods { let mod_config = root_config.get_mod_by_id(&installed_mod.mod_id()).unwrap(); let mod_source_root = root_config.get_mod_location(&mod_config); let mod_plugins: Vec = installed_mod .files() .iter() .filter(|f| Self::is_plugin_file(&f.0)) .map(|(from, _)| mod_source_root.join(from)) .collect(); let refs: Vec<_> = mod_plugins.iter().map(|e| e.as_path()).collect(); trace!("Loading {} plugins to game", refs.len()); self.game.load_plugins(&refs)?; } Ok(()) } pub fn add_plugins_from_install(&mut self) -> Result<(), LoadOrderError> { let plugins: Vec<_> = walk_files_recursive(self.target.join("Data"))? .filter(|f| Self::is_plugin_file(f.path())) .map(|f| f.path()) .collect(); let refs: Vec<_> = plugins.iter().map(|e| e.as_path()).collect(); trace!("Loading {} plugins to game", refs.len()); self.game.load_plugins(&refs)?; Ok(()) } fn is_plugin_file(filename: impl AsRef) -> bool { filename .as_ref() .extension() .is_some_and(|ext| ext == "esp" || ext == "esm" || ext == "esl") } pub fn load_order(&self) -> Result, LoadOrderError> { trace!("Generating new load order"); let all_plugins = self.game.loaded_plugins(); let plugins_names: Vec<&str> = all_plugins.iter().map(|e| e.name()).collect(); let sorted = self.game.sort_plugins(&plugins_names)?; Ok(sorted) } } #[derive(Error, Debug)] pub enum LoadOrderError { #[error("Failed to read game directory")] Io(#[from] io::Error), #[error("Failed to create libloot game")] Creating(#[from] GameHandleCreationError), #[error("Failed to add plugins to libloot")] Loading(#[from] LoadPluginsError), #[error("libloot failed to sort plugins")] Sorting(#[from] SortPluginsError), }