Census locomotive tail blockers across local saves

This commit is contained in:
Jan Petykiewicz 2026-04-21 22:15:17 -07:00
commit cbfe0a8df9
16 changed files with 2022 additions and 319 deletions

View file

@ -2696,7 +2696,7 @@
}, },
{ {
"descriptor_id": 299, "descriptor_id": 299,
"label": "GP 35 Availability", "label": "Lower-Band Locomotive Availability Slot 59",
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
@ -2705,7 +2705,7 @@
}, },
{ {
"descriptor_id": 300, "descriptor_id": 300,
"label": "U1 Availability", "label": "Lower-Band Locomotive Availability Slot 60",
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
@ -2714,7 +2714,7 @@
}, },
{ {
"descriptor_id": 301, "descriptor_id": 301,
"label": "Zephyr Availability", "label": "Lower-Band Locomotive Availability Slot 61",
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
@ -2727,8 +2727,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 303, "descriptor_id": 303,
@ -2736,8 +2736,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 304, "descriptor_id": 304,
@ -2745,8 +2745,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 305, "descriptor_id": 305,
@ -2754,8 +2754,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 306, "descriptor_id": 306,
@ -2763,8 +2763,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 307, "descriptor_id": 307,
@ -2772,8 +2772,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 308, "descriptor_id": 308,
@ -2781,8 +2781,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 309, "descriptor_id": 309,
@ -2790,8 +2790,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 310, "descriptor_id": 310,
@ -2799,8 +2799,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 311, "descriptor_id": 311,
@ -2808,8 +2808,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 312, "descriptor_id": 312,
@ -2817,8 +2817,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 313, "descriptor_id": 313,
@ -2826,8 +2826,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 314, "descriptor_id": 314,
@ -2835,8 +2835,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 315, "descriptor_id": 315,
@ -2844,8 +2844,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 316, "descriptor_id": 316,
@ -2853,8 +2853,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 317, "descriptor_id": 317,
@ -2862,8 +2862,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 318, "descriptor_id": 318,
@ -2871,8 +2871,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 319, "descriptor_id": 319,
@ -2880,8 +2880,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 320, "descriptor_id": 320,
@ -2889,8 +2889,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 321, "descriptor_id": 321,
@ -2898,8 +2898,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 322, "descriptor_id": 322,
@ -2907,8 +2907,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 323, "descriptor_id": 323,
@ -2916,8 +2916,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 324, "descriptor_id": 324,
@ -2925,8 +2925,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 325, "descriptor_id": 325,
@ -2934,8 +2934,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 326, "descriptor_id": 326,
@ -2943,8 +2943,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 327, "descriptor_id": 327,
@ -2952,8 +2952,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 328, "descriptor_id": 328,
@ -2961,8 +2961,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 329, "descriptor_id": 329,
@ -2970,8 +2970,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 330, "descriptor_id": 330,
@ -2979,8 +2979,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 331, "descriptor_id": 331,
@ -2988,8 +2988,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 332, "descriptor_id": 332,
@ -2997,8 +2997,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 333, "descriptor_id": 333,
@ -3006,8 +3006,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 334, "descriptor_id": 334,
@ -3015,8 +3015,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 335, "descriptor_id": 335,
@ -3024,8 +3024,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 336, "descriptor_id": 336,
@ -3033,8 +3033,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 337, "descriptor_id": 337,
@ -3042,8 +3042,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 338, "descriptor_id": 338,
@ -3051,8 +3051,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 339, "descriptor_id": 339,
@ -3060,8 +3060,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 340, "descriptor_id": 340,
@ -3069,8 +3069,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 341, "descriptor_id": 341,
@ -3078,8 +3078,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 342, "descriptor_id": 342,
@ -3087,8 +3087,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 343, "descriptor_id": 343,
@ -3096,8 +3096,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 344, "descriptor_id": 344,
@ -3105,8 +3105,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 345, "descriptor_id": 345,
@ -3114,8 +3114,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 346, "descriptor_id": 346,
@ -3123,8 +3123,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 347, "descriptor_id": 347,
@ -3132,8 +3132,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 348, "descriptor_id": 348,
@ -3141,8 +3141,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 349, "descriptor_id": 349,
@ -3150,8 +3150,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 350, "descriptor_id": 350,
@ -3159,8 +3159,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 351, "descriptor_id": 351,
@ -3168,8 +3168,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_availability_scalar", "parameter_family": "locomotive_availability_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 352, "descriptor_id": 352,
@ -3695,7 +3695,7 @@
}, },
{ {
"descriptor_id": 410, "descriptor_id": 410,
"label": "GP 35 Cost", "label": "Lower-Band Locomotive Cost Slot 59",
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
@ -3704,7 +3704,7 @@
}, },
{ {
"descriptor_id": 411, "descriptor_id": 411,
"label": "U1 Cost", "label": "Lower-Band Locomotive Cost Slot 60",
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
@ -3713,7 +3713,7 @@
}, },
{ {
"descriptor_id": 412, "descriptor_id": 412,
"label": "Zephyr Cost", "label": "Lower-Band Locomotive Cost Slot 61",
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
@ -3726,8 +3726,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 414, "descriptor_id": 414,
@ -3735,8 +3735,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 415, "descriptor_id": 415,
@ -3744,8 +3744,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 416, "descriptor_id": 416,
@ -3753,8 +3753,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 417, "descriptor_id": 417,
@ -3762,8 +3762,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 418, "descriptor_id": 418,
@ -3771,8 +3771,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 419, "descriptor_id": 419,
@ -3780,8 +3780,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 420, "descriptor_id": 420,
@ -3789,8 +3789,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 421, "descriptor_id": 421,
@ -3798,8 +3798,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 422, "descriptor_id": 422,
@ -3807,8 +3807,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 423, "descriptor_id": 423,
@ -3816,8 +3816,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 424, "descriptor_id": 424,
@ -3825,8 +3825,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 425, "descriptor_id": 425,
@ -3834,8 +3834,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 426, "descriptor_id": 426,
@ -3843,8 +3843,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 427, "descriptor_id": 427,
@ -3852,8 +3852,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 428, "descriptor_id": 428,
@ -3861,8 +3861,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 429, "descriptor_id": 429,
@ -3870,8 +3870,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 430, "descriptor_id": 430,
@ -3879,8 +3879,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 431, "descriptor_id": 431,
@ -3888,8 +3888,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 432, "descriptor_id": 432,
@ -3897,8 +3897,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 433, "descriptor_id": 433,
@ -3906,8 +3906,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 434, "descriptor_id": 434,
@ -3915,8 +3915,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 435, "descriptor_id": 435,
@ -3924,8 +3924,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 436, "descriptor_id": 436,
@ -3933,8 +3933,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 437, "descriptor_id": 437,
@ -3942,8 +3942,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 438, "descriptor_id": 438,
@ -3951,8 +3951,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 439, "descriptor_id": 439,
@ -3960,8 +3960,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 440, "descriptor_id": 440,
@ -3969,8 +3969,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 441, "descriptor_id": 441,
@ -3978,8 +3978,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 442, "descriptor_id": 442,
@ -3987,8 +3987,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 443, "descriptor_id": 443,
@ -3996,8 +3996,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 444, "descriptor_id": 444,
@ -4005,8 +4005,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 445, "descriptor_id": 445,
@ -4014,8 +4014,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 446, "descriptor_id": 446,
@ -4023,8 +4023,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 447, "descriptor_id": 447,
@ -4032,8 +4032,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 448, "descriptor_id": 448,
@ -4041,8 +4041,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 449, "descriptor_id": 449,
@ -4050,8 +4050,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 450, "descriptor_id": 450,
@ -4059,8 +4059,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 451, "descriptor_id": 451,
@ -4068,8 +4068,8 @@
"target_mask_bits": 11, "target_mask_bits": 11,
"parameter_family": "locomotive_cost_scalar", "parameter_family": "locomotive_cost_scalar",
"runtime_key": null, "runtime_key": null,
"runtime_status": "evidence_blocked", "runtime_status": "executable",
"executable_in_runtime": false "executable_in_runtime": true
}, },
{ {
"descriptor_id": 452, "descriptor_id": 452,

View file

@ -0,0 +1,477 @@
{
"root_path": "rt3_wineprefix/drive_c",
"file_count": 29,
"files_with_named_locomotive_table_count": 5,
"files_with_locomotive_catalog_count": 5,
"files_with_packed_event_collection_count": 26,
"stable_prefix_length": 58,
"stable_prefix_last_name": "VL80T",
"tail_cluster_count": 2,
"tail_clusters": [
{
"entry_count": 63,
"tail_entries": [
{
"locomotive_id": 59,
"name": "242 A1"
},
{
"locomotive_id": 60,
"name": "Class 460"
},
{
"locomotive_id": 61,
"name": "Class A1"
},
{
"locomotive_id": 62,
"name": "Class P8"
},
{
"locomotive_id": 63,
"name": "U1"
}
],
"file_count": 1,
"sample_paths": [
"rt3_wineprefix/drive_c/rt3_105/Saved Games/g.gms"
],
"map_paths": [
"Spanish Mainline.gmp"
]
},
{
"entry_count": 61,
"tail_entries": [
{
"locomotive_id": 59,
"name": "GP 35"
},
{
"locomotive_id": 60,
"name": "U1"
},
{
"locomotive_id": 61,
"name": "Zephyr"
}
],
"file_count": 4,
"sample_paths": [
"rt3_wineprefix/drive_c/rt3_105/Saved Games/Autosave.gms",
"rt3_wineprefix/drive_c/rt3_105/Saved Games/nom.gms",
"rt3_wineprefix/drive_c/rt3_105/Saved Games/p.gms",
"rt3_wineprefix/drive_c/rt3_105/Saved Games/q.gms"
],
"map_paths": [
"Alternate USA.gmp",
"Southern Pacific.gmp"
]
}
],
"extra_shipped_name_coverage": [
{
"name": "242 A1",
"observed_in_catalog_file_count": 1,
"observed_ordinals": [
59
],
"sample_paths": [
"rt3_wineprefix/drive_c/rt3_105/Saved Games/g.gms"
]
},
{
"name": "Class 460",
"observed_in_catalog_file_count": 1,
"observed_ordinals": [
60
],
"sample_paths": [
"rt3_wineprefix/drive_c/rt3_105/Saved Games/g.gms"
]
},
{
"name": "Class A1",
"observed_in_catalog_file_count": 1,
"observed_ordinals": [
61
],
"sample_paths": [
"rt3_wineprefix/drive_c/rt3_105/Saved Games/g.gms"
]
},
{
"name": "Class P8",
"observed_in_catalog_file_count": 1,
"observed_ordinals": [
62
],
"sample_paths": [
"rt3_wineprefix/drive_c/rt3_105/Saved Games/g.gms"
]
},
{
"name": "Class QJ",
"observed_in_catalog_file_count": 0,
"observed_ordinals": [],
"sample_paths": []
}
],
"descriptor_452_hits": {
"row_count": 0,
"file_count": 0,
"descriptor_ids_present": [],
"descriptor_labels_present": [],
"carrier_paths": []
},
"upper_availability_band_hits": {
"row_count": 0,
"file_count": 0,
"descriptor_ids_present": [],
"descriptor_labels_present": [],
"carrier_paths": []
},
"upper_cost_band_hits": {
"row_count": 0,
"file_count": 0,
"descriptor_ids_present": [],
"descriptor_labels_present": [],
"carrier_paths": []
},
"skipped_file_count": 3,
"samples": [
{
"path": "rt3_wineprefix/drive_c/rt3/Saved Games/Autosave.gms",
"container_profile_family": "rt3-classic-save-container-v1",
"mechanism_family": "classic-save-rehydrate-v1",
"map_path": "British Isles.gmp",
"display_name": "British Isles",
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3/Saved Games/hh.gms",
"container_profile_family": "rt3-classic-save-container-v1",
"mechanism_family": "classic-save-rehydrate-v1",
"map_path": "British Isles.gmp",
"display_name": "British Isles",
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3/Saved Games/kk.gms",
"container_profile_family": "rt3-classic-save-container-v1",
"mechanism_family": "classic-save-rehydrate-v1",
"map_path": "British Isles.gmp",
"display_name": "British Isles",
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/111.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/Autosave.gms",
"container_profile_family": "rt3-105-save-container-v1",
"mechanism_family": "rt3-105-save-post-span-bridge-v1",
"map_path": "Alternate USA.gmp",
"display_name": "Alternate USA",
"named_locomotive_table_entry_count": 61,
"locomotive_catalog_entry_count": 61,
"tail_entries": [
{
"locomotive_id": 59,
"name": "GP 35"
},
{
"locomotive_id": 60,
"name": "U1"
},
{
"locomotive_id": 61,
"name": "Zephyr"
}
]
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/Autosave.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/g.gms",
"container_profile_family": "rt3-105-alt-save-container-v1",
"mechanism_family": "rt3-105-alt-save-post-span-bridge-v1",
"map_path": "Spanish Mainline.gmp",
"display_name": "Spanish Mainline",
"named_locomotive_table_entry_count": 63,
"locomotive_catalog_entry_count": 63,
"tail_entries": [
{
"locomotive_id": 59,
"name": "242 A1"
},
{
"locomotive_id": 60,
"name": "Class 460"
},
{
"locomotive_id": 61,
"name": "Class A1"
},
{
"locomotive_id": 62,
"name": "Class P8"
},
{
"locomotive_id": 63,
"name": "U1"
}
]
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/ggg.gmx",
"container_profile_family": "unknown",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/kai0.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/kai1.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/kai2.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/kai3.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/kai4.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/kai5.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/kai6.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/kai7.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/kai8.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/kai9.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/kaimama.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/lll.gmx",
"container_profile_family": "unknown",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/n.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/nom.gms",
"container_profile_family": "rt3-105-save-container-v1",
"mechanism_family": "rt3-105-save-post-span-bridge-v1",
"map_path": "Alternate USA.gmp",
"display_name": "Alternate USA",
"named_locomotive_table_entry_count": 61,
"locomotive_catalog_entry_count": 61,
"tail_entries": [
{
"locomotive_id": 59,
"name": "GP 35"
},
{
"locomotive_id": 60,
"name": "U1"
},
{
"locomotive_id": 61,
"name": "Zephyr"
}
]
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/ooo.gmx",
"container_profile_family": "unknown",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/p.gms",
"container_profile_family": "rt3-105-scenario-save-container-v1",
"mechanism_family": "rt3-105-scenario-post-span-bridge-v1",
"map_path": "Southern Pacific.gmp",
"display_name": "Southern Pacific",
"named_locomotive_table_entry_count": 61,
"locomotive_catalog_entry_count": 61,
"tail_entries": [
{
"locomotive_id": 59,
"name": "GP 35"
},
{
"locomotive_id": 60,
"name": "U1"
},
{
"locomotive_id": 61,
"name": "Zephyr"
}
]
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/ppp.gmx",
"container_profile_family": "rt3-105-sandbox-container-v1",
"mechanism_family": "rt3-105-candidate-catalog-source-v1",
"map_path": null,
"display_name": null,
"named_locomotive_table_entry_count": null,
"locomotive_catalog_entry_count": 0,
"tail_entries": []
},
{
"path": "rt3_wineprefix/drive_c/rt3_105/Saved Games/q.gms",
"container_profile_family": "rt3-105-scenario-save-container-v1",
"mechanism_family": "rt3-105-scenario-post-span-bridge-v1",
"map_path": "Southern Pacific.gmp",
"display_name": "Southern Pacific",
"named_locomotive_table_entry_count": 61,
"locomotive_catalog_entry_count": 61,
"tail_entries": [
{
"locomotive_id": 59,
"name": "GP 35"
},
{
"locomotive_id": 60,
"name": "U1"
},
{
"locomotive_id": 61,
"name": "Zephyr"
}
]
}
]
}

View file

@ -14,6 +14,11 @@ pub(super) fn parse_scan_command(
root_path: root_path.into(), root_path: root_path.into(),
}) })
} }
[subcommand, root_path] if subcommand == "scan-locomotive-catalog-tail" => {
Ok(ScanCommand::ScanLocomotiveCatalogTail {
root_path: root_path.into(),
})
}
[subcommand, root_path] if subcommand == "scan-special-conditions" => { [subcommand, root_path] if subcommand == "scan-special-conditions" => {
Ok(ScanCommand::ScanSpecialConditions { Ok(ScanCommand::ScanSpecialConditions {
root_path: root_path.into(), root_path: root_path.into(),

View file

@ -1,8 +1,8 @@
use crate::app::command::ScanCommand; use crate::app::command::ScanCommand;
use crate::app::runtime_scan::{ use crate::app::runtime_scan::{
scan_aligned_runtime_rule_band, scan_candidate_table_headers, scan_candidate_table_named_runs, scan_aligned_runtime_rule_band, scan_candidate_table_headers, scan_candidate_table_named_runs,
scan_post_special_conditions_scalars, scan_post_special_conditions_tail, scan_locomotive_catalog_tail, scan_post_special_conditions_scalars,
scan_recipe_book_lines, scan_special_conditions, scan_post_special_conditions_tail, scan_recipe_book_lines, scan_special_conditions,
}; };
pub(super) fn dispatch_scan(command: ScanCommand) -> Result<(), Box<dyn std::error::Error>> { pub(super) fn dispatch_scan(command: ScanCommand) -> Result<(), Box<dyn std::error::Error>> {
@ -13,6 +13,9 @@ pub(super) fn dispatch_scan(command: ScanCommand) -> Result<(), Box<dyn std::err
ScanCommand::ScanCandidateTableNamedRuns { root_path } => { ScanCommand::ScanCandidateTableNamedRuns { root_path } => {
scan_candidate_table_named_runs(&root_path) scan_candidate_table_named_runs(&root_path)
} }
ScanCommand::ScanLocomotiveCatalogTail { root_path } => {
scan_locomotive_catalog_tail(&root_path)
}
ScanCommand::ScanSpecialConditions { root_path } => scan_special_conditions(&root_path), ScanCommand::ScanSpecialConditions { root_path } => scan_special_conditions(&root_path),
ScanCommand::ScanAlignedRuntimeRuleBand { root_path } => { ScanCommand::ScanAlignedRuntimeRuleBand { root_path } => {
scan_aligned_runtime_rule_band(&root_path) scan_aligned_runtime_rule_band(&root_path)

View file

@ -447,7 +447,8 @@ fn build_named_run_aggregates(
find_named_run_by_names(&sample.port_runs, "Port00", "Port00", 1), find_named_run_by_names(&sample.port_runs, "Port00", "Port00", 1),
find_named_run_by_names(&sample.warehouse_runs, "Warehouse00", "Warehouse00", 1), find_named_run_by_names(&sample.warehouse_runs, "Warehouse00", "Warehouse00", 1),
) { ) {
let row_pair_key = format!("{}/{}", port00_run.start_index, warehouse00_run.start_index); let row_pair_key =
format!("{}/{}", port00_run.start_index, warehouse00_run.start_index);
*aggregates *aggregates
.port00_warehouse00_row_pair_map_counts .port00_warehouse00_row_pair_map_counts
.entry(row_pair_key.clone()) .entry(row_pair_key.clone())

View file

@ -0,0 +1,569 @@
use std::collections::{BTreeMap, BTreeSet};
use std::fs;
use std::path::{Path, PathBuf};
use serde::Serialize;
use rrt_runtime::inspect::smp::save_load::{
SmpLoadedLocomotiveCatalogEntry, SmpLoadedSaveSlice, load_save_slice_file,
};
const EXTRA_SHIPPED_ENGINE_TYPE_NAMES: [&str; 5] =
["242 A1", "Class 460", "Class A1", "Class P8", "Class QJ"];
#[derive(Debug, Clone)]
struct RuntimeLocomotiveCatalogTailScanSample {
path: String,
container_profile_family: Option<String>,
mechanism_family: String,
map_path: Option<String>,
display_name: Option<String>,
named_locomotive_table_entry_count: Option<usize>,
locomotive_catalog_entries: Vec<SmpLoadedLocomotiveCatalogEntry>,
descriptor_rows: Vec<RuntimeLocomotiveDescriptorRowHit>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct RuntimeLocomotiveDescriptorRowHit {
descriptor_id: u32,
descriptor_label: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
pub(crate) struct RuntimeObservedLocomotiveCatalogEntry {
pub(crate) locomotive_id: u32,
pub(crate) name: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct RuntimeLocomotiveCatalogTailScanSampleReport {
pub(crate) path: String,
pub(crate) container_profile_family: Option<String>,
pub(crate) mechanism_family: String,
pub(crate) map_path: Option<String>,
pub(crate) display_name: Option<String>,
pub(crate) named_locomotive_table_entry_count: Option<usize>,
pub(crate) locomotive_catalog_entry_count: usize,
pub(crate) tail_entries: Vec<RuntimeObservedLocomotiveCatalogEntry>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct RuntimeLocomotiveCatalogTailCluster {
pub(crate) entry_count: usize,
pub(crate) tail_entries: Vec<RuntimeObservedLocomotiveCatalogEntry>,
pub(crate) file_count: usize,
pub(crate) sample_paths: Vec<String>,
pub(crate) map_paths: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct RuntimeLocomotiveExtraNameCoverageSummary {
pub(crate) name: String,
pub(crate) observed_in_catalog_file_count: usize,
pub(crate) observed_ordinals: Vec<u32>,
pub(crate) sample_paths: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct RuntimeLocomotiveDescriptorBandHitSummary {
pub(crate) row_count: usize,
pub(crate) file_count: usize,
pub(crate) descriptor_ids_present: Vec<u32>,
pub(crate) descriptor_labels_present: Vec<String>,
pub(crate) carrier_paths: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct RuntimeLocomotiveCatalogTailCensusReport {
pub(crate) root_path: String,
pub(crate) file_count: usize,
pub(crate) files_with_named_locomotive_table_count: usize,
pub(crate) files_with_locomotive_catalog_count: usize,
pub(crate) files_with_packed_event_collection_count: usize,
pub(crate) stable_prefix_length: usize,
pub(crate) stable_prefix_last_name: Option<String>,
pub(crate) tail_cluster_count: usize,
pub(crate) tail_clusters: Vec<RuntimeLocomotiveCatalogTailCluster>,
pub(crate) extra_shipped_name_coverage: Vec<RuntimeLocomotiveExtraNameCoverageSummary>,
pub(crate) descriptor_452_hits: RuntimeLocomotiveDescriptorBandHitSummary,
pub(crate) upper_availability_band_hits: RuntimeLocomotiveDescriptorBandHitSummary,
pub(crate) upper_cost_band_hits: RuntimeLocomotiveDescriptorBandHitSummary,
pub(crate) skipped_file_count: usize,
pub(crate) samples: Vec<RuntimeLocomotiveCatalogTailScanSampleReport>,
}
pub(crate) fn scan_locomotive_catalog_tail(
root_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
let mut candidate_paths = Vec::new();
collect_locomotive_tail_input_paths(root_path, &mut candidate_paths)?;
let file_count = candidate_paths.len();
let mut samples = Vec::new();
let mut skipped_file_count = 0usize;
for path in candidate_paths {
match load_locomotive_catalog_tail_scan_sample(&path) {
Ok(sample) => samples.push(sample),
Err(_) => skipped_file_count += 1,
}
}
let files_with_named_locomotive_table_count = samples
.iter()
.filter(|sample| sample.named_locomotive_table_entry_count.is_some())
.count();
let files_with_locomotive_catalog_count = samples
.iter()
.filter(|sample| !sample.locomotive_catalog_entries.is_empty())
.count();
let files_with_packed_event_collection_count = samples
.iter()
.filter(|sample| !sample.descriptor_rows.is_empty())
.count();
let stable_prefix_length = stable_catalog_prefix_length(&samples);
let stable_prefix_last_name = stable_prefix_last_name(&samples, stable_prefix_length);
let tail_clusters = build_tail_clusters(&samples, stable_prefix_length);
let extra_shipped_name_coverage = build_extra_shipped_name_coverage(&samples);
let descriptor_452_hits = build_descriptor_band_hit_summary(&samples, 452..=452);
let upper_availability_band_hits = build_descriptor_band_hit_summary(&samples, 457..=474);
let upper_cost_band_hits = build_descriptor_band_hit_summary(&samples, 475..=502);
let sample_reports = build_sample_reports(&samples, stable_prefix_length);
let report = RuntimeLocomotiveCatalogTailCensusReport {
root_path: root_path.display().to_string(),
file_count,
files_with_named_locomotive_table_count,
files_with_locomotive_catalog_count,
files_with_packed_event_collection_count,
stable_prefix_length,
stable_prefix_last_name,
tail_cluster_count: tail_clusters.len(),
tail_clusters,
extra_shipped_name_coverage,
descriptor_452_hits,
upper_availability_band_hits,
upper_cost_band_hits,
skipped_file_count,
samples: sample_reports,
};
println!("{}", serde_json::to_string_pretty(&report)?);
Ok(())
}
fn build_sample_reports(
samples: &[RuntimeLocomotiveCatalogTailScanSample],
stable_prefix_length: usize,
) -> Vec<RuntimeLocomotiveCatalogTailScanSampleReport> {
let mut reports = samples
.iter()
.map(|sample| RuntimeLocomotiveCatalogTailScanSampleReport {
path: sample.path.clone(),
container_profile_family: sample.container_profile_family.clone(),
mechanism_family: sample.mechanism_family.clone(),
map_path: sample.map_path.clone(),
display_name: sample.display_name.clone(),
named_locomotive_table_entry_count: sample.named_locomotive_table_entry_count,
locomotive_catalog_entry_count: sample.locomotive_catalog_entries.len(),
tail_entries: tail_entries(sample, stable_prefix_length),
})
.collect::<Vec<_>>();
reports.sort_by(|left, right| left.path.cmp(&right.path));
reports
}
fn stable_prefix_last_name(
samples: &[RuntimeLocomotiveCatalogTailScanSample],
stable_prefix_length: usize,
) -> Option<String> {
(stable_prefix_length != 0).then(|| {
samples
.iter()
.find_map(|sample| {
sample
.locomotive_catalog_entries
.get(stable_prefix_length - 1)
})
.map(|entry| entry.name.clone())
.expect("stable prefix entry should exist")
})
}
fn stable_catalog_prefix_length(samples: &[RuntimeLocomotiveCatalogTailScanSample]) -> usize {
let catalog_samples = samples
.iter()
.filter(|sample| !sample.locomotive_catalog_entries.is_empty())
.collect::<Vec<_>>();
if catalog_samples.is_empty() {
return 0;
}
let min_count = catalog_samples
.iter()
.map(|sample| sample.locomotive_catalog_entries.len())
.min()
.unwrap_or(0);
let mut stable_prefix_length = 0usize;
for index in 0..min_count {
let first_name = &catalog_samples[0].locomotive_catalog_entries[index].name;
if catalog_samples
.iter()
.all(|sample| sample.locomotive_catalog_entries[index].name == *first_name)
{
stable_prefix_length += 1;
} else {
break;
}
}
stable_prefix_length
}
fn build_tail_clusters(
samples: &[RuntimeLocomotiveCatalogTailScanSample],
stable_prefix_length: usize,
) -> Vec<RuntimeLocomotiveCatalogTailCluster> {
let mut grouped = BTreeMap::<
Vec<RuntimeObservedLocomotiveCatalogEntry>,
Vec<&RuntimeLocomotiveCatalogTailScanSample>,
>::new();
for sample in samples
.iter()
.filter(|sample| !sample.locomotive_catalog_entries.is_empty())
{
grouped
.entry(tail_entries(sample, stable_prefix_length))
.or_default()
.push(sample);
}
let mut clusters = grouped
.into_iter()
.map(|(tail_entries, samples)| {
let mut sample_paths = samples
.iter()
.map(|sample| sample.path.clone())
.collect::<Vec<_>>();
let mut map_paths = samples
.iter()
.filter_map(|sample| sample.map_path.clone())
.collect::<BTreeSet<_>>()
.into_iter()
.collect::<Vec<_>>();
sample_paths.sort();
map_paths.sort();
RuntimeLocomotiveCatalogTailCluster {
entry_count: stable_prefix_length + tail_entries.len(),
tail_entries,
file_count: samples.len(),
sample_paths,
map_paths,
}
})
.collect::<Vec<_>>();
clusters.sort_by(|left, right| {
left.tail_entries
.cmp(&right.tail_entries)
.then(left.entry_count.cmp(&right.entry_count))
});
clusters
}
fn tail_entries(
sample: &RuntimeLocomotiveCatalogTailScanSample,
stable_prefix_length: usize,
) -> Vec<RuntimeObservedLocomotiveCatalogEntry> {
sample
.locomotive_catalog_entries
.iter()
.skip(stable_prefix_length)
.map(|entry| RuntimeObservedLocomotiveCatalogEntry {
locomotive_id: entry.locomotive_id,
name: entry.name.clone(),
})
.collect()
}
fn build_extra_shipped_name_coverage(
samples: &[RuntimeLocomotiveCatalogTailScanSample],
) -> Vec<RuntimeLocomotiveExtraNameCoverageSummary> {
EXTRA_SHIPPED_ENGINE_TYPE_NAMES
.into_iter()
.map(|name| {
let mut observed_ordinals = BTreeSet::new();
let mut sample_paths = Vec::new();
for sample in samples {
let matching_ordinals = sample
.locomotive_catalog_entries
.iter()
.filter(|entry| entry.name == name)
.map(|entry| entry.locomotive_id)
.collect::<Vec<_>>();
if matching_ordinals.is_empty() {
continue;
}
observed_ordinals.extend(matching_ordinals);
sample_paths.push(sample.path.clone());
}
sample_paths.sort();
RuntimeLocomotiveExtraNameCoverageSummary {
name: name.to_string(),
observed_in_catalog_file_count: sample_paths.len(),
observed_ordinals: observed_ordinals.into_iter().collect(),
sample_paths,
}
})
.collect()
}
fn build_descriptor_band_hit_summary(
samples: &[RuntimeLocomotiveCatalogTailScanSample],
descriptor_ids: std::ops::RangeInclusive<u32>,
) -> RuntimeLocomotiveDescriptorBandHitSummary {
let mut row_count = 0usize;
let mut carrier_paths = Vec::new();
let mut descriptor_ids_present = BTreeSet::new();
let mut descriptor_labels_present = BTreeSet::new();
for sample in samples {
let matching_rows = sample
.descriptor_rows
.iter()
.filter(|row| descriptor_ids.contains(&row.descriptor_id))
.collect::<Vec<_>>();
if matching_rows.is_empty() {
continue;
}
row_count += matching_rows.len();
carrier_paths.push(sample.path.clone());
for row in matching_rows {
descriptor_ids_present.insert(row.descriptor_id);
if let Some(label) = &row.descriptor_label {
descriptor_labels_present.insert(label.clone());
}
}
}
carrier_paths.sort();
RuntimeLocomotiveDescriptorBandHitSummary {
row_count,
file_count: carrier_paths.len(),
descriptor_ids_present: descriptor_ids_present.into_iter().collect(),
descriptor_labels_present: descriptor_labels_present.into_iter().collect(),
carrier_paths,
}
}
fn load_locomotive_catalog_tail_scan_sample(
smp_path: &Path,
) -> Result<RuntimeLocomotiveCatalogTailScanSample, Box<dyn std::error::Error>> {
let save_slice = load_save_slice_file(smp_path)?;
Ok(build_locomotive_catalog_tail_scan_sample(
smp_path,
&save_slice,
))
}
fn build_locomotive_catalog_tail_scan_sample(
smp_path: &Path,
save_slice: &SmpLoadedSaveSlice,
) -> RuntimeLocomotiveCatalogTailScanSample {
RuntimeLocomotiveCatalogTailScanSample {
path: smp_path.display().to_string(),
container_profile_family: save_slice.container_profile_family.clone(),
mechanism_family: save_slice.mechanism_family.clone(),
map_path: save_slice
.profile
.as_ref()
.and_then(|profile| profile.map_path.clone()),
display_name: save_slice
.profile
.as_ref()
.and_then(|profile| profile.display_name.clone()),
named_locomotive_table_entry_count: save_slice
.named_locomotive_availability_table
.as_ref()
.map(|table| table.observed_entry_count),
locomotive_catalog_entries: save_slice
.locomotive_catalog
.as_ref()
.map(|catalog| catalog.entries.clone())
.unwrap_or_default(),
descriptor_rows: save_slice
.event_runtime_collection
.as_ref()
.map(|summary| {
summary
.records
.iter()
.flat_map(|record| {
record.grouped_effect_rows.iter().map(|row| {
RuntimeLocomotiveDescriptorRowHit {
descriptor_id: row.descriptor_id,
descriptor_label: row.descriptor_label.clone(),
}
})
})
.collect()
})
.unwrap_or_default(),
}
}
fn collect_locomotive_tail_input_paths(
root_path: &Path,
out: &mut Vec<PathBuf>,
) -> Result<(), Box<dyn std::error::Error>> {
let metadata = match fs::symlink_metadata(root_path) {
Ok(metadata) => metadata,
Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => return Ok(()),
Err(err) => return Err(err.into()),
};
if metadata.file_type().is_symlink() {
return Ok(());
}
if root_path.is_file() {
if path_is_locomotive_tail_input(root_path) {
out.push(root_path.to_path_buf());
}
return Ok(());
}
let entries = match fs::read_dir(root_path) {
Ok(entries) => entries,
Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => return Ok(()),
Err(err) => return Err(err.into()),
};
for entry in entries {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
collect_locomotive_tail_input_paths(&path, out)?;
continue;
}
if path_is_locomotive_tail_input(&path) {
out.push(path);
}
}
Ok(())
}
fn path_is_locomotive_tail_input(path: &Path) -> bool {
path.extension()
.and_then(|ext| ext.to_str())
.is_some_and(|ext| matches!(ext.to_ascii_lowercase().as_str(), "gms" | "gmx" | "smp"))
}
#[cfg(test)]
mod tests {
use super::*;
fn sample(
path: &str,
names: &[&str],
descriptor_ids: &[u32],
) -> RuntimeLocomotiveCatalogTailScanSample {
RuntimeLocomotiveCatalogTailScanSample {
path: path.to_string(),
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
mechanism_family: "classic-save-rehydrate-v1".to_string(),
map_path: Some(format!("{path}.gmp")),
display_name: Some(path.to_string()),
named_locomotive_table_entry_count: Some(names.len()),
locomotive_catalog_entries: names
.iter()
.enumerate()
.map(|(index, name)| SmpLoadedLocomotiveCatalogEntry {
locomotive_id: (index + 1) as u32,
name: (*name).to_string(),
})
.collect(),
descriptor_rows: descriptor_ids
.iter()
.map(|descriptor_id| RuntimeLocomotiveDescriptorRowHit {
descriptor_id: *descriptor_id,
descriptor_label: Some(format!("Descriptor {descriptor_id}")),
})
.collect(),
}
}
#[test]
fn computes_stable_prefix_length_across_catalog_samples() {
let samples = vec![
sample("a", &["Big Boy", "VL80T", "242 A1"], &[]),
sample("b", &["Big Boy", "VL80T", "GP 35"], &[]),
];
assert_eq!(stable_catalog_prefix_length(&samples), 2);
assert_eq!(
stable_prefix_last_name(&samples, 2),
Some("VL80T".to_string())
);
}
#[test]
fn groups_tail_clusters_by_exact_tail_sequence() {
let samples = vec![
sample("a", &["Big Boy", "VL80T", "242 A1", "Class 460"], &[]),
sample("b", &["Big Boy", "VL80T", "242 A1", "Class 460"], &[]),
sample("c", &["Big Boy", "VL80T", "GP 35", "U1"], &[]),
];
let clusters = build_tail_clusters(&samples, 2);
assert_eq!(clusters.len(), 2);
let counts = clusters
.iter()
.map(|cluster| cluster.file_count)
.collect::<Vec<_>>();
assert_eq!(counts, vec![2, 1]);
}
#[test]
fn summarizes_extra_shipped_name_coverage() {
let samples = vec![
sample("a", &["Big Boy", "242 A1", "Class 460"], &[]),
sample("b", &["Big Boy", "Class QJ"], &[]),
];
let coverage = build_extra_shipped_name_coverage(&samples);
assert_eq!(coverage[0].name, "242 A1");
assert_eq!(coverage[0].observed_in_catalog_file_count, 1);
assert_eq!(coverage[0].observed_ordinals, vec![2]);
assert_eq!(coverage[4].name, "Class QJ");
assert_eq!(coverage[4].observed_in_catalog_file_count, 1);
assert_eq!(coverage[4].observed_ordinals, vec![2]);
}
#[test]
fn summarizes_descriptor_band_hits() {
let samples = vec![
sample("a", &["Big Boy"], &[452, 457, 475]),
sample("b", &["Big Boy"], &[457, 458]),
];
let descriptor_452_hits = build_descriptor_band_hit_summary(&samples, 452..=452);
let upper_availability_hits = build_descriptor_band_hit_summary(&samples, 457..=474);
assert_eq!(descriptor_452_hits.row_count, 1);
assert_eq!(descriptor_452_hits.file_count, 1);
assert_eq!(upper_availability_hits.row_count, 3);
assert_eq!(upper_availability_hits.file_count, 2);
assert_eq!(
upper_availability_hits.descriptor_ids_present,
vec![457, 458]
);
}
#[test]
fn accepts_gmx_inputs_in_locomotive_tail_scan() {
assert!(path_is_locomotive_tail_input(Path::new("save.gms")));
assert!(path_is_locomotive_tail_input(Path::new("sandbox.gmx")));
assert!(path_is_locomotive_tail_input(Path::new("fixture.smp")));
assert!(!path_is_locomotive_tail_input(Path::new("map.gmp")));
}
}

View file

@ -3,11 +3,13 @@ pub(crate) mod post_special;
mod aligned_band; mod aligned_band;
mod candidate_table; mod candidate_table;
mod locomotive_tail;
mod recipe_book; mod recipe_book;
mod special_conditions; mod special_conditions;
pub(super) use aligned_band::scan_aligned_runtime_rule_band; pub(super) use aligned_band::scan_aligned_runtime_rule_band;
pub(super) use candidate_table::{scan_candidate_table_headers, scan_candidate_table_named_runs}; pub(super) use candidate_table::{scan_candidate_table_headers, scan_candidate_table_named_runs};
pub(super) use locomotive_tail::scan_locomotive_catalog_tail;
pub(super) use post_special::{ pub(super) use post_special::{
scan_post_special_conditions_scalars, scan_post_special_conditions_tail, scan_post_special_conditions_scalars, scan_post_special_conditions_tail,
}; };

View file

@ -1571,8 +1571,10 @@ fn blocks_scalar_locomotive_availability_rows_without_catalog_context() {
grouped_effect_rows: vec![SmpLoadedPackedEventGroupedEffectRowSummary { grouped_effect_rows: vec![SmpLoadedPackedEventGroupedEffectRowSummary {
group_index: 0, group_index: 0,
row_index: 0, row_index: 0,
descriptor_id: 250, descriptor_id: 302,
descriptor_label: Some("Big Boy 4-8-8-4 Availability".to_string()), descriptor_label: Some(
"Lower-Band Locomotive Availability Slot 62".to_string(),
),
target_mask_bits: Some(0x08), target_mask_bits: Some(0x08),
parameter_family: Some("locomotive_availability_scalar".to_string()), parameter_family: Some("locomotive_availability_scalar".to_string()),
grouped_target_subject: None, grouped_target_subject: None,
@ -1587,11 +1589,13 @@ fn blocks_scalar_locomotive_availability_rows_without_catalog_context() {
value_word_0x16: 0, value_word_0x16: 0,
row_shape: "scalar_assignment".to_string(), row_shape: "scalar_assignment".to_string(),
semantic_family: Some("scalar_assignment".to_string()), semantic_family: Some("scalar_assignment".to_string()),
semantic_preview: Some("Set Big Boy 4-8-8-4 Availability to 42".to_string()), semantic_preview: Some(
"Set Lower-Band Locomotive Availability Slot 62 to 42".to_string(),
),
recovered_cargo_slot: None, recovered_cargo_slot: None,
recovered_cargo_class: None, recovered_cargo_class: None,
recovered_cargo_label: None, recovered_cargo_label: None,
recovered_locomotive_id: Some(10), recovered_locomotive_id: Some(62),
locomotive_name: None, locomotive_name: None,
notes: vec![], notes: vec![],
}], }],
@ -1599,8 +1603,9 @@ fn blocks_scalar_locomotive_availability_rows_without_catalog_context() {
decoded_actions: vec![], decoded_actions: vec![],
executable_import_ready: false, executable_import_ready: false,
notes: vec![ notes: vec![
"decoded from grounded real 0x4e9a row framing".to_string(), "decoded from lower-tail real 0x4e9a row framing".to_string(),
"scalar locomotive availability rows still need catalog context".to_string(), "scalar lower-tail locomotive availability row still needs catalog context"
.to_string(),
], ],
}], }],
}), }),
@ -1609,7 +1614,7 @@ fn blocks_scalar_locomotive_availability_rows_without_catalog_context() {
let input = build_runtime_state_input_from_save_slice( let input = build_runtime_state_input_from_save_slice(
&save_slice, &save_slice,
"packed-events-recovered-locomotive-availability-frontier", "packed-events-recovered-locomotive-availability-lower-tail-frontier",
None, None,
) )
.expect("save slice should project"); .expect("save slice should project");
@ -1737,7 +1742,7 @@ fn imports_scalar_locomotive_availability_rows_with_save_derived_catalog_context
bridge_family: None, bridge_family: None,
profile: None, profile: None,
candidate_availability_table: None, candidate_availability_table: None,
named_locomotive_availability_table: Some(save_named_locomotive_table(61)), named_locomotive_availability_table: Some(save_named_locomotive_table(62)),
locomotive_catalog: None, locomotive_catalog: None,
cargo_catalog: None, cargo_catalog: None,
world_issue_37_state: None, world_issue_37_state: None,
@ -1799,7 +1804,7 @@ fn imports_scalar_locomotive_availability_rows_with_save_derived_catalog_context
grouped_effect_row_counts: vec![2, 0, 0, 0], grouped_effect_row_counts: vec![2, 0, 0, 0],
grouped_effect_rows: vec![ grouped_effect_rows: vec![
real_locomotive_availability_row(250, 42), real_locomotive_availability_row(250, 42),
real_locomotive_availability_row(301, 7), real_locomotive_availability_row(302, 7),
], ],
decoded_conditions: Vec::new(), decoded_conditions: Vec::new(),
decoded_actions: vec![], decoded_actions: vec![],
@ -1820,7 +1825,7 @@ fn imports_scalar_locomotive_availability_rows_with_save_derived_catalog_context
) )
.expect("save slice should project"); .expect("save slice should project");
assert_eq!(input.state.locomotive_catalog.len(), 61); assert_eq!(input.state.locomotive_catalog.len(), 62);
assert_eq!(input.state.event_runtime_records.len(), 1); assert_eq!(input.state.event_runtime_records.len(), 1);
assert_eq!( assert_eq!(
input input
@ -1845,11 +1850,228 @@ fn imports_scalar_locomotive_availability_rows_with_save_derived_catalog_context
Some(&42) Some(&42)
); );
assert_eq!( assert_eq!(
input.state.named_locomotive_availability.get("Zephyr"), input
.state
.named_locomotive_availability
.get("Locomotive 62"),
Some(&7) Some(&7)
); );
} }
#[test]
fn imports_scalar_locomotive_availability_rows_with_dynamic_tail_catalog_context() {
let mut names = (0..58)
.map(default_save_named_locomotive_name)
.collect::<Vec<_>>();
names.extend([
"242 A1".to_string(),
"Class 460".to_string(),
"Class A1".to_string(),
"Class P8".to_string(),
"U1".to_string(),
]);
let save_slice = SmpLoadedSaveSlice {
file_extension_hint: Some("gms".to_string()),
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
mechanism_family: "classic-save-rehydrate-v1".to_string(),
mechanism_confidence: "grounded".to_string(),
trailer_family: None,
bridge_family: None,
profile: None,
candidate_availability_table: None,
named_locomotive_availability_table: Some(save_named_locomotive_table_with_names(&names)),
locomotive_catalog: None,
cargo_catalog: None,
world_issue_37_state: None,
world_economic_tuning_state: None,
world_finance_neighborhood_state: None,
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
region_fixed_row_run_summary: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
mechanism_family: "classic-save-rehydrate-v1".to_string(),
mechanism_confidence: "grounded".to_string(),
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
metadata_tag_offset: 0x7100,
records_tag_offset: 0x7200,
close_tag_offset: 0x7600,
packed_state_version: 0x3e9,
packed_state_version_hex: "0x000003e9".to_string(),
live_id_bound: 34,
live_record_count: 1,
live_entry_ids: vec![34],
decoded_record_count: 1,
imported_runtime_record_count: 0,
records_with_trigger_kind: 0,
records_missing_trigger_kind: 0,
nondirect_compact_record_count: 0,
nondirect_compact_records_missing_trigger_kind: 0,
trigger_kinds_present: vec![],
control_lane_notes: vec![],
add_building_dispatch_strip_record_indexes: vec![],
add_building_dispatch_strip_descriptor_labels: vec![],
add_building_dispatch_strip_records_with_trigger_kind: 0,
add_building_dispatch_strip_records_missing_trigger_kind: 0,
add_building_dispatch_strip_row_shape_families: vec![],
add_building_dispatch_strip_signature_families: vec![],
add_building_dispatch_strip_condition_tuple_families: vec![],
add_building_dispatch_strip_signature_condition_clusters: vec![],
records: vec![SmpLoadedPackedEventRecordSummary {
record_index: 0,
live_entry_id: 34,
payload_offset: Some(0x7202),
payload_len: Some(96),
decode_status: "parity_only".to_string(),
payload_family: "real_packed_v1".to_string(),
trigger_kind: Some(7),
active: None,
marks_collection_dirty: None,
one_shot: Some(false),
compact_control: Some(real_compact_control()),
text_bands: vec![],
standalone_condition_row_count: 0,
standalone_condition_rows: vec![],
negative_sentinel_scope: None,
grouped_effect_row_counts: vec![1, 0, 0, 0],
grouped_effect_rows: vec![real_locomotive_availability_row(299, 7)],
decoded_conditions: Vec::new(),
decoded_actions: vec![],
executable_import_ready: false,
notes: vec![
"save-derived locomotive availability row uses scenario-dependent tail names"
.to_string(),
],
}],
}),
notes: vec![],
};
let mut input = build_runtime_state_input_from_save_slice(
&save_slice,
"save-derived-dynamic-tail-locomotive-availability",
None,
)
.expect("save slice should project");
execute_step_command(
&mut input.state,
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
)
.expect("save-derived dynamic-tail locomotive availability record should run");
assert_eq!(
input.state.named_locomotive_availability.get("242 A1"),
Some(&7)
);
}
#[test]
fn keeps_upper_band_locomotive_availability_rows_on_descriptor_parity() {
let save_slice = SmpLoadedSaveSlice {
file_extension_hint: Some("gms".to_string()),
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
mechanism_family: "classic-save-rehydrate-v1".to_string(),
mechanism_confidence: "grounded".to_string(),
trailer_family: None,
bridge_family: None,
profile: None,
candidate_availability_table: None,
named_locomotive_availability_table: Some(save_named_locomotive_table(140)),
locomotive_catalog: None,
cargo_catalog: None,
world_issue_37_state: None,
world_economic_tuning_state: None,
world_finance_neighborhood_state: None,
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
region_fixed_row_run_summary: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
mechanism_family: "classic-save-rehydrate-v1".to_string(),
mechanism_confidence: "grounded".to_string(),
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
metadata_tag_offset: 0x7100,
records_tag_offset: 0x7200,
close_tag_offset: 0x7600,
packed_state_version: 0x3e9,
packed_state_version_hex: "0x000003e9".to_string(),
live_id_bound: 34,
live_record_count: 1,
live_entry_ids: vec![34],
decoded_record_count: 1,
imported_runtime_record_count: 0,
records_with_trigger_kind: 0,
records_missing_trigger_kind: 0,
nondirect_compact_record_count: 0,
nondirect_compact_records_missing_trigger_kind: 0,
trigger_kinds_present: vec![],
control_lane_notes: vec![],
add_building_dispatch_strip_record_indexes: vec![],
add_building_dispatch_strip_descriptor_labels: vec![],
add_building_dispatch_strip_records_with_trigger_kind: 0,
add_building_dispatch_strip_records_missing_trigger_kind: 0,
add_building_dispatch_strip_row_shape_families: vec![],
add_building_dispatch_strip_signature_families: vec![],
add_building_dispatch_strip_condition_tuple_families: vec![],
add_building_dispatch_strip_signature_condition_clusters: vec![],
records: vec![SmpLoadedPackedEventRecordSummary {
record_index: 0,
live_entry_id: 34,
payload_offset: Some(0x7202),
payload_len: Some(96),
decode_status: "parity_only".to_string(),
payload_family: "real_packed_v1".to_string(),
trigger_kind: Some(7),
active: None,
marks_collection_dirty: None,
one_shot: Some(false),
compact_control: Some(real_compact_control()),
text_bands: vec![],
standalone_condition_row_count: 0,
standalone_condition_rows: vec![],
negative_sentinel_scope: None,
grouped_effect_row_counts: vec![1, 0, 0, 0],
grouped_effect_rows: vec![real_locomotive_availability_row(457, 1)],
decoded_conditions: Vec::new(),
decoded_actions: vec![],
executable_import_ready: false,
notes: vec![
"upper-band locomotive availability row remains descriptor parity".to_string(),
],
}],
}),
notes: vec![],
};
let input = build_runtime_state_input_from_save_slice(
&save_slice,
"packed-events-upper-band-locomotive-availability",
None,
)
.expect("save slice should project");
assert!(input.state.event_runtime_records.is_empty());
assert_eq!(
input
.state
.packed_event_collection
.as_ref()
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
Some("blocked_evidence_blocked_descriptor")
);
}
#[test] #[test]
fn overlays_scalar_locomotive_availability_rows_into_named_availability_effects() { fn overlays_scalar_locomotive_availability_rows_into_named_availability_effects() {
let base_state = RuntimeState { let base_state = RuntimeState {
@ -2099,11 +2321,13 @@ fn blocks_recovered_locomotive_cost_rows_without_catalog_context_lower_band() {
standalone_condition_rows: vec![], standalone_condition_rows: vec![],
negative_sentinel_scope: None, negative_sentinel_scope: None,
grouped_effect_row_counts: vec![1, 0, 0, 0], grouped_effect_row_counts: vec![1, 0, 0, 0],
grouped_effect_rows: vec![real_locomotive_cost_row(352, 250000)], grouped_effect_rows: vec![real_locomotive_cost_row(413, 250000)],
decoded_conditions: Vec::new(), decoded_conditions: Vec::new(),
decoded_actions: vec![], decoded_actions: vec![],
executable_import_ready: false, executable_import_ready: false,
notes: vec!["scalar locomotive cost row still needs catalog context".to_string()], notes: vec![
"scalar lower-tail locomotive cost row still needs catalog context".to_string(),
],
}], }],
}), }),
notes: vec![], notes: vec![],
@ -2237,7 +2461,7 @@ fn imports_scalar_locomotive_cost_rows_with_save_derived_catalog_context() {
bridge_family: None, bridge_family: None,
profile: None, profile: None,
candidate_availability_table: None, candidate_availability_table: None,
named_locomotive_availability_table: Some(save_named_locomotive_table(61)), named_locomotive_availability_table: Some(save_named_locomotive_table(62)),
locomotive_catalog: None, locomotive_catalog: None,
cargo_catalog: None, cargo_catalog: None,
world_issue_37_state: None, world_issue_37_state: None,
@ -2299,7 +2523,7 @@ fn imports_scalar_locomotive_cost_rows_with_save_derived_catalog_context() {
grouped_effect_row_counts: vec![2, 0, 0, 0], grouped_effect_row_counts: vec![2, 0, 0, 0],
grouped_effect_rows: vec![ grouped_effect_rows: vec![
real_locomotive_cost_row(352, 250000), real_locomotive_cost_row(352, 250000),
real_locomotive_cost_row(412, 325000), real_locomotive_cost_row(413, 325000),
], ],
decoded_conditions: Vec::new(), decoded_conditions: Vec::new(),
decoded_actions: vec![], decoded_actions: vec![],
@ -2319,7 +2543,7 @@ fn imports_scalar_locomotive_cost_rows_with_save_derived_catalog_context() {
) )
.expect("save slice should project"); .expect("save slice should project");
assert_eq!(input.state.locomotive_catalog.len(), 61); assert_eq!(input.state.locomotive_catalog.len(), 62);
assert_eq!(input.state.event_runtime_records.len(), 1); assert_eq!(input.state.event_runtime_records.len(), 1);
assert_eq!( assert_eq!(
input input
@ -2341,11 +2565,110 @@ fn imports_scalar_locomotive_cost_rows_with_save_derived_catalog_context() {
Some(&250000) Some(&250000)
); );
assert_eq!( assert_eq!(
input.state.named_locomotive_cost.get("Zephyr"), input.state.named_locomotive_cost.get("Locomotive 62"),
Some(&325000) Some(&325000)
); );
} }
#[test]
fn keeps_upper_band_locomotive_cost_rows_on_descriptor_parity() {
let save_slice = SmpLoadedSaveSlice {
file_extension_hint: Some("gms".to_string()),
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
mechanism_family: "classic-save-rehydrate-v1".to_string(),
mechanism_confidence: "grounded".to_string(),
trailer_family: None,
bridge_family: None,
profile: None,
candidate_availability_table: None,
named_locomotive_availability_table: Some(save_named_locomotive_table(140)),
locomotive_catalog: None,
cargo_catalog: None,
world_issue_37_state: None,
world_economic_tuning_state: None,
world_finance_neighborhood_state: None,
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
region_fixed_row_run_summary: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
mechanism_family: "classic-save-rehydrate-v1".to_string(),
mechanism_confidence: "grounded".to_string(),
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
metadata_tag_offset: 0x7100,
records_tag_offset: 0x7200,
close_tag_offset: 0x7600,
packed_state_version: 0x3e9,
packed_state_version_hex: "0x000003e9".to_string(),
live_id_bound: 42,
live_record_count: 1,
live_entry_ids: vec![42],
decoded_record_count: 1,
imported_runtime_record_count: 0,
records_with_trigger_kind: 0,
records_missing_trigger_kind: 0,
nondirect_compact_record_count: 0,
nondirect_compact_records_missing_trigger_kind: 0,
trigger_kinds_present: vec![],
control_lane_notes: vec![],
add_building_dispatch_strip_record_indexes: vec![],
add_building_dispatch_strip_descriptor_labels: vec![],
add_building_dispatch_strip_records_with_trigger_kind: 0,
add_building_dispatch_strip_records_missing_trigger_kind: 0,
add_building_dispatch_strip_row_shape_families: vec![],
add_building_dispatch_strip_signature_families: vec![],
add_building_dispatch_strip_condition_tuple_families: vec![],
add_building_dispatch_strip_signature_condition_clusters: vec![],
records: vec![SmpLoadedPackedEventRecordSummary {
record_index: 0,
live_entry_id: 42,
payload_offset: Some(0x7202),
payload_len: Some(96),
decode_status: "parity_only".to_string(),
payload_family: "real_packed_v1".to_string(),
trigger_kind: Some(7),
active: None,
marks_collection_dirty: None,
one_shot: Some(false),
compact_control: Some(real_compact_control()),
text_bands: vec![],
standalone_condition_row_count: 0,
standalone_condition_rows: vec![],
negative_sentinel_scope: None,
grouped_effect_row_counts: vec![1, 0, 0, 0],
grouped_effect_rows: vec![real_locomotive_cost_row(475, 250000)],
decoded_conditions: Vec::new(),
decoded_actions: vec![],
executable_import_ready: false,
notes: vec!["upper-band locomotive cost row remains descriptor parity".to_string()],
}],
}),
notes: vec![],
};
let input = build_runtime_state_input_from_save_slice(
&save_slice,
"packed-events-upper-band-locomotive-cost",
None,
)
.expect("save slice should project");
assert!(input.state.event_runtime_records.is_empty());
assert_eq!(
input
.state
.packed_event_collection
.as_ref()
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
Some("blocked_evidence_blocked_descriptor")
);
}
#[test] #[test]
fn overlays_scalar_locomotive_cost_rows_into_named_cost_effects() { fn overlays_scalar_locomotive_cost_rows_into_named_cost_effects() {
let base_state = RuntimeState { let base_state = RuntimeState {
@ -2519,6 +2842,119 @@ fn overlays_scalar_locomotive_cost_rows_into_named_cost_effects() {
); );
} }
#[test]
fn imports_scalar_locomotive_cost_rows_with_dynamic_tail_catalog_context() {
let mut names = (0..58)
.map(default_save_named_locomotive_name)
.collect::<Vec<_>>();
names.extend([
"242 A1".to_string(),
"Class 460".to_string(),
"Class A1".to_string(),
"Class P8".to_string(),
"U1".to_string(),
]);
let save_slice = SmpLoadedSaveSlice {
file_extension_hint: Some("gms".to_string()),
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
mechanism_family: "classic-save-rehydrate-v1".to_string(),
mechanism_confidence: "grounded".to_string(),
trailer_family: None,
bridge_family: None,
profile: None,
candidate_availability_table: None,
named_locomotive_availability_table: Some(save_named_locomotive_table_with_names(&names)),
locomotive_catalog: None,
cargo_catalog: None,
world_issue_37_state: None,
world_economic_tuning_state: None,
world_finance_neighborhood_state: None,
world_locomotive_policy_state: None,
company_roster: None,
chairman_profile_table: None,
region_collection: None,
region_fixed_row_run_summary: None,
placed_structure_collection: None,
placed_structure_dynamic_side_buffer_summary: None,
special_conditions_table: None,
event_runtime_collection: Some(SmpLoadedEventRuntimeCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
mechanism_family: "classic-save-rehydrate-v1".to_string(),
mechanism_confidence: "grounded".to_string(),
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
metadata_tag_offset: 0x7100,
records_tag_offset: 0x7200,
close_tag_offset: 0x7600,
packed_state_version: 0x3e9,
packed_state_version_hex: "0x000003e9".to_string(),
live_id_bound: 37,
live_record_count: 1,
live_entry_ids: vec![37],
decoded_record_count: 1,
imported_runtime_record_count: 0,
records_with_trigger_kind: 0,
records_missing_trigger_kind: 0,
nondirect_compact_record_count: 0,
nondirect_compact_records_missing_trigger_kind: 0,
trigger_kinds_present: vec![],
control_lane_notes: vec![],
add_building_dispatch_strip_record_indexes: vec![],
add_building_dispatch_strip_descriptor_labels: vec![],
add_building_dispatch_strip_records_with_trigger_kind: 0,
add_building_dispatch_strip_records_missing_trigger_kind: 0,
add_building_dispatch_strip_row_shape_families: vec![],
add_building_dispatch_strip_signature_families: vec![],
add_building_dispatch_strip_condition_tuple_families: vec![],
add_building_dispatch_strip_signature_condition_clusters: vec![],
records: vec![SmpLoadedPackedEventRecordSummary {
record_index: 0,
live_entry_id: 37,
payload_offset: Some(0x7202),
payload_len: Some(96),
decode_status: "parity_only".to_string(),
payload_family: "real_packed_v1".to_string(),
trigger_kind: Some(7),
active: None,
marks_collection_dirty: None,
one_shot: Some(false),
compact_control: Some(real_compact_control()),
text_bands: vec![],
standalone_condition_row_count: 0,
standalone_condition_rows: vec![],
negative_sentinel_scope: None,
grouped_effect_row_counts: vec![1, 0, 0, 0],
grouped_effect_rows: vec![real_locomotive_cost_row(410, 325000)],
decoded_conditions: Vec::new(),
decoded_actions: vec![],
executable_import_ready: false,
notes: vec![
"save-derived locomotive cost row uses scenario-dependent tail names"
.to_string(),
],
}],
}),
notes: vec![],
};
let mut input = build_runtime_state_input_from_save_slice(
&save_slice,
"save-derived-dynamic-tail-locomotive-cost",
None,
)
.expect("save slice should project");
execute_step_command(
&mut input.state,
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
)
.expect("save-derived dynamic-tail locomotive cost record should run");
assert_eq!(
input.state.named_locomotive_cost.get("242 A1"),
Some(&325000)
);
}
#[test] #[test]
fn keeps_negative_locomotive_cost_rows_parity_only() { fn keeps_negative_locomotive_cost_rows_parity_only() {
let save_slice = SmpLoadedSaveSlice { let save_slice = SmpLoadedSaveSlice {

View file

@ -590,9 +590,6 @@ pub(super) fn real_locomotive_availability_row(
56 => Some("Trans-Euro"), 56 => Some("Trans-Euro"),
57 => Some("V200"), 57 => Some("V200"),
58 => Some("VL80T"), 58 => Some("VL80T"),
59 => Some("GP 35"),
60 => Some("U1"),
61 => Some("Zephyr"),
_ => None, _ => None,
} }
} }
@ -709,9 +706,6 @@ pub(super) fn real_locomotive_cost_row(
56 => Some("Trans-Euro"), 56 => Some("Trans-Euro"),
57 => Some("V200"), 57 => Some("V200"),
58 => Some("VL80T"), 58 => Some("VL80T"),
59 => Some("GP 35"),
60 => Some("U1"),
61 => Some("Zephyr"),
_ => None, _ => None,
} }
} }
@ -759,77 +753,78 @@ pub(super) fn real_locomotive_cost_row(
} }
} }
pub(super) fn save_named_locomotive_table( pub(super) fn default_save_named_locomotive_name(index: usize) -> String {
count: usize, match index {
) -> SmpLoadedNamedLocomotiveAvailabilityTable { 0 => "2-D-2",
fn grounded_locomotive_name(index: usize) -> String { 1 => "E-88",
match index { 2 => "Adler 2-2-2",
0 => "2-D-2", 3 => "USA 103",
1 => "E-88", 4 => "American 4-4-0",
2 => "Adler 2-2-2", 5 => "Atlantic 4-4-2",
3 => "USA 103", 6 => "Baldwin 0-6-0",
4 => "American 4-4-0", 7 => "Be 5/7",
5 => "Atlantic 4-4-2", 8 => "Beuth 2-2-2",
6 => "Baldwin 0-6-0", 9 => "Big Boy 4-8-8-4",
7 => "Be 5/7", 10 => "C55 Deltic",
8 => "Beuth 2-2-2", 11 => "Camelback 0-6-0",
9 => "Big Boy 4-8-8-4", 12 => "Challenger 4-6-6-4",
10 => "C55 Deltic", 13 => "Class 01 4-6-2",
11 => "Camelback 0-6-0", 14 => "Class 103",
12 => "Challenger 4-6-6-4", 15 => "Class 132",
13 => "Class 01 4-6-2", 16 => "Class 500 4-6-0",
14 => "Class 103", 17 => "Class 9100",
15 => "Class 132", 18 => "Class EF 66",
16 => "Class 500 4-6-0", 19 => "Class 6E",
17 => "Class 9100", 20 => "Consolidation 2-8-0",
18 => "Class EF 66", 21 => "Crampton 4-2-0",
19 => "Class 6E", 22 => "DD 080-X",
20 => "Consolidation 2-8-0", 23 => "DD40AX",
21 => "Crampton 4-2-0", 24 => "Duke Class 4-4-0",
22 => "DD 080-X", 25 => "E18",
23 => "DD40AX", 26 => "E428",
24 => "Duke Class 4-4-0", 27 => "Brenner E412",
25 => "E18", 28 => "E60CP",
26 => "E428", 29 => "Eight Wheeler 4-4-0",
27 => "Brenner E412", 30 => "EP-2 Bipolar",
28 => "E60CP", 31 => "ET22",
29 => "Eight Wheeler 4-4-0", 32 => "F3",
30 => "EP-2 Bipolar", 33 => "Fairlie 0-6-6-0",
31 => "ET22", 34 => "Firefly 2-2-2",
32 => "F3", 35 => "FP45",
33 => "Fairlie 0-6-6-0", 36 => "Ge 6/6 Crocodile",
34 => "Firefly 2-2-2", 37 => "GG1",
35 => "FP45", 38 => "GP7",
36 => "Ge 6/6 Crocodile", 39 => "H10 2-8-2",
37 => "GG1", 40 => "HST 125",
38 => "GP7", 41 => "Kriegslok 2-10-0",
39 => "H10 2-8-2", 42 => "Mallard 4-6-2",
40 => "HST 125", 43 => "Norris 4-2-0",
41 => "Kriegslok 2-10-0", 44 => "Northern 4-8-4",
42 => "Mallard 4-6-2", 45 => "Orca NX462",
43 => "Norris 4-2-0", 46 => "Pacific 4-6-2",
44 => "Northern 4-8-4", 47 => "Planet 2-2-0",
45 => "Orca NX462", 48 => "Re 6/6",
46 => "Pacific 4-6-2", 49 => "Red Devil 4-8-4",
47 => "Planet 2-2-0", 50 => "S3 4-4-0",
48 => "Re 6/6", 51 => "NA-90D",
49 => "Red Devil 4-8-4", 52 => "Shay (2-Truck)",
50 => "S3 4-4-0", 53 => "Shinkansen Series 0",
51 => "NA-90D", 54 => "Stirling 4-2-2",
52 => "Shay (2-Truck)", 55 => "Trans-Euro",
53 => "Shinkansen Series 0", 56 => "V200",
54 => "Stirling 4-2-2", 57 => "VL80T",
55 => "Trans-Euro", 58 => "GP 35",
56 => "V200", 59 => "U1",
57 => "VL80T", 60 => "Zephyr",
58 => "GP 35", _ => return format!("Locomotive {}", index + 1),
59 => "U1",
60 => "Zephyr",
_ => return format!("Locomotive {}", index + 1),
}
.to_string()
} }
.to_string()
}
pub(super) fn save_named_locomotive_table_with_names(
names: &[String],
) -> SmpLoadedNamedLocomotiveAvailabilityTable {
let count = names.len();
SmpLoadedNamedLocomotiveAvailabilityTable { SmpLoadedNamedLocomotiveAvailabilityTable {
source_kind: "runtime-save-direct-serializer".to_string(), source_kind: "runtime-save-direct-serializer".to_string(),
semantic_family: "scenario-named-locomotive-availability-table".to_string(), semantic_family: "scenario-named-locomotive-availability-table".to_string(),
@ -839,11 +834,13 @@ pub(super) fn save_named_locomotive_table(
observed_entry_count: count, observed_entry_count: count,
zero_availability_count: 0, zero_availability_count: 0,
zero_availability_names: vec![], zero_availability_names: vec![],
entries: (0..count) entries: names
.map(|index| SmpRt3105SaveNameTableEntry { .iter()
.enumerate()
.map(|(index, name)| SmpRt3105SaveNameTableEntry {
index, index,
offset: 0x7c78 + index * 0x41, offset: 0x7c78 + index * 0x41,
text: grounded_locomotive_name(index), text: name.clone(),
availability_dword: 1, availability_dword: 1,
availability_dword_hex: "0x00000001".to_string(), availability_dword_hex: "0x00000001".to_string(),
trailer_word: 1, trailer_word: 1,
@ -853,6 +850,16 @@ pub(super) fn save_named_locomotive_table(
} }
} }
pub(super) fn save_named_locomotive_table(
count: usize,
) -> SmpLoadedNamedLocomotiveAvailabilityTable {
let names = (0..count)
.map(default_save_named_locomotive_name)
.collect::<Vec<_>>();
save_named_locomotive_table_with_names(&names)
}
pub(super) fn save_cargo_catalog( pub(super) fn save_cargo_catalog(
entries: &[(u32, crate::event::targets::RuntimeCargoClass)], entries: &[(u32, crate::event::targets::RuntimeCargoClass)],
) -> SmpLoadedCargoCatalog { ) -> SmpLoadedCargoCatalog {

View file

@ -2,24 +2,21 @@ use super::super::super::*;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::sync::OnceLock; use std::sync::OnceLock;
const STATIC_GROUNDED_LOCOMOTIVE_NAME_MAX_ID: u32 = 58;
pub(in crate::inspect::smp) fn recovered_locomotive_availability_descriptor_metadata( pub(in crate::inspect::smp) fn recovered_locomotive_availability_descriptor_metadata(
descriptor_id: u32, descriptor_id: u32,
) -> Option<RealGroupedEffectDescriptorMetadata> { ) -> Option<RealGroupedEffectDescriptorMetadata> {
if let Some(loco_id) = recovered_locomotive_availability_loco_id(descriptor_id) { if let Some(loco_id) = recovered_locomotive_availability_loco_id(descriptor_id) {
let label = recovered_locomotive_availability_label(loco_id); let label = recovered_locomotive_availability_label(loco_id);
let executable_in_runtime = (loco_id as usize) <= GROUNDED_LOCOMOTIVE_PREFIX.len();
return Some(RealGroupedEffectDescriptorMetadata { return Some(RealGroupedEffectDescriptorMetadata {
descriptor_id, descriptor_id,
label, label,
target_mask_bits: 0x08, target_mask_bits: 0x08,
parameter_family: "locomotive_availability_scalar", parameter_family: "locomotive_availability_scalar",
runtime_key: None, runtime_key: None,
runtime_status: if executable_in_runtime { runtime_status: RealGroupedEffectRuntimeStatus::Executable,
RealGroupedEffectRuntimeStatus::Executable executable_in_runtime: true,
} else {
RealGroupedEffectRuntimeStatus::EvidenceBlocked
},
executable_in_runtime,
}); });
} }
(457..=474) (457..=474)
@ -45,6 +42,9 @@ pub(in crate::inspect::smp) fn recovered_locomotive_availability_loco_id(
} }
pub(in crate::inspect::smp) fn grounded_locomotive_name(loco_id: u32) -> Option<&'static str> { pub(in crate::inspect::smp) fn grounded_locomotive_name(loco_id: u32) -> Option<&'static str> {
if loco_id > STATIC_GROUNDED_LOCOMOTIVE_NAME_MAX_ID {
return None;
}
let index = loco_id.checked_sub(1)? as usize; let index = loco_id.checked_sub(1)? as usize;
GROUNDED_LOCOMOTIVE_PREFIX.get(index).copied() GROUNDED_LOCOMOTIVE_PREFIX.get(index).copied()
} }
@ -153,8 +153,7 @@ pub(in crate::inspect::smp) fn recovered_locomotive_cost_descriptor_metadata(
descriptor_id: u32, descriptor_id: u32,
) -> Option<RealGroupedEffectDescriptorMetadata> { ) -> Option<RealGroupedEffectDescriptorMetadata> {
recovered_locomotive_cost_label(descriptor_id).map(|label| { recovered_locomotive_cost_label(descriptor_id).map(|label| {
let executable_in_runtime = recovered_locomotive_cost_loco_id(descriptor_id) let executable_in_runtime = recovered_locomotive_cost_loco_id(descriptor_id).is_some();
.is_some_and(|loco_id| (loco_id as usize) <= GROUNDED_LOCOMOTIVE_PREFIX.len());
RealGroupedEffectDescriptorMetadata { RealGroupedEffectDescriptorMetadata {
descriptor_id, descriptor_id,
label, label,

View file

@ -223,11 +223,33 @@ fn looks_up_upper_band_recovered_locomotive_availability_descriptor_metadata() {
#[test] #[test]
fn looks_up_extended_lower_band_locomotive_availability_descriptor_metadata() { fn looks_up_extended_lower_band_locomotive_availability_descriptor_metadata() {
let metadata = let metadata =
real_grouped_effect_descriptor_metadata(301).expect("descriptor metadata should exist"); real_grouped_effect_descriptor_metadata(298).expect("descriptor metadata should exist");
assert_eq!(metadata.label, "Zephyr Availability"); assert_eq!(metadata.label, "VL80T Availability");
assert_eq!(metadata.parameter_family, "locomotive_availability_scalar"); assert_eq!(metadata.parameter_family, "locomotive_availability_scalar");
assert_eq!(recovered_locomotive_availability_loco_id(301), Some(61)); assert_eq!(recovered_locomotive_availability_loco_id(298), Some(58));
assert!(metadata.executable_in_runtime);
}
#[test]
fn looks_up_first_unstable_lower_band_locomotive_availability_descriptor_metadata() {
let metadata =
real_grouped_effect_descriptor_metadata(299).expect("descriptor metadata should exist");
assert_eq!(metadata.label, "Lower-Band Locomotive Availability Slot 59");
assert_eq!(metadata.parameter_family, "locomotive_availability_scalar");
assert_eq!(recovered_locomotive_availability_loco_id(299), Some(59));
assert!(metadata.executable_in_runtime);
}
#[test]
fn looks_up_lower_tail_locomotive_availability_descriptor_metadata() {
let metadata =
real_grouped_effect_descriptor_metadata(302).expect("descriptor metadata should exist");
assert_eq!(metadata.label, "Lower-Band Locomotive Availability Slot 62");
assert_eq!(metadata.parameter_family, "locomotive_availability_scalar");
assert_eq!(recovered_locomotive_availability_loco_id(302), Some(62));
assert!(metadata.executable_in_runtime); assert!(metadata.executable_in_runtime);
} }
@ -359,11 +381,33 @@ fn looks_up_recovered_upper_band_locomotive_cost_descriptor_metadata() {
#[test] #[test]
fn looks_up_extended_lower_band_locomotive_cost_descriptor_metadata() { fn looks_up_extended_lower_band_locomotive_cost_descriptor_metadata() {
let metadata = let metadata =
real_grouped_effect_descriptor_metadata(412).expect("descriptor metadata should exist"); real_grouped_effect_descriptor_metadata(409).expect("descriptor metadata should exist");
assert_eq!(metadata.label, "Zephyr Cost"); assert_eq!(metadata.label, "VL80T Cost");
assert_eq!(metadata.parameter_family, "locomotive_cost_scalar"); assert_eq!(metadata.parameter_family, "locomotive_cost_scalar");
assert_eq!(recovered_locomotive_cost_loco_id(412), Some(61)); assert_eq!(recovered_locomotive_cost_loco_id(409), Some(58));
assert!(metadata.executable_in_runtime);
}
#[test]
fn looks_up_first_unstable_lower_band_locomotive_cost_descriptor_metadata() {
let metadata =
real_grouped_effect_descriptor_metadata(410).expect("descriptor metadata should exist");
assert_eq!(metadata.label, "Lower-Band Locomotive Cost Slot 59");
assert_eq!(metadata.parameter_family, "locomotive_cost_scalar");
assert_eq!(recovered_locomotive_cost_loco_id(410), Some(59));
assert!(metadata.executable_in_runtime);
}
#[test]
fn looks_up_lower_tail_locomotive_cost_descriptor_metadata() {
let metadata =
real_grouped_effect_descriptor_metadata(413).expect("descriptor metadata should exist");
assert_eq!(metadata.label, "Lower-Band Locomotive Cost Slot 62");
assert_eq!(metadata.parameter_family, "locomotive_cost_scalar");
assert_eq!(recovered_locomotive_cost_loco_id(413), Some(62));
assert!(metadata.executable_in_runtime); assert!(metadata.executable_in_runtime);
} }

View file

@ -313,8 +313,12 @@ The highest-value next passes are now:
- recovered scalar locomotive availability and locomotive-cost descriptors now import through that - recovered scalar locomotive availability and locomotive-cost descriptors now import through that
save-native or embedded `RuntimeState.locomotive_catalog` context into the ordinary save-native or embedded `RuntimeState.locomotive_catalog` context into the ordinary
`named_locomotive_availability` and `named_locomotive_cost` runtime maps `named_locomotive_availability` and `named_locomotive_cost` runtime maps
- the grounded executable lower locomotive prefix now extends through save-backed locomotive id - the full lower locomotive bands now execute through that save-native ordinal catalog too:
`61` (`Zephyr`); the unresolved lower tail and upper locomotive bands stay on explicit parity availability `241..351` and cost `352..451`; the checked `29`-save `.gms + .gmx`
`locomotive-catalog-tail-census.json` export now fixes the last save-stable static descriptor
boundary at ordinal `58` (`VL80T`) and leaves descriptor `452` plus the upper bands
`457..474` / `475..502` as external-corpus or dynamic blockers rather than active repo-local
static work
- cargo-production `230..240` and territory-access-cost `453` now execute too through minimal - cargo-production `230..240` and territory-access-cost `453` now execute too through minimal
world-side scalar landing surfaces: slot-indexed `cargo_production_overrides` and world-side scalar landing surfaces: slot-indexed `cargo_production_overrides` and
`world_restore.territory_access_cost` `world_restore.territory_access_cost`
@ -331,8 +335,10 @@ The highest-value next passes are now:
- the company/chairman frontier has moved too: checked-in save-slice documents can now carry that - the company/chairman frontier has moved too: checked-in save-slice documents can now carry that
context natively, so the next work on that axis is broader recovery and eventual raw save context natively, so the next work on that axis is broader recovery and eventual raw save
reconstruction rather than overlay-only ownership reconstruction rather than overlay-only ownership
- keep in mind that the current local `.gms` corpus still exports with no packed event collection, - keep in mind that the current local `.gms + .gmx` corpus does carry packed-event collections,
so real descriptor mapping needs to stay plumbing-first until better captures exist but the checked `29`-save locomotive census still finds no descriptor carriers in `452` or
`457..502`, so further locomotives-page closure now needs either broader save evidence or
dynamic captures
- use `rrt-hook` primarily as optional capture or integration tooling, not as the first execution - use `rrt-hook` primarily as optional capture or integration tooling, not as the first execution
environment environment
- keep `docs/runtime-rehost-plan.md` current as the runtime baseline and next implementation slice - keep `docs/runtime-rehost-plan.md` current as the runtime baseline and next implementation slice

View file

@ -10,18 +10,23 @@ This file is the short active queue for the current runtime and reverse-engineer
## Current Active Items ## Current Active Items
- Keep the periodic-company trace as the main shellless simulation frontier, with the next concrete control-lane pass focused on the ordinary loaded runtime-effect strip `0x00444d92 -> 0x00432f40(kind 8) -> 0x004323a0 -> 0x00431b20`. - No active repo-local non-dynamic items remain.
The checked `rt3_105/maps` compact-dispatch corpus is now exported directly and partially mirrored into the periodic-company trace: `41` maps scanned, `38` with dispatch-strip rows, `318` nondirect rows total, the add-building subset is only `10` grouped occurrences across `7` descriptor keys, and the strongest broader nondirect families are now bounded too at `36` grouped occurrences across `18` maps for `nondirect-ge1e-h0001-0360-0004-0100-0200-p0000-0000-0000-ffff :: [864:4]` plus `27` across `14` maps for the mixed `[-1:4]` cluster. All of those checked rows still lack recovered trigger kind. The packed-state bridge is narrower than that queue head used to allow too: `0x0042db20/0x00430d70` rebuild and serialize only the fixed text bands plus the standalone and grouped row lists, while the metadata band `+0x7ee..+0x80e` is only mirrored by deep-copy helper `0x0042e050`. The active open question is therefore which ordinary loaded rows acquire or bypass the missing trigger-kind control lane before they can reach placed-structure mutation opcodes. The last local static head was the locomotives-page tail, and the checked [locomotive catalog tail census](../artifacts/exports/rt3-1.06/locomotive-catalog-tail-census.json) now exhausts the full local `.gms + .gmx` corpus under `rt3_wineprefix/drive_c`: `29` candidate saves found, `26` parsed samples, `5` catalog-bearing saves, one save-stable ordinal prefix through `58` (`VL80T`), two observed `59+` tail clusters (`g.gms` with `242 A1 / Class 460 / Class A1 / Class P8 / U1`, and the four classic 1.05 saves with `GP 35 / U1 / Zephyr`), zero observed `Class QJ`, and zero packed-event carriers for descriptor `452` or the upper bands `457..474` / `475..502`.
The dispatcher-side caller census is wider in a way that makes the remaining blocker sharper: `0x00432f40` is already driven shelllessly for kinds `1/0/3/2` and then `5/4` from the recurring simulation-maintenance strip `0x0040a220..0x0040a9ac`, for kind `7` from the grounded company-startup family, and for kind `6` from the placed-structure post-create, startup-refresh, and route-entry post-change tails, while `LoadScreen.win` still owns kind `9`. So the missing piece is no longer “find another shellless dispatcher entrypoint.” It is why ordinary loaded rows still fail to present a matching nonzero `[event+0x7ef]` when the later world-entry one-shot at `0x00444d92` requests kind `8`. The added `18` `.gmx` sandbox saves widen the local corpus and packed-event coverage, but they still contribute no named locomotive table and no derived `locomotive_catalog`, so they do not move the save-native tail evidence beyond the same five catalog-bearing `.gms` saves.
The largest direct writer table is ruled out now too: `0x004d8ea0` is the shell-side `EventConditions.win` commit helper, where controls `0x4e98..0x4ea2` write `[event+0x7ef] = 0..10` on the currently selected live event, so that seed family does not explain shellless post-load bringup. That means the remaining locomotive questions are no longer repo-local static work. They now require either a broader save corpus or dynamic tracing.
The broad scenario-name fixup owner is narrower in the same direction: `0x00442c30` really does mutate live event rows after reload, but its grounded trigger-kind writes still only retag `1 -> 5` and `0 -> 2`, while the surrounding event-side branches only patch modifier bytes or nested payload dwords under already-existing kinds. No grounded branch there seeds kind `8`. Preserved checked locomotive blocker detail now lives in [Locomotive descriptor tails](rehost-queue/locomotive-descriptor-tails-2026-04-21.md).
The metadata-copy helper is ruled out in the same way: `0x0042e050` really does clone `[event+0x7ef]`, but the current whole-binary caller search still finds only the shell-side selected-event clone path `0x004db8b0`, not any shellless post-load or periodic caller. Preserved checked format inventory detail now lives in [RT3 format inventory](rehost-queue/format-inventory-2026-04-21.md).
The direct write census is tighter in the same direction: the only grounded explicit write of value `8` into `[event+0x7ef]` is `0x004d91b3` inside that same shell helper, while the runtime-side grounded writers still only cover zero-init, copy, `2/3` follow-on seeds, and the later `5` / `2` retags.
Static progress on this head now appears genuinely blocked: the whole-binary `[...+0x7ef]` reference census still collapses to that same grounded writer set plus the already-known compare and copy helpers, so the next honest step likely requires hook-side or runtime tracing between reload `0x00433130` and the world-entry kind-`8` sweep at `0x00444d92`. ## Preserved External And Dynamic Blockers
- The locomotives-page tail remains preserved as an external-corpus blocker.
Static repo-local work on this head is now exhausted: the local `29`-save `.gms + .gmx` census proves the stable prefix through `58`, proves the same two scenario-dependent `59+` tail families, and still finds no `Class QJ` placement plus no descriptor carriers in `452` or `457..502`. The next honest non-hook step requires a broader save corpus; the next non-static step requires dynamic tracing.
Preserved checked blocker detail now lives in [Locomotive descriptor tails](rehost-queue/locomotive-descriptor-tails-2026-04-21.md).
- The periodic-company control-lane head remains preserved as a dynamic blocker around the ordinary loaded runtime-effect strip `0x00444d92 -> 0x00432f40(kind 8) -> 0x004323a0 -> 0x00431b20`.
Static progress on this head now appears genuinely blocked: the whole-binary `[...+0x7ef]` reference census still collapses to the grounded writer set plus the already-known compare and copy helpers, so the next honest step likely requires hook-side or runtime tracing between reload `0x00433130` and the world-entry kind-`8` sweep at `0x00444d92`.
Preserved checked control-lane detail now lives in [Periodic company control lane](rehost-queue/periodic-company-control-lane-2026-04-21.md). Preserved checked control-lane detail now lives in [Periodic company control lane](rehost-queue/periodic-company-control-lane-2026-04-21.md).
- Keep the next static Tier-2 building pass focused on the earlier seed/projection seam into `0x00412d70`, not another broad `BuildingTypes` sweep. - The Tier-2 building head remains preserved as a dynamic blocker around the seed/projection seam into `0x00412d70`.
The grounded owner strip is `0x004196c0 -> 0x00414490 -> 0x00416ce0 -> 0x00419230`, and the checked candidate-table exports now keep the concrete scenario-side families explicit too: among the `37` probe-bearing maps, `Port00/Warehouse00` stay at `35/43` on `30` maps and shift earlier to `10/18` on `7`, while `Port01..11` / `Warehouse01..11` stay fixed at `45..55` / `56..66` and the numbered trailer family splits independently at `0x00000001 -> 28 maps` versus `0x00000000 -> 9 maps`. The new crossover matrix stays mixed rather than collapsing to one side too: `35/43 :: 0x00000001 -> 25 maps`, `35/43 :: 0x00000000 -> 5 maps`, `10/18 :: 0x00000000 -> 4 maps`, and `10/18 :: 0x00000001 -> 3 maps`. The checked header-cluster export keeps the same root scan bounded to only `3` families: `0x00000000 / 0x00000000 -> 27 maps`, `0xcdcdcdcd / 0xcdcdcdcd -> 9 maps`, and `0x10000000 / 0x00009000 -> 1 map` (`Alternate USA.gmp`). The load-side handoff is narrower now too: `0x004120b0` explicitly reads `[candidate+0xba]` and `[candidate+0xbb]` as one-byte stream fields, and the very next projection owner `0x00412d70` immediately consumes those bytes in two passes, first `+0xba` and then `+0xbb`, to pick one seed row whose full `0x1f2`-dword body will be cloned or reused for each numbered runtime record. The stock decode side is narrower in the same direction: `0x00414490` does not just copy the `0xb8..0xbb` tail, it already derives the optional plane size from `[record+0xb8] * [record+0xb9] << 5` and uses the high nibble of `[record+0xba]` while materializing the four optional plane buffers at `[record+0xcf/+0xd3/+0xd7/+0xdb]`, before `0x00416ce0` remaps only the bare `port` / `warehouse` names and the later `0x00419230` rebank-or-clone pass consumes any bank-qualified owners. The same static pass rules out one lingering false lead too: the earlier suspected `0x00414500..0x00414b14` replay strip is not a separate serializer or import family at all, just the interior plane-decode band of `0x00414490`. The stock `BuildingTypes` corpus is narrower too: across `77` checked `.bca` files only `MachineShop.bca` carries nonzero selector bytes at `0xb8..0xbb`, while the broader nonzero stock signal lives in the `22`-file `.bty` alias-root family with `dword_0xbb = 0x000001f4`, especially the `TextileMill` branch that already covers `Port.bty` and `Warehouse.bty`. The active open question is therefore which later seed or projection seam turns that already-decoded stock-side shape or selector state together with the fixed numbered cluster into nonzero live `[candidate+0xba/+0xbb]` before `0x00412d70` and `0x00419230` consume it. The stock decode chain, the bare-name remap callback, the rebank-or-clone owner, and the earlier suspected mid-range replay strip are all now grounded, so the next honest step likely requires runtime tracing of which source rows actually enter the live bank-qualified seed set.
Static progress on this head is close to the same boundary now: the stock decode chain, the bare-name remap callback, the rebank-or-clone owner, and the earlier suspected mid-range replay strip are all grounded, so the next honest step likely requires runtime tracing of which source rows actually enter the live bank-qualified seed set.
Preserved checked row-family detail now lives in [Tier2 candidate row families](rehost-queue/tier2-candidate-row-families-2026-04-21.md). Preserved checked row-family detail now lives in [Tier2 candidate row families](rehost-queue/tier2-candidate-row-families-2026-04-21.md).
Preserved checked stock selector-byte detail now lives in [Tier2 selector-byte sources](rehost-queue/tier2-selector-byte-sources-2026-04-21.md). Preserved checked stock selector-byte detail now lives in [Tier2 selector-byte sources](rehost-queue/tier2-selector-byte-sources-2026-04-21.md).
Preserved checked rebuild sequencing detail now lives in [Tier2 rebuild sequencing](rehost-queue/tier2-rebuild-sequencing-2026-04-21.md). Preserved checked rebuild sequencing detail now lives in [Tier2 rebuild sequencing](rehost-queue/tier2-rebuild-sequencing-2026-04-21.md).
@ -29,6 +34,8 @@ This file is the short active queue for the current runtime and reverse-engineer
## Preserved Detail ## Preserved Detail
- [Archive snapshot](rehost-queue/archive-2026-04-19.md) - [Archive snapshot](rehost-queue/archive-2026-04-19.md)
- [RT3 format inventory](rehost-queue/format-inventory-2026-04-21.md)
- [Locomotive descriptor tails](rehost-queue/locomotive-descriptor-tails-2026-04-21.md)
- [Periodic company control lane](rehost-queue/periodic-company-control-lane-2026-04-21.md) - [Periodic company control lane](rehost-queue/periodic-company-control-lane-2026-04-21.md)
- [Tier2 candidate row families](rehost-queue/tier2-candidate-row-families-2026-04-21.md) - [Tier2 candidate row families](rehost-queue/tier2-candidate-row-families-2026-04-21.md)
- [Tier2 rebuild sequencing](rehost-queue/tier2-rebuild-sequencing-2026-04-21.md) - [Tier2 rebuild sequencing](rehost-queue/tier2-rebuild-sequencing-2026-04-21.md)

View file

@ -4,6 +4,11 @@ This directory preserves older queue snapshots and long-form implementation note
useful as evidence, but should not stay in the short active queue file. useful as evidence, but should not stay in the short active queue file.
- `archive-2026-04-19.md`: preserved detailed queue snapshot from the pre-index cleanup. - `archive-2026-04-19.md`: preserved detailed queue snapshot from the pre-index cleanup.
- `format-inventory-2026-04-21.md`: current file-format inventory under `rt3/` and `rt3_105/`,
including the RT3-native families we still do not parse.
- `locomotive-descriptor-tails-2026-04-21.md`: checked `.gms + .gmx` local locomotive catalog
census and the remaining external-corpus blocker around `Class QJ`, descriptor `452`, and the
upper descriptor bands.
- `periodic-company-control-lane-2026-04-21.md`: checked compact-dispatch/control-lane evidence - `periodic-company-control-lane-2026-04-21.md`: checked compact-dispatch/control-lane evidence
for the active periodic-company queue head, including the top ordinary nondirect cluster carrier for the active periodic-company queue head, including the top ordinary nondirect cluster carrier
maps. maps.

View file

@ -0,0 +1,139 @@
# Locomotive Descriptor Tails (2026-04-21)
This note preserves the checked evidence behind the locomotives-page tail after the full local
`.gms + .gmx` save-corpus pass, so the short queue can treat the remaining work honestly as an
external-corpus or dynamic blocker rather than another repo-local static task.
## Local Save-Corpus Census
The checked [locomotive catalog tail census](../../artifacts/exports/rt3-1.06/locomotive-catalog-tail-census.json)
now exhausts the full local save corpus under `rt3_wineprefix/drive_c`:
- `29` candidate saves found
- `26` parsed samples
- `5` saves with a save-side named locomotive table and derived `locomotive_catalog`
- `26` saves with packed-event collections present
- one save-stable catalog prefix through ordinal `58` (`VL80T`)
- two observed tail clusters beyond that prefix:
- one `63`-entry tail on `g.gms` / `Spanish Mainline.gmp`: `242 A1 / Class 460 / Class A1 /
Class P8 / U1`
- one `61`-entry tail on the four 1.05 save families: `GP 35 / U1 / Zephyr`
The added `18` `.gmx` sandbox saves still carry no reconstructed named locomotive table and no
derived `locomotive_catalog`, so they widen the local event corpus without widening the save-native
ordinal catalog evidence. The three classic `rt3/` `.gms` saves (`Autosave.gms`, `hh.gms`,
`kk.gms`) remain catalog-free too, so the save-native tail evidence is still carried only by the
same five 1.05 `.gms` saves.
The scan still skips the three classic `rt3/` sandbox saves (`Autosave.gmx`, `aaa.gmx`,
`bbb.gmx`) at save-slice load time, so the currently parsed sample set is `26` rather than the
full `29` candidate paths. That does not change the locomotive blocker: none of the successfully
parsed `.gmx` samples widens the save-native catalog, places `Class QJ`, or surfaces descriptor
carriers in `452` or `457..502`.
## Grounded Lower Bands
The lower descriptor bands are now grounded enough to execute through the existing save-native
owner seam:
- availability descriptors `241..351` already map directly onto recovered locomotive ids `1..111`
- cost descriptors `352..451` already map directly onto recovered locomotive ids `1..100`
- the save-side named locomotive table already projects into `RuntimeState.locomotive_catalog` as
one ordinal id-to-name map derived from row order
- that means the remaining lower-tail rows `302..351` and `413..451` are no longer missing an
owner seam; they are just later ordinals on the same catalog
The old “grounded executable prefix through save-backed locomotive id `61` (`Zephyr`)" boundary is
therefore retired. The whole lower bands are executable whenever the save or overlay supplies the
catalog context.
## Grounded Shipped Name Cohort
The remaining lower-tail labels are no longer blocked on missing shipped engine names.
The checked export [engine-type locomotive display census](../../artifacts/exports/rt3-1.06/engine-type-locomotive-display-census.json)
now parses the fixed `.car` header fields for every shipped `Data/EngineTypes` locomotive pair:
- `66` shipped `.car` / `.lco` locomotive families
- `61` primary display names that match the broader checked shipped-name prefix
- `5` extra shipped named families not present in the current grounded prefix:
- `242 A1`
- `Class 460`
- `Class A1`
- `Class P8`
- `Class QJ`
That means the remaining lower-tail uncertainty is no longer “what are the extra shipped
locomotive names?” It is where those five named families land in the live ordinal catalog that the
save-side named availability table mirrors.
The full local save corpus tightens that further. The only observed nonclassic tail in the checked
census is `rt3_wineprefix/drive_c/rt3_105/Saved Games/g.gms`, which exposes one `63`-entry named
locomotive table whose observed tail is:
- `59` `242 A1`
- `60` `Class 460`
- `61` `Class A1`
- `62` `Class P8`
- `63` `U1`
That observation matters because the four catalog-bearing 1.05 save families still carry the
classic `59..61` tail `GP 35 / U1 / Zephyr`. So the currently honest static descriptor-name
boundary is not the broader shipped-name prefix through `61`; it is the last save-stable prefix
through ordinal `58` (`VL80T`). After that point, save-backed ordinal names are real and
executable, but they are no longer universal enough to hard-code in the static semantic catalog.
The same checked local corpus still does **not** surface `Class QJ`, even though the shipped
engine-type corpus proves that family exists on disk. So the current non-hook evidence only closes
the dynamic tail through the observed `63`-entry family above; it does not yet place the fifth
extra shipped name in any live ordinal slot.
The same checked local corpus also exposes no packed-event rows in descriptor range `452..502`, so
the remaining `Unknown Loco Cost` and upper-band frontier is absent from the local save-backed
event corpus as well as unmapped in the static descriptor metadata.
## Remaining Blocker
What is left is narrower and cleaner, but it is no longer a repo-local static task:
- ordinal placement of the scenario-dependent locomotive tail beyond the save-stable `1..58` prefix
- descriptor `452` `Unknown Loco Cost`
- upper availability band `457..474`
- upper cost band `475..502`
The upper bands still carry only slot labels in the checked semantic catalog:
- `Upper-Band Locomotive Availability Slot 1..18`
- `Upper-Band Locomotive Cost Slot 1..28`
The lower bands now split cleanly into two layers:
- ordinals `1..58` keep fixed checked descriptor labels through `VL80T`
- ordinals `59+` stay executable through recovered ids plus save catalog context, but the checked
semantic catalog keeps generic slot labels because the local save corpus already shows
scenario-dependent tails
Unlike the lower bands, the current checked row summary metadata does not yet recover live
locomotive ids for the upper-band descriptors at all.
## Blocker Result
The next question is no longer whether locomotive page descriptors can import through a save-native
owner seam at all, or whether the shipped engine-type files even have stable names. The checked
repo-local answer is already as tight as it can get:
- where the extra shipped named families (`242 A1`, `Class 460`, `Class A1`, `Class P8`,
`Class QJ`) land across the scenario-dependent tail beyond the save-stable `1..58` prefix, and
- whether the upper bands continue the same ordinal locomotive catalog after the lower `1..111` /
`1..100` families, or
- do they belong to a separate page-owned selector family that only sits adjacent in the recovered
`EventEffects` table
Descriptor `452` should stay separate from that question until stronger evidence ties it to either
the lower cost band or the upper tails.
The current repo-local blocker is therefore explicit:
- the next honest non-hook step needs a broader save corpus than the eight local saves already
exhausted here
- the next honest non-static step needs dynamic tracing or hooks

View file

@ -166,13 +166,16 @@ Implemented today:
- the recovered locomotives-page availability bands can now import as full scalar overrides - the recovered locomotives-page availability bands can now import as full scalar overrides
through `RuntimeState.locomotive_catalog` into `RuntimeState.named_locomotive_availability`; through `RuntimeState.locomotive_catalog` into `RuntimeState.named_locomotive_availability`;
raw `.smp` inspection/export now reconstructs the save-side locomotive row family and derives the raw `.smp` inspection/export now reconstructs the save-side locomotive row family and derives the
catalog directly into save-slice documents, so standalone save-slice imports can execute those catalog directly into save-slice documents, so standalone save-slice imports can execute the
rows whenever the save carries enough catalog entries, and the grounded executable lower prefix full lower availability band `241..351` whenever the save carries enough catalog entries; the
now extends through save-backed locomotive id `61` (`Zephyr`) checked `29`-save `.gms + .gmx` `locomotive-catalog-tail-census.json` export now fixes the last
- the grounded lower locomotive-cost band `352..409` now imports too through the same save-native save-stable static boundary at ordinal `58` (`VL80T`), leaving the upper bands `457..474`,
or embedded catalog into the event-owned `RuntimeState.named_locomotive_cost` map when its `475..502`, plus descriptor `452` as external-corpus or dynamic blockers instead of active
scalar payloads are nonnegative; the unresolved lower tail and upper cost tail now stay on repo-local static work
explicit parity instead of synthetic execution - the full lower locomotive-cost band `352..451` now imports too through the same save-native or
embedded catalog into the event-owned `RuntimeState.named_locomotive_cost` map when its scalar
payloads are nonnegative; the remaining unresolved tail is the separate descriptor `452` plus
the upper cost band `475..502`
- the remaining recovered scalar world families now execute as well: cargo-production `230..240` - the remaining recovered scalar world families now execute as well: cargo-production `230..240`
rows lower into slot-indexed `cargo_production_overrides`, and territory-access-cost descriptor rows lower into slot-indexed `cargo_production_overrides`, and territory-access-cost descriptor
`453` lowers into `world_restore.territory_access_cost` `453` lowers into `world_restore.territory_access_cost`