feat(cli): expose unit component provenance
This commit is contained in:
@@ -37,11 +37,12 @@ use fparkan_runtime::{
|
||||
};
|
||||
use fparkan_vfs::{DirectoryVfs, Vfs};
|
||||
use serde::Serialize;
|
||||
use std::fmt::Write as _;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
const ARCHIVE_INSPECT_SCHEMA: &str = "fparkan-archive-inspect-v1";
|
||||
const PROTOTYPE_INSPECT_SCHEMA: &str = "fparkan-prototype-inspect-v1";
|
||||
const PROTOTYPE_INSPECT_SCHEMA: &str = "fparkan-prototype-inspect-v2";
|
||||
const MISSION_GRAPH_SCHEMA: &str = "fparkan-mission-graph-v1";
|
||||
const MISSION_INSPECT_SCHEMA: &str = "fparkan-mission-inspect-v1";
|
||||
const TERRAIN_INSPECT_SCHEMA: &str = "fparkan-terrain-inspect-v1";
|
||||
@@ -65,6 +66,8 @@ struct PrototypeInspectOutput {
|
||||
roots: usize,
|
||||
node_count: usize,
|
||||
edge_count: usize,
|
||||
unit_component_records: Vec<UnitComponentInspectOutput>,
|
||||
edges: Vec<PrototypeGraphEdgeInspectOutput>,
|
||||
prototype_requests: usize,
|
||||
resolved: usize,
|
||||
unit_references: usize,
|
||||
@@ -196,6 +199,33 @@ struct GraphFailureOutput {
|
||||
resource: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct UnitComponentInspectOutput {
|
||||
root_index: usize,
|
||||
component_index: usize,
|
||||
archive_raw_hex: String,
|
||||
resource_raw_hex: String,
|
||||
kind: u32,
|
||||
parent_or_link: i32,
|
||||
description_raw_hex: String,
|
||||
tail0: u32,
|
||||
tail1: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct PrototypeGraphEdgeInspectOutput {
|
||||
id: u32,
|
||||
from: u32,
|
||||
to: u32,
|
||||
kind: &'static str,
|
||||
requiredness: &'static str,
|
||||
root_index: Option<usize>,
|
||||
parent_edge: Option<u32>,
|
||||
unit_component_index: Option<usize>,
|
||||
archive: Option<String>,
|
||||
resource_raw_hex: Option<String>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||
let result = run(&args);
|
||||
@@ -350,6 +380,58 @@ fn prototype_inspect_json(
|
||||
roots: report.root_count,
|
||||
node_count: graph.nodes.len(),
|
||||
edge_count: graph.edges.len(),
|
||||
unit_component_records: graph
|
||||
.root_unit_components
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(root_index, records)| {
|
||||
records
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(
|
||||
move |(component_index, record)| UnitComponentInspectOutput {
|
||||
root_index,
|
||||
component_index,
|
||||
archive_raw_hex: hex_bytes(&record.archive_raw),
|
||||
resource_raw_hex: hex_bytes(&record.resource_raw),
|
||||
kind: record.kind,
|
||||
parent_or_link: record.parent_or_link,
|
||||
description_raw_hex: hex_bytes(&record.description_raw),
|
||||
tail0: record.tail0,
|
||||
tail1: record.tail1,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
edges: graph
|
||||
.edges
|
||||
.iter()
|
||||
.map(|edge| PrototypeGraphEdgeInspectOutput {
|
||||
id: edge.id.0,
|
||||
from: edge.from.0,
|
||||
to: edge.to.0,
|
||||
kind: prototype_graph_edge_kind_label(edge.kind),
|
||||
requiredness: prototype_graph_requiredness_label(edge.requiredness),
|
||||
root_index: edge.provenance.as_ref().map(|value| value.root_index),
|
||||
parent_edge: edge
|
||||
.provenance
|
||||
.as_ref()
|
||||
.and_then(|value| value.parent_edge.map(|parent| parent.0)),
|
||||
unit_component_index: edge
|
||||
.provenance
|
||||
.as_ref()
|
||||
.and_then(|value| value.unit_component_index),
|
||||
archive: edge
|
||||
.provenance
|
||||
.as_ref()
|
||||
.and_then(|value| value.archive.clone()),
|
||||
resource_raw_hex: edge
|
||||
.provenance
|
||||
.as_ref()
|
||||
.and_then(|value| value.resource.as_ref())
|
||||
.map(|raw| hex_bytes(raw)),
|
||||
})
|
||||
.collect(),
|
||||
prototype_requests: graph.prototype_requests.len(),
|
||||
resolved: report.resolved_count,
|
||||
unit_references: report.unit_reference_count,
|
||||
@@ -638,6 +720,29 @@ fn graph_failure_output(failure: &fparkan_prototype::PrototypeGraphFailure) -> G
|
||||
}
|
||||
}
|
||||
|
||||
fn hex_bytes(raw: &[u8]) -> String {
|
||||
let mut output = String::with_capacity(raw.len().saturating_mul(2));
|
||||
for byte in raw {
|
||||
#[allow(clippy::expect_used)]
|
||||
write!(&mut output, "{byte:02x}").expect("writing into String cannot fail");
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
fn prototype_graph_edge_kind_label(
|
||||
edge: fparkan_prototype::PrototypeGraphEdgeKind,
|
||||
) -> &'static str {
|
||||
match edge {
|
||||
fparkan_prototype::PrototypeGraphEdgeKind::MissionToRoot => "mission_to_root",
|
||||
fparkan_prototype::PrototypeGraphEdgeKind::UnitDatToComponent => "unit_dat_to_component",
|
||||
fparkan_prototype::PrototypeGraphEdgeKind::PrototypeToMesh => "prototype_to_mesh",
|
||||
fparkan_prototype::PrototypeGraphEdgeKind::MeshToWear => "mesh_to_wear",
|
||||
fparkan_prototype::PrototypeGraphEdgeKind::WearToMaterial => "wear_to_material",
|
||||
fparkan_prototype::PrototypeGraphEdgeKind::MaterialToTexture => "material_to_texture",
|
||||
fparkan_prototype::PrototypeGraphEdgeKind::WearToLightmap => "wear_to_lightmap",
|
||||
}
|
||||
}
|
||||
|
||||
fn prototype_graph_edge_label(edge: fparkan_prototype::PrototypeGraphEdge) -> &'static str {
|
||||
match edge {
|
||||
fparkan_prototype::PrototypeGraphEdge::MissionToUnitDat => "mission_to_unit_dat",
|
||||
@@ -720,7 +825,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
json,
|
||||
"{\"schema_version\":\"fparkan-prototype-inspect-v1\",\"key\":\"root\",\"roots\":1,\"node_count\":0,\"edge_count\":0,\"prototype_requests\":1,\"resolved\":1,\"unit_references\":0,\"unit_components\":0,\"direct_references\":1,\"wear_requests\":0,\"wear\":0,\"materials\":0,\"textures\":0,\"lightmaps\":0,\"is_success\":true,\"failures\":[]}"
|
||||
"{\"schema_version\":\"fparkan-prototype-inspect-v2\",\"key\":\"root\",\"roots\":1,\"node_count\":0,\"edge_count\":0,\"unit_component_records\":[],\"edges\":[],\"prototype_requests\":1,\"resolved\":1,\"unit_references\":0,\"unit_components\":0,\"direct_references\":1,\"wear_requests\":0,\"wear\":0,\"materials\":0,\"textures\":0,\"lightmaps\":0,\"is_success\":true,\"failures\":[]}"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -484,6 +484,21 @@ Visual dependency expansion наследует этот индекс на edges
|
||||
diffuse texture и lightmap. Поэтому ошибка или asset в любой из этих фаз
|
||||
сохраняет путь до конкретной записи unit DAT, а не только до миссионного object.
|
||||
|
||||
Для воспроизводимой проверки используется `fparkan-cli prototype inspect`.
|
||||
Схема JSON `fparkan-prototype-inspect-v2` содержит lossless hex каждого
|
||||
32-byte поля unit record и materialized graph edges с `unit_component_index`.
|
||||
Например, для GOG AutoDemo:
|
||||
|
||||
```powershell
|
||||
cargo run -p fparkan-cli -- prototype inspect `
|
||||
--root 'C:\GOG Games\Parkan - Iron Strategy' `
|
||||
--key 'UNITS\UNITS\AutoDEMO\w_m_wlk2.dat' --format json
|
||||
```
|
||||
|
||||
Вывод фиксирует 18 component records, их MSH/WEAR/MAT0/Texm dependencies и
|
||||
точные parent edges. JSON предназначен для анализа и regression evidence, а
|
||||
не для интерпретации `kind`, links или opaque tails как готовой game logic.
|
||||
|
||||
## Вспомогательные форматы
|
||||
|
||||
MSH, материал и текстура отвечают за видимую форму. Полноценный прототип
|
||||
|
||||
Reference in New Issue
Block a user