diff --git a/src/lib.rs b/src/lib.rs index d9195f2e..276d0508 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![feature(let_chains)] +#![deny(clippy::unwrap_used)] pub mod manager; pub mod mergers; pub mod settings; @@ -38,14 +39,14 @@ fn find_modified_files(py: Python, mod_dir: String) -> PyResult> { let content = mod_dir.join(util::content()); let dlc = mod_dir.join(util::dlc()); let files: Vec = py.allow_threads(|| { - glob::glob(mod_dir.join("**/*").to_str().unwrap()) - .unwrap() + glob::glob(&mod_dir.join("**/*").to_string_lossy()) + .expect("Bad glob?!?!?!") .filter_map(std::result::Result::ok) .par_bridge() .filter(|f| { f.is_file() && (f.starts_with(&content) || f.starts_with(&dlc)) - && util::get_canon_name(f.strip_prefix(mod_dir).unwrap()) + && util::get_canon_name(unsafe { f.strip_prefix(mod_dir).unwrap_unchecked() }) .and_then(|canon| { fs::read(f) .ok() @@ -60,7 +61,7 @@ fn find_modified_files(py: Python, mod_dir: String) -> PyResult> { Ok(files .par_iter() .filter(|f| { - fs::metadata(f).unwrap().len() > 4 + fs::metadata(f).expect("No file metadata!?!?!?!").len() > 4 && f.extension() .and_then(|ext| ext.to_str()) .map(|ext| botw_utils::extensions::SARC_EXTS.contains(&ext)) @@ -71,7 +72,7 @@ fn find_modified_files(py: Python, mod_dir: String) -> PyResult> { find_modded_sarc_files( &sarc, file.starts_with(&dlc), - &file.strip_prefix(mod_dir).unwrap().to_slash_lossy(), + &unsafe { file.strip_prefix(mod_dir).unwrap_unchecked() }.to_slash_lossy(), ) }) .collect::>>()? @@ -110,7 +111,10 @@ fn find_modded_sarc_files(sarc: &Sarc, aoc: bool, path: &str) -> Result ModLinker<'py, 'set> { } let mod_folders: Vec = glob::glob(&settings.mods_dir().join("*").to_string_lossy()) - .unwrap() + .expect("Bad glob?!?!?") .filter_map(|p| p.ok()) .filter(|p| p.is_dir() && !p.join(".disabled").exists()) .collect::>() @@ -103,7 +103,7 @@ impl<'py, 'set> ModLinker<'py, 'set> { std::iter::once(p) .chain( glob::glob(&glob_str) - .unwrap() + .expect("Bad glob?!?!?") .filter_map(|p| p.ok()) .filter(|p| p.is_dir()), ) @@ -118,10 +118,10 @@ impl<'py, 'set> ModLinker<'py, 'set> { .try_for_each(|folder| -> Result<()> { let mod_files: Vec<(PathBuf, PathBuf)> = glob::glob(&folder.join("**/*").to_string_lossy()) - .unwrap() + .expect("Bad glob?!?!?!") .filter_map(|p| { p.ok().map(|p| { - (p.clone(), p.strip_prefix(&folder).unwrap().to_owned()) + (p.clone(), unsafe {p.strip_prefix(&folder).unwrap_unchecked()}.to_owned()) }) }) .filter(|(item, rel)| { @@ -145,7 +145,7 @@ impl<'py, 'set> ModLinker<'py, 'set> { .map(fs::create_dir_all) .transpose() .with_context(|| jstr!("Failed to create parent folder for file {rel.to_str().unwrap()}"))? - .unwrap(); + .expect("Whoa, why is there no parent folder?"); fs::hard_link(&item, &out) .with_context(|| jstr!("Failed to hard link {rel.to_str().unwrap()} to {out.to_str().unwrap()}")) .or_else(|_| { @@ -302,11 +302,11 @@ impl<'py, 'set> ModLinker<'py, 'set> { } } if glob::glob(&output.join("*").to_string_lossy()) - .unwrap() + .expect("Bad glob?!?!?!") .filter_map(|p| p.ok()) .count() == 0 - && std::fs::read_dir(settings.mods_dir()).unwrap().count() > 1 + && std::fs::read_dir(settings.mods_dir())?.count() > 1 { Err(anyhow::anyhow!("Output folder is empty")) } else { diff --git a/src/mergers/actorinfo.rs b/src/mergers/actorinfo.rs index 6269b7cf..214d0a35 100644 --- a/src/mergers/actorinfo.rs +++ b/src/mergers/actorinfo.rs @@ -23,7 +23,7 @@ static STOCK_ACTORINFO: Lazy>> = Lazy::new(|| { .iter() .map(|actor| -> Result<(u32, Byml)> { Ok(( - roead::aamp::hash_name(actor.as_hash().unwrap()["name"].as_string()?), + roead::aamp::hash_name(actor.as_hash()?["name"].as_string()?), actor.clone(), )) }) @@ -107,7 +107,7 @@ fn diff_actorinfo(py: Python, actorinfo_path: String) -> PyResult { }) }) .collect(); - Ok(Byml::Hash(diff).to_text().unwrap().as_bytes().to_vec()) + Ok(Byml::Hash(diff).to_text()?.as_bytes().to_vec()) } else { anyhow::bail!("Modded actor info is not a hash???") } @@ -120,11 +120,11 @@ fn merge_actorinfo(py: Python, modded_actors: Vec) -> PyResult<()> { let merge = || -> Result<()> { let modded_actor_root = Byml::from_binary(&modded_actors)?; let modded_actors: ActorMap = py.allow_threads(|| -> Result { - Ok(modded_actor_root + modded_actor_root .as_hash()? .into_par_iter() - .map(|(h, a)| (h.parse::().unwrap(), a.clone())) - .collect()) + .map(|(h, a)| Ok((h.parse::()?, a.clone()))) + .collect() })?; let mut merged_actors = stock_actorinfo()?.as_ref().clone(); merge_actormap(&mut merged_actors, &modded_actors); @@ -152,8 +152,8 @@ fn merge_actorinfo(py: Python, modded_actors: Vec) -> PyResult<()> { let output = util::settings() .master_content_dir() .join("Actor/ActorInfo.product.sbyml"); - if !output.parent().unwrap().exists() { - fs::create_dir_all(output.parent().unwrap())?; + if !output.parent().expect("No parent folder?!?!?").exists() { + fs::create_dir_all(output.parent().expect("No parent folder?!?!?"))?; } fs::write( output, diff --git a/src/mergers/maps.rs b/src/mergers/maps.rs index 6256e0cf..5160af0c 100644 --- a/src/mergers/maps.rs +++ b/src/mergers/maps.rs @@ -62,11 +62,16 @@ impl Display for MapUnit { impl TryFrom<&Path> for MapUnit { type Error = anyhow::Error; fn try_from(value: &Path) -> Result { - let mut split = value.file_stem().unwrap().to_str().unwrap().split('_'); + let mut split = value + .file_stem() + .unwrap_or_default() + .to_str() + .unwrap_or_default() + .split('_'); Ok(MapUnit { unit: split.next().context("Not a map unitt")?.into(), kind: split.next().context("Not a map unitt")?.into(), - aocfield: value.to_str().unwrap().contains("AocField"), + aocfield: value.to_str().unwrap_or_default().contains("AocField"), }) } } @@ -96,7 +101,10 @@ impl MapUnit { } fn get_aoc_path(&self) -> PathBuf { - util::settings().dlc_dir().unwrap().join(self.get_path()) + util::settings() + .dlc_dir() + .expect("There's no DLC folder") + .join(self.get_path()) } fn get_resource_path(&self) -> String { @@ -126,8 +134,7 @@ impl MapUnit { MapUnitType::Static => { let pack = util::get_stock_pack("TitleBG")?; Ok(Byml::from_binary(&decompress( - pack - .get_data(&self.get_path()) + pack.get_data(&self.get_path()) .with_context(|| { jstr!("Failed to read {&self.get_path()} from TitleBG.pack") })? @@ -146,8 +153,7 @@ impl MapUnit { MapUnitType::Static => { let pack = util::get_stock_pack("AocMainField")?; Ok(Byml::from_binary(&decompress( - pack - .get_data(&self.get_path()) + pack.get_data(&self.get_path()) .with_context(|| { jstr!("Failed to read {&self.get_path()} from TitleBG.pack") })? @@ -161,11 +167,11 @@ impl MapUnit { fn merge_entries(diff: &Hash, entries: &mut Vec) -> Result<()> { let stock_hashes: Vec = entries .iter() - .map(|e| e["HashId"].as_u32().unwrap()) - .collect(); + .map(|e| Ok(e["HashId"].as_u32()?)) + .collect::>()?; let mut orphans: Vec = vec![]; for (hash, entry) in diff["mod"].as_hash()? { - let hash = hash.parse::().unwrap(); + let hash = hash.parse::()?; if let Some(idx) = stock_hashes.iter().position(|h| *h == hash) { entries[idx] = entry.clone(); } else { @@ -188,19 +194,18 @@ fn merge_entries(diff: &Hash, entries: &mut Vec) -> Result<()> { .cloned() .chain(orphans.into_iter()) .filter(|e| { - !stock_hashes.contains( - &(e["HashId"] - .as_u32() - .or_else(|_| e["HashId"].as_i32().map(|i| i as u32)) - .unwrap()), - ) + e["HashId"] + .as_u32() + .or_else(|_| e["HashId"].as_i32().map(|i| i as u32)) + .map(|h| !stock_hashes.contains(&h)) + .unwrap_or(false) }), ); entries.sort_by_cached_key(|e| { e["HashId"] .as_u32() .or_else(|_| e["HashId"].as_i32().map(|i| i as u32)) - .unwrap() + .unwrap_or(0) }); Ok(()) } @@ -218,16 +223,18 @@ fn merge_map(map_unit: MapUnit, diff: &Hash, settings: &Settings) -> Result<(Str merge_entries(diff["Rails"].as_hash()?, rails)?; } let data = new_map.to_binary(settings.endian()); - let size = rstb::calc::calc_from_size_and_name( - data.len(), - "dummy.mubin", - if settings.wiiu { - rstb::Endian::Big - } else { - rstb::Endian::Little - }, - ) - .unwrap(); + let size = unsafe { + rstb::calc::calc_from_size_and_name( + data.len(), + "dummy.mubin", + if settings.wiiu { + rstb::Endian::Big + } else { + rstb::Endian::Little + }, + ) + .unwrap_unchecked() + }; let out = settings .master_mod_dir() .join(if util::settings().dlc_dir().is_some() { @@ -236,8 +243,8 @@ fn merge_map(map_unit: MapUnit, diff: &Hash, settings: &Settings) -> Result<(Str util::content() }) .join(map_unit.get_path()); - if !out.parent().unwrap().exists() { - fs::create_dir_all(out.parent().unwrap())?; + if !out.parent().expect("Folder has no parent?!?").exists() { + fs::create_dir_all(out.parent().expect("Folder has no parent?!?"))?; } fs::write(out, compress(data))?; Ok(( @@ -252,7 +259,7 @@ fn merge_map(map_unit: MapUnit, diff: &Hash, settings: &Settings) -> Result<(Str #[pyfunction] pub fn merge_maps(py: Python, diff_bytes: Vec) -> PyResult { - let diffs = Byml::from_binary(&diff_bytes).unwrap(); + let diffs = Byml::from_binary(&diff_bytes).map_err(anyhow::Error::from)?; let rstb_values: HashMap = if let Byml::Hash(diffs) = diffs { py.allow_threads(|| -> Result> { let settings = util::settings().clone(); diff --git a/src/mergers/pack.rs b/src/mergers/pack.rs index 3588be43..85dd4882 100644 --- a/src/mergers/pack.rs +++ b/src/mergers/pack.rs @@ -94,7 +94,7 @@ fn merge_sarc(sarcs: Vec, endian: Endian) -> Result> { let mut merged = merge_sarc(nest_sarcs, endian)?; if file_path .extension() - .map(|e| e.to_str().unwrap().starts_with('s')) + .map(|e| e.to_str().unwrap_or_default().starts_with('s')) .unwrap_or_default() { merged = compress(&merged); @@ -133,10 +133,16 @@ pub fn merge_sarcs(py: Python, diffs: HashMap>) -> PyResul }) .collect::>>()?; let mut merged = merge_sarc(sarcs, settings.endian())?; - if out.extension().unwrap().to_str().unwrap().starts_with('s') { + if out + .extension() + .unwrap_or_default() + .to_str() + .unwrap_or_default() + .starts_with('s') + { merged = compress(merged); } - fs::create_dir_all(out.parent().unwrap())?; + fs::create_dir_all(out.parent().expect("No parent folder??!?"))?; fs::write(out, merged)?; Ok(()) })?; diff --git a/src/mergers/texts.rs b/src/mergers/texts.rs index 4bced4ad..21da4409 100644 --- a/src/mergers/texts.rs +++ b/src/mergers/texts.rs @@ -31,9 +31,9 @@ pub fn diff_language( let diff = py.allow_threads(|| -> Result> { let language = &Path::new(&mod_bootup_path) .file_stem() - .unwrap() + .expect("Okay, how does this path have no name?") .to_str() - .unwrap()[7..]; + .expect("And this should definitely work, too")[7..]; let mod_bootup = Sarc::new(fs::read(&mod_bootup_path)?)?; let stock_bootup = Sarc::new(fs::read(&stock_bootup_path)?)?; let message_path = jstr!("Message/Msg_{&language}.product.ssarc"); @@ -67,7 +67,8 @@ pub fn diff_language( .with_context(|| jstr!("Invalid MSBT file: {&path}"))?; if let Some(stock_text) = stock_message .get_data(&path) - .unwrap() + .ok() + .flatten() .and_then(|data| Msyt::from_msbt_bytes(data).ok()) { if mod_text == stock_text { @@ -80,8 +81,7 @@ pub fn diff_language( if only_new_keys { !stock_text.entries.contains_key(*e) } else { - !stock_text.entries.contains_key(*e) - || *t != stock_text.entries.get(*e).unwrap() + stock_text.entries.get(*e) != Some(t) } }) .map(|(e, t)| (e.to_owned(), t.clone())) @@ -105,7 +105,8 @@ pub fn diff_language( .collect(); Ok(diffs) })?; - let diff_text = serde_json::to_string(&diff).unwrap(); + let diff_text = + serde_json::to_string(&diff).expect("It's whack if this diff doesn't serialize"); let json = PyModule::import(py, "json")?; #[allow(deprecated)] let dict = json.call_method1("loads", (&diff_text,))?; @@ -130,9 +131,9 @@ pub fn merge_language( py.allow_threads(|| -> Result<()> { let language = &Path::new(&stock_bootup_path) .file_stem() - .unwrap() + .expect("Okay, how does this path have no name?") .to_str() - .unwrap()[7..]; + .expect("And this should definitely work, too")[7..]; let stock_bootup = Sarc::new(fs::read(&stock_bootup_path)?)?; let message_path = format!("Message/Msg_{}.product.ssarc", &language); let stock_message = Sarc::new(decompress( diff --git a/src/settings.rs b/src/settings.rs index 679d8878..c359b10e 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -9,8 +9,9 @@ use std::{ sync::Arc, }; -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Serialize, Deserialize)] +#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, Hash, Serialize, Deserialize)] pub enum Language { + #[default] USen, EUen, USfr, @@ -35,37 +36,77 @@ impl std::fmt::Display for Language { #[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)] pub struct Settings { + #[serde(default = "default_store_dir")] + pub store_dir: PathBuf, + #[serde(default)] + pub lang: Language, + #[serde(default = "last_version")] + pub last_version: String, + #[serde(default = "fn_true")] + pub changelog: bool, + #[serde(default = "fn_true")] + pub wiiu: bool, + #[serde(default = "fn_true")] + pub auto_gb: bool, + #[serde(default)] pub cemu_dir: PathBuf, + #[serde(default)] pub game_dir: PathBuf, + #[serde(default)] pub game_dir_nx: PathBuf, + #[serde(default)] pub update_dir: PathBuf, + #[serde(default)] pub dlc_dir: PathBuf, + #[serde(default)] pub dlc_dir_nx: PathBuf, - pub store_dir: PathBuf, + #[serde(default)] pub export_dir: PathBuf, + #[serde(default)] pub export_dir_nx: PathBuf, + #[serde(default)] pub load_reverse: bool, + #[serde(default)] pub site_meta: PathBuf, + #[serde(default)] pub dark_theme: bool, + #[serde(default)] pub no_guess: bool, - pub lang: Language, + #[serde(default)] pub no_cemu: bool, - pub wiiu: bool, + #[serde(default)] pub no_hardlinks: bool, + #[serde(default)] pub force_7z: bool, + #[serde(default)] pub suppress_update: bool, + #[serde(default)] pub nsfw: bool, - pub last_version: String, - pub changelog: bool, + #[serde(default)] pub strip_gfx: bool, - pub auto_gb: bool, + #[serde(default)] pub show_gb: bool, } +#[inline] +fn default_store_dir() -> PathBuf { + DATA_DIR.clone() +} + +#[inline] +fn last_version() -> String { + env!("CARGO_PKG_VERSION").to_owned() +} + +#[inline(always)] +fn fn_true() -> bool { + true +} + impl Default for Settings { fn default() -> Self { Self { - store_dir: DATA_DIR.clone(), + store_dir: default_store_dir(), lang: Language::USen, last_version: env!("CARGO_PKG_VERSION").to_owned(), changelog: true, @@ -204,7 +245,7 @@ impl Settings { pub static SETTINGS: Lazy>> = Lazy::new(|| { let settings_path = Settings::path(); Arc::new(RwLock::new(if settings_path.exists() { - let text = fs::read_to_string(&settings_path).unwrap(); + let text = fs::read_to_string(&settings_path).expect("Couldn't read settings, that's bad"); serde_json::from_str(&text.cow_replace(": null", ": \"\"")) .expect("Failed to read settings file") } else { @@ -216,7 +257,7 @@ pub static SETTINGS: Lazy>> = Lazy::new(|| { pub static TMP_SETTINGS: Lazy>> = Lazy::new(|| { let settings_path = Settings::tmp_path(); Arc::new(RwLock::new(if settings_path.exists() { - let text = fs::read_to_string(&settings_path).unwrap(); + let text = fs::read_to_string(&settings_path).expect("Chouldn't read settings, that's bad"); serde_json::from_str(&text.cow_replace(": null", ": \"\"")) .expect("Failed to read temp settings file") } else { @@ -227,11 +268,13 @@ pub static TMP_SETTINGS: Lazy>> = Lazy::new(|| { pub static DATA_DIR: Lazy = Lazy::new(|| { if std::env::args().any(|f| &f == "--portable") { - std::env::current_dir().unwrap().join("bcml-data") + std::env::current_dir() + .expect("Big problems if no cwd") + .join("bcml-data") } else if cfg!(windows) { - dirs2::data_local_dir().unwrap() + dirs2::data_local_dir().expect("Big problems if no local data dir") } else { - dirs2::config_dir().unwrap() + dirs2::config_dir().expect("Big problems if no config dir") } .join("bcml") }); diff --git a/src/util.rs b/src/util.rs index 048e289d..1cc77cf5 100644 --- a/src/util.rs +++ b/src/util.rs @@ -72,10 +72,7 @@ pub fn get_game_file>(file: P) -> Result { if result.exists() { Ok(result) } else { - anyhow::bail!( - "Stock game file does not exist at {}", - result.to_str().unwrap() - ) + anyhow::bail!("Stock game file does not exist at {}", result.display()) } } } @@ -84,7 +81,7 @@ pub fn get_game_file>(file: P) -> Result { pub fn get_aoc_game_file>(file: P) -> Result { let result = settings().dlc_dir().map(|d| d.join(file.as_ref())); if result.as_ref().map(|d| d.exists()).unwrap_or_default() { - Ok(result.unwrap()) + Ok(unsafe { result.unwrap_unchecked() }) } else { anyhow::bail!("Stock DLC game file does not exist at {:?}", result) }