diff --git a/Cargo.lock b/Cargo.lock index 901a78c..c24edb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,6 +79,16 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -301,6 +311,7 @@ version = "0.1.0" dependencies = [ "clap", "env_logger", + "globset", "libloot", "log", "quick-xml", @@ -320,6 +331,19 @@ dependencies = [ "wasi", ] +[[package]] +name = "globset" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + [[package]] name = "hashbrown" version = "0.14.5" diff --git a/Cargo.toml b/Cargo.toml index 32e5530..9d46d0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] clap = { version = "4.5.60", features = ["derive"] } env_logger = "0.11.9" +globset = "0.4.18" libloot = "0.29.0" log = "0.4.29" quick-xml = { version = "0.39.2", features = ["serde-types", "serialize"] } diff --git a/src/basic_types.rs b/src/basic_types.rs index e7d1e04..08bbed9 100644 --- a/src/basic_types.rs +++ b/src/basic_types.rs @@ -84,6 +84,14 @@ pub struct ModConfig { /// Relative to the mod_location from root config pub path: PathBuf, + + /// If the files should be included on the root + #[serde(default)] + root_mod: bool, + + /// Globs of what files to ignore + #[serde(default)] + ignore: Vec, } impl ModConfig { @@ -91,8 +99,18 @@ impl ModConfig { Self { id: id.to_owned(), path: source.as_ref().to_owned(), + root_mod: false, + ignore: Vec::new(), } } + + pub fn is_root_mod(&self) -> bool { + self.root_mod + } + + pub fn ignore(&self) -> &[String] { + &self.ignore + } } /// An modded game with all plugins and files diff --git a/src/main.rs b/src/main.rs index 6b7124d..d84b959 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ use clap::Parser; +use globset::{Glob, GlobSet, GlobSetBuilder}; use log::debug; use std::{ error::Error, - fs, path::{Path, PathBuf}, }; @@ -32,6 +32,10 @@ pub fn gen_filelist_for_mod( ) -> Result, Box> { let mod_location = root_config.get_mod_location(mod_config); + if mod_config.is_root_mod() { + return gen_filelist_for_root_mod(mod_config, mod_location); + } + let module_config_path = resolve_case_insensitive(&mod_location, "fomod/ModuleConfig.xml")?; let files: Vec = match module_config_path { @@ -48,6 +52,32 @@ pub fn gen_filelist_for_mod( Ok(files) } +fn create_glob_filter(rules: &[String]) -> Result> { + let mut builder = GlobSetBuilder::new(); + + for p in rules { + builder.add(Glob::new(p)?); + } + + let set = builder.build()?; + Ok(set) +} + +fn gen_filelist_for_root_mod( + mod_config: &ModConfig, + mod_location: impl AsRef, +) -> Result, Box> { + let glob_filter = create_glob_filter(mod_config.ignore())?; + let files: Vec<_> = walk_files_recursive(&mod_location)? + .map(|entry| entry.path()) + .map(|path| path.strip_prefix(&mod_location).unwrap().to_owned()) + .filter(|rel_path| !glob_filter.is_match(rel_path)) + .map(|rel_path| ModFile::new(&rel_path, &rel_path, 0)) + .collect(); + + Ok(files) +} + pub fn gen_filelist_from_mod_dir( mod_location: impl AsRef, ) -> Result, Box> {