Extend atlas seam annotations across load/save and transport

This commit is contained in:
Jan Petykiewicz 2026-04-13 23:35:17 -07:00
commit 7b44279d7e
7 changed files with 1450 additions and 330 deletions

File diff suppressed because one or more lines are too long

View file

@ -18,7 +18,24 @@ The broader map-editor page owner is now bounded through
the paired overall growth selector whose effects later appear in the city-growth side of the the paired overall growth selector whose effects later appear in the city-growth side of the
simulation. `map_editor_city_region_panel_construct` and simulation. `map_editor_city_region_panel_construct` and
`map_editor_city_region_panel_handle_message` own the city-or-region editing lane with rename and `map_editor_city_region_panel_handle_message` own the city-or-region editing lane with rename and
copy-industry-data flows. That copy side is tighter now too: the handler first enters copy-industry-data flows. The adjacent economic tuning page is explicit now too:
`map_editor_economic_cost_slider_panel_construct` `0x004cadf0` registers
`map_editor_economic_cost_slider_dispatch` `0x004ca980` on six visible slider controls
`0x5bcd..0x5bd7`, re-publishes the current values from
`[world+0x0be2/+0x0be6/+0x0bea/+0x0bee/+0x0bf2/+0x0bf6]`, and pairs that band with the localized
captions `Prime Rate`, `Merger Premium`, and the construction/maintenance cost family
`Build Stations Cost` through `Steam Engine Cost`. The dispatch side writes those six floats
directly back into the same state band and mirrors the first lane into `[world+0x0bde]`, which
now ties the editor page directly to the `.smp` save/load plateau and the post-load/runtime
consumers grounded elsewhere in the atlas. The read-side presentation seam is explicit now too:
`map_editor_economic_cost_panel_refresh_preview_curve_and_numeric_rows` `0x004caaf0` is the
control-`0x5be1` sibling for that same page. It reads the live six-float band, builds one
preview curve from leading lane `[world+0x0be2]` plus the stepped multiplier table
`[world+0x0be6/+0x0bea/+0x0bee/+0x0bf2/+0x0bf6]`, and republishes the six formatted numeric
rows through the neighboring dynamic-text controls `0x5bce/0x5bd0/0x5bd2/0x5bd4/0x5bd6/0x5bd8`.
That closes the economic tuning page as one real save/load-facing editor family rather than only
a constructor plus one write-only slider dispatcher. The city-or-region copy side is tighter now too: the
handler first enters
`0x00420e00`, which rebuilds the selected region's profile collection `[region+0x37f]`, clones `0x00420e00`, which rebuilds the selected region's profile collection `[region+0x37f]`, clones
the source region's live label-weight entries when a source region is present, and otherwise the source region's live label-weight entries when a source region is present, and otherwise
reseeds a fixed default weight set through repeated `0x004206b0` calls. The no-source companion reseeds a fixed default weight set through repeated `0x004206b0` calls. The no-source companion
@ -49,6 +66,13 @@ The broader map-editor page owner is now bounded through
region id `[region+0x23a]` by squared XY distance through `0x0051db80`, and returns `0` when no region id `[region+0x23a]` by squared XY distance through `0x0051db80`, and returns `0` when no
qualifying region is found; `0x004220b0` is the small wrapper above it that returns the matched qualifying region is found; `0x004220b0` is the small wrapper above it that returns the matched
region name `[region+0x356]` or the fallback localized scratch string at `0x0062ba90`. region name `[region+0x356]` or the fallback localized scratch string at `0x0062ba90`.
The adjacent scenario-rule page is tighter on the same save/load axis too:
`map_editor_scenario_special_conditions_panel_clear_visible_row_band` `0x004cb1a0` and
`map_editor_scenario_special_conditions_panel_format_row_by_index` `0x004cb1c0` are now the
concrete range and row callbacks behind list control `0xa7fa`, so the persisted 49-dword
scenario-rule band `[world+0x4a7f..+0x4b3f]` is no longer only constructor-shaped in the atlas.
The constructor `0x004cb2b0` binds those callbacks, counts enabled rows directly from that same
dword band, and keeps the trailing scalar `[world+0x4b43]` in the same page family.
`map_editor_territory_panel_construct` and `map_editor_territory_panel_construct` and
`map_editor_territory_panel_handle_message` own the territory rename and border-remap lane; `map_editor_territory_panel_handle_message` own the territory rename and border-remap lane;
`map_editor_locomotive_availability_panel_construct` plus `map_editor_locomotive_availability_panel_construct` plus
@ -69,26 +93,40 @@ The broader map-editor page owner is now bounded through
result is now a full chain rather than only the importer: result is now a full chain rather than only the importer:
`scenario_state_rebuild_port_warehouse_cargo_recipe_runtime_tables` first imports those same five `scenario_state_rebuild_port_warehouse_cargo_recipe_runtime_tables` first imports those same five
lines into one repeated array of identical `0xbc`-byte runtime descriptors with no row-index lines into one repeated array of identical `0xbc`-byte runtime descriptors with no row-index
special casing, and the candidate-side rebuild pass at special casing, while mode `3` keeps reseeding the primary token lane until it differs from the
one-row subordinate lane and then writes literal `1.0` into the subordinate amount slot
`[desc+0x48]`. The candidate-side rebuild pass at
`structure_candidate_collection_rebuild_runtime_records_from_scenario_state` `0x00412d70` then `structure_candidate_collection_rebuild_runtime_records_from_scenario_state` `0x00412d70` then
projects those descriptors into the live structure collection at `0x0062b268` in two projects those descriptors into the live structure collection at `0x0062b268` in two
availability-bank passes. The current local disassembly now shows that this bridge reuses live availability-bank passes. The current local disassembly now shows that this bridge reuses live
candidate slots by prior ordinal field `[candidate+0x794]` when possible, otherwise allocates one candidate slots by prior ordinal field `[candidate+0x794]` when possible, otherwise allocates one
fresh candidate, clones from the current bank's template source candidate, and only then copies in fresh candidate, clones from the current bank's template source candidate, and only then copies in
the imported recipe-line count, shared production-cap float, and packed `0xbc` descriptor strip the imported recipe-line count, shared production-cap float, and packed `0xbc` descriptor strip
from scenario state. Each rebuilt candidate then feeds from scenario state. The label side is narrower than the earlier note too: current local
disassembly of `0x00412d70` does not consult the recipe-book name at `[state+0x0fe8]` at all.
Instead it formats display stem `[candidate+0x04]` directly from one of two fixed built-in roots
chosen by availability bit `[candidate+0xba] & 1`, together with the current ordinal. Each
rebuilt candidate then feeds
`structure_candidate_rebuild_cargo_membership_and_scaled_rate_tables` `0x00411ee0`, which rebuilds `structure_candidate_rebuild_cargo_membership_and_scaled_rate_tables` `0x00411ee0`, which rebuilds
the per-cargo runtime summary tables. That helper now reads more concretely too: it clears both the per-cargo runtime summary tables. That helper now reads more concretely too: it clears both
emitted cargo-id tables at `[candidate+0x79c]` and `[candidate+0x7a0]`, rebuilds one local emitted cargo-id tables at `[candidate+0x79c]` and `[candidate+0x7a0]`, clears the per-cargo
`0x35`-cargo mark band from the descriptor strip, accumulates the scaled per-cargo runtime rates float band at `[candidate+0xa1..+0xb8]`, and fills one local `0x35`-cargo status scratch from
into `[candidate+0xa1..+0xb8]`, and then allocates the two final compact membership tables from the descriptor strip. Each subordinate row contributes status `1` when the cargo is only
the `mark>=1` and `mark>=2` sets with counts stored at `[candidate+0x7a4]` and `[candidate+0x7a8]`. referenced and status `2` when the row is currently year-active, with the helper keeping the
The same scan also makes the production-mode split explicit on the runtime side: descriptor mode maximum status per remapped cargo id through `0x0062ba8c+0x9a`. It then allocates the two final
`0` uses the shared production cap at `[candidate+0x2a]` divided by the descriptor amount, while compact membership tables from the `status>=1` and `status>=2` sets with counts stored at
nonzero mode bypasses that scaling path. The immediate sibling `[candidate+0x7a4]` and `[candidate+0x7a8]`. The same scan also makes the production-mode split
explicit on the runtime side: descriptor mode `0` uses the shared production cap at
`[candidate+0x2a]` divided by the descriptor amount, while nonzero mode bypasses that scaling
path and publishes the row amount directly. At that point the candidate import strip is
structurally closed in current local evidence: the local import and rebuild seam is no longer
where the loader reconstructs runtime descriptor or membership tables, but only what the
unresolved supply-marker token family means before those already-grounded candidates are projected
back into live cargo ids. The immediate sibling
`structure_candidate_refresh_recipe_runtime_mode_flags_0x78c_0x790` `0x00411ce0`, which for `structure_candidate_refresh_recipe_runtime_mode_flags_0x78c_0x790` `0x00411ce0`, which for
subtype byte `[candidate+0x32] == 2` scans the imported descriptor strip and derives two compact subtype byte `[candidate+0x32] == 2` scans the imported descriptor strip and derives two compact
post-import flags from mode `0` presence and subordinate-row presence. The stream-load side is post-import flags from mode `0` presence and whether every mode-`0` descriptor keeps at least
one subordinate row. The stream-load side is
tighter in the same way: the constructor-side load owner tighter in the same way: the constructor-side load owner
`structure_candidate_collection_construct_and_stream_load_runtime_records` `0x004131f0` `structure_candidate_collection_construct_and_stream_load_runtime_records` `0x004131f0`
seeds global pool `0x0062b268` and immediately re-enters the broader collection importer seeds global pool `0x0062b268` and immediately re-enters the broader collection importer
@ -99,14 +137,39 @@ The broader map-editor page owner is now bounded through
`0x00412ab0`, the collection aggregate subtotal pass over `[pool+0x8c..+0x9c]`, rebuilds the `0x00412ab0`, the collection aggregate subtotal pass over `[pool+0x8c..+0x9c]`, rebuilds the
fixed name catalog at `0x0061dbc2/0x0061dc09` for non-subtype-`1` zero-availability candidates fixed name catalog at `0x0061dbc2/0x0061dc09` for non-subtype-`1` zero-availability candidates
with live ids `<= 0x6e` via localized string `0x0b53`, and only then re-enters the same with live ids `<= 0x6e` via localized string `0x0b53`, and only then re-enters the same
named-availability refresh at `0x00412c10` before returning. The neighboring placed-structure named-availability refresh at `0x00412c10` before returning. The broader scenario-state reset
side is explicit now too: `0x00436d10` is the shared reset-and-rebuild owner beneath startup and
world-entry paths. It zeroes the late scenario-state bands, seeds the fixed year defaults and
chairman-slot rows, rebuilds the two named availability collections at `[state+0x66b2]` and
`[state+0x66b6]`, re-seeds the twelve recipe books at `[state+0x0fe7]` from the fixed template
at `0x005c9f78`, refreshes selected-year state through `0x00409e80`, `0x00433bd0`,
`0x00435603`, and the year-gap scalar helper `0x00434130`, and only then re-enters
`0x00435630`, `0x0041e970`, `0x00412bd0`, and `0x00436af0`. The startup side is tighter now too:
`0x00438890` does not jump straight into one selector-specific file-load branch. It first
re-enters `0x00436d10`, mirrors one shell-managed label list from `0x006d4020+0x429b0` into local
state, allocates the common runtime pools (`0x0062ba8c`, `0x0062b268`, `0x0062b2fc`,
`0x0062ba88`, `0x006ada84`, `0x0062c120`, `0x006cfcbc`, `0x0062be10`, `0x006ceb9c`,
`0x006cea4c`, `0x006acd34`, `0x0062b244`), and pre-seeds named locomotive-availability rows
through `0x004350b0` before the profile selector at `[profile+0x01]` chooses tutorial,
setup-generation, or file-load lanes. The neighboring placed-structure
side is bounded too: global pool `0x0062b26c` comes from side is bounded too: global pool `0x0062b26c` comes from
`placed_structure_collection_construct_empty_runtime_pool` `0x00413230`, while the paired tagged `placed_structure_collection_construct_empty_runtime_pool` `0x00413230`, while the paired tagged
collection owners `0x00413280` and `0x00413440` now own the broader placed-structure stream collection owners `0x00413280` and `0x00413440` now own the broader placed-structure stream
load/save path around tags `0x36b1/0x36b2/0x36b3` and the per-entry virtual load/save slots load/save path around tags `0x36b1/0x36b2/0x36b3` and the per-entry virtual load/save slots
`+0x40/+0x44`. The adjacent collection pass `0x00412ab0` is tighter in `+0x40/+0x44`. The adjacent collection pass `0x00412ab0` is tighter in
the same way: it now reads as a pure stem-policy sweep that refreshes candidate dword `[+0xbc]` the same way: it now reads as a pure stem-policy sweep that refreshes candidate dword `[+0xbc]`
from defaults `0x1869f/3/4` plus the fixed override table at `0x005edca8..0x005edd20`. The from defaults `0x1869f/3/4` plus the fixed override table at `0x005edca8..0x005edd20`, with the
non-default `3/4` branches driven directly by post-import flag `[candidate+0x78c]` and subtype
byte `[candidate+0x32]`. The stream-load side is tighter too: `0x004120b0` now clearly reads the
fixed header fields first, allocates the descriptor strip as `count*0xbc + 1`, zeroes every
loaded `0xbc` descriptor slot before import, resolves primary mode-`0` cargo names from
`+0x08 -> +0x1c` and subordinate row cargo names from `+0x30 -> +0x44`, clamps descriptor start
year `[desc+0x20]` upward to the resolved cargo availability floor `[cargo+0x21]`, and only then
reruns `0x00411ee0` plus `0x00411ce0`. Its version gates are explicit too: bundles before
`0x3ed` omit `[candidate+0x3b..+0xbb]`, bundles before `0x3f2` keep the narrow `0x02`-byte form
of `[candidate+0xc0]`, nonzero `[candidate+0x4b]` forces `[candidate+0x47] = 1.0` and rescales
`[candidate+0x43]` to at least `0x1388`, and the later stem-index resolution first checks two
built-in alias stems before the full fixed row table at `0x005ed338..0x005edca4`. The
current local file-side result is now tighter too: the current local file-side result is now tighter too: the
grounded recipe-book root at `0x0fe7` is preserved byte-for-byte across the checked map/save grounded recipe-book root at `0x0fe7` is preserved byte-for-byte across the checked map/save
scenario pairs, so the loader can safely treat the twelve `0x4e1`-byte books as preserved scenario pairs, so the loader can safely treat the twelve `0x4e1`-byte books as preserved
@ -122,16 +185,20 @@ The broader map-editor page owner is now bounded through
to the supply-half runtime branch and bypassing that scaling on the normalized demand half. The to the supply-half runtime branch and bypassing that scaling on the normalized demand half. The
lower gameplay side is tighter now too: `structure_candidate_query_cargo_runtime_summary_channels` lower gameplay side is tighter now too: `structure_candidate_query_cargo_runtime_summary_channels`
`0x00412650` is the first grounded consumer beneath that rebuild chain, because it lazily rebuilds `0x00412650` is the first grounded consumer beneath that rebuild chain, because it lazily rebuilds
four per-cargo summary banks and returns one direct-supply channel, one cap-normalized supply four per-cargo summary banks and returns one direct primary-cargo scalar channel, one cap-share
channel, one demand or input channel, and one scaled production-output subrow channel for a primary-cargo scalar channel, one nonzero-mode subrow channel, and one zero-mode cap-scaled
requested cargo id. The internal indexing is tighter now too: the direct-supply lane indexes from subrow channel for a requested cargo id. The internal indexing is tighter now too: the two
`[desc+0x1c]`, the demand or input lane uses that same resolved cargo id in the no-subrow branch, primary-cargo banks index from `[desc+0x1c]`, while both subordinate-row banks index each row
and the scaled production-output lane indexes each subordinate row directly from directly from `[desc+0x44+row*0x1c]`. The mode split is tighter in the same way: nonzero-mode
`[desc+0x44+row*0x1c]`, with no post-resolution failure guard between the importer writes and the descriptors bypass the primary-cargo banks entirely and only feed the nonzero-mode subrow bank,
summary-bank updates. So the strongest current read is narrower than a full cargo-id decode: if while mode-`0` descriptors either add their direct scalar to `[this+0x03a]` or, when subrows are
present, add one cap-share scalar to `[this+0x0a4]` and one cap-scaled row contribution to
`[this+0x178]`. There is still no post-resolution failure guard between the importer writes and
the summary-bank updates. So the strongest current read is narrower than a full cargo-id decode: if
the imported low-16 marker rows fail the exact matcher at `0x0041e9f0`, the resulting `0` ids the imported low-16 marker rows fail the exact matcher at `0x0041e9f0`, the resulting `0` ids
will still hit the first summary-bank bucket inside `0x00412650`, but the semantic meaning of will still hit the first summary-bank bucket inside `0x00412650`, but the semantic meaning of
cargo id `0` itself remains ungrounded. The sibling helper cargo id `0` itself remains ungrounded. So the remaining gap here is semantic only, not another
missing import or rebuild owner. The sibling helper
`structure_candidate_supports_or_references_cargo_id` `structure_candidate_supports_or_references_cargo_id`
`0x004129d0` then uses those same banks plus the cached cargo-membership arrays to answer whether `0x004129d0` then uses those same banks plus the cached cargo-membership arrays to answer whether
a live candidate materially references a cargo at all. One compare step tighter, the raw line a live candidate materially references a cargo at all. One compare step tighter, the raw line
@ -195,12 +262,13 @@ The broader map-editor page owner is now bounded through
cargo-name resolution unless another upstream transform exists. cargo-name resolution unless another upstream transform exists.
The wrapper layer above that query no longer looks like a hiding place for special treatment The wrapper layer above that query no longer looks like a hiding place for special treatment
either. Local `objdump` now shows `0x00412960` simply summing the two supply-side floats returned either. Local `objdump` now shows `0x00412960` simply summing the two supply-side floats returned
by `0x00412650`, while `0x004129a0` returns the single scaled production-output lane directly; by `0x00412650`, while `0x004129a0` returns the single zero-mode cap-scaled subrow lane directly;
neither wrapper checks for cargo id `0` after the query returns. The broader world-side neither wrapper checks for cargo id `0` after the query returns. The broader world-side
accumulator at `0x0041e7be` is tighter in the same way: it calls `0x00412650`, requires all four accumulator at `0x0041e7be` is tighter in the same way: it calls `0x00412650`, requires all four
returned channels to be positive before continuing, queries one linked-instance count through returned channels to be positive before continuing, queries one linked-instance count through
`0x00413940`, scales each of the four channel values by that count, and then writes the finished `0x00413940`, scales the direct-primary, cap-share-primary, nonzero-mode-subrow, and
quartet into candidate dwords `[candidate+0x8e/+0x92/+0x96/+0x9a]` while stamping zero-mode-cap-scaled-subrow lanes by that count, and then writes the finished quartet into
candidate dwords `[candidate+0x8e/+0x92/+0x96/+0x9a]` while stamping
`[candidate+0x8a]` from current world field `[0x006cec78+0x15]`. So the current strongest read `[candidate+0x8a]` from current world field `[0x006cec78+0x15]`. So the current strongest read
remains structural rather than semantic: unresolved marker rows can still propagate through the remains structural rather than semantic: unresolved marker rows can still propagate through the
first bank bucket, but the first place that meaning matters is a normal positivity-gated first bank bucket, but the first place that meaning matters is a normal positivity-gated
@ -426,17 +494,19 @@ The broader map-editor page owner is now bounded through
when `0x004349a0` reports more than one active profile. After those gates it forwards target mode when `0x004349a0` reports more than one active profile. After those gates it forwards target mode
`([shell+0x68] == 0)` into `([shell+0x68] == 0)` into
`shell_set_editor_map_mode_and_refresh_detail_panel_world_and_graphics_side_effects` `0x00482e50`. `shell_set_editor_map_mode_and_refresh_detail_panel_world_and_graphics_side_effects` `0x00482e50`.
The adjacent startup-year strip is grounded now too: The adjacent time-of-day strip is grounded now too:
`shell_command_raise_startup_selected_year_scalar_by_half_step_and_refresh_calendar` `0x00441cb0` `shell_command_increment_time_of_day_presentation_scalar_by_half_step_and_refresh_calendar`
and `shell_command_lower_startup_selected_year_scalar_by_half_step_and_refresh_calendar` `0x00441cb0` and
`0x00441d20` are the paired registered commands beneath localized ids `0x0db5/0x0db6`. Both `shell_command_decrement_time_of_day_presentation_scalar_by_half_step_and_refresh_calendar`
`0x00441d20` are the paired registered commands beneath localized ids `0x0db5/0x0db6`, whose
RT3.lng captions are `Increment Time Of Day` and `Decrement Time Of Day`. Both
require active scenario state plus the same single-profile gate from `0x004349a0`, clear shell require active scenario state plus the same single-profile gate from `0x004349a0`, clear shell
dwords `[0x006cec74+0x233]` and `[+0x124]`, then adjust startup scalar `[0x006cec78+0xbfa]` by dwords `[0x006cec74+0x233]` and `[+0x124]`, then adjust startup scalar `[0x006cec78+0xbfa]` by
`+0.5` or `-0.5` before re-entering `+0.5` or `-0.5` before re-entering
`world_set_selected_year_and_refresh_calendar_presentation_state` `0x00409e80` with absolute `world_set_selected_year_and_refresh_calendar_presentation_state` `0x00409e80` with absolute
counter `[0x006cec78+0x15]` and refreshing the live world view through `0x00439a80`. The exact counter `[0x006cec78+0x15]` and refreshing the live world view through `0x00439a80`. So the
player-facing command labels are still open, but the static owner boundary is now clear: these registration-side identity is now closed: these are the paired half-step time-of-day
are not generic shell scalars, they are the paired half-step selected-year presentation commands. presentation commands, not generic shell scalars and not the earlier selected-year read.
The adjacent progress-status side is tighter now too: The adjacent progress-status side is tighter now too:
`shell_build_percent_status_payload_clamped_0_to_100` `0x00441d90` is the shared save or load `shell_build_percent_status_payload_clamped_0_to_100` `0x00441d90` is the shared save or load
progress payload builder, and progress payload builder, and
@ -889,7 +959,8 @@ The broader map-editor page owner is now bounded through
`0x00437d70`. That owner increments the shared shell counter at `[0x006d401c+0xc60]`, opens the `0x00437d70`. That owner increments the shared shell counter at `[0x006d401c+0xc60]`, opens the
localized cheat prompt `2922` `Do I detect a cheater in the house?\n\nEnter code (or <ESC> to localized cheat prompt `2922` `Do I detect a cheater in the house?\n\nEnter code (or <ESC> to
cancel):`, and then scans the fixed 26-entry table at `0x005ee2c8`. The active selector strip is cancel):`, and then scans the fixed 26-entry table at `0x005ee2c8`. The active selector strip is
now grounded: winner/loss strings `3618/3619/3620/3622` route through `0x004367c0`, selector `1` now grounded: winner/loss strings `3618/3619/3620/3622` route through
`world_set_outcome_mode_and_copy_cheat_win_or_loss_status_text` `0x004367c0`, selector `1`
jumps to the out-of-line reset branch at `0x004d676c` that clears the selected-company stat bands jumps to the out-of-line reset branch at `0x004d676c` that clears the selected-company stat bands
rooted at `[company+0x0cfb]`, `[company+0x0d7f]`, and `[company+0x1c47]`, selectors `2` and `3` rooted at `[company+0x0cfb]`, `[company+0x0d7f]`, and `[company+0x1c47]`, selectors `2` and `3`
post deltas into the selected company and selected chairman profile through `0x0042a080` and post deltas into the selected company and selected chairman profile through `0x0042a080` and
@ -958,10 +1029,11 @@ The broader map-editor page owner is now bounded through
refreshes the live company-detail branch in mode `7`, the stock-buy branch in mode `0x0b`, the refreshes the live company-detail branch in mode `7`, the stock-buy branch in mode `0x0b`, the
mode-`8` timed overlay only when the current subject id matches the caller dword, and the mode-`8` timed overlay only when the current subject id matches the caller dword, and the
`Overview.win` side in mode `9`. Its wrapper `Overview.win` side in mode `9`. Its wrapper
`shell_refresh_active_window_followons_and_adjacent_station_or_company_lists` `0x00436170` `shell_refresh_active_window_followons_and_adjacent_station_or_train_lists` `0x00436170`
forwards through that helper and then adds the station-list refresh through forwards through that helper and then adds the station-list refresh through
`shell_station_list_window_refresh_rows_selection_and_status` `0x00506f30` when mode `4` is `shell_station_list_window_refresh_rows_selection_and_status` `0x00506f30` when mode `4` is
active plus the company-list refresh through `0x005158f0(-1)` when mode `1` is active. The active plus the `TrainList.win` refresh through
`shell_train_list_window_refresh_controls` `0x005158f0(-1)` when mode `1` is active. The
shared world-side follow-on beneath those shell branches is explicit now too: shared world-side follow-on beneath those shell branches is explicit now too:
`world_refresh_collection_side_effects_after_broad_state_change` `0x004361d0` sweeps the live `world_refresh_collection_side_effects_after_broad_state_change` `0x004361d0` sweeps the live
region, placed-structure, and three adjacent world collections, re-entering their per-record region, placed-structure, and three adjacent world collections, re-entering their per-record

