Ground widened add-building descriptor names
This commit is contained in:
parent
b2da02befa
commit
a3df447186
6 changed files with 287 additions and 15 deletions
|
|
@ -125,6 +125,9 @@ enum Command {
|
|||
RuntimeInspectSmp {
|
||||
smp_path: PathBuf,
|
||||
},
|
||||
RuntimeInspectCandidateTable {
|
||||
smp_path: PathBuf,
|
||||
},
|
||||
RuntimeInspectCompactEventDispatchCluster {
|
||||
root_path: PathBuf,
|
||||
},
|
||||
|
|
@ -521,6 +524,34 @@ struct RuntimeCandidateTableSample {
|
|||
availability_by_name: BTreeMap<String, u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct RuntimeCandidateTableEntrySample {
|
||||
index: usize,
|
||||
offset: usize,
|
||||
text: String,
|
||||
availability_dword: u32,
|
||||
availability_dword_hex: String,
|
||||
trailer_word: u32,
|
||||
trailer_word_hex: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct RuntimeCandidateTableInspectionReport {
|
||||
path: String,
|
||||
profile_family: String,
|
||||
source_kind: String,
|
||||
semantic_family: String,
|
||||
header_word_0_hex: String,
|
||||
header_word_1_hex: String,
|
||||
header_word_2_hex: String,
|
||||
observed_entry_capacity: usize,
|
||||
observed_entry_count: usize,
|
||||
zero_trailer_entry_count: usize,
|
||||
nonzero_trailer_entry_count: usize,
|
||||
zero_trailer_entry_names: Vec<String>,
|
||||
entries: Vec<RuntimeCandidateTableEntrySample>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct RuntimeCandidateTableComparisonReport {
|
||||
file_count: usize,
|
||||
|
|
@ -946,6 +977,9 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
Command::RuntimeInspectSmp { smp_path } => {
|
||||
run_runtime_inspect_smp(&smp_path)?;
|
||||
}
|
||||
Command::RuntimeInspectCandidateTable { smp_path } => {
|
||||
run_runtime_inspect_candidate_table(&smp_path)?;
|
||||
}
|
||||
Command::RuntimeInspectCompactEventDispatchCluster { root_path } => {
|
||||
run_runtime_inspect_compact_event_dispatch_cluster(&root_path)?;
|
||||
}
|
||||
|
|
@ -1167,6 +1201,13 @@ fn parse_command() -> Result<Command, Box<dyn std::error::Error>> {
|
|||
smp_path: PathBuf::from(path),
|
||||
})
|
||||
}
|
||||
[command, subcommand, path]
|
||||
if command == "runtime" && subcommand == "inspect-candidate-table" =>
|
||||
{
|
||||
Ok(Command::RuntimeInspectCandidateTable {
|
||||
smp_path: PathBuf::from(path),
|
||||
})
|
||||
}
|
||||
[command, subcommand, root_path]
|
||||
if command == "runtime"
|
||||
&& subcommand == "inspect-compact-event-dispatch-cluster" =>
|
||||
|
|
@ -1459,7 +1500,7 @@ fn parse_command() -> Result<Command, Box<dyn std::error::Error>> {
|
|||
})
|
||||
}
|
||||
_ => Err(
|
||||
"usage: rrt-cli [validate [repo-root] | finance eval <snapshot.json> | finance diff <left.json> <right.json> | runtime validate-fixture <fixture.json> | runtime summarize-fixture <fixture.json> | runtime export-fixture-state <fixture.json> <snapshot.json> | runtime diff-state <left.json> <right.json> | runtime summarize-state <snapshot.json> | runtime import-state <input.json> <snapshot.json> | runtime inspect-smp <file.smp> | runtime inspect-compact-event-dispatch-cluster <maps-dir> | runtime summarize-save-load <file.smp> | runtime load-save-slice <file.smp> | runtime inspect-save-company-chairman <file.smp> | runtime inspect-save-placed-structure-triplets <file.smp> | runtime compare-region-fixed-row-runs <left.gms> <right.gms> | runtime inspect-periodic-company-service-trace <file.smp> | runtime inspect-region-service-trace <file.smp> | runtime inspect-infrastructure-asset-trace <file.smp> | runtime inspect-save-region-queued-notice-records <file.smp> | runtime inspect-placed-structure-dynamic-side-buffer <file.smp> | runtime inspect-unclassified-save-collections <file.smp> | runtime import-save-state <file.smp> <snapshot.json> | runtime export-save-slice <file.smp> <save-slice.json> | runtime export-overlay-import <snapshot.json> <save-slice.json> <overlay-import.json> | runtime inspect-pk4 <file.pk4> | runtime inspect-cargo-types <CargoTypes-dir> | runtime inspect-cargo-skins <Cargo106.PK4> | runtime inspect-cargo-economy-sources <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-production-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-price-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-win <file.win> | runtime extract-pk4-entry <file.pk4> <entry-name> <output-path> | runtime inspect-campaign-exe <RT3.exe> | runtime compare-classic-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-105-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-candidate-table <file1> <file2> [fileN...] | runtime compare-recipe-book-lines <file1> <file2> [fileN...] | runtime compare-setup-payload-core <file1> <file2> [fileN...] | runtime compare-setup-launch-payload <file1> <file2> [fileN...] | runtime compare-post-special-conditions-scalars <file1> <file2> [fileN...] | runtime scan-candidate-table-headers <root-dir> | runtime scan-special-conditions <root-dir> | runtime scan-aligned-runtime-rule-band <root-dir> | runtime scan-post-special-conditions-scalars <root-dir> | runtime scan-post-special-conditions-tail <root-dir> | runtime scan-recipe-book-lines <root-dir> | runtime export-profile-block <save.gms> <profile.json>]"
|
||||
"usage: rrt-cli [validate [repo-root] | finance eval <snapshot.json> | finance diff <left.json> <right.json> | runtime validate-fixture <fixture.json> | runtime summarize-fixture <fixture.json> | runtime export-fixture-state <fixture.json> <snapshot.json> | runtime diff-state <left.json> <right.json> | runtime summarize-state <snapshot.json> | runtime import-state <input.json> <snapshot.json> | runtime inspect-smp <file.smp> | runtime inspect-candidate-table <file.smp> | runtime inspect-compact-event-dispatch-cluster <maps-dir> | runtime summarize-save-load <file.smp> | runtime load-save-slice <file.smp> | runtime inspect-save-company-chairman <file.smp> | runtime inspect-save-placed-structure-triplets <file.smp> | runtime compare-region-fixed-row-runs <left.gms> <right.gms> | runtime inspect-periodic-company-service-trace <file.smp> | runtime inspect-region-service-trace <file.smp> | runtime inspect-infrastructure-asset-trace <file.smp> | runtime inspect-save-region-queued-notice-records <file.smp> | runtime inspect-placed-structure-dynamic-side-buffer <file.smp> | runtime inspect-unclassified-save-collections <file.smp> | runtime import-save-state <file.smp> <snapshot.json> | runtime export-save-slice <file.smp> <save-slice.json> | runtime export-overlay-import <snapshot.json> <save-slice.json> <overlay-import.json> | runtime inspect-pk4 <file.pk4> | runtime inspect-cargo-types <CargoTypes-dir> | runtime inspect-cargo-skins <Cargo106.PK4> | runtime inspect-cargo-economy-sources <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-production-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-price-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-win <file.win> | runtime extract-pk4-entry <file.pk4> <entry-name> <output-path> | runtime inspect-campaign-exe <RT3.exe> | runtime compare-classic-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-105-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-candidate-table <file1> <file2> [fileN...] | runtime compare-recipe-book-lines <file1> <file2> [fileN...] | runtime compare-setup-payload-core <file1> <file2> [fileN...] | runtime compare-setup-launch-payload <file1> <file2> [fileN...] | runtime compare-post-special-conditions-scalars <file1> <file2> [fileN...] | runtime scan-candidate-table-headers <root-dir> | runtime scan-special-conditions <root-dir> | runtime scan-aligned-runtime-rule-band <root-dir> | runtime scan-post-special-conditions-scalars <root-dir> | runtime scan-post-special-conditions-tail <root-dir> | runtime scan-recipe-book-lines <root-dir> | runtime export-profile-block <save.gms> <profile.json>]"
|
||||
.into(),
|
||||
),
|
||||
}
|
||||
|
|
@ -2246,6 +2287,12 @@ fn run_runtime_compare_candidate_table(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn run_runtime_inspect_candidate_table(smp_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let report = load_candidate_table_inspection_report(smp_path)?;
|
||||
println!("{}", serde_json::to_string_pretty(&report)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_runtime_compare_recipe_book_lines(
|
||||
smp_paths: &[PathBuf],
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
|
@ -3119,6 +3166,161 @@ fn load_candidate_table_sample(
|
|||
})
|
||||
}
|
||||
|
||||
fn load_candidate_table_inspection_report(
|
||||
smp_path: &Path,
|
||||
) -> Result<RuntimeCandidateTableInspectionReport, Box<dyn std::error::Error>> {
|
||||
let inspection = inspect_smp_file(smp_path)?;
|
||||
if let Some(probe) = inspection.rt3_105_save_name_table_probe {
|
||||
return Ok(RuntimeCandidateTableInspectionReport {
|
||||
path: smp_path.display().to_string(),
|
||||
profile_family: probe.profile_family,
|
||||
source_kind: probe.source_kind,
|
||||
semantic_family: probe.semantic_family,
|
||||
header_word_0_hex: probe.header_word_0_hex,
|
||||
header_word_1_hex: probe.header_word_1_hex,
|
||||
header_word_2_hex: probe.header_word_2_hex,
|
||||
observed_entry_capacity: probe.observed_entry_capacity,
|
||||
observed_entry_count: probe.observed_entry_count,
|
||||
zero_trailer_entry_count: probe.zero_trailer_entry_count,
|
||||
nonzero_trailer_entry_count: probe.nonzero_trailer_entry_count,
|
||||
zero_trailer_entry_names: probe.zero_trailer_entry_names,
|
||||
entries: probe
|
||||
.entries
|
||||
.into_iter()
|
||||
.map(|entry| RuntimeCandidateTableEntrySample {
|
||||
index: entry.index,
|
||||
offset: entry.offset,
|
||||
text: entry.text,
|
||||
availability_dword: entry.availability_dword,
|
||||
availability_dword_hex: entry.availability_dword_hex,
|
||||
trailer_word: entry.trailer_word,
|
||||
trailer_word_hex: entry.trailer_word_hex,
|
||||
})
|
||||
.collect(),
|
||||
});
|
||||
}
|
||||
|
||||
let bytes = fs::read(smp_path)?;
|
||||
let header_offset = 0x6a70usize;
|
||||
let entries_offset = 0x6ad1usize;
|
||||
let block_end_offset = 0x73c0usize;
|
||||
let entry_stride = 0x22usize;
|
||||
if bytes.len() < block_end_offset
|
||||
|| !matches_candidate_table_header_bytes(&bytes, header_offset)
|
||||
{
|
||||
return Err(format!(
|
||||
"{} did not expose an RT3 1.05 candidate-availability table",
|
||||
smp_path.display()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let observed_entry_capacity = read_u32_le(&bytes, header_offset + 0x1c)
|
||||
.ok_or_else(|| format!("{} is missing candidate table capacity", smp_path.display()))?
|
||||
as usize;
|
||||
let observed_entry_count = read_u32_le(&bytes, header_offset + 0x20)
|
||||
.ok_or_else(|| format!("{} is missing candidate table count", smp_path.display()))?
|
||||
as usize;
|
||||
if observed_entry_capacity < observed_entry_count {
|
||||
return Err(format!(
|
||||
"{} has invalid candidate table capacity/count {observed_entry_capacity}/{observed_entry_count}",
|
||||
smp_path.display()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
let entries_end_offset = entries_offset
|
||||
.checked_add(
|
||||
observed_entry_count
|
||||
.checked_mul(entry_stride)
|
||||
.ok_or("candidate table length overflow")?,
|
||||
)
|
||||
.ok_or("candidate table end overflow")?;
|
||||
if entries_end_offset > block_end_offset {
|
||||
return Err(format!(
|
||||
"{} candidate table overruns fixed block end",
|
||||
smp_path.display()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let mut zero_trailer_entry_names = Vec::new();
|
||||
let mut entries = Vec::new();
|
||||
for index in 0..observed_entry_count {
|
||||
let offset = entries_offset + index * entry_stride;
|
||||
let chunk = &bytes[offset..offset + entry_stride];
|
||||
let nul_index = chunk
|
||||
.iter()
|
||||
.position(|byte| *byte == 0)
|
||||
.unwrap_or(entry_stride - 4);
|
||||
let text = std::str::from_utf8(&chunk[..nul_index]).map_err(|_| {
|
||||
format!(
|
||||
"{} contains invalid UTF-8 in candidate table",
|
||||
smp_path.display()
|
||||
)
|
||||
})?;
|
||||
let availability_dword =
|
||||
read_u32_le(&bytes, offset + entry_stride - 4).ok_or_else(|| {
|
||||
format!(
|
||||
"{} is missing candidate availability dword",
|
||||
smp_path.display()
|
||||
)
|
||||
})?;
|
||||
if availability_dword == 0 {
|
||||
zero_trailer_entry_names.push(text.to_string());
|
||||
}
|
||||
entries.push(RuntimeCandidateTableEntrySample {
|
||||
index,
|
||||
offset,
|
||||
text: text.to_string(),
|
||||
availability_dword,
|
||||
availability_dword_hex: format!("0x{availability_dword:08x}"),
|
||||
trailer_word: availability_dword,
|
||||
trailer_word_hex: format!("0x{availability_dword:08x}"),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(RuntimeCandidateTableInspectionReport {
|
||||
path: smp_path.display().to_string(),
|
||||
profile_family: classify_candidate_table_header_profile(
|
||||
smp_path
|
||||
.extension()
|
||||
.and_then(|ext| ext.to_str())
|
||||
.map(|ext| ext.to_ascii_lowercase()),
|
||||
&bytes,
|
||||
),
|
||||
source_kind: match smp_path
|
||||
.extension()
|
||||
.and_then(|ext| ext.to_str())
|
||||
.map(|ext| ext.to_ascii_lowercase())
|
||||
.as_deref()
|
||||
{
|
||||
Some("gmp") => "map-fixed-catalog-range",
|
||||
Some("gms") => "save-fixed-catalog-range",
|
||||
_ => "fixed-catalog-range",
|
||||
}
|
||||
.to_string(),
|
||||
semantic_family: "scenario-named-candidate-availability-table".to_string(),
|
||||
header_word_0_hex: format!(
|
||||
"0x{:08x}",
|
||||
read_u32_le(&bytes, header_offset).ok_or("missing candidate header word 0")?
|
||||
),
|
||||
header_word_1_hex: format!(
|
||||
"0x{:08x}",
|
||||
read_u32_le(&bytes, header_offset + 4).ok_or("missing candidate header word 1")?
|
||||
),
|
||||
header_word_2_hex: format!(
|
||||
"0x{:08x}",
|
||||
read_u32_le(&bytes, header_offset + 8).ok_or("missing candidate header word 2")?
|
||||
),
|
||||
observed_entry_capacity,
|
||||
observed_entry_count,
|
||||
zero_trailer_entry_count: zero_trailer_entry_names.len(),
|
||||
nonzero_trailer_entry_count: observed_entry_count - zero_trailer_entry_names.len(),
|
||||
zero_trailer_entry_names,
|
||||
entries,
|
||||
})
|
||||
}
|
||||
|
||||
fn load_recipe_book_line_sample(
|
||||
smp_path: &Path,
|
||||
) -> Result<RuntimeRecipeBookLineSample, Box<dyn std::error::Error>> {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ pub const REQUIRED_EXPORTS: &[&str] = &[
|
|||
"artifacts/exports/rt3-1.06/pending-template-store-management.md",
|
||||
"artifacts/exports/rt3-1.06/event-effects-table.json",
|
||||
"artifacts/exports/rt3-1.06/event-effects-cargo-bindings.json",
|
||||
"artifacts/exports/rt3-1.06/event-effects-building-bindings.json",
|
||||
"artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json",
|
||||
"artifacts/exports/rt3-1.06/economy-cargo-sources.json",
|
||||
"artifacts/exports/rt3-1.06/selected-year-bucket-ladder.json",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue