feat(cli): inspect mission transforms
This commit is contained in:
Generated
+1
@@ -400,6 +400,7 @@ dependencies = [
|
||||
"fparkan-assets",
|
||||
"fparkan-corpus",
|
||||
"fparkan-inspection",
|
||||
"fparkan-path",
|
||||
"fparkan-prototype",
|
||||
"fparkan-resource",
|
||||
"fparkan-runtime",
|
||||
|
||||
@@ -10,6 +10,7 @@ fparkan-assets = { path = "../../crates/fparkan-assets", version = "0.1.0" }
|
||||
fparkan-corpus = { path = "../../crates/fparkan-corpus", version = "0.1.0" }
|
||||
fparkan-prototype = { path = "../../crates/fparkan-prototype", version = "0.1.0" }
|
||||
fparkan-inspection = { path = "../../crates/fparkan-inspection", version = "0.1.0" }
|
||||
fparkan-path = { path = "../../crates/fparkan-path", version = "0.1.0" }
|
||||
fparkan-resource = { path = "../../crates/fparkan-resource", version = "0.1.0" }
|
||||
fparkan-runtime = { path = "../../crates/fparkan-runtime", version = "0.1.0" }
|
||||
fparkan-vfs = { path = "../../crates/fparkan-vfs", version = "0.1.0" }
|
||||
|
||||
@@ -21,16 +21,19 @@
|
||||
#![allow(clippy::print_stderr, clippy::print_stdout)]
|
||||
//! `FParkan` command-line tools.
|
||||
|
||||
use fparkan_assets::extend_graph_report_with_visual_dependencies;
|
||||
use fparkan_assets::{
|
||||
decode_mission_payload, extend_graph_report_with_visual_dependencies, TmaProfile,
|
||||
};
|
||||
use fparkan_corpus::{discover, render_report_json, report, DiscoverOptions};
|
||||
use fparkan_inspection::inspect_archive_file;
|
||||
use fparkan_inspection::ArchiveInspection;
|
||||
use fparkan_path::{normalize_relative, PathPolicy};
|
||||
use fparkan_prototype::build_prototype_graph_report;
|
||||
use fparkan_resource::{resource_name, CachedResourceRepository};
|
||||
use fparkan_runtime::{
|
||||
create, load_mission, EngineConfig, EngineMode, EngineServices, MissionRequest,
|
||||
};
|
||||
use fparkan_vfs::DirectoryVfs;
|
||||
use fparkan_vfs::{DirectoryVfs, Vfs};
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
@@ -38,6 +41,7 @@ use std::sync::Arc;
|
||||
const ARCHIVE_INSPECT_SCHEMA: &str = "fparkan-archive-inspect-v1";
|
||||
const PROTOTYPE_INSPECT_SCHEMA: &str = "fparkan-prototype-inspect-v1";
|
||||
const MISSION_GRAPH_SCHEMA: &str = "fparkan-mission-graph-v1";
|
||||
const MISSION_INSPECT_SCHEMA: &str = "fparkan-mission-inspect-v1";
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ArchiveInspectOutput<'a> {
|
||||
@@ -94,6 +98,22 @@ struct MissionGraphOutput {
|
||||
failures: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MissionInspectOutput {
|
||||
schema_version: &'static str,
|
||||
mission: String,
|
||||
objects: Vec<MissionObjectInspectOutput>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MissionObjectInspectOutput {
|
||||
index: usize,
|
||||
resource: String,
|
||||
position: [f32; 3],
|
||||
orientation_raw: [f32; 3],
|
||||
scale: [f32; 3],
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct GraphFailureOutput {
|
||||
root_index: usize,
|
||||
@@ -154,6 +174,10 @@ fn run(args: &[String]) -> Result<(), String> {
|
||||
let rest = strip_format_json(rest)?;
|
||||
graph_mission(&rest)
|
||||
}
|
||||
[domain, command, rest @ ..] if domain == "mission" && command == "inspect" => {
|
||||
let rest = strip_format_json(rest)?;
|
||||
inspect_mission(&rest)
|
||||
}
|
||||
_ => Err(usage()),
|
||||
}
|
||||
}
|
||||
@@ -310,6 +334,38 @@ fn graph_mission(args: &[String]) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inspect_mission(args: &[String]) -> Result<(), String> {
|
||||
let root = parse_root_alias(args)?;
|
||||
let mission = parse_required(args, &["--mission"], "--mission")?;
|
||||
let mission_path = normalize_relative(mission.as_bytes(), PathPolicy::StrictLegacy)
|
||||
.map_err(|err| err.to_string())?;
|
||||
let vfs = DirectoryVfs::new(root);
|
||||
let bytes = vfs.read(&mission_path).map_err(|err| err.to_string())?;
|
||||
let document =
|
||||
decode_mission_payload(bytes, TmaProfile::Strict).map_err(|err| err.to_string())?;
|
||||
let objects = document
|
||||
.objects
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, object)| MissionObjectInspectOutput {
|
||||
index,
|
||||
resource: String::from_utf8_lossy(&object.resource_name.raw).into_owned(),
|
||||
position: object.position,
|
||||
orientation_raw: object.orientation,
|
||||
scale: object.scale,
|
||||
})
|
||||
.collect();
|
||||
println!(
|
||||
"{}",
|
||||
serialize_json(&MissionInspectOutput {
|
||||
schema_version: MISSION_INSPECT_SCHEMA,
|
||||
mission,
|
||||
objects,
|
||||
})?
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inspect_archive(args: &[String]) -> Result<(), String> {
|
||||
let path = parse_archive_path(args)?;
|
||||
let inspection = inspect_archive_file(&path, 0).map_err(|err| err.clone())?;
|
||||
@@ -416,7 +472,7 @@ fn prototype_graph_requiredness_label(
|
||||
}
|
||||
|
||||
fn usage() -> String {
|
||||
"usage: fparkan corpus discover|validate --root <path> [--format json] | archive inspect <file> [--format json] | prototype inspect --root <path> --key <key> [--format json] | mission graph --root <path> --mission <path> [--format json]".to_string()
|
||||
"usage: fparkan corpus discover|validate --root <path> [--format json] | archive inspect <file> [--format json] | prototype inspect --root <path> --key <key> [--format json] | mission graph|inspect --root <path> --mission <path> [--format json]".to_string()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -507,4 +563,25 @@ mod tests {
|
||||
"{\"schema_version\":\"fparkan-mission-graph-v1\",\"mission\":\"MISSIONS/Autodemo.00/data.tma\",\"objects\":2,\"paths\":3,\"clans\":4,\"extras\":5,\"roots\":6,\"node_count\":7,\"edge_count\":8,\"direct_references\":9,\"unit_references\":10,\"unit_components\":11,\"prototype_requests\":12,\"wear_requests\":13,\"wear\":14,\"materials\":15,\"textures\":16,\"lightmaps\":17,\"is_success\":true,\"failures\":0}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mission_inspect_output_retains_raw_transform_fields() {
|
||||
let json = serialize_json(&MissionInspectOutput {
|
||||
schema_version: MISSION_INSPECT_SCHEMA,
|
||||
mission: "MISSIONS/test/data.tma".to_string(),
|
||||
objects: vec![MissionObjectInspectOutput {
|
||||
index: 1,
|
||||
resource: "unit.dat".to_string(),
|
||||
position: [1.0, 2.0, 3.0],
|
||||
orientation_raw: [4.0, 5.0, 6.0],
|
||||
scale: [7.0, 8.0, 9.0],
|
||||
}],
|
||||
})
|
||||
.expect("serialize mission inspection");
|
||||
|
||||
assert_eq!(
|
||||
json,
|
||||
"{\"schema_version\":\"fparkan-mission-inspect-v1\",\"mission\":\"MISSIONS/test/data.tma\",\"objects\":[{\"index\":1,\"resource\":\"unit.dat\",\"position\":[1.0,2.0,3.0],\"orientation_raw\":[4.0,5.0,6.0],\"scale\":[7.0,8.0,9.0]}]}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,6 +1017,18 @@ warnings/errors `0/0`. The readback FNV-1a remained
|
||||
does **not** prove the second root is visible or equivalent: it is recorded as
|
||||
a camera/frustum/overlap investigation target, not hidden as a parity result.
|
||||
|
||||
The reproducible `fparkan-cli mission inspect` probe closes two tempting but
|
||||
incorrect explanations for that equality. GOG `Autodemo.00` root 0 is
|
||||
`w_m_wlk2.dat` at `(418.10318, 717.433, 3.0409389)` while root 1 is
|
||||
`w_s_wlk1.dat` at `(479.12396, 795.95337, 1.6228507)`; therefore the TMA data
|
||||
is neither duplicate nor co-located. Separately, the Vulkan static submit loop
|
||||
calls `cmd_draw_indexed` for every prepared draw range and its depth comparison
|
||||
is `LESS_OR_EQUAL`. The unresolved equality is consequently evidence about the
|
||||
diagnostic camera/projection or visibility framing, not grounds to patch a
|
||||
missing draw or a `LESS` depth bug. The inspector intentionally reports raw
|
||||
orientation as well as position and scale so later camera/transform work can
|
||||
be checked against the original mission bytes.
|
||||
|
||||
### Camera ownership boundary from the GOG renderer
|
||||
|
||||
The GOG `World3D.dll` export `LoadCamera` at RVA `0x1FB06` is only an import
|
||||
|
||||
Reference in New Issue
Block a user