View file

@ -4,16 +4,16 @@
`shell_active_mode_run_profile_startup_and_load_dispatch` at `0x00438890`, the shell-mode `shell_active_mode_run_profile_startup_and_load_dispatch` at `0x00438890`, the shell-mode
switcher `shell_transition_mode` at `0x00482ec0`, the first grounded world-entry branch switcher `shell_transition_mode` at `0x00482ec0`, the first grounded world-entry branch
`world_entry_transition_and_runtime_bringup` at `0x00443a50`, `world_entry_transition_and_runtime_bringup` at `0x00443a50`,
`shell_map_file_world_bundle_coordinator` at `0x00445de0`, reference-database setup via `shell_map_file_world_bundle_coordinator` at `0x00445de0`, early reference-package save via
`map_bundle_open_reference_databases` at `0x00444dd0`, and narrower loaders such as `map_bundle_open_reference_package_and_serialize_early_world_datasets` at `0x00444dd0`, and narrower tagged collection owners such as
`map_load_geographic_label_database` and `map_load_city_database`. `geographic_label_database_refresh_records_from_tagged_bundle` and `city_database_entry_collection_refresh_records_from_tagged_bundle`.
- Trigger/Cadence: shell tutorial launch, editor or detail-panel file actions through `fileopt.win`, - Trigger/Cadence: shell tutorial launch, editor or detail-panel file actions through `fileopt.win`,
map-scenario open paths, and scenario-text export batch commands. map-scenario open paths, and scenario-text export batch commands.
- Key Dispatchers: `shell_map_file_entry_coordinator`, - Key Dispatchers: `shell_map_file_entry_coordinator`,
`shell_active_mode_run_profile_startup_and_load_dispatch`, `shell_transition_mode`, `shell_active_mode_run_profile_startup_and_load_dispatch`, `shell_transition_mode`,
`world_entry_transition_and_runtime_bringup`, `world_runtime_release_global_services`, `shell_map_file_world_bundle_coordinator`, `world_entry_transition_and_runtime_bringup`, `world_runtime_release_global_services`, `shell_map_file_world_bundle_coordinator`,
`map_bundle_open_reference_databases`, `map_load_geographic_label_database`, `map_bundle_open_reference_package_and_serialize_early_world_datasets`, `geographic_label_database_refresh_records_from_tagged_bundle`,
`map_load_city_database`, `scenario_text_export_build_language_file`, `city_database_entry_collection_refresh_records_from_tagged_bundle`, `scenario_text_export_build_language_file`,
`scenario_text_export_report_language_file`, `scenario_text_export_batch_process_maps`. `scenario_text_export_report_language_file`, `scenario_text_export_batch_process_maps`.
- State Anchors: shell-side file staging buffers at `0x0062bee0` and `0x0062bec4`, shell and mode - State Anchors: shell-side file staging buffers at `0x0062bee0` and `0x0062bec4`, shell and mode
globals at `0x006cec74` and `0x006cec78`, world object root `0x0062c120`, map bundle state globals at `0x006cec74` and `0x006cec78`, world object root `0x0062c120`, map bundle state
@ -37,7 +37,31 @@
mode `11` is tighter now too: it still maps to `.gmt`, but instead of looking like another mode `11` is tighter now too: it still maps to `.gmt`, but instead of looking like another
gameplay save family it conditionally diverts into the same `.gmt` preview-surface pipeline owned gameplay save family it conditionally diverts into the same `.gmt` preview-surface pipeline owned
by the Multiplayer preview dataset object at `0x006cd8d8`, and only falls back to the normal by the Multiplayer preview dataset object at `0x006cd8d8`, and only falls back to the normal
reference-bundle path when that dataset object is absent. The shell-side mode owner above those reference-bundle path when that dataset object is absent. That fallback owner is tighter now too:
`0x00444dd0` is not a generic database opener, but the early package-save prelude that seeds the
shared stage/progress globals, opens one `0x30d40` bundle through `0x00530c80`, handles the
localized failure modal `0x0fda`, and then serializes the first direct package band in a fixed
order: chunks `0x32c8/0x32c9`, direct saves of `[world+0x66be]`, `[world+0x66b2]`, and
`[world+0x66b6]`, chunk `0x32dc`, the staged `0x108` profile block under `0x3714/0x3715`, then
`world_serialize_runtime_grid_and_secondary_raster_tables_into_bundle` `0x00449520` for the
early world-grid and sidecar-table band, then
`aux_candidate_collection_serialize_records_into_bundle_payload` `0x00416a70` for the direct
`0x0062b2fc` source-or-auxiliary record family, and only after that the neighboring reference
and manager families before `shell_map_file_world_bundle_coordinator` continues with the later
tagged collections. That `0x0062b2fc` seam is tighter now too: `0x00416a70` first builds one
temporary scored queue from each record's linked chain at `[entry+0x04]`, writes one leading
`0xbabe` header, and then serializes the fixed fields, counted dword runs, optional byte payload,
paired one-byte bands, and trailing dword for each queued record; the load-side companion is
`aux_candidate_collection_construct_stream_load_records_and_refresh_runtime_followons`
`0x004196c0`, which now clearly tails into
`aux_candidate_collection_rebank_or_clone_records_by_availability_pass_and_refresh_owner_links`
`0x00419230` before the later world-load side continues, with destructor
`aux_candidate_collection_release_templates_queues_and_indexed_storage` `0x00419680`. The fixed
tail is explicit now too: `0x00444dd0` writes one direct dword from
`[world+0x19]`, one zeroed `0x1f4`-byte slab under `0x32cf`, closes the package, derives the
preview path through `0x00442740`, and then conditionally emits the companion-image and
companion-payload sidecars through `0x00441f70` and `0x00442900` when `[world+0x66c8]` and
`[world+0x66c9]` are set. The shell-side mode owner above those
file coordinators is clearer now too. `shell_transition_mode` no longer reads like a generic mode file coordinators is clearer now too. `shell_transition_mode` no longer reads like a generic mode
switch: its ABI is now grounded as a `thiscall` with two stack arguments because the body reads switch: its ABI is now grounded as a `thiscall` with two stack arguments because the body reads
the requested mode from `[esp+0x0c]` and returns with `ret 8`. The grounded world-entry the requested mode from `[esp+0x0c]` and returns with `ret 8`. The grounded world-entry
@ -52,6 +76,49 @@
`0x0062b2fc`, `0x0062b268`, `0x006cea4c`, and `0x006acd34` in that order before it drains the `0x0062b2fc`, `0x0062b268`, `0x006cea4c`, and `0x006acd34` in that order before it drains the
shell-helper handle band `[world+0x46a80..+0x46aa0]`, the variable owner band `[world+0x46aa4]`, shell-helper handle band `[world+0x46a80..+0x46aa0]`, the variable owner band `[world+0x46aa4]`,
and the linked chains at `[world+0x66a6]` and `[world+0x66aa]`. and the linked chains at `[world+0x66a6]` and `[world+0x66aa]`.
The corresponding world-load allocation branch is tighter now too: inside `0x00438c70`,
`0x0062b2fc` is allocated as a `0xb8`-byte object and then constructed through
`aux_candidate_collection_construct_seed_globals_and_helper_bands_then_import_records`
`0x0041aa50`, which seeds the collection base, resets the neighboring constructor globals, builds
the helper bands at `[this+0x88/+0x8c/+0x90]` through `0x0041a990`, and only then tails into the
tagged import owner `0x004196c0`. The next two roots in the same load strip are now explicit
too: `0x0062ba8c` is allocated and constructed through
`structure_candidate_collection_construct_and_stream_load_records_then_refresh_counts`
`0x0041f4e0`, which enters the tagged import owner `0x0041ede0` and then refreshes the aggregate
filter/year-visible counts through `0x0041e970`; and `0x006ada84` is allocated and constructed
through `locomotive_collection_construct_and_stream_load_records` `0x00462520`, which enters the
linked-era locomotive import owner `0x00461f10` and then refreshes the live availability override
band through `0x00461e00`. The same fan-out now also has explicit constructor ownership for the
adjacent typed roots: the live company collection `0x0062be10` is constructed through
`company_collection_construct` `0x00429950`, the live profile/chairman collection `0x006ceb9c`
is constructed through `profile_collection_construct` `0x00477740`, the live train collection
`0x006cfcbc` is constructed through `train_collection_construct` `0x004b2340`, the sibling
indexed collection `0x006acd34` is constructed through
`runtime_object_collection_construct_vtable_5cae10` `0x00455320`, the support family
`0x0062b244` is constructed through
`support_collection_construct_seed_counters_and_clear_large_sideband` `0x0040aeb0`, and the live
world root `0x0062c120` is published through
`world_runtime_construct_root_and_seed_global_0x62c120` `0x0044cf70` before the heavier bundle
load body continues through `0x00449200` and `0x0044cfb0`. The profile-side tagged header
siblings are explicit too: runtime load re-enters
`profile_collection_refresh_tagged_header_counts_from_bundle` `0x00477780`, while package save
later mirrors the same `0x5209/0x520a/0x520b` trio back out through
`profile_collection_serialize_tagged_header_counts_into_bundle` `0x004777e0`. The same tagged
symmetry is now bounded for the live company collection too: world-load refresh re-enters
`company_collection_load_tagged_header_counts_and_refresh_live_records_from_bundle` `0x00429af0`,
while the package-save path mirrors the same `0x61a9/0x61aa/0x61ab` bracket through
`company_collection_serialize_tagged_header_counts_and_save_live_records_into_bundle`
`0x00429b90`, whose per-company callback is currently the no-op stub `0x00424000` while the
load-side per-company follow-on is `company_refresh_post_load_year_clamp_and_runtime_support_fields`
`0x004268e0`. The same tagged symmetry is now bounded for the live train collection too:
world-load refresh re-enters
`train_collection_load_tagged_header_counts_and_refresh_live_records_from_bundle` `0x004b2700`,
while the package-save path mirrors the same `0x5209/0x520a/0x520b` header bracket and per-train
record walk through
`train_collection_serialize_tagged_header_counts_and_save_live_records_into_bundle`
`0x004b27a0`; the per-train payload seam itself is now explicit too, with route-list load through
`train_refresh_tagged_route_list_payload_from_bundle` `0x004a84b0` and route-list save through
`train_serialize_tagged_route_list_payload_into_bundle` `0x004a7030`.
The later world-entry reactivation branch correspondingly uses `(1, esi)` rather than `(1, 0)`. The later world-entry reactivation branch correspondingly uses `(1, esi)` rather than `(1, 0)`.
The current live hook probes now push the remaining auto-load gap much later too: on the The current live hook probes now push the remaining auto-load gap much later too: on the
hook-driven path `shell_transition_mode(4, 0)` returns cleanly, and the full old-mode teardown hook-driven path `shell_transition_mode(4, 0)` returns cleanly, and the full old-mode teardown
@ -89,30 +156,25 @@
ready-count log so the mode-`4` startup lane either stages immediately or shows exactly how far ready-count log so the mode-`4` startup lane either stages immediately or shows exactly how far
the gate gets. That adjustment worked on the next run: the hook now stages and completes the the gate gets. That adjustment worked on the next run: the hook now stages and completes the
`shell_transition_mode` path again, with `LoadScreen.win` construction and publish returning `shell_transition_mode` path again, with `LoadScreen.win` construction and publish returning
cleanly. But the post-publish startup subchain is still unresolved: there is still no trusted cleanly. The post-publish startup subchain is no longer unresolved on the static side, though.
`0x46c40` allocator hit, no direct `0x004336d0` entry, and no direct `0x00438890` entry. So Direct disassembly of the mode-`4` branch at `0x0048302a..0x004830ca` now closes it completely:
the next clean runtime boundary is the tiny `LoadScreen.win` scalar setter at `0x004ea710`, after constructing `LoadScreen.win`, the branch sets shell state `[transition+0x08] = 4`,
which sits immediately before the `0x0053b070` allocation in the static mode-`4` branch. The chooses one page scalar `110.0f`, `236.0f`, or `5.0f`, writes that page id through
immediate next runtime check is even more concrete than the helper hook, though: inspect the `shell_load_screen_window_set_active_page` `0x004ea710`, allocates one `0x46c40`-byte runtime
state that `0x004ea710` should leave behind. The hook now logs the post-transition object through `0x0053b070`, resets it through
`LoadScreen.win` singleton, its field `[+0x78]`, `0x006cec78`, the shell state's `[+0x0c]` `world_runtime_reset_startup_dispatch_state_bands` `0x004336d0`, stores the result into
active-mode object field, and the startup selector. If `0x004ea710` really ran on the mode-`4` `0x006cec78`, and then directly enters
branch, `[LoadScreen.win+0x78]` should no longer be zero after `shell_transition_mode` returns. `shell_active_mode_run_profile_startup_and_load_dispatch` `0x00438890` as `(1, 0)` before
The latest run answered that directly: after transition return, `field_active_mode_object` is publishing the active-mode object back through `0x005389c0`. So the static startup chain after
still the `LoadScreen.win` singleton, `0x006cec78` is still null, `[LoadScreen.win+0x78]` is `LoadScreen.win` publish is now explicit rather than inferred from the earlier hook traces. The
still zero, and the startup selector is still `3`. So the current best read is that RT3 is remaining runtime-side question is narrower: why the earlier hook snapshots still showed
still parked in the plain `LoadScreen.win` state at transition return rather than having entered `[LoadScreen.win+0x78] == 0` and `0x006cec78 == 0` immediately after transition return even
the separate runtime-object path yet. That shifts the best next runtime boundary from “deeper though the static mode-`4` branch does not leave those fields that way once it reaches the
inside `shell_transition_mode`” to “what later active-mode service tick, if any, promotes the startup-dispatch path. The internal selector split in `0x438890` is tighter now too:
load-screen object into the startup-dispatch path.” The next run now logs the first few `[0x006cec7c+0x01]` is a separate seven-way startup selector, not the shell mode id. Values `1`
shell-state service ticks after auto-load is attempted with that same state tuple and `7` load `Tutorial_2.gmp` and `Tutorial_1.gmp`, values `3/5/6` collapse into the same
(`0x006cec78`, `[shell_state+0x0c]`, `0x006d10b0`, `[LoadScreen.win+0x78]`, selector), so the profile-seeded file-load lane through `0x445ac0([0x006cec7c]+0x11, 4, &out_success)`, value `2`
next question is very narrow: does one later service tick finally promote the plain is a world-root initialization lane
`LoadScreen.win` state into the startup-runtime object path, or does it stay frozen as-is? The
internal selector split in `0x438890` is tighter now too: `[0x006cec7c+0x01]` is a separate
seven-way startup selector, not the shell mode id. Values `1` and `7` load `Tutorial_2.gmp` and
`Tutorial_1.gmp`, values `3/5/6` collapse into the same profile-seeded file-load lane through
`0x445ac0([0x006cec7c]+0x11, 4, &out_success)`, value `2` is a world-root initialization lane
that allocates `0x0062c120` and then forces selector `3`, and value `4` is the setup-side world that allocates `0x0062c120` and then forces selector `3`, and value `4` is the setup-side world
reset or regeneration lane that rebuilds `0x0062c120` from `0x006d14cc/0x006d14d0` before later reset or regeneration lane that rebuilds `0x0062c120` from `0x006d14cc/0x006d14d0` before later
world setup continues. The write side is tighter now too: `Campaign.win` writes selector `6`, world setup continues. The write side is tighter now too: `Campaign.win` writes selector `6`,
@ -346,11 +408,14 @@
sixteen-byte per-scenario campaign selector or unlock band consumed directly by `Campaign.win`. sixteen-byte per-scenario campaign selector or unlock band consumed directly by `Campaign.win`.
The neighboring profile helpers are tighter now too: `0x0047bbf0` is the broad default reset for The neighboring profile helpers are tighter now too: `0x0047bbf0` is the broad default reset for
the staged `0x108`-byte runtime-profile record, clearing the whole record and then reseeding the the staged `0x108`-byte runtime-profile record, clearing the whole record and then reseeding the
visible setup and campaign anchors `[profile+0x77]`, `[profile+0x79]`, `[profile+0x7d]`, visible setup and campaign anchors `[profile+0x77]`, `[profile+0x79]`, `[profile+0x7d]`, the
`[profile+0x83]`, `[profile+0x87]`, and `[profile+0x97]`; and `0x0047bc50` is the compact scan random-like dword `[profile+0x83]`, the first setup row-marker byte `[profile+0x87]`, and the
helper over `[profile+0xc6..+0xd5]` that returns the first selector byte below `2`, which keeps file-backed launch or rehydrate latch `[profile+0x97]`; `0x00502c00` is now tighter on the same
that band tied to staged campaign progress or unlock state rather than to the earlier setup-panel slab because its file-backed lane copies the selected row's primary and secondary `0x32`-byte
payload fields. string bands into `[profile+0x11]` and `[profile+0x44]` while arming presence byte
`[profile+0x10]`; and `0x0047bc50` is the compact scan helper over `[profile+0xc6..+0xd5]` that
returns the first selector byte below `2`, which keeps that band tied to staged campaign progress
or unlock state rather than to the earlier setup-panel payload fields.
The message-dispatch side is tighter now too. The local classifier at `0x004b91d8` routes the The message-dispatch side is tighter now too. The local classifier at `0x004b91d8` routes the
control range `0x0c352..0x0c39b` through five concrete case classes: `0x0c352..0x0c361` enter control range `0x0c352..0x0c39b` through five concrete case classes: `0x0c352..0x0c361` enter
the shared selector or launch branch, `0x0c362..0x0c367` force local page `1`, the shared selector or launch branch, `0x0c362..0x0c367` force local page `1`,
@ -441,10 +506,12 @@
`.gmp`, `.gmx`, `.gmc`, `.gms`, `.gmt`, `.smp`, `Quicksave`, the `0x004dd010` mode table at `.gmp`, `.gmx`, `.gmc`, `.gms`, `.gmt`, `.smp`, `Quicksave`, the `0x004dd010` mode table at
`0x005f3d58`, the auxiliary-owner presence check at `0x00434050`, and the `.gmt` handoff through `0x005f3d58`, the auxiliary-owner presence check at `0x00434050`, and the `.gmt` handoff through
`0x00469d30`, together with localized string evidence from ids `3018` and `3898`. `0x00469d30`, together with localized string evidence from ids `3018` and `3898`.
- Direct shell stubs above that coordinator are tighter now too: `0x004408b0` is the pure - Direct shell stubs above that coordinator are tighter now too: `0x004408b0` is the ordinary
zero-flag wrapper into `shell_map_file_world_bundle_coordinator`, while `0x004408d0` is the `Save game` wrapper and forwards the pure zero-flag triplet into
sibling wrapper with flag triplet `(0, 1, 0)`. The exact user-facing command names for those two `shell_map_file_world_bundle_coordinator`; `0x004408d0` is the sibling `Quick save` wrapper,
wrappers are still open, but the dispatcher shape is no longer. forwarding flag triplet `(0, 1, 0)`. RT3.lng closes the neighboring `shell_map_file_entry_coordinator`
pair in the same way: `0x00441ac0` is `Load game` and `0x00441af0` is `Quick load`, with the
same `(0, 0, 0)` versus `(0, 1, 0)` split above `0x00445ac0`.
- Open Questions: bit `0x1` on both broad coordinators now grounds the Quicksave name seed and the - Open Questions: bit `0x1` on both broad coordinators now grounds the Quicksave name seed and the
former third `fileopt.win` flag has been ruled out as a file-flow question because it just opens former third `fileopt.win` flag has been ruled out as a file-flow question because it just opens
`SettingsWindow.win`. The old broad extension question is mostly resolved: `.gmp` is the `SettingsWindow.win`. The old broad extension question is mostly resolved: `.gmp` is the

