Skip to main content

modde_games/oblivion_remastered/
scanner.rs

1//! Mod scanner for Oblivion Remastered.
2
3use anyhow::Result;
4
5use crate::scanner_patterns::{FileGroupRule, SingleFileModRule};
6use crate::traits::{DiscoveredMod, ModScanner, ScanContext};
7
8/// [`ModScanner`] that discovers installed Oblivion Remastered mods.
9pub struct OblivionRemasteredScanner;
10
11pub static OBLIVION_REMASTERED_SCANNER: OblivionRemasteredScanner = OblivionRemasteredScanner;
12
13const GROUP_EXTENSIONS: &[&str] = &["pak", "ucas", "utoc"];
14
15impl ModScanner for OblivionRemasteredScanner {
16    fn scan_directories(&self) -> &[&str] {
17        &["Content/Paks/~mods", "Data"]
18    }
19
20    fn scan_filesystem(&self, ctx: &ScanContext<'_>) -> Result<Vec<DiscoveredMod>> {
21        let mut out = Vec::new();
22        let paks = super::paks_root(ctx.install_dir).join("~mods");
23        FileGroupRule {
24            rel_dir: "",
25            extensions: GROUP_EXTENSIONS,
26            mod_id_prefix: "pak",
27            source_location: "paks-mods",
28            confidence: 0.9,
29        }
30        .scan_dir(ctx.install_dir, &paks, &mut out);
31
32        SingleFileModRule {
33            rel_dir: "Data",
34            extension: "esp",
35            ignored_prefixes: &[],
36            mod_id_prefix: "plugin",
37            source_location: "Data",
38            confidence: 0.8,
39        }
40        .scan(ctx.install_dir, &mut out)?;
41        Ok(out)
42    }
43
44    fn mod_id_footprint(&self, mod_id: &str) -> Option<modde_core::scanner::ModFootprint> {
45        if let Some(stem) = mod_id.strip_prefix("pak/") {
46            return Some(modde_core::scanner::ModFootprint::File(format!(
47                "oblivionremastered/content/paks/~mods/{}.pak",
48                stem.to_lowercase()
49            )));
50        }
51        let stem = mod_id.strip_prefix("plugin/")?.to_lowercase();
52        Some(modde_core::scanner::ModFootprint::File(format!(
53            "data/{stem}.esp"
54        )))
55    }
56}