Skip to content

Commit

Permalink
fiox casing bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
rfuzzo committed Mar 8, 2024
1 parent f1f225f commit 9c07736
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 96 deletions.
30 changes: 22 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub mod rules;
pub mod sorter;

use ini::Ini;
use log::{error, info, warn};
use log::{debug, error, info, warn};
use openmw_cfg::config_path;
use reqwest::header::LAST_MODIFIED;
use rules::*;
Expand Down Expand Up @@ -99,26 +99,30 @@ pub fn sort(
warn!("No rules found to evaluate");
} else {
info!("Evaluating mod list...\n");
debug!("{:?}", &mods);

let mods_cpy: Vec<_> = mods.iter().map(|f| f.to_lowercase()).collect();
for rule in &mut parser.rules {
if rule.eval(&mods) {
if rule.eval(&mods_cpy) {
match rule {
EWarningRule::Note(n) => {
info!("[NOTE]\n{}", n.get_comment());
info!("[{}]\n", n.plugins.join(";"));
debug!("Reference: [{}]", n.plugins.join(";"));
}
EWarningRule::Conflict(c) => {
warn!("[CONFLICT]\n{}", c.get_comment());
info!("[{}]\n", c.plugins.join(";"));
debug!("Reference: [{}]", c.plugins.join(";"));
}
EWarningRule::Requires(r) => {
warn!("[REQUIRES]\n{}", r.get_comment());
info!("[{}]\n", r.plugins.join(";"));
error!("[REQUIRES]\n{}", r.get_comment());
debug!("Reference: [{}]", r.plugins.join(";"));
}
EWarningRule::Patch(p) => {
warn!("[Patch]\n{}", p.get_comment());
info!("[{}]\n", p.plugins.join(";"));
debug!("Reference: [{}]", p.plugins.join(";"));
}
}
println!();
}
}
}
Expand All @@ -138,7 +142,16 @@ pub fn sort(
Ok(result) => {
if dry_run {
info!("Dry run...");
info!("New:\n{:?}", result);

debug!("Old:\n{:?}", &mods);
debug!("New:\n{:?}", result);

if mods.eq(&result) {
info!("Mods are in correct order, no sorting needed.");
} else {
info!("New order:\n{:?}", result);
}

ExitCode::SUCCESS
} else {
info!("Current:\n{:?}", &mods);
Expand Down Expand Up @@ -733,6 +746,7 @@ where
result
}

/// Checks if the list contains the str
pub fn wild_contains(list: &[String], str: &String) -> Option<Vec<String>> {
if str.contains('*') || str.contains('?') || str.contains("<ver>") {
let mut results = vec![];
Expand Down
25 changes: 21 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,18 @@ enum Command {
}

fn main() -> ExitCode {
let cli = Cli::parse();
let cli = match Cli::try_parse() {
Ok(cli) => cli,
Err(e) => {
eprintln!("{}", e);

println!("\nPress any button to continue");
let mut buffer = String::new();
let _ = std::io::stdin().read_line(&mut buffer);

return ExitCode::FAILURE;
}
};

// logger
let mut level = ELogLevel::Info;
Expand All @@ -85,18 +96,24 @@ fn main() -> ExitCode {

// detect game
let game = if let Some(game) = cli.game {
info!("Set game to: {:?}", game);
game
} else if is_current_directory_name("Cyberpunk 2077") {
info!("Detected game: {:?}", ESupportedGame::Cyberpunk);
ESupportedGame::Cyberpunk
} else if is_current_directory_name("Morrowind") {
info!("Detected game: {:?}", ESupportedGame::Morrowind);
ESupportedGame::Morrowind
} else {
error!("No game specified or detected");
if !cli.non_interactive {
println!("Press any button to continue");
let mut buffer = String::new();
let _ = std::io::stdin().read_line(&mut buffer);
}
return ExitCode::FAILURE;
};

info!("Detected game: {:?}", game);

let code = match &cli.command {
Command::List { root } => list_mods(root, game),
Command::Verify { rules_dir } => verify(game, rules_dir),
Expand All @@ -119,7 +136,7 @@ fn main() -> ExitCode {
};

if !cli.non_interactive {
println!("Press any button to continue");
println!("\nPress any button to continue");
let mut buffer = String::new();
let _ = std::io::stdin().read_line(&mut buffer);
}
Expand Down
137 changes: 55 additions & 82 deletions src/sorter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,16 @@ impl Sorter {
&self,
n: usize,
edges: &[(usize, usize)],
index_dict: &HashMap<&str, usize>,
index_dict_rev: &HashMap<usize, &str>,
index_dict: &HashMap<String, usize>,
index_dict_rev: &HashMap<usize, String>,
result: &mut Vec<String>,
last_index: &mut usize,
) -> bool {
match self.sort_type {
ESortType::Unstable => panic!("not supported"),
ESortType::StableOpt => Self::stable_topo_sort_opt2(
n,
edges,
index_dict,
index_dict_rev,
result,
last_index,
),
ESortType::StableOpt => {
Self::stable_topo_sort_opt2(n, edges, index_dict_rev, result, last_index)
}
ESortType::StableFull => {
Self::stable_topo_sort_full(n, edges, index_dict, result, last_index)
}
Expand All @@ -61,7 +56,7 @@ impl Sorter {
pub fn stable_topo_sort_full(
n: usize,
edges: &[(usize, usize)],
index_dict: &HashMap<&str, usize>,
index_dict: &HashMap<String, usize>,
result: &mut Vec<String>,
last_index: &mut usize,
) -> bool {
Expand All @@ -86,8 +81,7 @@ impl Sorter {
pub fn stable_topo_sort_opt2(
_n: usize,
edges: &[(usize, usize)],
_index_dict: &HashMap<&str, usize>,
index_dict_rev: &HashMap<usize, &str>,
index_dict_rev: &HashMap<usize, String>,
result: &mut Vec<String>,
last_index: &mut usize,
) -> bool {
Expand All @@ -97,8 +91,8 @@ impl Sorter {
let i = edge.0;
let j = edge.1;

let x = index_dict_rev[&i];
let y = index_dict_rev[&j];
let x = &index_dict_rev[&i];
let y = &index_dict_rev[&j];

let idx_of_x = result.iter().position(|f| f == x).unwrap();
let idx_of_y = result.iter().position(|f| f == y).unwrap();
Expand All @@ -118,82 +112,50 @@ impl Sorter {
b
}

// pub fn stable_topo_sort_opt(
// _n: usize,
// edges: &[(usize, usize)],
// _index_dict: &HashMap<&str, usize>,
// index_dict_rev: &HashMap<usize, &str>,
// result: &mut Vec<String>,
// last_index: &mut usize,
// ) -> bool {
// // optimize B: only check edges
// //let mut b = false;
// for (idx, edge) in edges.iter().enumerate() {
// let i = edge.0;
// let j = edge.1;

// let x = index_dict_rev[&i];
// let y = index_dict_rev[&j];

// let idx_of_x = result.iter().position(|f| f == x).unwrap();
// let idx_of_y = result.iter().position(|f| f == y).unwrap();

// // if i not before j x should be before y
// if idx_of_x > idx_of_y {
// let t = result[idx_of_x].to_owned();
// result.remove(idx_of_x);
// result.insert(idx_of_y, t);

// *last_index = idx;

// //b = true;
// return true;
// }
// }

// //b
// false
// }

/// Sorts the input mods topologically. Mods input is case sensitive!
///
/// # Panics
///
/// Panics if .
///
/// # Errors
///
/// This function will return an error if any parsing fails
pub fn topo_sort(
&mut self,
mods: &[String],
mods_cased: &[String],
order_rules: &[EOrderRule],
) -> Result<Vec<String>, &'static str> {
let mut g = IndexGraph::with_vertices(mods.len());

let mut index_dict: HashMap<&str, usize> = HashMap::new();
for (i, m) in mods.iter().enumerate() {
index_dict.insert(m, i);
}
// reverse
let mut index_dict_rev: HashMap<usize, &str> = HashMap::default();
for (k, v) in &index_dict {
index_dict_rev.insert(*v, k);
}

// early out
if order_rules.is_empty() {
log::info!("No order rules found, nothing to sort");
return Err("No order rules found");
}

let order = get_ordering_from_order_rules(order_rules);
// build hashmaps for lookup
// first map lowercase to cased, this is kinda dumb, but our input is small enough that I don't care about the memory hit
let mut mods: Vec<String> = vec![];

let mut index_dict: HashMap<String, usize> = HashMap::new();
let mut index_dict_rev: HashMap<usize, String> = HashMap::default();
let mut mod_map: HashMap<usize, String> = HashMap::default();
for (i, m) in mods_cased.iter().enumerate() {
let lower_case = m.to_lowercase();

index_dict.insert(lower_case.clone(), i);
index_dict_rev.insert(i, lower_case.clone());

mod_map.insert(i, m.to_owned());
mods.push(lower_case.to_owned());
}

// add edges
let mut g = IndexGraph::with_vertices(mods.len());
let order_pairs = get_ordering_from_order_rules(order_rules);
let mut edges: Vec<(usize, usize)> = vec![];
for (a, b) in order {
// do not check for wildcards
// if mods.contains(a) && mods.contains(b) {
// let idx_a = index_dict[a.as_str()];
// let idx_b = index_dict[b.as_str()];
// if !edges.contains(&(idx_a, idx_b)) {
// edges.push((idx_a, idx_b));
// g.add_edge(idx_a, idx_b);
// }
// }

if let Some(results_for_a) = wild_contains(mods, &a) {
if let Some(results_for_b) = wild_contains(mods, &b) {
for (a, b) in order_pairs {
if let Some(results_for_a) = wild_contains(&mods, &a) {
if let Some(results_for_b) = wild_contains(&mods, &b) {
// foreach esm i, add an edge to all esps j
for i in &results_for_a {
for j in &results_for_b {
Expand Down Expand Up @@ -228,7 +190,7 @@ impl Sorter {
error!("cycles:");
for e in er {
error!("\t{}: {}", e, index_dict_rev[&e]);
res.push(index_dict_rev[&e]);
res.push(index_dict_rev[&e].clone());
}
}

Expand All @@ -239,7 +201,12 @@ impl Sorter {
return Err("Graph contains a cycle");
}

return Ok(sort.iter().map(|f| mods[*f].to_owned()).collect::<Vec<_>>());
// map sorted index back to mods
let mut result = vec![];
for idx in sort {
result.push(mod_map[&idx].to_owned());
}
return Ok(result);
}

// sort
Expand Down Expand Up @@ -296,7 +263,13 @@ impl Sorter {
&mut index,
) {
// Return the sorted vector
return Ok(mods_copy);
// map sorted index back to mods
let mut result = vec![];
for lower_case_name in mods_copy {
let idx = index_dict[&lower_case_name.clone()];
result.push(mod_map[&idx].to_owned());
}
return Ok(result);
}
log::debug!("{}, index {}", i, index);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ mod integration_tests {
parser
.init_from_file("./tests/plox/rules_conflict.txt")
.expect("failed rule parsing");
assert_eq!(5, parser.rules.len());
assert_eq!(6, parser.rules.len());
}

#[test]
Expand Down
5 changes: 5 additions & 0 deletions tests/plox/rules_conflict.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
[Conflict] ; ( https://www.nexusmods.com/morrowind/mods/49075 ) (yohannes)
"OAAB Golden Reeds" is already incorporated into "OAAB Grazelands".
OAAB_GoldenReeds.esp
OAAB_Grazelands.esp

[conflict]
you should use v0.3 or better of yacoby's "encumbrance bar" with "rage indicator", otherwise the bars will conflict.
( ref: readme - rage indicator.esp)
Expand Down
Loading

0 comments on commit 9c07736

Please sign in to comment.