fix: close stage 0-2 synthetic gates

This commit is contained in:
2026-06-23 22:32:50 +04:00
parent f8e447ffee
commit 9cc24e715d
38 changed files with 4038 additions and 1737 deletions
+58 -17
View File
@@ -1,21 +1,40 @@
#![forbid(unsafe_code)]
#![cfg_attr(
test,
allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_precision_loss,
clippy::expect_used,
clippy::float_cmp,
clippy::identity_op,
clippy::too_many_lines,
clippy::uninlined_format_args,
clippy::map_unwrap_or,
clippy::needless_raw_string_hashes,
clippy::semicolon_if_nothing_returned,
clippy::type_complexity,
clippy::panic,
clippy::unwrap_used
)
)]
//! Shared inspection helpers for format-backed tooling.
use fparkan_msh::{decode_msh, validate_msh};
use fparkan_nres::{decode as decode_nres, NresDocument, ReadProfile};
use fparkan_resource::{archive_path, resource_name, CachedResourceRepository};
use fparkan_resource::{archive_path, resource_name, CachedResourceRepository, ResourceRepository};
use fparkan_rsli::decode as decode_rsli;
use fparkan_terrain_format::{decode_land_map, decode_land_msh};
use fparkan_texm::decode_texm;
use fparkan_vfs::{DirectoryVfs, Vfs};
use fparkan_vfs::DirectoryVfs;
use std::fs;
use std::path::{Path, PathBuf};
use std::path::Path;
use std::sync::Arc;
/// Archive inspection variants.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ArchiveInspection {
/// NRes inspection summary.
/// `NRes` inspection summary.
Nres {
/// Archive entry count.
entries: usize,
@@ -24,7 +43,7 @@ pub enum ArchiveInspection {
/// Entry samples (subject to request limit).
sample: Vec<NresEntrySummary>,
},
/// RsLi inspection summary.
/// `RsLi` inspection summary.
Rsli {
/// Archive entry count.
entries: usize,
@@ -33,7 +52,7 @@ pub enum ArchiveInspection {
Unsupported,
}
/// NRes entry summary.
/// `NRes` entry summary.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NresEntrySummary {
/// ASCII/legacy resource name.
@@ -107,6 +126,10 @@ pub enum LandFileKind {
}
/// Inspects a format archive.
///
/// # Errors
///
/// Returns a string error when the archive cannot be read or decoded.
pub fn inspect_archive_file(path: &Path, sample_limit: usize) -> Result<ArchiveInspection, String> {
let bytes = fs::read(path).map_err(|err| format!("{}: {err}", path.display()))?;
inspect_archive_bytes(&bytes, sample_limit, Some(path))
@@ -138,8 +161,11 @@ fn inspect_archive_bytes(
sample,
})
} else if bytes.get(0..4) == Some(b"NL\0\x01") {
let document = decode_rsli(Arc::from(bytes.to_vec().into_boxed_slice()), fparkan_rsli::ReadProfile::Compatible)
.map_err(|err| err.to_string())?;
let document = decode_rsli(
Arc::from(bytes.to_vec().into_boxed_slice()),
fparkan_rsli::ReadProfile::Compatible,
)
.map_err(|err| err.to_string())?;
Ok(ArchiveInspection::Rsli {
entries: document.entries().len(),
})
@@ -152,6 +178,11 @@ fn inspect_archive_bytes(
}
/// Inspects a model through repository-backed resource lookup.
///
/// # Errors
///
/// Returns a string error when the resource cannot be resolved or parsed as a
/// valid model payload.
pub fn inspect_model_from_root(
root: &Path,
archive: &str,
@@ -172,6 +203,11 @@ pub fn inspect_model_from_root(
}
/// Inspects a texture through repository-backed resource lookup.
///
/// # Errors
///
/// Returns a string error when the resource cannot be resolved or parsed as a
/// valid texture payload.
pub fn inspect_texture_from_root(
root: &Path,
archive: &str,
@@ -189,13 +225,15 @@ pub fn inspect_texture_from_root(
}
/// Inspects a terrain land file by path.
///
/// # Errors
///
/// Returns a string error when the file cannot be read or parsed as the
/// requested terrain payload kind.
pub fn inspect_land_file(path: &Path, kind: LandFileKind) -> Result<MapInspection, String> {
let bytes = fs::read(path).map_err(|err| format!("{}: {err}", path.display()))?;
let document = decode_nres(
Arc::from(bytes.into_boxed_slice()),
ReadProfile::Compatible,
)
.map_err(|err| err.to_string())?;
let document = decode_nres(Arc::from(bytes.into_boxed_slice()), ReadProfile::Compatible)
.map_err(|err| err.to_string())?;
match kind {
LandFileKind::LandMsh => inspect_land_msh(&document),
LandFileKind::LandMap => inspect_land_map(&document),
@@ -254,17 +292,18 @@ fn read_resource_bytes(root: &Path, archive: &str, name: &str) -> Result<Arc<[u8
mod tests {
use super::*;
use std::io::Write as _;
use std::path::PathBuf;
#[test]
fn inspect_rsli_counts_entries() {
fn inspect_rsli_rejects_malformed_archive() {
let dir = temp_dir("inspect");
let path = dir.join("test.rsli");
let mut file = fs::File::create(&path).expect("file");
file.write_all(b"NL\0\x01").expect("magic");
drop(file);
let inspection = inspect_archive_file(&path, 0).expect("inspect");
assert!(matches!(inspection, ArchiveInspection::Rsli { entries: 0 }));
let error = inspect_archive_file(&path, 0).expect_err("malformed archive");
assert!(error.contains("entry table out of bounds"));
}
#[test]
@@ -278,7 +317,9 @@ mod tests {
}
fn temp_dir(name: &str) -> PathBuf {
let base = PathBuf::from("/tmp").join("fparkan-inspection-tests").join(name);
let base = PathBuf::from("/tmp")
.join("fparkan-inspection-tests")
.join(name);
let _ = fs::remove_dir_all(&base);
fs::create_dir_all(&base).expect("tmp dir");
base