feat: Refactor code structure and enhance functionality across multiple crates
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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))),
|
||||
@@ -339,6 +331,22 @@ fn parse_synthetic_model_with_optional_res4_res5_res10() {
|
||||
assert_eq!(model.node_names, Some(vec![Some("Hull".to_string()), None]));
|
||||
}
|
||||
|
||||
#[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();
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user