diff --git a/src/form_id.rs b/src/form_id.rs index 8e12a91..582cf5f 100644 --- a/src/form_id.rs +++ b/src/form_id.rs @@ -19,7 +19,6 @@ use std::cmp::Ordering; use std::hash::{Hash, Hasher}; -use crate::game_id::GameId; use crate::u32_to_usize; #[derive(Clone, Debug)] @@ -27,22 +26,15 @@ pub struct HashedFormId { // Precalculate and store whether this FormID is for an overridden record // for efficiency, as alignment padding means it wastes no space. overridden_record: bool, - game_id: GameId, object_index: u32, hashed_plugin_name: u64, } impl HashedFormId { - pub fn new( - game_id: GameId, - hashed_parent_plugin_name: u64, - hashed_masters: &[u64], - raw_form_id: u32, - ) -> Self { + pub fn new(hashed_parent_plugin_name: u64, hashed_masters: &[u64], raw_form_id: u32) -> Self { let mod_index = u32_to_usize(raw_form_id >> 24); Self { overridden_record: mod_index < hashed_masters.len(), - game_id, object_index: raw_form_id & 0xFF_FFFF, hashed_plugin_name: hashed_masters .get(mod_index) @@ -51,13 +43,8 @@ impl HashedFormId { } } - pub fn is_valid_in_light_plugin(&self) -> bool { - match self.game_id { - GameId::SkyrimSE => self.object_index >= 0x800 && self.object_index <= 0xFFF, - GameId::Fallout4 => self.object_index >= 0x001 && self.object_index <= 0xFFF, - GameId::Starfield => self.object_index <= 0xFFF, - _ => false, - } + pub fn object_index(&self) -> u32 { + self.object_index } pub fn is_overridden_record(&self) -> bool { @@ -115,218 +102,87 @@ mod tests { #[test] fn is_overridden_record_should_be_true_if_mod_index_is_less_than_masters_length() { - let form_id = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01); + let form_id = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01); assert!(form_id.is_overridden_record()); - let form_id = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01000001); + let form_id = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01000001); assert!(form_id.is_overridden_record()); } #[test] fn is_overridden_record_should_be_false_if_mod_index_is_not_less_than_masters_length() { - let form_id = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x02000001); + let form_id = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x02000001); assert!(!form_id.is_overridden_record()); - let form_id = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x03000001); + let form_id = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x03000001); assert!(!form_id.is_overridden_record()); } - #[allow(non_snake_case)] - #[test] - fn is_valid_in_light_plugin_should_be_false_if_game_is_morrowind() { - // Check every possible object ID, it's only takes a second or so. - for index in 0..=0xFF_FFFF { - let form_id = HashedFormId::new(GameId::Morrowind, PARENT_PLUGIN_NAME, MASTERS, index); - - assert!(!form_id.is_valid_in_light_plugin()); - } - } - - #[allow(non_snake_case)] - #[test] - fn is_valid_in_light_plugin_should_be_false_if_game_is_oblivion() { - // Check every possible object ID, it's only takes a second or so. - for index in 0..=0xFF_FFFF { - let form_id = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, index); - - assert!(!form_id.is_valid_in_light_plugin()); - } - } - - #[allow(non_snake_case)] - #[test] - fn is_valid_in_light_plugin_should_be_false_if_game_is_skyrim() { - // Check every possible object ID, it's only takes a second or so. - for index in 0..=0xFF_FFFF { - let form_id = HashedFormId::new(GameId::Skyrim, PARENT_PLUGIN_NAME, MASTERS, index); - - assert!(!form_id.is_valid_in_light_plugin()); - } - } - - #[allow(non_snake_case)] - #[test] - fn is_valid_in_light_plugin_should_be_false_if_game_is_fallout_3() { - // Check every possible object ID, it's only takes a second or so. - for index in 0..=0xFF_FFFF { - let form_id = HashedFormId::new(GameId::Fallout3, PARENT_PLUGIN_NAME, MASTERS, index); - - assert!(!form_id.is_valid_in_light_plugin()); - } - } - - #[allow(non_snake_case)] - #[test] - fn is_valid_in_light_plugin_should_be_false_if_game_is_fallout_nv() { - // Check every possible object ID, it's only takes a second or so. - for index in 0..=0xFF_FFFF { - let form_id = HashedFormId::new(GameId::FalloutNV, PARENT_PLUGIN_NAME, MASTERS, index); - - assert!(!form_id.is_valid_in_light_plugin()); - } - } - - #[allow(non_snake_case)] - #[test] - fn is_valid_in_light_plugin_should_be_true_if_game_is_skyrim_se_and_object_index_is_between_0x800_and_0xFFF_inclusive( - ) { - for index in 0x800..=0xFFF { - let form_id = HashedFormId::new(GameId::SkyrimSE, PARENT_PLUGIN_NAME, MASTERS, index); - - assert!(form_id.is_valid_in_light_plugin()); - } - } - - #[allow(non_snake_case)] - #[test] - fn is_valid_in_light_plugin_should_be_true_if_game_is_fallout_4_and_object_index_is_between_0x001_and_0xFFF_inclusive( - ) { - for index in 0x001..=0xFFF { - let form_id = HashedFormId::new(GameId::Fallout4, PARENT_PLUGIN_NAME, MASTERS, index); - - assert!(form_id.is_valid_in_light_plugin()); - } - } - - #[allow(non_snake_case)] - #[test] - fn is_valid_in_light_plugin_should_be_false_if_game_is_starfield_and_object_index_is_less_than_0xFFF( - ) { - for index in 0..=0xFFF { - let form_id = HashedFormId::new(GameId::Starfield, PARENT_PLUGIN_NAME, MASTERS, index); - - assert!(form_id.is_valid_in_light_plugin()); - } - } - - #[allow(non_snake_case)] - #[test] - fn is_valid_in_light_plugin_should_be_false_if_game_is_skyrim_se_and_object_index_is_outwith_0x800_and_0xFFF_inclusive( - ) { - for index in 0..0x800 { - let form_id = HashedFormId::new(GameId::SkyrimSE, PARENT_PLUGIN_NAME, MASTERS, index); - - assert!(!form_id.is_valid_in_light_plugin()); - } - - for index in 0x1000..=0xFF_FFFF { - let form_id = HashedFormId::new(GameId::SkyrimSE, PARENT_PLUGIN_NAME, MASTERS, index); - - assert!(!form_id.is_valid_in_light_plugin()); - } - } - - #[allow(non_snake_case)] - #[test] - fn is_valid_in_light_plugin_should_be_false_if_game_is_fallout_4_and_object_index_is_outwith_0x001_and_0xFFF_inclusive( - ) { - let form_id = HashedFormId::new(GameId::Fallout4, PARENT_PLUGIN_NAME, MASTERS, 0); - - assert!(!form_id.is_valid_in_light_plugin()); - - for index in 0x1000..=0xFF_FFFF { - let form_id = HashedFormId::new(GameId::Fallout4, PARENT_PLUGIN_NAME, MASTERS, index); - - assert!(!form_id.is_valid_in_light_plugin()); - } - } - - #[allow(non_snake_case)] - #[test] - fn is_valid_in_light_plugin_should_be_false_if_game_is_starfield_and_object_index_is_greater_than_0xFFF( - ) { - for index in 0x1000..=0xFF_FFFF { - let form_id = HashedFormId::new(GameId::Starfield, PARENT_PLUGIN_NAME, MASTERS, index); - - assert!(!form_id.is_valid_in_light_plugin()); - } - } - #[test] fn object_index_should_equal_last_three_bytes_of_raw_form_id_value() { - let form_id = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01); + let form_id = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01); assert_eq!(0x01, form_id.object_index); - let form_id = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01000001); + let form_id = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01000001); assert_eq!(0x01, form_id.object_index); } #[test] fn should_store_master_at_mod_index_as_plugin_name() { - let form_id = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01); + let form_id = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01); assert_eq!(MASTERS[0], form_id.hashed_plugin_name); - let form_id = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01000001); + let form_id = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01000001); assert_eq!(MASTERS[1], form_id.hashed_plugin_name); } #[test] fn should_store_parent_plugin_name_for_mod_index_greater_than_last_index_of_masters() { - let form_id = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, NO_MASTERS, 0x01); + let form_id = HashedFormId::new(PARENT_PLUGIN_NAME, NO_MASTERS, 0x01); assert_eq!(PARENT_PLUGIN_NAME, form_id.hashed_plugin_name); - let form_id = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x05000001); + let form_id = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x05000001); assert_eq!(PARENT_PLUGIN_NAME, form_id.hashed_plugin_name); } #[test] fn form_ids_should_not_be_equal_if_plugin_names_are_unequal() { - let form_id1 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01); - let form_id2 = HashedFormId::new(GameId::Oblivion, OTHER_PLUGIN_NAME, MASTERS, 0x05000001); + let form_id1 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01); + let form_id2 = HashedFormId::new(OTHER_PLUGIN_NAME, MASTERS, 0x05000001); assert_ne!(form_id1, form_id2); } #[test] fn form_ids_should_not_be_equal_if_object_indices_are_unequal() { - let form_id1 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01); - let form_id2 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x02); + let form_id1 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01); + let form_id2 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x02); assert_ne!(form_id1, form_id2); } #[test] fn form_ids_with_equal_plugin_names_and_object_ids_should_be_equal() { - let form_id1 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, NO_MASTERS, 0x01); - let form_id2 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x05000001); + let form_id1 = HashedFormId::new(PARENT_PLUGIN_NAME, NO_MASTERS, 0x01); + let form_id2 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x05000001); assert_eq!(form_id1, form_id2); } #[test] fn form_ids_can_be_equal_if_one_is_an_override_record_and_the_other_is_not() { - let form_id1 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01); - let form_id2 = HashedFormId::new(GameId::Oblivion, MASTERS[0], NO_MASTERS, 0x05000001); + let form_id1 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01); + let form_id2 = HashedFormId::new(MASTERS[0], NO_MASTERS, 0x05000001); assert_ne!(form_id1.overridden_record, form_id2.overridden_record); assert_eq!(form_id1, form_id2); @@ -334,14 +190,14 @@ mod tests { #[test] fn form_ids_should_be_ordered_according_to_object_index_then_hashed_plugin_names() { - let form_id1 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01); - let form_id2 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x02); + let form_id1 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01); + let form_id2 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x02); assert_eq!(Ordering::Less, form_id1.cmp(&form_id2)); assert_eq!(Ordering::Greater, form_id2.cmp(&form_id1)); - let form_id1 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x05000001); - let form_id2 = HashedFormId::new(GameId::Oblivion, OTHER_PLUGIN_NAME, MASTERS, 0x05000001); + let form_id1 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x05000001); + let form_id2 = HashedFormId::new(OTHER_PLUGIN_NAME, MASTERS, 0x05000001); assert_eq!(Ordering::Less, form_id1.cmp(&form_id2)); assert_eq!(Ordering::Greater, form_id2.cmp(&form_id1)); @@ -349,8 +205,8 @@ mod tests { #[test] fn form_ids_should_not_be_ordered_according_to_override_record_flag_value() { - let form_id1 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01); - let form_id2 = HashedFormId::new(GameId::Oblivion, MASTERS[0], NO_MASTERS, 0x05000001); + let form_id1 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01); + let form_id2 = HashedFormId::new(MASTERS[0], NO_MASTERS, 0x05000001); assert_ne!(form_id1.overridden_record, form_id2.overridden_record); assert_eq!(Ordering::Equal, form_id2.cmp(&form_id1)); @@ -358,32 +214,32 @@ mod tests { #[test] fn form_id_hashes_should_not_be_equal_if_plugin_names_are_unequal() { - let form_id1 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01); - let form_id2 = HashedFormId::new(GameId::Oblivion, OTHER_PLUGIN_NAME, MASTERS, 0x05000001); + let form_id1 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01); + let form_id2 = HashedFormId::new(OTHER_PLUGIN_NAME, MASTERS, 0x05000001); assert_ne!(hash(&form_id1), hash(&form_id2)); } #[test] fn form_id_hashes_should_not_be_equal_if_object_indices_are_unequal() { - let form_id1 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01); - let form_id2 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x02); + let form_id1 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01); + let form_id2 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x02); assert_ne!(hash(&form_id1), hash(&form_id2)); } #[test] fn form_id_hashes_with_equal_plugin_names_and_object_ids_should_be_equal() { - let form_id1 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, NO_MASTERS, 0x01); - let form_id2 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x05000001); + let form_id1 = HashedFormId::new(PARENT_PLUGIN_NAME, NO_MASTERS, 0x01); + let form_id2 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x05000001); assert_eq!(hash(&form_id1), hash(&form_id2)); } #[test] fn form_id_hashes_can_be_equal_with_unequal_override_record_flag_values() { - let form_id1 = HashedFormId::new(GameId::Oblivion, PARENT_PLUGIN_NAME, MASTERS, 0x01); - let form_id2 = HashedFormId::new(GameId::Oblivion, MASTERS[0], NO_MASTERS, 0x05000001); + let form_id1 = HashedFormId::new(PARENT_PLUGIN_NAME, MASTERS, 0x01); + let form_id2 = HashedFormId::new(MASTERS[0], NO_MASTERS, 0x05000001); assert_ne!(form_id1.overridden_record, form_id2.overridden_record); assert_eq!(hash(&form_id1), hash(&form_id2)); diff --git a/src/plugin.rs b/src/plugin.rs index 285052e..e7b2d20 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -19,6 +19,7 @@ use std::fs::File; use std::io::{BufRead, BufReader, Seek}; +use std::ops::RangeInclusive; use std::path::{Path, PathBuf}; use std::str; @@ -343,10 +344,14 @@ impl Plugin { if self.game_id.supports_light_plugins() { match &self.data.record_ids { RecordIds::None => true, - RecordIds::FormIds(form_ids) => form_ids - .iter() - .filter(|f| !f.is_overridden_record()) - .all(HashedFormId::is_valid_in_light_plugin), + RecordIds::FormIds(form_ids) => { + let valid_range = self.valid_light_form_id_range(); + + form_ids + .iter() + .filter(|f| !f.is_overridden_record()) + .all(|f| valid_range.contains(&f.object_index())) + } RecordIds::NamespacedIds(_) => false, // this should never happen. } } else { @@ -399,6 +404,23 @@ impl Plugin { _ => false, } } + + fn valid_light_form_id_range(&self) -> RangeInclusive { + match self.game_id { + GameId::SkyrimSE => match self.header_version() { + Some(v) if v < 1.71 => 0x800..=0xFFF, + Some(_) => 0..=0xFFF, + None => 0..=0, + }, + GameId::Fallout4 => match self.header_version() { + Some(v) if v < 1.0 => 0x800..=0xFFF, + Some(_) => 0x001..=0xFFF, + None => 0..=0, + }, + GameId::Starfield => 0..=0xFFF, + _ => 0..=0, + } + } } fn sorted_slices_intersect(left: &[T], right: &[T]) -> bool { @@ -418,18 +440,13 @@ fn sorted_slices_intersect(left: &[T], right: &[T]) -> bool { false } -fn hashed_form_ids( - form_ids: &[u32], - game_id: GameId, - filename: &str, - masters: &[String], -) -> Vec { +fn hashed_form_ids(form_ids: &[u32], filename: &str, masters: &[String]) -> Vec { let hashed_filename = hash(filename); let hashed_masters: Vec<_> = masters.iter().map(|m| hash(m)).collect(); let mut form_ids: Vec<_> = form_ids .iter() - .map(|form_id| HashedFormId::new(game_id, hashed_filename, &hashed_masters, *form_id)) + .map(|form_id| HashedFormId::new(hashed_filename, &hashed_masters, *form_id)) .collect(); form_ids.sort(); @@ -535,7 +552,7 @@ fn parse_record_ids<'a>( })?; let (remaining_input, form_ids) = parse_form_ids(input, game_id)?; - let form_ids = hashed_form_ids(&form_ids, game_id, filename, &masters).into(); + let form_ids = hashed_form_ids(&form_ids, filename, &masters).into(); Ok((remaining_input, form_ids)) } @@ -553,7 +570,7 @@ fn read_record_ids( let masters = masters(header_record)?; let form_ids = read_form_ids(reader, game_id)?; - let form_ids = hashed_form_ids(&form_ids, game_id, filename, &masters).into(); + let form_ids = hashed_form_ids(&form_ids, filename, &masters).into(); Ok(form_ids) } @@ -897,6 +914,19 @@ mod tests { assert_eq!(0, plugin1.overlap_size(&[&plugin2])); } + #[test] + fn valid_light_form_id_range_should_be_empty() { + let mut plugin = Plugin::new( + GameId::Morrowind, + Path::new("testing-plugins/Morrowind/Data Files/Blank - Master Dependent.esm"), + ); + assert!(plugin.parse_file(false).is_ok()); + + let range = plugin.valid_light_form_id_range(); + assert_eq!(&0, range.start()); + assert_eq!(&0, range.end()); + } + #[test] fn is_valid_as_light_plugin_should_always_be_false() { let mut plugin = Plugin::new( @@ -933,6 +963,19 @@ mod tests { assert_eq!(0.8, plugin.header_version().unwrap()); } + #[test] + fn valid_light_form_id_range_should_be_empty() { + let mut plugin = Plugin::new( + GameId::Oblivion, + Path::new("testing-plugins/Oblivion/Data/Blank - Master Dependent.esm"), + ); + assert!(plugin.parse_file(false).is_ok()); + + let range = plugin.valid_light_form_id_range(); + assert_eq!(&0, range.start()); + assert_eq!(&0, range.end()); + } + #[test] fn is_valid_as_light_plugin_should_always_be_false() { let mut plugin = Plugin::new( @@ -1175,6 +1218,19 @@ mod tests { assert_eq!(0, plugin1.overlap_size(&[&plugin2])); } + #[test] + fn valid_light_form_id_range_should_be_empty() { + let mut plugin = Plugin::new( + GameId::Skyrim, + Path::new("testing-plugins/Skyrim/Data/Blank - Master Dependent.esm"), + ); + assert!(plugin.parse_file(false).is_ok()); + + let range = plugin.valid_light_form_id_range(); + assert_eq!(&0, range.start()); + assert_eq!(&0, range.end()); + } + #[test] fn is_valid_as_light_plugin_should_always_be_false() { let mut plugin = Plugin::new( @@ -1289,38 +1345,56 @@ mod tests { } #[test] - fn is_valid_as_light_plugin_should_be_true_if_the_plugin_has_no_form_ids_outside_the_valid_range( - ) { + fn valid_light_form_id_range_should_be_0x800_to_0xfff_if_hedr_version_is_less_than_1_71() { let mut plugin = Plugin::new( GameId::SkyrimSE, Path::new("testing-plugins/SkyrimSE/Data/Blank - Master Dependent.esm"), ); assert!(plugin.parse_file(false).is_ok()); - assert!(plugin.is_valid_as_light_plugin()); + let range = plugin.valid_light_form_id_range(); + assert_eq!(&0x800, range.start()); + assert_eq!(&0xFFF, range.end()); } #[test] - fn is_valid_as_light_plugin_should_be_true_if_the_plugin_has_an_override_form_id_outside_the_valid_range( - ) { + fn valid_light_form_id_range_should_be_0_to_0xfff_if_hedr_version_is_1_71_or_greater() { let mut plugin = Plugin::new( GameId::SkyrimSE, Path::new("testing-plugins/SkyrimSE/Data/Blank - Master Dependent.esm"), ); let mut bytes = read(plugin.path()).unwrap(); - assert_eq!(0xF0, bytes[0x7A]); - assert_eq!(0x0C, bytes[0x7B]); - bytes[0x7A] = 0xFF; - bytes[0x7B] = 0x07; + assert_eq!(0xD7, bytes[0x1E]); + assert_eq!(0xA3, bytes[0x1F]); + assert_eq!(0x70, bytes[0x20]); + assert_eq!(0x3F, bytes[0x21]); + bytes[0x1E] = 0x48; + bytes[0x1F] = 0xE1; + bytes[0x20] = 0xDA; + bytes[0x21] = 0x3F; assert!(plugin.parse(&bytes, false).is_ok()); + let range = plugin.valid_light_form_id_range(); + assert_eq!(&0, range.start()); + assert_eq!(&0xFFF, range.end()); + } + + #[test] + fn is_valid_as_light_plugin_should_be_true_if_the_plugin_has_no_form_ids_outside_the_valid_range( + ) { + let mut plugin = Plugin::new( + GameId::SkyrimSE, + Path::new("testing-plugins/SkyrimSE/Data/Blank - Master Dependent.esm"), + ); + assert!(plugin.parse_file(false).is_ok()); + assert!(plugin.is_valid_as_light_plugin()); } #[test] - fn is_valid_as_light_plugin_should_be_false_if_the_plugin_has_a_new_form_id_less_than_0x800( + fn is_valid_as_light_plugin_should_be_true_if_the_plugin_has_an_override_form_id_outside_the_valid_range( ) { let mut plugin = Plugin::new( GameId::SkyrimSE, @@ -1328,14 +1402,14 @@ mod tests { ); let mut bytes = read(plugin.path()).unwrap(); - assert_eq!(0xEB, bytes[0x386]); - assert_eq!(0x0C, bytes[0x387]); - bytes[0x386] = 0xFF; - bytes[0x387] = 0x07; + assert_eq!(0xF0, bytes[0x7A]); + assert_eq!(0x0C, bytes[0x7B]); + bytes[0x7A] = 0xFF; + bytes[0x7B] = 0x07; assert!(plugin.parse(&bytes, false).is_ok()); - assert!(!plugin.is_valid_as_light_plugin()); + assert!(plugin.is_valid_as_light_plugin()); } #[test] @@ -1371,6 +1445,19 @@ mod tests { assert!(!plugin.is_light_plugin()); } + #[test] + fn valid_light_form_id_range_should_be_empty() { + let mut plugin = Plugin::new( + GameId::Fallout3, + Path::new("testing-plugins/Skyrim/Data/Blank - Master Dependent.esm"), + ); + assert!(plugin.parse_file(false).is_ok()); + + let range = plugin.valid_light_form_id_range(); + assert_eq!(&0, range.start()); + assert_eq!(&0, range.end()); + } + #[test] fn is_valid_as_light_plugin_should_always_be_false() { let mut plugin = Plugin::new( @@ -1395,6 +1482,19 @@ mod tests { assert!(!plugin.is_light_plugin()); } + #[test] + fn valid_light_form_id_range_should_be_empty() { + let mut plugin = Plugin::new( + GameId::Fallout3, + Path::new("testing-plugins/Skyrim/Data/Blank - Master Dependent.esm"), + ); + assert!(plugin.parse_file(false).is_ok()); + + let range = plugin.valid_light_form_id_range(); + assert_eq!(&0, range.start()); + assert_eq!(&0, range.end()); + } + #[test] fn is_valid_as_light_plugin_should_always_be_false() { let mut plugin = Plugin::new( @@ -1409,6 +1509,8 @@ mod tests { mod fallout4 { use super::super::*; + use std::fs::read; + #[test] fn is_master_file_should_use_file_extension_and_flag() { use std::fs::copy; @@ -1458,6 +1560,43 @@ mod tests { assert!(plugin.is_light_plugin()); } + #[test] + fn valid_light_form_id_range_should_be_1_to_0xfff_if_hedr_version_is_less_than_1() { + let mut plugin = Plugin::new( + GameId::Fallout4, + Path::new("testing-plugins/SkyrimSE/Data/Blank - Master Dependent.esm"), + ); + assert!(plugin.parse_file(false).is_ok()); + + let range = plugin.valid_light_form_id_range(); + assert_eq!(&0x800, range.start()); + assert_eq!(&0xFFF, range.end()); + } + + #[test] + fn valid_light_form_id_range_should_be_1_to_0xfff_if_hedr_version_is_1_or_greater() { + let mut plugin = Plugin::new( + GameId::Fallout4, + Path::new("testing-plugins/SkyrimSE/Data/Blank - Master Dependent.esm"), + ); + let mut bytes = read(plugin.path()).unwrap(); + + assert_eq!(0xD7, bytes[0x1E]); + assert_eq!(0xA3, bytes[0x1F]); + assert_eq!(0x70, bytes[0x20]); + assert_eq!(0x3F, bytes[0x21]); + bytes[0x1E] = 0; + bytes[0x1F] = 0; + bytes[0x20] = 0x80; + bytes[0x21] = 0x3F; + + assert!(plugin.parse(&bytes, false).is_ok()); + + let range = plugin.valid_light_form_id_range(); + assert_eq!(&1, range.start()); + assert_eq!(&0xFFF, range.end()); + } + #[test] fn is_valid_as_light_plugin_should_be_true_if_the_plugin_has_no_form_ids_outside_the_valid_range( ) { @@ -1599,6 +1738,19 @@ mod tests { assert!(!plugin.is_override_plugin()); } + #[test] + fn valid_light_form_id_range_should_be_0_to_0xfff() { + let mut plugin = Plugin::new( + GameId::Starfield, + Path::new("testing-plugins/SkyrimSE/Data/Blank - Master Dependent.esm"), + ); + assert!(plugin.parse_file(false).is_ok()); + + let range = plugin.valid_light_form_id_range(); + assert_eq!(&0, range.start()); + assert_eq!(&0xFFF, range.end()); + } + #[test] fn is_valid_as_light_plugin_should_be_true_if_the_plugin_has_no_form_ids_outside_the_valid_range( ) { @@ -1820,11 +1972,10 @@ mod tests { let raw_form_ids = vec![0x0000_0001, 0x0100_0002]; let masters = vec!["tést.esm".to_string()]; - let form_ids = hashed_form_ids(&raw_form_ids, GameId::Oblivion, "Blàñk.esp", &masters); + let form_ids = hashed_form_ids(&raw_form_ids, "Blàñk.esp", &masters); let other_masters = vec!["TÉST.ESM".to_string()]; - let other_form_ids = - hashed_form_ids(&raw_form_ids, GameId::Oblivion, "BLÀÑK.ESP", &other_masters); + let other_form_ids = hashed_form_ids(&raw_form_ids, "BLÀÑK.ESP", &other_masters); assert_eq!(form_ids, other_form_ids); }