View file

@ -112,7 +112,11 @@
side is separated now too: `0x0046cce0`, `0x0046cd10`, `0x0046ce90`, and `0x0046d230` are the side is separated now too: `0x0046cce0`, `0x0046cd10`, `0x0046ce90`, and `0x0046d230` are the
current small latch/state owners under `0x006cd91c`, `0x006d1280`, `0x006d1284`, and current small latch/state owners under `0x006cd91c`, `0x006d1280`, `0x006d1284`, and
`0x006ce9c8`, while `0x0046cd30` and `0x0046ce10` are the paired current-session string/scalar `0x006ce9c8`, while `0x0046cd30` and `0x0046ce10` are the paired current-session string/scalar
submit-and-apply handlers over `[entry+0x258c/+0x268c/+0x2690/+0x2694]`. The same callback table submit-and-apply handlers over `[entry+0x258c/+0x268c/+0x2690/+0x2694]`. One neighboring
cross-subsystem callback is tighter now too: `0x0046cf40` mirrors one remote late
setup-preview/status payload block into `[world+0x66be]`, the same world band later serialized
and restored under bundle chunk ids `0x2ee0/0x2ee1` and re-normalized after restore through
`0x0047bc80`. The same callback table
also owns one small fixed transfer-progress family rooted at `0x006ce2e8`: `0x0046cfe0` also owns one small fixed transfer-progress family rooted at `0x006ce2e8`: `0x0046cfe0`
allocates the first free `0x5c` slot and optionally formats one label string, `0x0046d090` allocates the first free `0x5c` slot and optionally formats one label string, `0x0046d090`
appends progress payload into the matched slot while publishing one percent string through appends progress payload into the matched slot while publishing one percent string through
@ -358,7 +362,9 @@
through `0x596090`, and on success refreshes the current status text at `[transport+0xaf4]` through `0x596090`, and on success refreshes the current status text at `[transport+0xaf4]`
before rerunning `0x5965d0(1)`. The same local pass also makes the negative boundary tighter: before rerunning `0x5965d0(1)`. The same local pass also makes the negative boundary tighter:
setup still touches the surrounding callback-table and replay-band fields without ever seeding a setup still touches the surrounding callback-table and replay-band fields without ever seeding a
nonzero value into `[transport+0x1778]`, so that sidecar remains an upstream producer gap. nonzero value into `[transport+0x1778]`, so the local static seam is closed here: no ordinary
constructor, reset, service, or callback-table owner in `RT3.exe` currently writes that sidecar,
and the producer looks upstream of this local cluster.
Beside it, `0x58e200` is the broader callback-table attach or refresh owner: Beside it, `0x58e200` is the broader callback-table attach or refresh owner:
it seeds one immediate-drain context id, conditionally copies the local name into `[transport+0x04]`, it seeds one immediate-drain context id, conditionally copies the local name into `[transport+0x04]`,
clears `[transport]`, `[transport+0x48]`, and `[transport+0x1edc]`, stores follow-on callback clears `[transport]`, `[transport+0x48]`, and `[transport+0x1edc]`, stores follow-on callback
@ -398,6 +404,12 @@
refresh path is tighter too: `0x59d5b0` builds one zeroed `0x1e0`-byte stub from the caller refresh path is tighter too: `0x59d5b0` builds one zeroed `0x1e0`-byte stub from the caller
string and appends it into the flat registered-name vector `[transport+0x54c]` through the string and appends it into the flat registered-name vector `[transport+0x54c]` through the
shared `generic_vector_push_back` helper `0x59e4d0`, while the smaller sibling `0x58e7a0` sits shared `generic_vector_push_back` helper `0x59e4d0`, while the smaller sibling `0x58e7a0` sits
beside it as the selector-slot text and registered-name removal path. The selector-text submit
owner itself is explicit now too: `0x593c40` is not just a generic “route request” wrapper. It
rejects null, empty, or `>= 0x101`-byte text, falls back to fixed sample `0x005c87a8` when the
caller sample pointer is null, allocates a type-`2` transient work record through `0x5934e0`,
stores the selector id in work field `+0x1c`, refreshes selector-side naming through
`0x59fc80/0x595140`, and only then hands the request into `0x58e720` with callback `0x593bb0`.
under `0x5954b0` and formats one selector-slot line before removing the corresponding under `0x5954b0` and formats one selector-slot line before removing the corresponding
registered-name entry through `0x59d760`. The selector callback-name side under the same reset registered-name entry through `0x59d760`. The selector callback-name side under the same reset
tail is explicit now too: `0x596900` first walks the shared selector callback-name store at tail is explicit now too: `0x596900` first walks the shared selector callback-name store at
@ -491,8 +503,16 @@
`multiplayer_transport_invoke_bound_route_callback_if_present` `0x592710`, which simply calls the `multiplayer_transport_invoke_bound_route_callback_if_present` `0x592710`, which simply calls the
bound route's optional callback slot `[binding+0x14]` with companion argument `[binding+0x18]`. bound route's optional callback slot `[binding+0x14]` with companion argument `[binding+0x18]`.
When the tuple does not match, the same gate instead compares descriptor fields `maxplayers` and When the tuple does not match, the same gate instead compares descriptor fields `maxplayers` and
`numplayers`; only a descriptor with spare capacity reaches that same callback handoff. Current `numplayers`; only a descriptor with spare capacity reaches that same callback handoff. The owner
evidence now also closes the clone side of that staged path: `0x596270` copies the first nine directly above that gate is explicit now too: `0x595dc0` first rejects when staged-route busy
latch `[transport+0x1e8c]` or selector-slot object `[transport+0x38c]` is live, then reuses the
same tuple-or-capacity result from `0x595d60`; only a positive result lets it refresh selector
state through `0x5973b0`, reset selector slot `2` through `0x5954b0`, stage the descriptor
through `0x5958e0`, and finally drop route mode back to `0` through `0x595650` when that staging
path fails. That makes the seam read as one bounded ladder instead of three isolated helpers:
descriptor tuple match or spare-capacity gate, optional bound-route callback handoff, then the
busy-latch-screened route-transition owner above it. Current evidence now also closes the clone
side of that staged path: `0x596270` copies the first nine
dwords of the source staged callback payload, clears the intrusive next-link, and then replays dwords of the source staged callback payload, clears the intrusive next-link, and then replays
the source keyed-property list through shim `0x596260`, which simply reinserts each owned the source keyed-property list through shim `0x596260`, which simply reinserts each owned
`(key,value)` pair into the clone's property store through `0x58d0f0`, before the clone is `(key,value)` pair into the clone's property store through `0x58d0f0`, before the clone is
@ -513,8 +533,12 @@
`multiplayer_route_callback_runtime_release_shared_string_copy` `0x5905a0` now bound the shared `multiplayer_route_callback_runtime_release_shared_string_copy` `0x5905a0` now bound the shared
string pool used by that decoded schema, and the higher bring-up owner `0x596090` now clearly string pool used by that decoded schema, and the higher bring-up owner `0x596090` now clearly
splits between `[transport+0xba4]` with owner callback `0x595a40`, the local field-cache family splits between `[transport+0xba4]` with owner callback `0x595a40`, the local field-cache family
`[transport+0x1724]` seeded through `0x5a08f0/0x595b60`, and `[transport+0x1164]` with owner `[transport+0x1724]` seeded through `0x5a08f0/0x595b60` with fixed stem `0x00629d50`, and
callback `0x595bc0`, while `[transport+0x1164]` with owner callback `0x595bc0`. The same constructor also builds the
`gsi_am_rating` primary-endpoint table `[transport+0x18bc]` from local transport name
`[transport+0x60]` plus suffix `0x005dccfc` under callback `0x595e10`, and seeds the queued
descriptor family `[transport+0x1e7c]` through `0x5a08f0/0x595f70` with fixed stem
`0x00629d54`, while
`multiplayer_transport_route_callback_table_service_receive_decode_state_machine` `0x5908c0` `multiplayer_transport_route_callback_table_service_receive_decode_state_machine` `0x5908c0`
is the current live receive/decode state machine serviced by `0x591290` in table states `2/3`. is the current live receive/decode state machine serviced by `0x591290` in table states `2/3`.
The callback-owner mode split above that runtime is now explicit too: append-notify `0x590370` The callback-owner mode split above that runtime is now explicit too: append-notify `0x590370`
@ -548,7 +572,13 @@
route in mode `4` through `0x590ed0`. Success seeds cached progress percentage route in mode `4` through `0x590ed0`. Success seeds cached progress percentage
`[transport+0x1774] = 1` and immediately enqueues one mode-`3` field snapshot through `0x592b50`; `[transport+0x1774] = 1` and immediately enqueues one mode-`3` field snapshot through `0x592b50`;
failure falls back through the same clear path. `0x596530` is the `gsi_am_rating` reopen failure falls back through the same clear path. `0x596530` is the `gsi_am_rating` reopen
path above `[transport+0x18bc]`. On that latter branch, `0x590dc0` is now bounded as the path above `[transport+0x18bc]`, and that owner is tighter now too: when precondition
`[transport+0xba0]` is clear it does not even attempt the reopen and instead stamps
`[transport+0x1ed4] = 1`; otherwise it resets the am-rating route family, clears the callback
table rooted at `[transport+0x18bc]`, tries `0x590ed0(mode 4)` from stored route label
`[transport+0x1ed0]`, and only on success sets `[transport+0x1ec4] = 1` while clearing
`[transport+0x1ed4]`. On that latter
branch, `0x590dc0` is now bounded as the
state-`0` raw-endpoint seed pass over the live route handle, repeatedly pulling endpoint tuples state-`0` raw-endpoint seed pass over the live route handle, repeatedly pulling endpoint tuples
through `0x58bc7e` record type `0x1f3` before stamping descriptor flag byte `0x15` with `0x11`. through `0x58bc7e` record type `0x1f3` before stamping descriptor flag byte `0x15` with `0x11`.
That makes the remaining source-flag meaning narrower too: current evidence now supports reading That makes the remaining source-flag meaning narrower too: current evidence now supports reading
@ -560,15 +590,22 @@
`[transport+0x1ed4] = 1` and then picks route mode `1` or `3` based on whether deferred `[transport+0x1ed4] = 1` and then picks route mode `1` or `3` based on whether deferred
descriptor pointer `[transport+0x1ed8]` is null, and mode `5` mirrors staged companion dword descriptor pointer `[transport+0x1ed8]` is null, and mode `5` mirrors staged companion dword
`[transport+0x490]` into both `[transport+0x54]` and `[transport+0x1724+0x24]`. `[transport+0x490]` into both `[transport+0x54]` and `[transport+0x1724+0x24]`.
The adjacent capacity-descriptor side is tighter too: `0x595bc0` now clearly publishes a The adjacent capacity-descriptor side is tighter too: `0x595bc0` is now clearly the owner
descriptor block from live descriptor properties `hostname`, `numwaiting`, `maxwaiting`, callback for the capacity-descriptor route callback table rooted at `[transport+0x1164]`, not a
`numservers`, and `numplayers` plus three carried sidecar scalars. Its live mode `0` path reads direct transport method on `ecx = transport`. The route-callback-table constructor `0x5905e0`
those descriptor properties through `0x58d1f0`, `0x58d170`, and `0x58d6d0`, then forwards them installs it with owner cookie `transport`, and the live route machinery later invokes it from
through opcode-`2` builder `0x592ae0`; its replay-linked modes `3/5` instead enqueue an all-zero append-notify `0x590370` and decode-service `0x5908c0/0x5911e0` with `ecx = table object`, the
descriptor payload while preserving only the borrowed callback-wrapper triplet from the cached route descriptor or special decode frame on the stack, and the transport cookie on the stack.
sidecar record and then unlink that cached record through `0x5933a0`; and its callback modes Inside that callback it still always reads `[transport+0x1778]` first. Modes `3/5` consume that
`1/2/6` are now explicit no-op fallthroughs. That sidecar at sidecar immediately, while live mode `0` first resolves primary IPv4 plus `hostname`,
`[transport+0x1778]` is tighter now too: current evidence says it behaves as one cached pointer `numwaiting`, `maxwaiting`, `numservers`, and `numplayers` through `0x58d1f0`, `0x58d170`, and
`0x58d6d0`, and only then forwards those live payload lanes together with the same sidecar
triplet `[+0x0c/+0x10/+0x18]` into opcode-`2` builder `0x592ae0`. So mode `0` is not a
sidecar-free fallback; it still requires the borrowed sidecar before it can publish the
populated descriptor block. The replay-linked modes `3/5` instead enqueue an all-zero
descriptor payload while preserving only that same borrowed callback-wrapper triplet and then
unlinking the cached record through `0x5933a0`; and its callback modes `1/2/6` are now explicit
no-op fallthroughs. That sidecar at `[transport+0x1778]` is tighter now too: current evidence says it behaves as one cached pointer
into the transient work-record family at `[transport+0x1780]`, because every meaningful branch into the transient work-record family at `[transport+0x1780]`, because every meaningful branch
in `0x595bc0` reads the same `+0x0c/+0x10/+0x18` metadata triplet and replay modes later consume in `0x595bc0` reads the same `+0x0c/+0x10/+0x18` metadata triplet and replay modes later consume
the pointer through `0x5933a0`. The negative result is stronger too: local text-side xrefs still the pointer through `0x5933a0`. The negative result is stronger too: local text-side xrefs still
@ -598,13 +635,17 @@
`0`, `3`, and `5`; the upstream route-callback-table owner still delivers modes `1`, `2`, and `0`, `3`, and `5`; the upstream route-callback-table owner still delivers modes `1`, `2`, and
`6`, but those are explicit no-ops in this capacity leaf. So the owner wiring itself is no `6`, but those are explicit no-ops in this capacity leaf. So the owner wiring itself is no
longer the open edge; only the upstream sidecar producer remains unresolved. The neighboring longer the open edge; only the upstream sidecar producer remains unresolved. The neighboring
work queue is tighter too: `0x593330/0x593370/0x593380` now bound `[transport+0x1780]` as the work queue is tighter too: `0x593330/0x593370/0x593380` now bind `[transport+0x1780]` as the
construct/clear/destroy owner family, while `0x5933a0`, `0x5934e0`, and `0x593570` ground the construct/clear/destroy owner family and explicitly treat `[transport+0x1784]` and
remove, allocate, and completion side over that same collection. The completion owner is tighter `[transport+0x1788]` as the queued-work and completed-work counters beside that collection,
now too: `0x593570` clears in-flight latch `[transport+0x44]`, stores the final attach result in while `0x5933a0`, `0x5934e0`, and `0x593570` ground the remove, allocate, and completion side
`[transport+0x48]`, stamps `[transport+0x50]` on success, refreshes local name buffer over that same queue. The completion owner is tighter now too: `0x593570` clears in-flight
latch `[transport+0x44]`, stores the final attach result in `[transport+0x48]`, stamps current
route scalar `[transport+0x50]` from `0x58f450` on success, refreshes local name buffer
`[transport+0x04]` from `0x58e630`, republishes the staged metadata triplet through opcode-`1` `[transport+0x04]` from `0x58e630`, republishes the staged metadata triplet through opcode-`1`
trigger wrapper `0x592a40`, and only then unlinks the consumed work record. The small sibling trigger wrapper `0x592a40`, and only then unlinks the consumed work record. When validation of a
nominal success fails through `0x58d7e0/0x58d810`, the same owner still replays that callback
trigger but also arms deferred reset latch `[transport+0x1edc]`. The small sibling
`0x593400` is tighter too: it is a pure work-record uniqueness predicate over field `+0x0c`. Its caller is `0x593400` is tighter too: it is a pure work-record uniqueness predicate over field `+0x0c`. Its caller is
tighter now too: `0x58d720` is an immediate-drain quiescence gate over one transport context id, tighter now too: `0x58d720` is an immediate-drain quiescence gate over one transport context id,
using `0x593400` for the queued work family at `[transport+0x1780]` and `0x592970` for the using `0x593400` for the queued work family at `[transport+0x1780]` and `0x592970` for the
@ -654,11 +695,21 @@
buffer from the current local transport name at `[transport+0x60]` plus format `0x005dccfc`, buffer from the current local transport name at `[transport+0x60]` plus format `0x005dccfc`,
constructs `[transport+0x18bc]` with callback `0x595e10` and that stack-built label, seeds constructs `[transport+0x18bc]` with callback `0x595e10` and that stack-built label, seeds
queued descriptor family `[transport+0x1e7c]` through `0x595f70`, clears staged payload slot queued descriptor family `[transport+0x1e7c]` through `0x595f70`, clears staged payload slot
`[transport+0xb50]`, and then sets callback-plumbing latch `[transport+0xba0] = 1`. `0x596210` `[transport+0xb50]`, and then sets callback-plumbing latch `[transport+0xba0] = 1`. That
is the recurring service sweep over those same three constructor now reads cleanly as pure callback-table and cache bringup: it leaves the later
tables plus the field-cache and queued-descriptor families; `0x596060` is the explicit live-route entry to the dedicated open or reopen owners `0x5962e0` and `0x596530` instead of
`gsi_am_rating` runtime-and-queue reset; and `0x596530` is the reopen-from-stored-label sibling trying to start either live route itself. One level lower, `0x5962e0` now reads as the
above that same am-rating table. The matching local cleanup is tighter too: `0x595b80` is now field-subscription open owner rather than a vague route
helper: it clears the old `[transport+0xba4]` runtime, rebuilds the route label from the
optional caller suffix plus the fixed tail word at `0x005d0b78`, materializes one callback-key
buffer from the fixed stem `0x005e22a0..0x005e22b2`, seeds fixed field ids `1` and `0x0b`,
appends per-field selector-name ids through `0x00629958`, opens the live route in mode `4`, and
then seeds `[transport+0x1774] = 1` before the first immediate field snapshot. `0x596210` is the
recurring service sweep over those same three tables plus the field-cache and queued-descriptor
families; `0x596060` is the explicit `gsi_am_rating` runtime-and-queue reset; `0x596530` is the
reopen-from-stored-label sibling above that same am-rating table; and `0x5965a0` is the single-shot
status-route connect latch that sets `[transport+0xb40]` before forwarding into `0x5973d0`,
leaving the rollback and clear path to `0x5965d0`. The matching local cleanup is tighter too: `0x595b80` is now
explicitly the field-subscription-side live-runtime reset plus field-cache clear plus active explicitly the field-subscription-side live-runtime reset plus field-cache clear plus active
opcode-`3` purge, `0x595ce0` resets only the capacity-descriptor route callback runtime at opcode-`3` purge, `0x595ce0` resets only the capacity-descriptor route callback runtime at
`[transport+0x1164]`, `0x5961b0` is the full destroy-side owner over the three tables plus both `[transport+0x1164]`, `0x5961b0` is the full destroy-side owner over the three tables plus both
@ -675,10 +726,16 @@
body and still never writes a nonzero value into `[transport+0x1778]` before later explicit body and still never writes a nonzero value into `[transport+0x1778]` before later explicit
neighbor initialization. The callback-binding owner stack now tightens that boundary too: neighbor initialization. The callback-binding owner stack now tightens that boundary too:
`0x5934e0` stages the shared work-record metadata triplet, `0x593650` binds it into the `0x5934e0` stages the shared work-record metadata triplet, `0x593650` binds it into the
callback-table worker path, and `0x593570` later consumes and republishes it, while callback-table worker path with one fixed second worker callback and one optional first worker
callback gated by `[transport+0x4c]`, and `0x593570` later consumes and republishes it, while
`[transport+0x1778]` still appears only as the borrowed sidecar read in `0x595bc0`. So this `[transport+0x1778]` still appears only as the borrowed sidecar read in `0x595bc0`. So this
edge is now locally closed, and the remaining producer looks like an upstream callback or worker local ownership seam is closed: within the mapped transport cluster there is no remaining
handoff rather than one missing ordinary field store in the local cluster. The adjacent staged-route ordinary producer to find, and the remaining source now looks like an upstream callback or worker
handoff rather than one missing field store in the local binary. The neighboring callback-vector
strip is explicit now too: `0x597300` is a no-op owner-callback stub, `0x597303` is the
validated-cookie event forwarder over callback slot `[transport+0x17f0]`, and `0x597330` is the
validated extended-payload forwarder over `[transport+0x17f4]`; both reuse the same owner
context at `[transport+0x17f8]`. The adjacent staged-route
callback side is tighter too: `0x595860` is now bounded as the callback side is tighter too: `0x595860` is now bounded as the
submit-result handler beneath `0x5958e0`, and the old `[transport+0xac0]` ambiguity there is now submit-result handler beneath `0x5958e0`, and the old `[transport+0xac0]` ambiguity there is now
gone. That branch is using the already-grounded third selector-generation counter at `[0xac0]` gone. That branch is using the already-grounded third selector-generation counter at `[0xac0]`
@ -769,12 +826,7 @@
only show it being preserved by later generic helpers like only show it being preserved by later generic helpers like
`generic_record_0x1c_deep_copy_with_owned_string_at_0x08` `0x591410` and the adjacent `generic_record_0x1c_deep_copy_with_owned_string_at_0x08` `0x591410` and the adjacent
callback-marshaling wrappers `0x591480` and `0x591510`, not read through any dedicated semantic callback-marshaling wrappers `0x591480` and `0x591510`, not read through any dedicated semantic
accessor yet. The route-event dispatcher side is tighter too: `0x595dc0` is no longer just one accessor yet. The mode-`5` tails in both
vague transition shim. It first rejects when staged-route busy latch `[transport+0x1e8c]` or
selector-slot object `[transport+0x38c]` is live, then reuses `0x595d60`; only a positive
capacity result lets it refresh selector state through `0x5973b0`, reset selector slot `2`
through `0x5954b0`, and stage the descriptor through `0x5958e0` before route mode `0` is
entered through `0x595650`. The mode-`5` tails in both
callback families do not copy a descriptor-local field but instead mirror the transport-staged callback families do not copy a descriptor-local field but instead mirror the transport-staged
companion dword at `[this+0x490]` into `[this+0x54]` and queue-side slot `[this+0x1724+0x24]`. The companion dword at `[this+0x490]` into `[this+0x54]` and queue-side slot `[this+0x1724+0x24]`. The
`gsi_am_rating` maintenance lane is tighter now too: after pruning failed descriptors it sorts `gsi_am_rating` maintenance lane is tighter now too: after pruning failed descriptors it sorts
@ -947,24 +999,44 @@
force mode `2` when `[this+0xac0] <= 1` and otherwise promote through mode `3` or `4` according force mode `2` when `[this+0xac0] <= 1` and otherwise promote through mode `3` or `4` according
to `[this+0xb48]`, while zero results set `[this+0x1ed8] = 1` and then fall back through route to `[this+0xb48]`, while zero results set `[this+0x1ed8] = 1` and then fall back through route
mode `0` or `1` according to deferred am-rating latch `[this+0x1ed4]`. On success the route-mode setter stores the current mode `0` or `1` according to deferred am-rating latch `[this+0x1ed4]`. On success the route-mode setter stores the current
mode at `[this+0x18b8]` and then runs one of the concrete branches: mode `0` refreshes mode at `[this+0x18b8]`, mirrors mode changes into the bound-route callback lane through
selector-route side effects through `0x5973b0`, resets selector slot `2`, tries to reopen the `multiplayer_transport_enqueue_bound_route_mode_snapshot_opcode4_if_live` `0x5932a0`, and then
`gsi_am_rating` callback-table family, and then falls back according to deferred route-status runs one of the concrete branches: mode `0` releases the current
flag `[this+0x1ed8]`; mode `1` conditionally reopens that family and then, when no live route is live route, resets selector slot `2`, and only when the am-rating route is not already live at
present, tries to connect the live route through `0x597480`; mode `2` resets the am-rating `[this+0x1ec4]` tries to reopen the `gsi_am_rating` callback-table family through `0x596530`;
family and promotes a successful live-route connect into mode `1`; once that family is already a failed reopen then falls back according to deferred route-status flag `[this+0x1ed8]`. Mode
populated, the later callback-owner at `0x595f70` takes over by pruning stale entries, refreshing `1` conditionally reopens that am-rating family only when both `[this+0x1ec4]` and
`gsi_am_rating`, selecting the current head endpoint, and staging the route-callback payload `[this+0x1ed4]` are clear and then, when no live route is present, tries to connect the live
through `0x5958e0`; mode `3` resets the route through `multiplayer_transport_try_connect_live_route` `0x597480`; once that family is already populated, the later callback-owner at
am-rating family, refreshes selector-route state, resets selector slot `2`, and releases the `0x595f70` takes over by pruning stale entries, refreshing `gsi_am_rating`, selecting the
current route binding; mode `4` is the narrow status-route recovery retry; and mode `5` resets current head endpoint, and staging the route-callback payload through `0x5958e0`. Mode `2`
the am-rating family, refreshes selector-route state, and releases the current route binding first releases any stale live route through `multiplayer_transport_release_live_route`
without the selector-slot reset. The live-route connect path is tighter now too: `0x597480` `0x5973b0` when status latch `[this+0xb40]` is clear, then resets the am-rating family and
builds one `0x20`-byte local identifier from `[this+0x60]` plus suffix template `0x005dccfc`, promotes a successful live-route connect into mode `1`. Mode `3` resets the am-rating family,
seeds the callback vector `0x596fd0/0x5970e0/0x597180/0x5971b0/0x597270/0x5972c0`, and chooses releases the live route through `0x5973b0`, resets selector slot `2`, and then tears down the
either the default route id `0x1964` through `0x58cc40` or the binding-specific id in current bound-route payload through
`[binding+0x30]` through `0x58c9b0`; on the binding-specific path it also clears pending rebuild `multiplayer_transport_release_current_route_binding_detach_descriptor_callback_and_clear_slot`
cookie `[binding+0x34]` and marks the constructed route live via `[route+0xbc] = 1`. The stable `0x595620`; mode `4` is the narrow status-route recovery retry; and mode `5` resets the
am-rating family, releases the live route through `0x5973b0`, and runs that same
`0x595620` teardown path without the selector-slot reset. That helper is narrower than the older
generic wording implied: it detaches the current binding from route-label object `[this+0x1ed0]`
through `0x58f3c0`, releases the staged bound-route callback payload at `[this+0x1ec8]` through
`0x5933a0`, and only then clears `[this+0x1ec8]`. The status-route connect path is tighter now too:
`0x5973d0` seeds `[this+0xaf4]`, uses the same six callback lanes, mirrors stored route label
`[this+0x9a8]` into `[this+0xb3c]`, patches the created route's extended-payload callback slot,
and clears `[this+0xb38]` on success; when the caller route id is not `-1` it also zero-extends
the supplied route word before forwarding it into `0x58c9b0`. The local status-route helper
strip is explicit too: `0x597350` releases `[this+0xaf0]` through `0x58cfd0`, `0x597370`
services only that route through `0x58cf90`, and `0x597380` is the broader two-route sweep that
services `[this+0xaf0]` and `[this+0x1ecc]` through the common route-object service helper
`0x58d040`. The live-route connect path is tighter in the same way:
`0x597480` builds one `0x20`-byte local identifier from `[this+0x60]` plus suffix template
`0x005dccfc`, seeds the callback vector `0x596fd0/0x5970e0/0x597180/0x5971b0/0x597270/0x5972c0`,
and chooses either the default route id `0x1964` through `0x58cc40` or the binding-specific id
in `[binding+0x30]` through `0x58c9b0`; on the binding-specific path it also clears pending
rebuild cookie `[binding+0x34]` and marks the constructed route live via `[route+0xbc] = 1`,
while the success tail mirrors stored route label `[this+0x9a8]` into `[this+0xb3c]` and clears
live-route mode mask `[this+0xb38]`. The stable
transitions therefore still switch by releasing route objects or route bindings and rebuilding transitions therefore still switch by releasing route objects or route bindings and rebuilding
route state, not by mutating callback slots in place. The parser behavior is now tighter as well: semicolon lines only dispatch when route state, not by mutating callback slots in place. The parser behavior is now tighter as well: semicolon lines only dispatch when
`[route+0xd4]` is non-null, and the subtype-`6` raw fallback only dispatches when `[route+0xa4]` `[route+0xd4]` is non-null, and the subtype-`6` raw fallback only dispatches when `[route+0xa4]`

