diff options
Diffstat (limited to 'crates/msh-core')
| -rw-r--r-- | crates/msh-core/Cargo.toml | 5 | ||||
| -rw-r--r-- | crates/msh-core/src/lib.rs | 8 | ||||
| -rw-r--r-- | crates/msh-core/src/tests.rs | 53 |
3 files changed, 47 insertions, 19 deletions
diff --git a/crates/msh-core/Cargo.toml b/crates/msh-core/Cargo.toml index cdea317..86b0846 100644 --- a/crates/msh-core/Cargo.toml +++ b/crates/msh-core/Cargo.toml @@ -4,4 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] +encoding_rs = "0.8" nres = { path = "../nres" } + +[dev-dependencies] +common = { path = "../common" } +proptest = "1" diff --git a/crates/msh-core/src/lib.rs b/crates/msh-core/src/lib.rs index 1a50fb7..bc51357 100644 --- a/crates/msh-core/src/lib.rs +++ b/crates/msh-core/src/lib.rs @@ -1,6 +1,7 @@ pub mod error; use crate::error::Error; +use encoding_rs::WINDOWS_1251; use std::sync::Arc; pub type Result<T> = core::result::Result<T, Error>; @@ -347,13 +348,18 @@ fn parse_res10_names(data: &[u8], node_count: usize) -> Result<Vec<Option<String } else { slice }; - let decoded = String::from_utf8_lossy(text).to_string(); + let decoded = decode_cp1251(text); out.push(Some(decoded)); off = end; } Ok(out) } +fn decode_cp1251(bytes: &[u8]) -> String { + let (decoded, _, _) = WINDOWS_1251.decode(bytes); + decoded.into_owned() +} + struct RawResource { meta: nres::EntryMeta, bytes: Vec<u8>, diff --git a/crates/msh-core/src/tests.rs b/crates/msh-core/src/tests.rs index 07b05c7..90a7fdc 100644 --- a/crates/msh-core/src/tests.rs +++ b/crates/msh-core/src/tests.rs @@ -1,22 +1,10 @@ use super::*; +use common::collect_files_recursive; use nres::Archive; +use proptest::prelude::*; use std::fs; use std::path::{Path, PathBuf}; -fn collect_files_recursive(root: &Path, out: &mut Vec<PathBuf>) { - let Ok(entries) = fs::read_dir(root) else { - return; - }; - for entry in entries.flatten() { - let path = entry.path(); - if path.is_dir() { - collect_files_recursive(&path, out); - } else if path.is_file() { - out.push(path); - } - } -} - fn nres_test_files() -> Vec<PathBuf> { let root = Path::new(env!("CARGO_MANIFEST_DIR")) .join("..") @@ -169,18 +157,17 @@ fn res13_single_batch(index_start: u32, index_count: u16) -> Vec<u8> { batch } -fn res10_names(names: &[Option<&str>]) -> Vec<u8> { +fn res10_names_raw(names: &[Option<&[u8]>]) -> Vec<u8> { let mut out = Vec::new(); for name in names { match name { Some(name) => { - let bytes = name.as_bytes(); out.extend_from_slice( - &u32::try_from(bytes.len()) + &u32::try_from(name.len()) .expect("name size overflow in test") .to_le_bytes(), ); - out.extend_from_slice(bytes); + out.extend_from_slice(name); out.push(0); } None => out.extend_from_slice(&0u32.to_le_bytes()), @@ -189,6 +176,11 @@ fn res10_names(names: &[Option<&str>]) -> Vec<u8> { out } +fn res10_names(names: &[Option<&str>]) -> Vec<u8> { + let raw: Vec<Option<&[u8]>> = names.iter().map(|name| name.map(str::as_bytes)).collect(); + res10_names_raw(&raw) +} + fn base_synthetic_entries() -> Vec<SyntheticEntry> { vec![ synthetic_entry(RES1_NODE_TABLE, "Res1", 38, res1_stride38_nodes(1, Some(0))), @@ -340,6 +332,22 @@ fn parse_synthetic_model_with_optional_res4_res5_res10() { } #[test] +fn parse_res10_names_decodes_cp1251() { + let mut entries = base_synthetic_entries(); + entries[0] = synthetic_entry(RES1_NODE_TABLE, "Res1", 38, res1_stride38_nodes(1, Some(0))); + entries.push(synthetic_entry( + RES10_NAMES, + "Res10", + 1, + res10_names_raw(&[Some(&[0xC0])]), + )); + let payload = build_nested_nres(&entries); + + let model = parse_model_payload(&payload).expect("failed to parse model with cp1251 name"); + assert_eq!(model.node_names, Some(vec![Some("А".to_string())])); +} + +#[test] fn parse_fails_when_required_resource_missing() { let mut entries = base_synthetic_entries(); entries.retain(|entry| entry.kind != RES13_BATCHES); @@ -419,3 +427,12 @@ fn parse_fails_for_batch_index_range_out_of_bounds() { }) )); } + +proptest! { + #![proptest_config(ProptestConfig::with_cases(64))] + + #[test] + fn parse_model_payload_never_panics_on_random_bytes(data in proptest::collection::vec(any::<u8>(), 0..8192)) { + let _ = parse_model_payload(&data); + } +} |