View file

@ -37,7 +37,7 @@
`Computing Transportation and Pricing...` stays visible while the pipeline runs `Computing Transportation and Pricing...` stays visible while the pipeline runs
`world_compute_transport_and_pricing_grid` `0x0044fb70`, the early collection-owned staging pass `world_compute_transport_and_pricing_grid` `0x0044fb70`, the early collection-owned staging pass
`world_rebuild_secondary_raster_derived_surface_and_companion_planes_in_rect` `0x0044e940`, `world_rebuild_secondary_raster_derived_surface_and_companion_planes_in_rect` `0x0044e940`,
`world_setup_building_collection_phase` `0x0041ea50`, and the conditional region pair `structure_candidate_collection_run_post_load_local_service_setup_phase` `0x0041ea50`, and the conditional region pair
`world_region_collection_seed_default_regions` `0x00421b60` plus `world_region_collection_seed_default_regions` `0x00421b60` plus
`world_region_collection_refresh_neighbor_and_profile_bands` `0x00420f30`. One save-load-side `world_region_collection_refresh_neighbor_and_profile_bands` `0x00420f30`. One save-load-side
status-stack strip is now tighter too: `0x004422d0/0x00442330` push and pop the same four shell status-stack strip is now tighter too: `0x004422d0/0x00442330` push and pop the same four shell
@ -2153,9 +2153,14 @@ Current evidence grounds the shell-controller-backed input and frame path as the
bits `0x40`, `0x20`, and `0x10` in the same flag byte during route-entry rewrites. Those bits are bits `0x40`, `0x20`, and `0x10` in the same flag byte during route-entry rewrites. Those bits are
also no longer copy-only: the neighboring shell helper also no longer copy-only: the neighboring shell helper
`shell_building_detail_refresh_flagged_service_capability_rows` `0x004b9a20` now consumes them to `shell_building_detail_refresh_flagged_service_capability_rows` `0x004b9a20` now consumes them to
restyle the `BuildingDetail.win` row bands `0x7d07..0x7d1c` and `0x7f58..0x801f`. The exact restyle the `BuildingDetail.win` row bands `0x7d07..0x7d1c` and `0x7f58..0x801f`. The important
player-facing labels for those rows are still open, but the subflags now have one real shell-side boundary is tighter now too: the `0x7d07..0x7d1c` band is a style-only indicator family in current
consumer instead of only preservation logic. The broader `BuildingDetail.win` refresh family is disassembly, not a hidden text-publishing row family with missing captions. The grounded
player-facing text sits elsewhere: the fixed express-side triplet `0x7f58..0x7f5a` is table-fed
from RT3.lng `494/497/498`, and the sibling special service rows still align to `Dining Car` and
`Caboose`. So the subflags now have one real shell-side consumer instead of only preservation
logic, and the old “missing label” seam on the indicator rows is gone. The broader
`BuildingDetail.win` refresh family is
tighter too: `shell_building_detail_refresh_subject_cargo_and_service_rows` `0x004ba3d0` now tighter too: `shell_building_detail_refresh_subject_cargo_and_service_rows` `0x004ba3d0` now
clearly owns the selected subject rows around `0x7d06`, `0x7d96..`, and `0x7d0e`, resolving clearly owns the selected subject rows around `0x7d06`, `0x7d96..`, and `0x7d0e`, resolving
ordinary ids through the live candidate collection and the special express-side ids through the ordinary ids through the live candidate collection and the special express-side ids through the
@ -2203,6 +2208,12 @@ Current evidence grounds the shell-controller-backed input and frame path as the
installed by the attach helper, and `+0x0e` is the stable second-site field. One more split is installed by the attach helper, and `+0x0e` is the stable second-site field. One more split is
tighter now too: `placed_structure_route_link_recompute_endpoint_pair_state` `0x00467c30` tighter now too: `placed_structure_route_link_recompute_endpoint_pair_state` `0x00467c30`
currently uses `+0x0a` and `+0x0e` as the active endpoint site pair while recomputing state currently uses `+0x0a` and `+0x0e` as the active endpoint site pair while recomputing state
byte `+0x12`. The load/save boundary is tighter now too: package save currently persists only the
direct indexed-collection header band of `0x006ada90` through `0x00517d90`, world-entry bring-up
mirrors only that header refresh through `0x00518680`, and the live endpoint or grid state is
then regenerated later through `placed_structure_route_link_collection_recompute_all_endpoint_pair_state`
`0x004682c0` and `placed_structure_route_link_collection_rebuild_route_style_grid_counters_and_endpoint_state`
`0x00468300` rather than through a separate tagged per-record payload lane.
byte `+0x12`; `+0x0c` is still not directly read there. The family also has a clearer release byte `+0x12`; `+0x0c` is still not directly read there. The family also has a clearer release
and refresh side now: `placed_structure_route_link_release_and_detach` `0x004680b0` rolls back and refresh side now: `placed_structure_route_link_release_and_detach` `0x004680b0` rolls back
the class counters and then tails into `placed_structure_route_link_detach_current_owner_chain` the class counters and then tails into `placed_structure_route_link_detach_current_owner_chain`
@ -2749,8 +2760,9 @@ Current evidence grounds the shell-controller-backed input and frame path as the
best read as the shared `(x,y) -> world-cell*` resolver rather than another object-specific best read as the shared `(x,y) -> world-cell*` resolver rather than another object-specific
method. method.
The next sibling table at `0x005c9750` is tighter now too, though still not fully decoded. The The next sibling table at `0x005c9750` is tighter now too, though still not fully decoded. The
structural anchor is `map_load_city_database` `0x00474610`: that loader stages bundle tags structural anchor is `city_database_entry_collection_refresh_records_from_tagged_bundle`
`0x61a9..0x61ab`, iterates one collection, and dispatches each entry through vtable slot `+0x44`. `0x00474540`: that load-side owner stages bundle tags `0x61a9..0x61ab`, iterates one
collection, and dispatches each entry through vtable slot `+0x40`.
In `0x005c9750`, that same slot resolves into the sibling record family beside In `0x005c9750`, that same slot resolves into the sibling record family beside
`0x0041ab70/0x0041ab80`, which is enough to treat the table as the city-database entry family `0x0041ab70/0x0041ab80`, which is enough to treat the table as the city-database entry family
rather than a generic unresolved runtime object. The small load-side slot `0x0041ab70` simply rather than a generic unresolved runtime object. The small load-side slot `0x0041ab70` simply
@ -2760,10 +2772,11 @@ Current evidence grounds the shell-controller-backed input and frame path as the
one named handle through owner `0x006d4020`, writes the resulting handle into `[this+0x1c]`, one named handle through owner `0x006d4020`, writes the resulting handle into `[this+0x1c]`,
queries three base floats through `0x0045c480`, and returns one scaled float derived from the queries three base floats through `0x0045c480`, and returns one scaled float derived from the
first queried component. That method is currently called from later world-side branches at first queried component. That method is currently called from later world-side branches at
`0x0046e4f7`, `0x004aafee`, and `0x004ab020`. The exact user-facing semantics of the handle and `0x0046e4f7`, `0x004aafee`, and `0x004ab020`. The exact user-facing semantic of the handle still
the many constant-return virtuals in the same table are still open, but the family boundary is no is not grounded, but the leftover rows in the same table are now bounded as low-value
longer arbitrary: this is now the current city-database entry vtable cluster, not another constant-return stubs rather than a meaningful unresolved behavior seam. The family boundary is
placed-structure specialization. One correction matters here too: the lower therefore no longer arbitrary: this is now the current city-database entry vtable cluster, not
another placed-structure specialization. One correction matters here too: the lower
`0x00455660/0x00455800/0x00455810/0x00455930` helpers are no longer city-only. Local `.rdata` `0x00455660/0x00455800/0x00455810/0x00455930` helpers are no longer city-only. Local `.rdata`
now shows the same shared slots under both the city-entry table `0x005c9750` and the sibling now shows the same shared slots under both the city-entry table `0x005c9750` and the sibling
`Infrastructure` table `0x005cfd00`, whose constructors at `0x0048a240/0x0048a2dc/0x00490a3c` `Infrastructure` table `0x005cfd00`, whose constructors at `0x0048a240/0x0048a2dc/0x00490a3c`
@ -3080,12 +3093,14 @@ The low helper strip beneath that shared family is tighter now too: `0x0052ecd0`
`0x00474110`, allocates one `0x250`-byte live record, resolves it, constructs it through `0x00474110`, allocates one `0x250`-byte live record, resolves it, constructs it through
`0x00474130`, and then tears the temporary entry down through `0x004743d0`; `0x00474510` `0x00474130`, and then tears the temporary entry down through `0x004743d0`; `0x00474510`
resolves one entry id, releases it through `0x00474400`, and removes it from the collection. The resolves one entry id, releases it through `0x00474400`, and removes it from the collection. The
collection-owned load loop at `0x00474540` sits directly beneath `map_load_city_database` collection-owned load loop at `0x00474540` now has a direct save-side companion too:
`0x00474610`: it opens the same `0x61a9/0x61aa/0x61ab` bracket on the caller-supplied bundle, `city_database_entry_collection_serialize_records_into_tagged_bundle` `0x00474610` opens the
binds the selected path context, iterates the current collection, dispatches each record through same `0x61a9/0x61aa/0x61ab` bracket through `0x00531340`, forwards collection metadata through
vtable slot `+0x40`, accumulates the returned byte counts, and tears down the temporary entry `0x00517d90`, and dispatches each record through vtable slot `+0x44`. The refresh side binds the
after each record. That still leaves broader semantic questions open, but the current static edges selected path context, iterates the current collection, dispatches each record through vtable
around the city-entry family are now largely exhausted. The adjacent scaffolding is bounded too. slot `+0x40`, accumulates the returned byte counts, and tears down the temporary entry after each
record. That still leaves broader semantic questions open, but the current static edges around the
city-entry family are now largely exhausted. The adjacent scaffolding is bounded too.
Base helper `0x00455b20` initializes the shared `0x23a`-sized record family by installing base Base helper `0x00455b20` initializes the shared `0x23a`-sized record family by installing base
vtable `0x005cb1c0`, clearing the scalar bands `[+0x206/+0x20a/+0x20e/+0x22e/+0x232]`, zeroing vtable `0x005cb1c0`, clearing the scalar bands `[+0x206/+0x20a/+0x20e/+0x22e/+0x232]`, zeroing
the seven-dword block `[+0x212..+0x22a]`, and re-entering `0x0052ecd0`; that is why it shows up the seven-dword block `[+0x212..+0x22a]`, and re-entering `0x0052ecd0`; that is why it shows up
@ -3093,8 +3108,9 @@ The low helper strip beneath that shared family is tighter now too: `0x0052ecd0`
construction paths outside the city family. The city collection itself now has a bounded teardown construction paths outside the city family. The city collection itself now has a bounded teardown
side too: `0x00474480` is the small release-and-free wrapper for the collection rooted at side too: `0x00474480` is the small release-and-free wrapper for the collection rooted at
`0x005ce4a8`, and teardown caller `0x004492c3` uses it before freeing the global collection `0x005ce4a8`, and teardown caller `0x004492c3` uses it before freeing the global collection
pointer at `0x006cea50`. On the load side, `0x00445713`'s broader setup branch re-enters pointer at `0x006cea50`. On the save side, `0x00445713`'s broader package branch re-enters
`map_load_city_database` `0x00474610` on that same global `0x006cea50` collection. Outside the `city_database_entry_collection_serialize_records_into_tagged_bundle` `0x00474610` on that same
global `0x006cea50` collection. Outside the
city-specific branch, the two tiny dispatch wrappers `0x00455a40` and `0x00455a50` are also now city-specific branch, the two tiny dispatch wrappers `0x00455a40` and `0x00455a50` are also now
bounded as raw vtable-slot helpers: the first jumps through entry slot `+0x44`, while the second bounded as raw vtable-slot helpers: the first jumps through entry slot `+0x44`, while the second
pushes one caller argument into slot `+0x40` and then clears the global roots pushes one caller argument into slot `+0x40` and then clears the global roots

View file

@ -32,6 +32,18 @@ The neighboring connection-state note pair now appears
currently owns only the `Coming To`, `Going From`, `Current Supply`, `Current Demand`, `--None--`, currently owns only the `Coming To`, `Going From`, `Current Supply`, `Current Demand`, `--None--`,
and `All` legend lanes. and `All` legend lanes.
The shell-side preview arm or clear path above that overlay is explicit now too.
`shell_station_detail_clear_active_candidate_service_preview` `0x00504a90` resets the shared
`(station id, candidate id)` pair, drops the local preview latch at `[window+0x7c]`, decrements
the sibling global refcount `0x0062be84`, and on matching live world-preview state tails into
`world_clear_aux_preview_mode_0x2171_and_overlay_companion_live_flag` `0x00452d30`.
`shell_station_detail_set_active_candidate_service_preview` `0x00504ae0` first clears any prior
preview, then for a nonzero candidate id pushes the current station and candidate through the
world-side setter strip `0x00452f60`, `0x00452d80`, `0x00452db0`, and `0x00452ca0` before
latching `[window+0x7c]`, incrementing `0x0062be84`, and storing the active pair back into
`0x005ee4fc/0x005ee500`. That closes the shell-to-world preview seam end-to-end: row callbacks
arm or clear one explicit candidate pair, and the world-side overlay consumes exactly that pair.
That formatter boundary is tighter now too. `0x004207d0` returns `0` immediately when preview-global That formatter boundary is tighter now too. `0x004207d0` returns `0` immediately when preview-global
`0x0062be84` is armed, forwards directly into the shared fallback formatter `0x00455860` when `0x0062be84` is armed, forwards directly into the shared fallback formatter `0x00455860` when
subtype dword `[this+0x23e]` is nonzero, and only otherwise runs its own connection-status path. On subtype dword `[this+0x23e]` is nonzero, and only otherwise runs its own connection-status path. On
@ -110,11 +122,15 @@ The reusable bridge between the status formatter and the
filters and scores candidate city entries, re-enters the same shared heavy builder through filters and scores candidate city entries, re-enters the same shared heavy builder through
`city_connection_try_build_route_between_region_entry_pair` `0x00404c60` for the dense pair `city_connection_try_build_route_between_region_entry_pair` `0x00404c60` for the dense pair
sweep and the final retry, and then publishes `2889` `%1 has started a new company - the %2` sweep and the final retry, and then publishes `2889` `%1 has started a new company - the %2`
or `2890` `%1 has connected %2 to %3.` through the shell news path. The remaining open edge on or `2890` `%1 has connected %2 to %3.` through the shell news path. Wider governance and shell
this branch is therefore narrower now: it is mostly whether `0x39` should be read as the xrefs now close that last `0x39` ambiguity too: `company_compute_prime_rate_from_issue39_scenario_baseline`
narrower city-connection public-opinion lane or as part of a broader management-attitude family, `0x00424580`, the `CompanyDetail` and bond-side notes, and the already-grounded
not the ownership of the connection-bonus formatter, peer-route candidate path, or company news `Prime Rate` label family all point at the same metric, so this city-connection branch is
gate. reusing the explicit issue-`0x39` prime-rate lane rather than a separate public-opinion or
management-attitude family. The local static seam on this branch is therefore closed: `0x39`
itself is no longer ambiguous here, and the remaining work is only how much of the wider
company-start or city-connection ladder above that helper should be described as
recent-profit/fuel-burden pressure versus the already-grounded prime-rate term.
The ordinary `StationDetail.win` caller strip is tighter now too. In the non-scenario, non-class The ordinary `StationDetail.win` caller strip is tighter now too. In the non-scenario, non-class
`3/4` branch, `shell_station_detail_window_refresh_controls` `0x00506610` rebuilds the nearby-site `3/4` branch, `shell_station_detail_window_refresh_controls` `0x00506610` rebuilds the nearby-site
@ -129,8 +145,67 @@ nearby-site helper has a similarly concrete split now: for each of the fixed fiv
counts entries through `0x0047dc90`, emits the descriptive text block through `0x0051b700 -> counts entries through `0x0047dc90`, emits the descriptive text block through `0x0051b700 ->
0x0053f9c0`, and then builds the paired jump-control payload through `0x0053b070 -> 0x0055a040 -> 0x0053f9c0`, and then builds the paired jump-control payload through `0x0053b070 -> 0x0055a040 ->
0x0053f9c0`, with text rows under `0xb40a..0xb40e` and jump controls under `0xb410..0xb414`. So 0x0053f9c0`, with text rows under `0xb40a..0xb40e` and jump controls under `0xb410..0xb414`. So
the remaining uncertainty in the ordinary branch is no longer the ownership of those lanes; it is the local static seam in the ordinary branch is closed: the ownership of those lanes now belongs
the broader user-facing meaning of later callers above the row callbacks. to the concrete refresh helpers named here, and the remaining work is only the broader
user-facing meaning of later callers above the row callbacks.
The helper strip directly beneath that ordinary branch is explicit now too.
`shell_station_detail_clear_dynamic_rows_and_haul_widgets_if_dirty` `0x005042c0` uses dirty latch
`0x006d16f0` plus the live window singleton `0x006d16d8` to clear the candidate-service and
nearby-site row bands `0xb414..0xb5a3`, `0xb40a..0xb413`, and `0xb400..0xb409`, then disables the
paired haul controls `0xb3f6` and `0xb3f7` before resetting the latch. The current-subject gates
above the lower action strip are tighter in the same way:
`shell_station_detail_has_valid_selected_station` `0x00504370` is the narrow selected-station
presence probe over detail-manager state `[0x006d0818+0x90]`, while
`shell_station_detail_selected_station_belongs_to_selected_company` `0x00504390` resolves that same
station and compares its linked company through `placed_structure_query_linked_company_id`
`0x0047efe0` against the active selected company from `0x004337a0`. The ownership-blocked modal
above that gate is explicit too:
`shell_station_detail_present_selected_station_not_controlled_notice` `0x005043f0` formats
localized id `3595` with the owning railroad name when that owner resolves and otherwise falls back
to the empty-string root before opening the standard shell message-box path at `0x004c98a0`. One
remaining local helper is no longer anonymous either:
`shell_station_detail_refresh_class_partitioned_action_controls_0xb3bb_to_0xb3bf` `0x005044b0`
restyles the lower action controls into affordance states `0x65` or `0x87` according to the linked
candidate class byte `[candidate+0x8c]`, keeping `0xb3bb/0xb3bd/0xb3be` active only for class `0`
and `0xb3bc/0xb3bf` active only for class `1`. That closes the helper seam beneath
`shell_station_detail_window_handle_message` `0x00505e50` and
`shell_station_detail_window_refresh_controls` `0x00506610`: the remaining work in this branch is
above those message and refresh owners, not inside the lower helper strip itself. One layer higher,
the lower action dispatch is no longer opaque either. In the message-`0xcb` path,
`shell_station_detail_window_handle_message` first reduces the clicked control to one small family
split: `0xb3bd` selects family `1`, while the sibling controls select family `2`. It then reduces
linked-site flag bits `[entry+0xc3]` to one action index `0..5` and resolves the resulting
`(action_index, family)` pair through
`aux_candidate_collection_find_entry_id_by_station_detail_action_index_and_family` `0x00419590`,
which formats one localized action stem from the fixed tables at `0x005f3c6c/0x005f3c80` and scans
the auxiliary candidate pool `0x0062b2fc` for the first matching stem at `[entry+0x04]`. Only
after that auxiliary id resolves does the branch enter the deeper world-side validation owner
`placed_structure_validate_station_detail_aux_candidate_action_and_optionally_apply_linked_site_mutation`
`0x0040dc40` plus the two confirmation-modal branches rooted at localized ids `0x0bfe` and
`0x0bff`. The constructor-side special-action strip is explicit now too:
`shell_station_detail_window_construct` `0x005068c0` wires action controls `0xb3b7/0xb3b8`,
navigation controls `0xb3b3/0xb3b4`, and helper roots `0x006d16dc..0x006d16ec`; then it restyles
special control `0xb3f8` with one exact three-way split. When editor-map latch `[0x006cec74+0x68]`
is clear, `0xb3f8` is forced to style `0x87`. When that latch is set and scenario slot
`[0x006cec78+0x4af7]` is nonzero, the same control takes style `0x74` if selected-station byte
`[station+0x5c9]` is nonzero and style `0x65` otherwise. So that control is no longer just “some
editor or scenario action”; the local branch now grounds it as one editor-gated, scenario-gated,
selected-station-special-case affordance under the main StationDetail constructor. That deeper
owner then continues into
`0x0bff`. That deeper owner is no longer just a black box either: it first requires the linked peer
at `[site+0x2a8]` to pass the station-or-transit class gate, resolves the auxiliary candidate,
computes one projected scalar cost from candidate field `[candidate+0x22]`, owner company
`[site+0x276]`, current world coords, and scenario-side scale owner `0x00436590(0x3b, ...)`, and
returns that scalar through the optional out-float. When the caller supplies the optional notice
sink and the current owner company exists, the helper compares that scalar against company metric
`(0x0d, 0x2329)` and on failure formats localized notice `0x0b52` before returning. The success
side is tighter too: it materializes one packed projected-cell list through `0x00417840`, runs the
shared placement validator `0x004197e0`, restores the temporary compact-grid replay bits through
`0x004142c0/0x004142d0`, and only then commits the linked-site mutation through
`0x0040d1f0`, `0x00480710`, `0x0045b160`, `0x0045b9b0`, `0x00418be0`, and `0x0040cd70`, posting
the signed scalar into the owner company through `0x0042a040` and accumulating the running total
in `[site+0x27a]`. So the lower action band is now grounded as a real validate-or-apply linked-site
mutation path with an explicit affordability notice branch, not just one anonymous modal gate.
#### Route-list side #### Route-list side
The neighboring helper The neighboring helper
@ -138,9 +213,10 @@ The neighboring helper
append-if-missing builder for the six-byte route-entry list rooted at `[site+0x462]` and append-if-missing builder for the six-byte route-entry list rooted at `[site+0x462]` and
`[site+0x466]`. One tighter boundary closes the overlay-side uncertainty further: the directional `[site+0x466]`. One tighter boundary closes the overlay-side uncertainty further: the directional
overlay query at `0x0047e690` only consumes the leading `u16` peer-site key when it walks that overlay query at `0x0047e690` only consumes the leading `u16` peer-site key when it walks that
list and does not currently read the trailing `u32` companion payload at all. So the remaining list and does not currently read the trailing `u32` companion payload at all. So the local
uncertainty is no longer route-list ownership or overlay dependence on that payload; it is only overlay-side seam is closed: route-list ownership and overlay dependence on that payload are no
the broader meaning of the payload for other caller families. longer open here, and the remaining work is only the broader meaning of the payload for other
caller families.
#### Route-entry and Cache Side #### Route-entry and Cache Side
The adjacent helper strip is tighter now too. The adjacent helper strip is tighter now too.
@ -179,6 +255,6 @@ The nearby linked-instance raster wrappers are no
`0x0047f0e0` and `0x0047f0e0` and
`placed_structure_resolve_linked_instance_secondary_raster_class_record_via_world_query` `placed_structure_resolve_linked_instance_secondary_raster_class_record_via_world_query`
`0x0047f1f0` now bound the two record-returning lanes used by the later world-side and `0x0047f1f0` now bound the two record-returning lanes used by the later world-side and
linked-peer filters. That means the remaining uncertainty in this neighborhood is no longer the linked-peer filters. So the local static seam in this neighborhood is closed too: route-entry and
route-entry or terrain-class helper ownership; it is the broader user-facing meaning of the later terrain-class helper ownership are no longer open here, and the remaining work is only the
callers that consume those gates. broader user-facing meaning of the later callers that consume those gates.